Eliminate GPUI View, ViewContext, and WindowContext types (#22632)

There's still a bit more work to do on this, but this PR is compiling
(with warnings) after eliminating the key types. When the tasks below
are complete, this will be the new narrative for GPUI:

- `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit
of state, and if `T` implements `Render`, then `Entity<T>` implements
`Element`.
- `&mut App` This replaces `AppContext` and represents the app.
- `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It
is provided by the framework when updating an entity.
- `&mut Window` Broken out of `&mut WindowContext` which no longer
exists. Every method that once took `&mut WindowContext` now takes `&mut
Window, &mut App` and every method that took `&mut ViewContext<T>` now
takes `&mut Window, &mut Context<T>`

Not pictured here are the two other failed attempts. It's been quite a
month!

Tasks:

- [x] Remove `View`, `ViewContext`, `WindowContext` and thread through
`Window`
- [x] [@cole-miller @mikayla-maki] Redraw window when entities change
- [x] [@cole-miller @mikayla-maki] Get examples and Zed running
- [x] [@cole-miller @mikayla-maki] Fix Zed rendering
- [x] [@mikayla-maki] Fix todo! macros and comments
- [x] Fix a bug where the editor would not be redrawn because of view
caching
- [x] remove publicness window.notify() and replace with
`AppContext::notify`
- [x] remove `observe_new_window_models`, replace with
`observe_new_models` with an optional window
- [x] Fix a bug where the project panel would not be redrawn because of
the wrong refresh() call being used
- [x] Fix the tests
- [x] Fix warnings by eliminating `Window` params or using `_`
- [x] Fix conflicts
- [x] Simplify generic code where possible
- [x] Rename types
- [ ] Update docs

### issues post merge

- [x] Issues switching between normal and insert mode
- [x] Assistant re-rendering failure
- [x] Vim test failures
- [x] Mac build issue



Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Joseph <joseph@zed.dev>
Co-authored-by: max <max@zed.dev>
Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local>
Co-authored-by: Mikayla <mikayla.c.maki@gmail.com>
Co-authored-by: joão <joao@zed.dev>
This commit is contained in:
Nathan Sobo 2025-01-25 20:02:45 -07:00 committed by GitHub
parent 21b4a0d50e
commit 6fca1d2b0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
648 changed files with 36248 additions and 28208 deletions

View file

@ -1,21 +1,26 @@
use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, Bias, Direction, Editor};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use crate::{state::Mode, Vim};
actions!(vim, [ChangeListOlder, ChangeListNewer]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &ChangeListOlder, cx| {
vim.move_to_change(Direction::Prev, cx);
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &ChangeListOlder, window, cx| {
vim.move_to_change(Direction::Prev, window, cx);
});
Vim::action(editor, cx, |vim, _: &ChangeListNewer, cx| {
vim.move_to_change(Direction::Next, cx);
Vim::action(editor, cx, |vim, _: &ChangeListNewer, window, cx| {
vim.move_to_change(Direction::Next, window, cx);
});
}
impl Vim {
fn move_to_change(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
fn move_to_change(
&mut self,
direction: Direction,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count = Vim::take_count(cx).unwrap_or(1);
if self.change_list.is_empty() {
return;
@ -31,8 +36,8 @@ impl Vim {
let Some(selections) = self.change_list.get(next).cloned() else {
return;
};
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
let map = s.display_map();
s.select_display_ranges(selections.into_iter().map(|a| {
let point = a.to_display_point(&map);
@ -42,8 +47,8 @@ impl Vim {
});
}
pub(crate) fn push_to_change_list(&mut self, cx: &mut ViewContext<Self>) {
let Some((map, selections)) = self.update_editor(cx, |_, editor, cx| {
pub(crate) fn push_to_change_list(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some((map, selections)) = self.update_editor(window, cx, |_, editor, _, cx| {
editor.selections.all_adjusted_display(cx)
}) else {
return;

View file

@ -7,9 +7,7 @@ use editor::{
scroll::Autoscroll,
Bias, Editor, ToPoint,
};
use gpui::{
actions, impl_internal_actions, Action, AppContext, Global, ViewContext, WindowContext,
};
use gpui::{actions, impl_internal_actions, Action, App, Context, Global, Window};
use language::Point;
use multi_buffer::MultiBufferRow;
use regex::Regex;
@ -101,27 +99,27 @@ impl Deref for WrappedAction {
}
}
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &VisualCommand, cx| {
let Some(workspace) = vim.workspace(cx) else {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &VisualCommand, window, cx| {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
command_palette::CommandPalette::toggle(workspace, "'<,'>", cx);
command_palette::CommandPalette::toggle(workspace, "'<,'>", window, cx);
})
});
Vim::action(editor, cx, |vim, _: &ShellCommand, cx| {
let Some(workspace) = vim.workspace(cx) else {
Vim::action(editor, cx, |vim, _: &ShellCommand, window, cx| {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
command_palette::CommandPalette::toggle(workspace, "'<,'>!", cx);
command_palette::CommandPalette::toggle(workspace, "'<,'>!", window, cx);
})
});
Vim::action(editor, cx, |vim, _: &CountCommand, cx| {
let Some(workspace) = vim.workspace(cx) else {
Vim::action(editor, cx, |vim, _: &CountCommand, window, cx| {
let Some(workspace) = vim.workspace(window) else {
return;
};
let count = Vim::take_count(cx).unwrap_or(1);
@ -131,27 +129,27 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
".".to_string()
};
workspace.update(cx, |workspace, cx| {
command_palette::CommandPalette::toggle(workspace, &n, cx);
command_palette::CommandPalette::toggle(workspace, &n, window, cx);
})
});
Vim::action(editor, cx, |vim, action: &GoToLine, cx| {
vim.switch_mode(Mode::Normal, false, cx);
let result = vim.update_editor(cx, |vim, editor, cx| {
let snapshot = editor.snapshot(cx);
let buffer_row = action.range.head().buffer_row(vim, editor, cx)?;
Vim::action(editor, cx, |vim, action: &GoToLine, window, cx| {
vim.switch_mode(Mode::Normal, false, window, cx);
let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let buffer_row = action.range.head().buffer_row(vim, editor, window, cx)?;
let current = editor.selections.newest::<Point>(cx);
let target = snapshot
.buffer_snapshot
.clip_point(Point::new(buffer_row.0, current.head().column), Bias::Left);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges([target..target]);
});
anyhow::Ok(())
});
if let Some(e @ Err(_)) = result {
let Some(workspace) = vim.workspace(cx) else {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
@ -161,10 +159,10 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
}
});
Vim::action(editor, cx, |vim, action: &YankCommand, cx| {
vim.update_editor(cx, |vim, editor, cx| {
let snapshot = editor.snapshot(cx);
if let Ok(range) = action.range.buffer_range(vim, editor, cx) {
Vim::action(editor, cx, |vim, action: &YankCommand, window, cx| {
vim.update_editor(window, cx, |vim, editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
if let Ok(range) = action.range.buffer_range(vim, editor, window, cx) {
let end = if range.end < snapshot.buffer_snapshot.max_row() {
Point::new(range.end.0 + 1, 0)
} else {
@ -181,21 +179,21 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
});
});
Vim::action(editor, cx, |_, action: &WithCount, cx| {
Vim::action(editor, cx, |_, action: &WithCount, window, cx| {
for _ in 0..action.count {
cx.dispatch_action(action.action.boxed_clone())
window.dispatch_action(action.action.boxed_clone(), cx)
}
});
Vim::action(editor, cx, |vim, action: &WithRange, cx| {
let result = vim.update_editor(cx, |vim, editor, cx| {
action.range.buffer_range(vim, editor, cx)
Vim::action(editor, cx, |vim, action: &WithRange, window, cx| {
let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
action.range.buffer_range(vim, editor, window, cx)
});
let range = match result {
None => return,
Some(e @ Err(_)) => {
let Some(workspace) = vim.workspace(cx) else {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
@ -207,24 +205,24 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
};
let previous_selections = vim
.update_editor(cx, |_, editor, cx| {
.update_editor(window, cx, |_, editor, window, cx| {
let selections = action.restore_selection.then(|| {
editor
.selections
.disjoint_anchor_ranges()
.collect::<Vec<_>>()
});
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
let end = Point::new(range.end.0, s.buffer().line_len(range.end));
s.select_ranges([end..Point::new(range.start.0, 0)]);
});
selections
})
.flatten();
cx.dispatch_action(action.action.boxed_clone());
cx.defer(move |vim, cx| {
vim.update_editor(cx, |_, editor, cx| {
editor.change_selections(None, cx, |s| {
window.dispatch_action(action.action.boxed_clone(), cx);
cx.defer_in(window, move |vim, window, cx| {
vim.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(None, window, cx, |s| {
if let Some(previous_selections) = previous_selections {
s.select_ranges(previous_selections);
} else {
@ -237,12 +235,12 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
});
});
Vim::action(editor, cx, |vim, action: &OnMatchingLines, cx| {
action.run(vim, cx)
Vim::action(editor, cx, |vim, action: &OnMatchingLines, window, cx| {
action.run(vim, window, cx)
});
Vim::action(editor, cx, |vim, action: &ShellExec, cx| {
action.run(vim, cx)
Vim::action(editor, cx, |vim, action: &ShellExec, window, cx| {
action.run(vim, window, cx)
})
}
@ -306,7 +304,7 @@ impl VimCommand {
&self,
mut query: &str,
range: &Option<CommandRange>,
cx: &AppContext,
cx: &App,
) -> Option<Box<dyn Action>> {
let has_bang = query.ends_with('!');
if has_bang {
@ -463,9 +461,10 @@ impl Position {
&self,
vim: &Vim,
editor: &mut Editor,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Result<MultiBufferRow> {
let snapshot = editor.snapshot(cx);
let snapshot = editor.snapshot(window, cx);
let target = match self {
Position::Line { row, offset } => {
if let Some(anchor) = editor.active_excerpt(cx).and_then(|(_, buffer, _)| {
@ -524,11 +523,12 @@ impl CommandRange {
&self,
vim: &Vim,
editor: &mut Editor,
cx: &mut WindowContext,
window: &mut Window,
cx: &mut App,
) -> Result<Range<MultiBufferRow>> {
let start = self.start.buffer_row(vim, editor, cx)?;
let start = self.start.buffer_row(vim, editor, window, cx)?;
let end = if let Some(end) = self.end.as_ref() {
end.buffer_row(vim, editor, cx)?
end.buffer_row(vim, editor, window, cx)?
} else {
start
};
@ -552,7 +552,7 @@ impl CommandRange {
}
}
fn generate_commands(_: &AppContext) -> Vec<VimCommand> {
fn generate_commands(_: &App) -> Vec<VimCommand> {
vec![
VimCommand::new(
("w", "rite"),
@ -758,7 +758,7 @@ struct VimCommands(Vec<VimCommand>);
unsafe impl Sync for VimCommands {}
impl Global for VimCommands {}
fn commands(cx: &AppContext) -> &Vec<VimCommand> {
fn commands(cx: &App) -> &Vec<VimCommand> {
static COMMANDS: OnceLock<VimCommands> = OnceLock::new();
&COMMANDS
.get_or_init(|| VimCommands(generate_commands(cx)))
@ -797,7 +797,7 @@ fn wrap_count(action: Box<dyn Action>, range: &CommandRange) -> Option<Box<dyn A
})
}
pub fn command_interceptor(mut input: &str, cx: &AppContext) -> Option<CommandInterceptResult> {
pub fn command_interceptor(mut input: &str, cx: &App) -> Option<CommandInterceptResult> {
// NOTE: We also need to support passing arguments to commands like :w
// (ideally with filename autocompletion).
while input.starts_with(':') {
@ -939,7 +939,7 @@ impl OnMatchingLines {
mut chars: Peekable<Chars>,
invert: bool,
range: CommandRange,
cx: &AppContext,
cx: &App,
) -> Option<Self> {
let delimiter = chars.next().filter(|c| {
!c.is_alphanumeric() && *c != '"' && *c != '|' && *c != '\'' && *c != '!'
@ -981,15 +981,15 @@ impl OnMatchingLines {
})
}
pub fn run(&self, vim: &mut Vim, cx: &mut ViewContext<Vim>) {
let result = vim.update_editor(cx, |vim, editor, cx| {
self.range.buffer_range(vim, editor, cx)
pub fn run(&self, vim: &mut Vim, window: &mut Window, cx: &mut Context<Vim>) {
let result = vim.update_editor(window, cx, |vim, editor, window, cx| {
self.range.buffer_range(vim, editor, window, cx)
});
let range = match result {
None => return,
Some(e @ Err(_)) => {
let Some(workspace) = vim.workspace(cx) else {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
@ -1006,7 +1006,7 @@ impl OnMatchingLines {
let mut regexes = match Regex::new(&self.search) {
Ok(regex) => vec![(regex, !self.invert)],
e @ Err(_) => {
let Some(workspace) = vim.workspace(cx) else {
let Some(workspace) = vim.workspace(window) else {
return;
};
workspace.update(cx, |workspace, cx| {
@ -1028,15 +1028,16 @@ impl OnMatchingLines {
regexes.push((regex, !inner.invert))
}
if let Some(pane) = vim.pane(cx) {
if let Some(pane) = vim.pane(window, cx) {
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()
{
search_bar.update(cx, |search_bar, cx| {
if search_bar.show(cx) {
if search_bar.show(window, cx) {
let _ = search_bar.search(
&last_pattern,
Some(SearchOptions::REGEX | SearchOptions::CASE_SENSITIVE),
window,
cx,
);
}
@ -1045,15 +1046,15 @@ impl OnMatchingLines {
});
};
vim.update_editor(cx, |_, editor, cx| {
let snapshot = editor.snapshot(cx);
vim.update_editor(window, cx, |_, editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let mut row = range.start.0;
let point_range = Point::new(range.start.0, 0)
..snapshot
.buffer_snapshot
.clip_point(Point::new(range.end.0 + 1, 0), Bias::Left);
cx.spawn(|editor, mut cx| async move {
cx.spawn_in(window, |editor, mut cx| async move {
let new_selections = cx
.background_executor()
.spawn(async move {
@ -1088,15 +1089,15 @@ impl OnMatchingLines {
return;
}
editor
.update(&mut cx, |editor, cx| {
editor.start_transaction_at(Instant::now(), cx);
editor.change_selections(None, cx, |s| {
.update_in(&mut cx, |editor, window, cx| {
editor.start_transaction_at(Instant::now(), window, cx);
editor.change_selections(None, window, cx, |s| {
s.replace_cursors_with(|_| new_selections);
});
cx.dispatch_action(action);
cx.defer(move |editor, cx| {
window.dispatch_action(action, cx);
cx.defer_in(window, move |editor, window, cx| {
let newest = editor.selections.newest::<Point>(cx).clone();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.select(vec![newest]);
});
editor.end_transaction_at(Instant::now(), cx);
@ -1117,17 +1118,22 @@ pub struct ShellExec {
}
impl Vim {
pub fn cancel_running_command(&mut self, cx: &mut ViewContext<Self>) {
pub fn cancel_running_command(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.running_command.take().is_some() {
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, _| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, _window, _cx| {
editor.clear_row_highlights::<ShellExec>();
})
});
}
}
fn prepare_shell_command(&mut self, command: &str, cx: &mut ViewContext<Self>) -> String {
fn prepare_shell_command(
&mut self,
command: &str,
window: &mut Window,
cx: &mut Context<Self>,
) -> String {
let mut ret = String::new();
// N.B. non-standard escaping rules:
// * !echo % => "echo README.md"
@ -1145,7 +1151,7 @@ impl Vim {
}
match c {
'%' => {
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, _window, cx| {
if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
if let Some(file) = buffer.read(cx).file() {
if let Some(local) = file.as_local() {
@ -1173,21 +1179,22 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Vim>,
window: &mut Window,
cx: &mut Context<Vim>,
) {
self.stop_recording(cx);
let Some(workspace) = self.workspace(cx) else {
let Some(workspace) = self.workspace(window) else {
return;
};
let command = self.update_editor(cx, |_, editor, cx| {
let snapshot = editor.snapshot(cx);
let command = self.update_editor(window, cx, |_, editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let start = editor.selections.newest_display(cx);
let text_layout_details = editor.text_layout_details(cx);
let text_layout_details = editor.text_layout_details(window);
let mut range = motion
.range(&snapshot, start.clone(), times, false, &text_layout_details)
.unwrap_or(start.range());
if range.start != start.start {
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.select_ranges([
range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
]);
@ -1204,7 +1211,7 @@ impl Vim {
});
if let Some(command) = command {
workspace.update(cx, |workspace, cx| {
command_palette::CommandPalette::toggle(workspace, &command, cx);
command_palette::CommandPalette::toggle(workspace, &command, window, cx);
});
}
}
@ -1213,20 +1220,21 @@ impl Vim {
&mut self,
object: Object,
around: bool,
cx: &mut ViewContext<Vim>,
window: &mut Window,
cx: &mut Context<Vim>,
) {
self.stop_recording(cx);
let Some(workspace) = self.workspace(cx) else {
let Some(workspace) = self.workspace(window) else {
return;
};
let command = self.update_editor(cx, |_, editor, cx| {
let snapshot = editor.snapshot(cx);
let command = self.update_editor(window, cx, |_, editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let start = editor.selections.newest_display(cx);
let range = object
.range(&snapshot, start.clone(), around)
.unwrap_or(start.range());
if range.start != start.start {
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.select_ranges([
range.start.to_point(&snapshot)..range.start.to_point(&snapshot)
]);
@ -1240,7 +1248,7 @@ impl Vim {
});
if let Some(command) = command {
workspace.update(cx, |workspace, cx| {
command_palette::CommandPalette::toggle(workspace, &command, cx);
command_palette::CommandPalette::toggle(workspace, &command, window, cx);
});
}
}
@ -1265,13 +1273,13 @@ impl ShellExec {
)
}
pub fn run(&self, vim: &mut Vim, cx: &mut ViewContext<Vim>) {
let Some(workspace) = vim.workspace(cx) else {
pub fn run(&self, vim: &mut Vim, window: &mut Window, cx: &mut Context<Vim>) {
let Some(workspace) = vim.workspace(window) else {
return;
};
let project = workspace.read(cx).project().clone();
let command = vim.prepare_shell_command(&self.command, cx);
let command = vim.prepare_shell_command(&self.command, window, cx);
if self.range.is_none() && !self.is_read {
workspace.update(cx, |workspace, cx| {
@ -1305,10 +1313,10 @@ impl ShellExec {
let mut input_snapshot = None;
let mut input_range = None;
let mut needs_newline_prefix = false;
vim.update_editor(cx, |vim, editor, cx| {
vim.update_editor(window, cx, |vim, editor, window, cx| {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let range = if let Some(range) = self.range.clone() {
let Some(range) = range.buffer_range(vim, editor, cx).log_err() else {
let Some(range) = range.buffer_range(vim, editor, window, cx).log_err() else {
return;
};
Point::new(range.start.0, 0)
@ -1364,10 +1372,10 @@ impl ShellExec {
};
let is_read = self.is_read;
let task = cx.spawn(|vim, mut cx| async move {
let task = cx.spawn_in(window, |vim, mut cx| async move {
let Some(mut running) = process.spawn().log_err() else {
vim.update(&mut cx, |vim, cx| {
vim.cancel_running_command(cx);
vim.update_in(&mut cx, |vim, window, cx| {
vim.cancel_running_command(window, cx);
})
.log_err();
return;
@ -1395,8 +1403,8 @@ impl ShellExec {
.await;
let Some(output) = output.log_err() else {
vim.update(&mut cx, |vim, cx| {
vim.cancel_running_command(cx);
vim.update_in(&mut cx, |vim, window, cx| {
vim.cancel_running_command(window, cx);
})
.log_err();
return;
@ -1411,12 +1419,12 @@ impl ShellExec {
text.push('\n');
}
vim.update(&mut cx, |vim, cx| {
vim.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
vim.update_in(&mut cx, |vim, window, cx| {
vim.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.edit([(range.clone(), text)], cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
let point = if is_read {
let point = range.end.to_point(&snapshot);
Point::new(point.row.saturating_sub(1), 0)
@ -1428,7 +1436,7 @@ impl ShellExec {
})
})
});
vim.cancel_running_command(cx);
vim.cancel_running_command(window, cx);
})
.log_err();
});
@ -1445,9 +1453,8 @@ mod test {
test::{NeovimBackedTestContext, VimTestContext},
};
use editor::Editor;
use gpui::TestAppContext;
use gpui::{Context, TestAppContext};
use indoc::indoc;
use ui::ViewContext;
use workspace::Workspace;
#[gpui::test]
@ -1545,7 +1552,7 @@ mod test {
async fn test_command_write(cx: &mut TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
let path = Path::new("/root/dir/file.rs");
let fs = cx.workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
cx.simulate_keystrokes("i @ escape");
cx.simulate_keystrokes(": w enter");
@ -1573,13 +1580,13 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
cx.simulate_keystrokes(": n e w enter");
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.simulate_keystrokes(": q enter");
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 1));
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 1));
cx.simulate_keystrokes(": n e w enter");
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.simulate_keystrokes(": q a enter");
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 0));
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 0));
}
#[gpui::test]
@ -1641,7 +1648,7 @@ mod test {
workspace: &mut Workspace,
expected_path: &str,
expected_text: &str,
cx: &mut ViewContext<Workspace>,
cx: &mut Context<Workspace>,
) {
let active_editor = workspace.active_item_as::<Editor>(cx).unwrap();
@ -1665,12 +1672,12 @@ mod test {
let mut cx = VimTestContext::new(cx, true).await;
// Assert base state, that we're in /root/dir/file.rs
cx.workspace(|workspace, cx| {
cx.workspace(|workspace, _, cx| {
assert_active_item(workspace, "/root/dir/file.rs", "", cx);
});
// Insert a new file
let fs = cx.workspace(|workspace, cx| workspace.project().read(cx).fs().clone());
let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone());
fs.as_fake()
.insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec())
.await;
@ -1685,13 +1692,14 @@ mod test {
cx.simulate_keystrokes("g f");
// We now have two items
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.workspace(|workspace, cx| {
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2));
cx.workspace(|workspace, _, cx| {
assert_active_item(workspace, "/root/dir/file2.rs", "This is file2.rs", cx);
});
// Update editor to point to `file2.rs`
cx.editor = cx.workspace(|workspace, cx| workspace.active_item_as::<Editor>(cx).unwrap());
cx.editor =
cx.workspace(|workspace, _, cx| workspace.active_item_as::<Editor>(cx).unwrap());
// Put the path to the third file into the currently open buffer,
// but remove its suffix, because we want that lookup to happen automatically.
@ -1701,8 +1709,8 @@ mod test {
cx.simulate_keystrokes("g f");
// We now have three items
cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 3));
cx.workspace(|workspace, cx| {
cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 3));
cx.workspace(|workspace, _, cx| {
assert_active_item(workspace, "/root/dir/file3.rs", "go to file3", cx);
});
}

View file

@ -2,12 +2,11 @@ use std::sync::Arc;
use collections::HashMap;
use editor::Editor;
use gpui::{impl_actions, AppContext, Keystroke, KeystrokeEvent};
use gpui::{impl_actions, App, Context, Keystroke, KeystrokeEvent, Window};
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::LazyLock;
use ui::ViewContext;
use crate::{state::Operator, Vim, VimSettings};
@ -17,7 +16,7 @@ mod default;
struct Literal(String, char);
impl_actions!(vim, [Literal]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::literal)
}
@ -31,7 +30,7 @@ static DEFAULT_DIGRAPHS_MAP: LazyLock<HashMap<String, Arc<str>>> = LazyLock::new
map
});
fn lookup_digraph(a: char, b: char, cx: &AppContext) -> Arc<str> {
fn lookup_digraph(a: char, b: char, cx: &App) -> Arc<str> {
let custom_digraphs = &VimSettings::get_global(cx).custom_digraphs;
let input = format!("{a}{b}");
let reversed = format!("{b}{a}");
@ -50,38 +49,42 @@ impl Vim {
&mut self,
first_char: char,
second_char: char,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let text = lookup_digraph(first_char, second_char, cx);
self.pop_operator(cx);
self.pop_operator(window, cx);
if self.editor_input_enabled() {
self.update_editor(cx, |_, editor, cx| editor.insert(&text, cx));
self.update_editor(window, cx, |_, editor, window, cx| {
editor.insert(&text, window, cx)
});
} else {
self.input_ignored(text, cx);
self.input_ignored(text, window, cx);
}
}
fn literal(&mut self, action: &Literal, cx: &mut ViewContext<Self>) {
fn literal(&mut self, action: &Literal, window: &mut Window, cx: &mut Context<Self>) {
if let Some(Operator::Literal { prefix }) = self.active_operator() {
if let Some(prefix) = prefix {
if let Some(keystroke) = Keystroke::parse(&action.0).ok() {
cx.window_context().defer(|cx| {
cx.dispatch_keystroke(keystroke);
window.defer(cx, |window, cx| {
window.dispatch_keystroke(keystroke, cx);
});
}
return self.handle_literal_input(prefix, "", cx);
return self.handle_literal_input(prefix, "", window, cx);
}
}
self.insert_literal(Some(action.1), "", cx);
self.insert_literal(Some(action.1), "", window, cx);
}
pub fn handle_literal_keystroke(
&mut self,
keystroke_event: &KeystrokeEvent,
prefix: String,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
// handled by handle_literal_input
if keystroke_event.keystroke.key_char.is_some() {
@ -89,17 +92,17 @@ impl Vim {
};
if prefix.len() > 0 {
self.handle_literal_input(prefix, "", cx);
self.handle_literal_input(prefix, "", window, cx);
} else {
self.pop_operator(cx);
self.pop_operator(window, cx);
}
// give another chance to handle the binding outside
// of waiting mode.
if keystroke_event.action.is_none() {
let keystroke = keystroke_event.keystroke.clone();
cx.window_context().defer(|cx| {
cx.dispatch_keystroke(keystroke);
window.defer(cx, |window, cx| {
window.dispatch_keystroke(keystroke, cx);
});
}
return;
@ -109,7 +112,8 @@ impl Vim {
&mut self,
mut prefix: String,
text: &str,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let first = prefix.chars().next();
let next = text.chars().next().unwrap_or(' ');
@ -119,7 +123,7 @@ impl Vim {
prefix.push(next);
if prefix.len() == 4 {
let ch: char = u8::from_str_radix(&prefix[1..], 8).unwrap_or(255).into();
return self.insert_literal(Some(ch), "", cx);
return self.insert_literal(Some(ch), "", window, cx);
}
} else {
let ch = if prefix.len() > 1 {
@ -127,7 +131,7 @@ impl Vim {
} else {
None
};
return self.insert_literal(ch, text, cx);
return self.insert_literal(ch, text, window, cx);
}
}
Some('x' | 'X' | 'u' | 'U') => {
@ -145,7 +149,7 @@ impl Vim {
.ok()
.and_then(|n| n.try_into().ok())
.unwrap_or('\u{FFFD}');
return self.insert_literal(Some(ch), "", cx);
return self.insert_literal(Some(ch), "", window, cx);
}
} else {
let ch = if prefix.len() > 1 {
@ -158,7 +162,7 @@ impl Vim {
} else {
None
};
return self.insert_literal(ch, text, cx);
return self.insert_literal(ch, text, window, cx);
}
}
Some('0'..='9') => {
@ -166,32 +170,39 @@ impl Vim {
prefix.push(next);
if prefix.len() == 3 {
let ch: char = u8::from_str_radix(&prefix, 10).unwrap_or(255).into();
return self.insert_literal(Some(ch), "", cx);
return self.insert_literal(Some(ch), "", window, cx);
}
} else {
let ch: char = u8::from_str_radix(&prefix, 10).unwrap_or(255).into();
return self.insert_literal(Some(ch), "", cx);
return self.insert_literal(Some(ch), "", window, cx);
}
}
None if matches!(next, 'o' | 'O' | 'x' | 'X' | 'u' | 'U' | '0'..='9') => {
prefix.push(next)
}
_ => {
return self.insert_literal(None, text, cx);
return self.insert_literal(None, text, window, cx);
}
};
self.pop_operator(cx);
self.pop_operator(window, cx);
self.push_operator(
Operator::Literal {
prefix: Some(prefix),
},
window,
cx,
);
}
fn insert_literal(&mut self, ch: Option<char>, suffix: &str, cx: &mut ViewContext<Self>) {
self.pop_operator(cx);
fn insert_literal(
&mut self,
ch: Option<char>,
suffix: &str,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.pop_operator(window, cx);
let mut text = String::new();
if let Some(c) = ch {
if c == '\n' {
@ -203,9 +214,11 @@ impl Vim {
text.push_str(suffix);
if self.editor_input_enabled() {
self.update_editor(cx, |_, editor, cx| editor.insert(&text, cx));
self.update_editor(window, cx, |_, editor, window, cx| {
editor.insert(&text, window, cx)
});
} else {
self.input_ignored(text.into(), cx);
self.input_ignored(text.into(), window, cx);
}
}
}

View file

@ -1,26 +1,31 @@
use editor::{movement, scroll::Autoscroll, DisplayPoint, Editor};
use gpui::{actions, Action};
use gpui::{Context, Window};
use language::{CharClassifier, CharKind};
use ui::ViewContext;
use crate::{motion::Motion, state::Mode, Vim};
actions!(vim, [HelixNormalAfter, HelixDelete]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::helix_normal_after);
Vim::action(editor, cx, Vim::helix_delete);
}
impl Vim {
pub fn helix_normal_after(&mut self, action: &HelixNormalAfter, cx: &mut ViewContext<Self>) {
pub fn helix_normal_after(
&mut self,
action: &HelixNormalAfter,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.active_operator().is_some() {
self.operator_stack.clear();
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
return;
}
self.stop_recording_immediately(action.boxed_clone(), cx);
self.switch_mode(Mode::HelixNormal, false, cx);
self.switch_mode(Mode::HelixNormal, false, window, cx);
return;
}
@ -28,19 +33,21 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.helix_move_cursor(motion, times, cx);
self.helix_move_cursor(motion, times, window, cx);
}
fn helix_find_range_forward(
&mut self,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
) {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let times = times.unwrap_or(1);
@ -88,11 +95,12 @@ impl Vim {
fn helix_find_range_backward(
&mut self,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
) {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let times = times.unwrap_or(1);
@ -145,11 +153,12 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let goal = selection.goal;
let cursor = if selection.is_empty() || selection.reversed {
@ -172,11 +181,12 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
match motion {
Motion::NextWordStart { ignore_punctuation } => {
self.helix_find_range_forward(times, cx, |left, right, classifier| {
self.helix_find_range_forward(times, window, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
@ -188,7 +198,7 @@ impl Vim {
})
}
Motion::NextWordEnd { ignore_punctuation } => {
self.helix_find_range_forward(times, cx, |left, right, classifier| {
self.helix_find_range_forward(times, window, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
@ -200,7 +210,7 @@ impl Vim {
})
}
Motion::PreviousWordStart { ignore_punctuation } => {
self.helix_find_range_backward(times, cx, |left, right, classifier| {
self.helix_find_range_backward(times, window, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
@ -212,7 +222,7 @@ impl Vim {
})
}
Motion::PreviousWordEnd { ignore_punctuation } => {
self.helix_find_range_backward(times, cx, |left, right, classifier| {
self.helix_find_range_backward(times, window, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
@ -224,18 +234,18 @@ impl Vim {
found
})
}
_ => self.helix_move_and_collapse(motion, times, cx),
_ => self.helix_move_and_collapse(motion, times, window, cx),
}
}
pub fn helix_delete(&mut self, _: &HelixDelete, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
pub fn helix_delete(&mut self, _: &HelixDelete, window: &mut Window, cx: &mut Context<Self>) {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
// Fixup selections so they have helix's semantics.
// Specifically:
// - Make sure that each cursor acts as a 1 character wide selection
editor.transact(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.transact(window, cx, |editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
if selection.is_empty() && !selection.reversed {
selection.end = movement::right(map, selection.end);
@ -245,7 +255,7 @@ impl Vim {
});
vim.copy_selections_content(editor, false, cx);
editor.insert("", cx);
editor.insert("", window, cx);
});
}
}

View file

@ -2,8 +2,8 @@ use crate::{motion::Motion, object::Object, state::Mode, Vim};
use collections::HashMap;
use editor::{display_map::ToDisplayPoint, Bias, Editor};
use gpui::actions;
use gpui::{Context, Window};
use language::SelectionGoal;
use ui::ViewContext;
#[derive(PartialEq, Eq)]
pub(crate) enum IndentDirection {
@ -14,58 +14,58 @@ pub(crate) enum IndentDirection {
actions!(vim, [Indent, Outdent, AutoIndent]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Indent, cx| {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &Indent, window, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
vim.store_visual_marks(cx);
vim.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
vim.store_visual_marks(window, cx);
vim.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let original_positions = vim.save_selection_starts(editor, cx);
for _ in 0..count {
editor.indent(&Default::default(), cx);
editor.indent(&Default::default(), window, cx);
}
vim.restore_selection_cursors(editor, cx, original_positions);
vim.restore_selection_cursors(editor, window, cx, original_positions);
});
});
if vim.mode.is_visual() {
vim.switch_mode(Mode::Normal, true, cx)
vim.switch_mode(Mode::Normal, true, window, cx)
}
});
Vim::action(editor, cx, |vim, _: &Outdent, cx| {
Vim::action(editor, cx, |vim, _: &Outdent, window, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
vim.store_visual_marks(cx);
vim.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
vim.store_visual_marks(window, cx);
vim.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let original_positions = vim.save_selection_starts(editor, cx);
for _ in 0..count {
editor.outdent(&Default::default(), cx);
editor.outdent(&Default::default(), window, cx);
}
vim.restore_selection_cursors(editor, cx, original_positions);
vim.restore_selection_cursors(editor, window, cx, original_positions);
});
});
if vim.mode.is_visual() {
vim.switch_mode(Mode::Normal, true, cx)
vim.switch_mode(Mode::Normal, true, window, cx)
}
});
Vim::action(editor, cx, |vim, _: &AutoIndent, cx| {
Vim::action(editor, cx, |vim, _: &AutoIndent, window, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
vim.store_visual_marks(cx);
vim.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
vim.store_visual_marks(window, cx);
vim.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let original_positions = vim.save_selection_starts(editor, cx);
for _ in 0..count {
editor.autoindent(&Default::default(), cx);
editor.autoindent(&Default::default(), window, cx);
}
vim.restore_selection_cursors(editor, cx, original_positions);
vim.restore_selection_cursors(editor, window, cx, original_positions);
});
});
if vim.mode.is_visual() {
vim.switch_mode(Mode::Normal, true, cx)
vim.switch_mode(Mode::Normal, true, window, cx)
}
});
}
@ -76,14 +76,15 @@ impl Vim {
motion: Motion,
times: Option<usize>,
dir: IndentDirection,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let mut selection_starts: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
selection_starts.insert(selection.id, anchor);
@ -91,11 +92,11 @@ impl Vim {
});
});
match dir {
IndentDirection::In => editor.indent(&Default::default(), cx),
IndentDirection::Out => editor.outdent(&Default::default(), cx),
IndentDirection::Auto => editor.autoindent(&Default::default(), cx),
IndentDirection::In => editor.indent(&Default::default(), window, cx),
IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
}
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = selection_starts.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@ -110,13 +111,14 @@ impl Vim {
object: Object,
around: bool,
dir: IndentDirection,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
original_positions.insert(selection.id, anchor);
@ -124,11 +126,11 @@ impl Vim {
});
});
match dir {
IndentDirection::In => editor.indent(&Default::default(), cx),
IndentDirection::Out => editor.outdent(&Default::default(), cx),
IndentDirection::Auto => editor.autoindent(&Default::default(), cx),
IndentDirection::In => editor.indent(&Default::default(), window, cx),
IndentDirection::Out => editor.outdent(&Default::default(), window, cx),
IndentDirection::Auto => editor.autoindent(&Default::default(), window, cx),
}
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = original_positions.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

View file

@ -1,44 +1,54 @@
use crate::{state::Mode, Vim};
use editor::{scroll::Autoscroll, Bias, Editor};
use gpui::{actions, Action, ViewContext};
use gpui::{actions, Action, Context, Window};
use language::SelectionGoal;
actions!(vim, [NormalBefore, TemporaryNormal]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::normal_before);
Vim::action(editor, cx, Vim::temporary_normal);
}
impl Vim {
fn normal_before(&mut self, action: &NormalBefore, cx: &mut ViewContext<Self>) {
fn normal_before(
&mut self,
action: &NormalBefore,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.active_operator().is_some() {
self.operator_stack.clear();
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
return;
}
let count = Vim::take_count(cx).unwrap_or(1);
self.stop_recording_immediately(action.boxed_clone(), cx);
if count <= 1 || Vim::globals(cx).dot_replaying {
self.create_mark("^".into(), false, cx);
self.update_editor(cx, |_, editor, cx| {
editor.dismiss_menus_and_popups(false, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.create_mark("^".into(), false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
editor.dismiss_menus_and_popups(false, window, cx);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, mut cursor, _| {
*cursor.column_mut() = cursor.column().saturating_sub(1);
(map.clip_point(cursor, Bias::Left), SelectionGoal::None)
});
});
});
self.switch_mode(Mode::Normal, false, cx);
self.switch_mode(Mode::Normal, false, window, cx);
return;
}
self.repeat(true, cx)
self.repeat(true, window, cx)
}
fn temporary_normal(&mut self, _: &TemporaryNormal, cx: &mut ViewContext<Self>) {
self.switch_mode(Mode::Normal, true, cx);
fn temporary_normal(
&mut self,
_: &TemporaryNormal,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.switch_mode(Mode::Normal, true, window, cx);
self.temp_mode = true;
}
}

View file

@ -1,4 +1,4 @@
use gpui::{div, Element, Render, Subscription, View, ViewContext, WeakView};
use gpui::{div, Context, Element, Entity, Render, Subscription, WeakEntity, Window};
use itertools::Itertools;
use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
@ -6,27 +6,30 @@ use crate::{Vim, VimEvent, VimGlobals};
/// The ModeIndicator displays the current mode in the status bar.
pub struct ModeIndicator {
vim: Option<WeakView<Vim>>,
vim: Option<WeakEntity<Vim>>,
pending_keys: Option<String>,
vim_subscription: Option<Subscription>,
}
impl ModeIndicator {
/// Construct a new mode indicator in this window.
pub fn new(cx: &mut ViewContext<Self>) -> Self {
cx.observe_pending_input(|this, cx| {
this.update_pending_keys(cx);
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
cx.observe_pending_input(window, |this: &mut Self, window, cx| {
this.update_pending_keys(window);
cx.notify();
})
.detach();
let handle = cx.view().clone();
let window = cx.window_handle();
cx.observe_new_views::<Vim>(move |_, cx| {
if cx.window_handle() != window {
let handle = cx.model().clone();
let window_handle = window.window_handle();
cx.observe_new::<Vim>(move |_, window, cx| {
let Some(window) = window else {
return;
};
if window.window_handle() != window_handle {
return;
}
let vim = cx.view().clone();
let vim = cx.model().clone();
handle.update(cx, |_, cx| {
cx.subscribe(&vim, |mode_indicator, vim, event, cx| match event {
VimEvent::Focused => {
@ -47,8 +50,8 @@ impl ModeIndicator {
}
}
fn update_pending_keys(&mut self, cx: &mut ViewContext<Self>) {
self.pending_keys = cx.pending_input_keystrokes().map(|keystrokes| {
fn update_pending_keys(&mut self, window: &mut Window) {
self.pending_keys = window.pending_input_keystrokes().map(|keystrokes| {
keystrokes
.iter()
.map(|keystroke| format!("{}", keystroke))
@ -56,11 +59,11 @@ impl ModeIndicator {
});
}
fn vim(&self) -> Option<View<Vim>> {
fn vim(&self) -> Option<Entity<Vim>> {
self.vim.as_ref().and_then(|vim| vim.upgrade())
}
fn current_operators_description(&self, vim: View<Vim>, cx: &mut ViewContext<Self>) -> String {
fn current_operators_description(&self, vim: Entity<Vim>, cx: &mut Context<Self>) -> String {
let recording = Vim::globals(cx)
.recording_register
.map(|reg| format!("recording @{reg} "))
@ -90,7 +93,7 @@ impl ModeIndicator {
}
impl Render for ModeIndicator {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let vim = self.vim();
let Some(vim) = vim else {
return div().into_any();
@ -125,7 +128,8 @@ impl StatusItemView for ModeIndicator {
fn set_active_pane_item(
&mut self,
_active_pane_item: Option<&dyn ItemHandle>,
_cx: &mut ViewContext<Self>,
_window: &mut Window,
_cx: &mut Context<Self>,
) {
}
}

View file

@ -6,7 +6,7 @@ use editor::{
scroll::Autoscroll,
Anchor, Bias, DisplayPoint, Editor, RowExt, ToOffset, ToPoint,
};
use gpui::{actions, impl_actions, px, ViewContext};
use gpui::{actions, impl_actions, px, Context, Window};
use language::{CharKind, Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow;
use schemars::JsonSchema;
@ -304,223 +304,242 @@ actions!(
]
);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Left, cx| vim.motion(Motion::Left, cx));
Vim::action(editor, cx, |vim, _: &Backspace, cx| {
vim.motion(Motion::Backspace, cx)
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &Left, window, cx| {
vim.motion(Motion::Left, window, cx)
});
Vim::action(editor, cx, |vim, action: &Down, cx| {
Vim::action(editor, cx, |vim, _: &Backspace, window, cx| {
vim.motion(Motion::Backspace, window, cx)
});
Vim::action(editor, cx, |vim, action: &Down, window, cx| {
vim.motion(
Motion::Down {
display_lines: action.display_lines,
},
window,
cx,
)
});
Vim::action(editor, cx, |vim, action: &Up, cx| {
Vim::action(editor, cx, |vim, action: &Up, window, cx| {
vim.motion(
Motion::Up {
display_lines: action.display_lines,
},
window,
cx,
)
});
Vim::action(editor, cx, |vim, _: &Right, cx| {
vim.motion(Motion::Right, cx)
Vim::action(editor, cx, |vim, _: &Right, window, cx| {
vim.motion(Motion::Right, window, cx)
});
Vim::action(editor, cx, |vim, _: &Space, cx| {
vim.motion(Motion::Space, cx)
Vim::action(editor, cx, |vim, _: &Space, window, cx| {
vim.motion(Motion::Space, window, cx)
});
Vim::action(editor, cx, |vim, action: &FirstNonWhitespace, cx| {
vim.motion(
Motion::FirstNonWhitespace {
display_lines: action.display_lines,
},
cx,
)
});
Vim::action(editor, cx, |vim, action: &StartOfLine, cx| {
Vim::action(
editor,
cx,
|vim, action: &FirstNonWhitespace, window, cx| {
vim.motion(
Motion::FirstNonWhitespace {
display_lines: action.display_lines,
},
window,
cx,
)
},
);
Vim::action(editor, cx, |vim, action: &StartOfLine, window, cx| {
vim.motion(
Motion::StartOfLine {
display_lines: action.display_lines,
},
window,
cx,
)
});
Vim::action(editor, cx, |vim, action: &EndOfLine, cx| {
Vim::action(editor, cx, |vim, action: &EndOfLine, window, cx| {
vim.motion(
Motion::EndOfLine {
display_lines: action.display_lines,
},
window,
cx,
)
});
Vim::action(editor, cx, |vim, _: &CurrentLine, cx| {
vim.motion(Motion::CurrentLine, cx)
Vim::action(editor, cx, |vim, _: &CurrentLine, window, cx| {
vim.motion(Motion::CurrentLine, window, cx)
});
Vim::action(editor, cx, |vim, _: &StartOfParagraph, cx| {
vim.motion(Motion::StartOfParagraph, cx)
Vim::action(editor, cx, |vim, _: &StartOfParagraph, window, cx| {
vim.motion(Motion::StartOfParagraph, window, cx)
});
Vim::action(editor, cx, |vim, _: &EndOfParagraph, cx| {
vim.motion(Motion::EndOfParagraph, cx)
Vim::action(editor, cx, |vim, _: &EndOfParagraph, window, cx| {
vim.motion(Motion::EndOfParagraph, window, cx)
});
Vim::action(editor, cx, |vim, _: &SentenceForward, cx| {
vim.motion(Motion::SentenceForward, cx)
Vim::action(editor, cx, |vim, _: &SentenceForward, window, cx| {
vim.motion(Motion::SentenceForward, window, cx)
});
Vim::action(editor, cx, |vim, _: &SentenceBackward, cx| {
vim.motion(Motion::SentenceBackward, cx)
Vim::action(editor, cx, |vim, _: &SentenceBackward, window, cx| {
vim.motion(Motion::SentenceBackward, window, cx)
});
Vim::action(editor, cx, |vim, _: &StartOfDocument, cx| {
vim.motion(Motion::StartOfDocument, cx)
Vim::action(editor, cx, |vim, _: &StartOfDocument, window, cx| {
vim.motion(Motion::StartOfDocument, window, cx)
});
Vim::action(editor, cx, |vim, _: &EndOfDocument, cx| {
vim.motion(Motion::EndOfDocument, cx)
Vim::action(editor, cx, |vim, _: &EndOfDocument, window, cx| {
vim.motion(Motion::EndOfDocument, window, cx)
});
Vim::action(editor, cx, |vim, _: &Matching, cx| {
vim.motion(Motion::Matching, cx)
Vim::action(editor, cx, |vim, _: &Matching, window, cx| {
vim.motion(Motion::Matching, window, cx)
});
Vim::action(
editor,
cx,
|vim, &UnmatchedForward { char }: &UnmatchedForward, cx| {
vim.motion(Motion::UnmatchedForward { char }, cx)
|vim, &UnmatchedForward { char }: &UnmatchedForward, window, cx| {
vim.motion(Motion::UnmatchedForward { char }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &UnmatchedBackward { char }: &UnmatchedBackward, cx| {
vim.motion(Motion::UnmatchedBackward { char }, cx)
|vim, &UnmatchedBackward { char }: &UnmatchedBackward, window, cx| {
vim.motion(Motion::UnmatchedBackward { char }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &NextWordStart { ignore_punctuation }: &NextWordStart, cx| {
vim.motion(Motion::NextWordStart { ignore_punctuation }, cx)
|vim, &NextWordStart { ignore_punctuation }: &NextWordStart, window, cx| {
vim.motion(Motion::NextWordStart { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &NextWordEnd { ignore_punctuation }: &NextWordEnd, cx| {
vim.motion(Motion::NextWordEnd { ignore_punctuation }, cx)
|vim, &NextWordEnd { ignore_punctuation }: &NextWordEnd, window, cx| {
vim.motion(Motion::NextWordEnd { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, cx| {
vim.motion(Motion::PreviousWordStart { ignore_punctuation }, cx)
|vim, &PreviousWordStart { ignore_punctuation }: &PreviousWordStart, window, cx| {
vim.motion(Motion::PreviousWordStart { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &PreviousWordEnd { ignore_punctuation }, cx| {
vim.motion(Motion::PreviousWordEnd { ignore_punctuation }, cx)
|vim, &PreviousWordEnd { ignore_punctuation }, window, cx| {
vim.motion(Motion::PreviousWordEnd { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &NextSubwordStart { ignore_punctuation }: &NextSubwordStart, cx| {
vim.motion(Motion::NextSubwordStart { ignore_punctuation }, cx)
|vim, &NextSubwordStart { ignore_punctuation }: &NextSubwordStart, window, cx| {
vim.motion(Motion::NextSubwordStart { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &NextSubwordEnd { ignore_punctuation }: &NextSubwordEnd, cx| {
vim.motion(Motion::NextSubwordEnd { ignore_punctuation }, cx)
|vim, &NextSubwordEnd { ignore_punctuation }: &NextSubwordEnd, window, cx| {
vim.motion(Motion::NextSubwordEnd { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &PreviousSubwordStart { ignore_punctuation }: &PreviousSubwordStart, cx| {
vim.motion(Motion::PreviousSubwordStart { ignore_punctuation }, cx)
|vim, &PreviousSubwordStart { ignore_punctuation }: &PreviousSubwordStart, window, cx| {
vim.motion(
Motion::PreviousSubwordStart { ignore_punctuation },
window,
cx,
)
},
);
Vim::action(
editor,
cx,
|vim, &PreviousSubwordEnd { ignore_punctuation }, cx| {
vim.motion(Motion::PreviousSubwordEnd { ignore_punctuation }, cx)
|vim, &PreviousSubwordEnd { ignore_punctuation }, window, cx| {
vim.motion(
Motion::PreviousSubwordEnd { ignore_punctuation },
window,
cx,
)
},
);
Vim::action(editor, cx, |vim, &NextLineStart, cx| {
vim.motion(Motion::NextLineStart, cx)
Vim::action(editor, cx, |vim, &NextLineStart, window, cx| {
vim.motion(Motion::NextLineStart, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousLineStart, cx| {
vim.motion(Motion::PreviousLineStart, cx)
Vim::action(editor, cx, |vim, &PreviousLineStart, window, cx| {
vim.motion(Motion::PreviousLineStart, window, cx)
});
Vim::action(editor, cx, |vim, &StartOfLineDownward, cx| {
vim.motion(Motion::StartOfLineDownward, cx)
Vim::action(editor, cx, |vim, &StartOfLineDownward, window, cx| {
vim.motion(Motion::StartOfLineDownward, window, cx)
});
Vim::action(editor, cx, |vim, &EndOfLineDownward, cx| {
vim.motion(Motion::EndOfLineDownward, cx)
Vim::action(editor, cx, |vim, &EndOfLineDownward, window, cx| {
vim.motion(Motion::EndOfLineDownward, window, cx)
});
Vim::action(editor, cx, |vim, &GoToColumn, cx| {
vim.motion(Motion::GoToColumn, cx)
Vim::action(editor, cx, |vim, &GoToColumn, window, cx| {
vim.motion(Motion::GoToColumn, window, cx)
});
Vim::action(editor, cx, |vim, _: &RepeatFind, cx| {
Vim::action(editor, cx, |vim, _: &RepeatFind, window, cx| {
if let Some(last_find) = Vim::globals(cx).last_find.clone().map(Box::new) {
vim.motion(Motion::RepeatFind { last_find }, cx);
vim.motion(Motion::RepeatFind { last_find }, window, cx);
}
});
Vim::action(editor, cx, |vim, _: &RepeatFindReversed, cx| {
Vim::action(editor, cx, |vim, _: &RepeatFindReversed, window, cx| {
if let Some(last_find) = Vim::globals(cx).last_find.clone().map(Box::new) {
vim.motion(Motion::RepeatFindReversed { last_find }, cx);
vim.motion(Motion::RepeatFindReversed { last_find }, window, cx);
}
});
Vim::action(editor, cx, |vim, &WindowTop, cx| {
vim.motion(Motion::WindowTop, cx)
Vim::action(editor, cx, |vim, &WindowTop, window, cx| {
vim.motion(Motion::WindowTop, window, cx)
});
Vim::action(editor, cx, |vim, &WindowMiddle, cx| {
vim.motion(Motion::WindowMiddle, cx)
Vim::action(editor, cx, |vim, &WindowMiddle, window, cx| {
vim.motion(Motion::WindowMiddle, window, cx)
});
Vim::action(editor, cx, |vim, &WindowBottom, cx| {
vim.motion(Motion::WindowBottom, cx)
Vim::action(editor, cx, |vim, &WindowBottom, window, cx| {
vim.motion(Motion::WindowBottom, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousSectionStart, cx| {
vim.motion(Motion::PreviousSectionStart, cx)
Vim::action(editor, cx, |vim, &PreviousSectionStart, window, cx| {
vim.motion(Motion::PreviousSectionStart, window, cx)
});
Vim::action(editor, cx, |vim, &NextSectionStart, cx| {
vim.motion(Motion::NextSectionStart, cx)
Vim::action(editor, cx, |vim, &NextSectionStart, window, cx| {
vim.motion(Motion::NextSectionStart, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousSectionEnd, cx| {
vim.motion(Motion::PreviousSectionEnd, cx)
Vim::action(editor, cx, |vim, &PreviousSectionEnd, window, cx| {
vim.motion(Motion::PreviousSectionEnd, window, cx)
});
Vim::action(editor, cx, |vim, &NextSectionEnd, cx| {
vim.motion(Motion::NextSectionEnd, cx)
Vim::action(editor, cx, |vim, &NextSectionEnd, window, cx| {
vim.motion(Motion::NextSectionEnd, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousMethodStart, cx| {
vim.motion(Motion::PreviousMethodStart, cx)
Vim::action(editor, cx, |vim, &PreviousMethodStart, window, cx| {
vim.motion(Motion::PreviousMethodStart, window, cx)
});
Vim::action(editor, cx, |vim, &NextMethodStart, cx| {
vim.motion(Motion::NextMethodStart, cx)
Vim::action(editor, cx, |vim, &NextMethodStart, window, cx| {
vim.motion(Motion::NextMethodStart, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousMethodEnd, cx| {
vim.motion(Motion::PreviousMethodEnd, cx)
Vim::action(editor, cx, |vim, &PreviousMethodEnd, window, cx| {
vim.motion(Motion::PreviousMethodEnd, window, cx)
});
Vim::action(editor, cx, |vim, &NextMethodEnd, cx| {
vim.motion(Motion::NextMethodEnd, cx)
Vim::action(editor, cx, |vim, &NextMethodEnd, window, cx| {
vim.motion(Motion::NextMethodEnd, window, cx)
});
Vim::action(editor, cx, |vim, &NextComment, cx| {
vim.motion(Motion::NextComment, cx)
Vim::action(editor, cx, |vim, &NextComment, window, cx| {
vim.motion(Motion::NextComment, window, cx)
});
Vim::action(editor, cx, |vim, &PreviousComment, cx| {
vim.motion(Motion::PreviousComment, cx)
Vim::action(editor, cx, |vim, &PreviousComment, window, cx| {
vim.motion(Motion::PreviousComment, window, cx)
});
}
impl Vim {
pub(crate) fn search_motion(&mut self, m: Motion, cx: &mut ViewContext<Self>) {
pub(crate) fn search_motion(&mut self, m: Motion, window: &mut Window, cx: &mut Context<Self>) {
if let Motion::ZedSearchResult {
prior_selections, ..
} = &m
@ -528,8 +547,8 @@ impl Vim {
match self.mode {
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
if !prior_selections.is_empty() {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges(prior_selections.iter().cloned())
})
});
@ -545,16 +564,16 @@ impl Vim {
}
}
self.motion(m, cx)
self.motion(m, window, cx)
}
pub(crate) fn motion(&mut self, motion: Motion, cx: &mut ViewContext<Self>) {
pub(crate) fn motion(&mut self, motion: Motion, window: &mut Window, cx: &mut Context<Self>) {
if let Some(Operator::FindForward { .. })
| Some(Operator::Sneak { .. })
| Some(Operator::SneakBackward { .. })
| Some(Operator::FindBackward { .. }) = self.active_operator()
{
self.pop_operator(cx);
self.pop_operator(window, cx);
}
let count = Vim::take_count(cx);
@ -567,18 +586,18 @@ impl Vim {
target: Some(SurroundsType::Motion(motion)),
});
} else {
self.normal_motion(motion.clone(), active_operator.clone(), count, cx)
self.normal_motion(motion.clone(), active_operator.clone(), count, window, cx)
}
}
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
self.visual_motion(motion.clone(), count, cx)
self.visual_motion(motion.clone(), count, window, cx)
}
Mode::HelixNormal => self.helix_normal_motion(motion.clone(), count, cx),
Mode::HelixNormal => self.helix_normal_motion(motion.clone(), count, window, cx),
}
self.clear_operator(cx);
self.clear_operator(window, cx);
if let Some(operator) = waiting_operator {
self.push_operator(operator, cx);
self.push_operator(operator, window, cx);
Vim::globals(cx).pre_count = count
}
}
@ -3415,7 +3434,7 @@ mod test {
Mode::Normal,
);
cx.update_editor(|editor, cx| {
cx.update_editor(|editor, _window, cx| {
let range = editor.selections.newest_anchor().range();
let inlay_text = " field: int,\n field2: string\n field3: float";
let inlay = Inlay::inline_completion(1, range.start, inlay_text);

View file

@ -29,7 +29,7 @@ use editor::Anchor;
use editor::Bias;
use editor::Editor;
use editor::{display_map::ToDisplayPoint, movement};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use language::{Point, SelectionGoal, ToPoint};
use log::error;
use multi_buffer::MultiBufferRow;
@ -62,7 +62,7 @@ actions!(
]
);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::insert_after);
Vim::action(editor, cx, Vim::insert_before);
Vim::action(editor, cx, Vim::insert_first_non_whitespace);
@ -78,17 +78,17 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, Vim::paste);
Vim::action(editor, cx, Vim::show_location);
Vim::action(editor, cx, |vim, _: &DeleteLeft, cx| {
Vim::action(editor, cx, |vim, _: &DeleteLeft, window, cx| {
vim.record_current_action(cx);
let times = Vim::take_count(cx);
vim.delete_motion(Motion::Left, times, cx);
vim.delete_motion(Motion::Left, times, window, cx);
});
Vim::action(editor, cx, |vim, _: &DeleteRight, cx| {
Vim::action(editor, cx, |vim, _: &DeleteRight, window, cx| {
vim.record_current_action(cx);
let times = Vim::take_count(cx);
vim.delete_motion(Motion::Right, times, cx);
vim.delete_motion(Motion::Right, times, window, cx);
});
Vim::action(editor, cx, |vim, _: &ChangeToEndOfLine, cx| {
Vim::action(editor, cx, |vim, _: &ChangeToEndOfLine, window, cx| {
vim.start_recording(cx);
let times = Vim::take_count(cx);
vim.change_motion(
@ -96,10 +96,11 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
display_lines: false,
},
times,
window,
cx,
);
});
Vim::action(editor, cx, |vim, _: &DeleteToEndOfLine, cx| {
Vim::action(editor, cx, |vim, _: &DeleteToEndOfLine, window, cx| {
vim.record_current_action(cx);
let times = Vim::take_count(cx);
vim.delete_motion(
@ -107,30 +108,31 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
display_lines: false,
},
times,
window,
cx,
);
});
Vim::action(editor, cx, |vim, _: &JoinLines, cx| {
vim.join_lines_impl(true, cx);
Vim::action(editor, cx, |vim, _: &JoinLines, window, cx| {
vim.join_lines_impl(true, window, cx);
});
Vim::action(editor, cx, |vim, _: &JoinLinesNoWhitespace, cx| {
vim.join_lines_impl(false, cx);
Vim::action(editor, cx, |vim, _: &JoinLinesNoWhitespace, window, cx| {
vim.join_lines_impl(false, window, cx);
});
Vim::action(editor, cx, |vim, _: &Undo, cx| {
Vim::action(editor, cx, |vim, _: &Undo, window, cx| {
let times = Vim::take_count(cx);
vim.update_editor(cx, |_, editor, cx| {
vim.update_editor(window, cx, |_, editor, window, cx| {
for _ in 0..times.unwrap_or(1) {
editor.undo(&editor::actions::Undo, cx);
editor.undo(&editor::actions::Undo, window, cx);
}
});
});
Vim::action(editor, cx, |vim, _: &Redo, cx| {
Vim::action(editor, cx, |vim, _: &Redo, window, cx| {
let times = Vim::take_count(cx);
vim.update_editor(cx, |_, editor, cx| {
vim.update_editor(window, cx, |_, editor, window, cx| {
for _ in 0..times.unwrap_or(1) {
editor.redo(&editor::actions::Redo, cx);
editor.redo(&editor::actions::Redo, window, cx);
}
});
});
@ -148,75 +150,84 @@ impl Vim {
motion: Motion,
operator: Option<Operator>,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
match operator {
None => self.move_cursor(motion, times, cx),
Some(Operator::Change) => self.change_motion(motion, times, cx),
Some(Operator::Delete) => self.delete_motion(motion, times, cx),
Some(Operator::Yank) => self.yank_motion(motion, times, cx),
None => self.move_cursor(motion, times, window, cx),
Some(Operator::Change) => self.change_motion(motion, times, window, cx),
Some(Operator::Delete) => self.delete_motion(motion, times, window, cx),
Some(Operator::Yank) => self.yank_motion(motion, times, window, cx),
Some(Operator::AddSurrounds { target: None }) => {}
Some(Operator::Indent) => self.indent_motion(motion, times, IndentDirection::In, cx),
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, cx),
Some(Operator::Outdent) => self.indent_motion(motion, times, IndentDirection::Out, cx),
Some(Operator::AutoIndent) => {
self.indent_motion(motion, times, IndentDirection::Auto, cx)
Some(Operator::Indent) => {
self.indent_motion(motion, times, IndentDirection::In, window, cx)
}
Some(Operator::ShellCommand) => self.shell_command_motion(motion, times, cx),
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, window, cx),
Some(Operator::Outdent) => {
self.indent_motion(motion, times, IndentDirection::Out, window, cx)
}
Some(Operator::AutoIndent) => {
self.indent_motion(motion, times, IndentDirection::Auto, window, cx)
}
Some(Operator::ShellCommand) => self.shell_command_motion(motion, times, window, cx),
Some(Operator::Lowercase) => {
self.change_case_motion(motion, times, CaseTarget::Lowercase, cx)
self.change_case_motion(motion, times, CaseTarget::Lowercase, window, cx)
}
Some(Operator::Uppercase) => {
self.change_case_motion(motion, times, CaseTarget::Uppercase, cx)
self.change_case_motion(motion, times, CaseTarget::Uppercase, window, cx)
}
Some(Operator::OppositeCase) => {
self.change_case_motion(motion, times, CaseTarget::OppositeCase, cx)
self.change_case_motion(motion, times, CaseTarget::OppositeCase, window, cx)
}
Some(Operator::ToggleComments) => {
self.toggle_comments_motion(motion, times, window, cx)
}
Some(Operator::ToggleComments) => self.toggle_comments_motion(motion, times, cx),
Some(operator) => {
// Can't do anything for text objects, Ignoring
error!("Unexpected normal mode motion operator: {:?}", operator)
}
}
// Exit temporary normal mode (if active).
self.exit_temporary_normal(cx);
self.exit_temporary_normal(window, cx);
}
pub fn normal_object(&mut self, object: Object, cx: &mut ViewContext<Self>) {
pub fn normal_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Self>) {
let mut waiting_operator: Option<Operator> = None;
match self.maybe_pop_operator() {
Some(Operator::Object { around }) => match self.maybe_pop_operator() {
Some(Operator::Change) => self.change_object(object, around, cx),
Some(Operator::Delete) => self.delete_object(object, around, cx),
Some(Operator::Yank) => self.yank_object(object, around, cx),
Some(Operator::Change) => self.change_object(object, around, window, cx),
Some(Operator::Delete) => self.delete_object(object, around, window, cx),
Some(Operator::Yank) => self.yank_object(object, around, window, cx),
Some(Operator::Indent) => {
self.indent_object(object, around, IndentDirection::In, cx)
self.indent_object(object, around, IndentDirection::In, window, cx)
}
Some(Operator::Outdent) => {
self.indent_object(object, around, IndentDirection::Out, cx)
self.indent_object(object, around, IndentDirection::Out, window, cx)
}
Some(Operator::AutoIndent) => {
self.indent_object(object, around, IndentDirection::Auto, cx)
self.indent_object(object, around, IndentDirection::Auto, window, cx)
}
Some(Operator::ShellCommand) => {
self.shell_command_object(object, around, cx);
self.shell_command_object(object, around, window, cx);
}
Some(Operator::Rewrap) => self.rewrap_object(object, around, cx),
Some(Operator::Rewrap) => self.rewrap_object(object, around, window, cx),
Some(Operator::Lowercase) => {
self.change_case_object(object, around, CaseTarget::Lowercase, cx)
self.change_case_object(object, around, CaseTarget::Lowercase, window, cx)
}
Some(Operator::Uppercase) => {
self.change_case_object(object, around, CaseTarget::Uppercase, cx)
self.change_case_object(object, around, CaseTarget::Uppercase, window, cx)
}
Some(Operator::OppositeCase) => {
self.change_case_object(object, around, CaseTarget::OppositeCase, cx)
self.change_case_object(object, around, CaseTarget::OppositeCase, window, cx)
}
Some(Operator::AddSurrounds { target: None }) => {
waiting_operator = Some(Operator::AddSurrounds {
target: Some(SurroundsType::Object(object, around)),
});
}
Some(Operator::ToggleComments) => self.toggle_comments_object(object, around, cx),
Some(Operator::ToggleComments) => {
self.toggle_comments_object(object, around, window, cx)
}
_ => {
// Can't do anything for namespace operators. Ignoring
}
@ -225,7 +236,7 @@ impl Vim {
waiting_operator = Some(Operator::DeleteSurrounds);
}
Some(Operator::ChangeSurrounds { target: None }) => {
if self.check_and_move_to_valid_bracket_pair(object, cx) {
if self.check_and_move_to_valid_bracket_pair(object, window, cx) {
waiting_operator = Some(Operator::ChangeSurrounds {
target: Some(object),
});
@ -235,9 +246,9 @@ impl Vim {
// Can't do anything with change/delete/yank/surrounds and text objects. Ignoring
}
}
self.clear_operator(cx);
self.clear_operator(window, cx);
if let Some(operator) = waiting_operator {
self.push_operator(operator, cx);
self.push_operator(operator, window, cx);
}
}
@ -245,11 +256,12 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, goal| {
motion
.move_point(map, cursor, goal, times, &text_layout_details)
@ -259,30 +271,31 @@ impl Vim {
});
}
fn insert_after(&mut self, _: &InsertAfter, cx: &mut ViewContext<Self>) {
fn insert_after(&mut self, _: &InsertAfter, window: &mut Window, cx: &mut Context<Self>) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
});
});
}
fn insert_before(&mut self, _: &InsertBefore, cx: &mut ViewContext<Self>) {
fn insert_before(&mut self, _: &InsertBefore, window: &mut Window, cx: &mut Context<Self>) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.switch_mode(Mode::Insert, false, window, cx);
}
fn insert_first_non_whitespace(
&mut self,
_: &InsertFirstNonWhitespace,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(
first_non_whitespace(map, false, cursor),
@ -293,11 +306,16 @@ impl Vim {
});
}
fn insert_end_of_line(&mut self, _: &InsertEndOfLine, cx: &mut ViewContext<Self>) {
fn insert_end_of_line(
&mut self,
_: &InsertEndOfLine,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(next_line_end(map, cursor, 1), SelectionGoal::None)
});
@ -305,23 +323,33 @@ impl Vim {
});
}
fn insert_at_previous(&mut self, _: &InsertAtPrevious, cx: &mut ViewContext<Self>) {
fn insert_at_previous(
&mut self,
_: &InsertAtPrevious,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |vim, editor, cx| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
if let Some(marks) = vim.marks.get("^") {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_anchor_ranges(marks.iter().map(|mark| *mark..*mark))
});
}
});
}
fn insert_line_above(&mut self, _: &InsertLineAbove, cx: &mut ViewContext<Self>) {
fn insert_line_above(
&mut self,
_: &InsertLineAbove,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let selections = editor.selections.all::<Point>(cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
@ -342,7 +370,7 @@ impl Vim {
})
.collect::<Vec<_>>();
editor.edit_with_autoindent(edits, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
let insert_point = motion::end_of_line(map, false, previous_line, 1);
@ -353,12 +381,17 @@ impl Vim {
});
}
fn insert_line_below(&mut self, _: &InsertLineBelow, cx: &mut ViewContext<Self>) {
fn insert_line_below(
&mut self,
_: &InsertLineBelow,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.start_recording(cx);
self.switch_mode(Mode::Insert, false, cx);
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.switch_mode(Mode::Insert, false, window, cx);
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let selections = editor.selections.all::<Point>(cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
@ -378,7 +411,7 @@ impl Vim {
(end_of_line..end_of_line, "\n".to_string() + &indent)
})
.collect::<Vec<_>>();
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.maybe_move_cursors_with(|map, cursor, goal| {
Motion::CurrentLine.move_point(
map,
@ -394,7 +427,12 @@ impl Vim {
});
}
fn join_lines_impl(&mut self, insert_whitespace: bool, cx: &mut ViewContext<Self>) {
fn join_lines_impl(
&mut self,
insert_whitespace: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.record_current_action(cx);
let mut times = Vim::take_count(cx).unwrap_or(1);
if self.mode.is_visual() {
@ -404,26 +442,26 @@ impl Vim {
times -= 1;
}
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
for _ in 0..times {
editor.join_lines_impl(insert_whitespace, cx)
editor.join_lines_impl(insert_whitespace, window, cx)
}
})
});
if self.mode.is_visual() {
self.switch_mode(Mode::Normal, true, cx)
self.switch_mode(Mode::Normal, true, window, cx)
}
}
fn yank_line(&mut self, _: &YankLine, cx: &mut ViewContext<Self>) {
fn yank_line(&mut self, _: &YankLine, window: &mut Window, cx: &mut Context<Self>) {
let count = Vim::take_count(cx);
self.yank_motion(motion::Motion::CurrentLine, count, cx)
self.yank_motion(motion::Motion::CurrentLine, count, window, cx)
}
fn show_location(&mut self, _: &ShowLocation, cx: &mut ViewContext<Self>) {
fn show_location(&mut self, _: &ShowLocation, window: &mut Window, cx: &mut Context<Self>) {
let count = Vim::take_count(cx);
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, _window, cx| {
let selection = editor.selections.newest_anchor();
if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
let filename = if let Some(file) = buffer.read(cx).file() {
@ -460,26 +498,31 @@ impl Vim {
});
}
fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
fn toggle_comments(&mut self, _: &ToggleComments, window: &mut Window, cx: &mut Context<Self>) {
self.record_current_action(cx);
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let original_positions = vim.save_selection_starts(editor, cx);
editor.toggle_comments(&Default::default(), cx);
vim.restore_selection_cursors(editor, cx, original_positions);
editor.toggle_comments(&Default::default(), window, cx);
vim.restore_selection_cursors(editor, window, cx, original_positions);
});
});
if self.mode.is_visual() {
self.switch_mode(Mode::Normal, true, cx)
self.switch_mode(Mode::Normal, true, window, cx)
}
}
pub(crate) fn normal_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
pub(crate) fn normal_replace(
&mut self,
text: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count = Vim::take_count(cx).unwrap_or(1);
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (map, display_selections) = editor.selections.all_display(cx);
@ -503,7 +546,7 @@ impl Vim {
editor.edit(edits, cx);
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let point = movement::saturating_left(map, selection.head());
selection.collapse_to(point, SelectionGoal::None)
@ -511,13 +554,14 @@ impl Vim {
});
});
});
self.pop_operator(cx);
self.pop_operator(window, cx);
}
pub fn save_selection_starts(
&self,
editor: &Editor,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) -> HashMap<usize, Anchor> {
let (map, selections) = editor.selections.all_display(cx);
selections
@ -534,10 +578,11 @@ impl Vim {
pub fn restore_selection_cursors(
&self,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
window: &mut Window,
cx: &mut Context<Editor>,
mut positions: HashMap<usize, Anchor>,
) {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
if let Some(anchor) = positions.remove(&selection.id) {
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@ -546,9 +591,9 @@ impl Vim {
});
}
fn exit_temporary_normal(&mut self, cx: &mut ViewContext<Self>) {
fn exit_temporary_normal(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.temp_mode {
self.switch_mode(Mode::Insert, true, cx);
self.switch_mode(Mode::Insert, true, window, cx);
}
}
}
@ -1385,7 +1430,7 @@ mod test {
#[gpui::test]
async fn test_subword_motions(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys(vec![
KeyBinding::new(
"w",
@ -1469,7 +1514,7 @@ mod test {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_neovim_option("textwidth=5").await;
cx.update(|cx| {
cx.update(|_, cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.preferred_line_length = Some(5);

View file

@ -1,6 +1,6 @@
use collections::HashMap;
use editor::{display_map::ToDisplayPoint, scroll::Autoscroll};
use gpui::ViewContext;
use gpui::{Context, Window};
use language::{Bias, Point, SelectionGoal};
use multi_buffer::MultiBufferRow;
@ -24,15 +24,16 @@ impl Vim {
motion: Motion,
times: Option<usize>,
mode: CaseTarget,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let mut selection_starts: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Left);
selection_starts.insert(selection.id, anchor);
@ -40,13 +41,17 @@ impl Vim {
});
});
match mode {
CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
CaseTarget::Lowercase => {
editor.convert_to_lower_case(&Default::default(), window, cx)
}
CaseTarget::Uppercase => {
editor.convert_to_upper_case(&Default::default(), window, cx)
}
CaseTarget::OppositeCase => {
editor.convert_to_opposite_case(&Default::default(), cx)
editor.convert_to_opposite_case(&Default::default(), window, cx)
}
}
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = selection_starts.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@ -62,13 +67,14 @@ impl Vim {
object: Object,
around: bool,
mode: CaseTarget,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
object.expand_selection(map, selection, around);
original_positions.insert(
@ -78,13 +84,17 @@ impl Vim {
});
});
match mode {
CaseTarget::Lowercase => editor.convert_to_lower_case(&Default::default(), cx),
CaseTarget::Uppercase => editor.convert_to_upper_case(&Default::default(), cx),
CaseTarget::Lowercase => {
editor.convert_to_lower_case(&Default::default(), window, cx)
}
CaseTarget::Uppercase => {
editor.convert_to_upper_case(&Default::default(), window, cx)
}
CaseTarget::OppositeCase => {
editor.convert_to_opposite_case(&Default::default(), cx)
editor.convert_to_opposite_case(&Default::default(), window, cx)
}
}
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = original_positions.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@ -94,8 +104,8 @@ impl Vim {
});
}
pub fn change_case(&mut self, _: &ChangeCase, cx: &mut ViewContext<Self>) {
self.manipulate_text(cx, |c| {
pub fn change_case(&mut self, _: &ChangeCase, window: &mut Window, cx: &mut Context<Self>) {
self.manipulate_text(window, cx, |c| {
if c.is_lowercase() {
c.to_uppercase().collect::<Vec<char>>()
} else {
@ -104,23 +114,33 @@ impl Vim {
})
}
pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
self.manipulate_text(cx, |c| c.to_uppercase().collect::<Vec<char>>())
pub fn convert_to_upper_case(
&mut self,
_: &ConvertToUpperCase,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.manipulate_text(window, cx, |c| c.to_uppercase().collect::<Vec<char>>())
}
pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
self.manipulate_text(cx, |c| c.to_lowercase().collect::<Vec<char>>())
pub fn convert_to_lower_case(
&mut self,
_: &ConvertToLowerCase,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.manipulate_text(window, cx, |c| c.to_lowercase().collect::<Vec<char>>())
}
fn manipulate_text<F>(&mut self, cx: &mut ViewContext<Self>, transform: F)
fn manipulate_text<F>(&mut self, window: &mut Window, cx: &mut Context<Self>, transform: F)
where
F: Fn(char) -> Vec<char> + Copy,
{
self.record_current_action(cx);
self.store_visual_marks(cx);
self.store_visual_marks(window, cx);
let count = Vim::take_count(cx).unwrap_or(1) as u32;
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let mut ranges = Vec::new();
let mut cursor_positions = Vec::new();
let snapshot = editor.buffer().read(cx).snapshot(cx);
@ -162,7 +182,7 @@ impl Vim {
}
}
}
editor.transact(cx, |editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
for range in ranges.into_iter().rev() {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let text = snapshot
@ -172,12 +192,12 @@ impl Vim {
.collect::<String>();
editor.edit([(range, text)], cx)
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges(cursor_positions)
})
});
});
self.switch_mode(Mode::Normal, true, cx)
self.switch_mode(Mode::Normal, true, window, cx)
}
}

View file

@ -10,15 +10,16 @@ use editor::{
scroll::Autoscroll,
Bias, DisplayPoint,
};
use gpui::{Context, Window};
use language::Selection;
use ui::ViewContext;
impl Vim {
pub fn change_motion(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
// Some motions ignore failure when switching to normal mode
let mut motion_succeeded = matches!(
@ -29,12 +30,12 @@ impl Vim {
| Motion::Backspace
| Motion::StartOfLine { .. }
);
self.update_editor(cx, |vim, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
// We are swapping to insert mode anyway. Just set the line end clipping behavior now
editor.set_clip_at_line_ends(false, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
motion_succeeded |= match motion {
Motion::NextWordStart { ignore_punctuation }
@ -76,41 +77,47 @@ impl Vim {
});
});
vim.copy_selections_content(editor, motion.linewise(), cx);
editor.insert("", cx);
editor.refresh_inline_completion(true, false, cx);
editor.insert("", window, cx);
editor.refresh_inline_completion(true, false, window, cx);
});
});
if motion_succeeded {
self.switch_mode(Mode::Insert, false, cx)
self.switch_mode(Mode::Insert, false, window, cx)
} else {
self.switch_mode(Mode::Normal, false, cx)
self.switch_mode(Mode::Normal, false, window, cx)
}
}
pub fn change_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
pub fn change_object(
&mut self,
object: Object,
around: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
let mut objects_found = false;
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
// We are swapping to insert mode anyway. Just set the line end clipping behavior now
editor.set_clip_at_line_ends(false, cx);
editor.transact(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.transact(window, cx, |editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
objects_found |= object.expand_selection(map, selection, around);
});
});
if objects_found {
vim.copy_selections_content(editor, false, cx);
editor.insert("", cx);
editor.refresh_inline_completion(true, false, cx);
editor.insert("", window, cx);
editor.refresh_inline_completion(true, false, window, cx);
}
});
});
if objects_found {
self.switch_mode(Mode::Insert, false, cx);
self.switch_mode(Mode::Insert, false, window, cx);
} else {
self.switch_mode(Mode::Normal, false, cx);
self.switch_mode(Mode::Normal, false, window, cx);
}
}
}

View file

@ -5,24 +5,25 @@ use editor::{
scroll::Autoscroll,
Bias, DisplayPoint,
};
use gpui::{Context, Window};
use language::{Point, Selection};
use multi_buffer::MultiBufferRow;
use ui::ViewContext;
impl Vim {
pub fn delete_motion(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |vim, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let mut original_columns: HashMap<_, _> = Default::default();
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let original_head = selection.head();
original_columns.insert(selection.id, original_head.column());
@ -60,11 +61,11 @@ impl Vim {
});
});
vim.copy_selections_content(editor, motion.linewise(), cx);
editor.insert("", cx);
editor.insert("", window, cx);
// Fixup cursor position after the deletion
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let mut cursor = selection.head();
if motion.linewise() {
@ -76,20 +77,26 @@ impl Vim {
selection.collapse_to(cursor, selection.goal)
});
});
editor.refresh_inline_completion(true, false, cx);
editor.refresh_inline_completion(true, false, window, cx);
});
});
}
pub fn delete_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
pub fn delete_object(
&mut self,
object: Object,
around: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
// Emulates behavior in vim where if we expanded backwards to include a newline
// the cursor gets set back to the start of the line
let mut should_move_to_start: HashSet<_> = Default::default();
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
object.expand_selection(map, selection, around);
let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range();
@ -142,11 +149,11 @@ impl Vim {
});
});
vim.copy_selections_content(editor, false, cx);
editor.insert("", cx);
editor.insert("", window, cx);
// Fixup cursor position after the deletion
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let mut cursor = selection.head();
if should_move_to_start.contains(&selection.id) {
@ -156,7 +163,7 @@ impl Vim {
selection.collapse_to(cursor, selection.goal)
});
});
editor.refresh_inline_completion(true, false, cx);
editor.refresh_inline_completion(true, false, window, cx);
});
});
}

View file

@ -1,5 +1,5 @@
use editor::{scroll::Autoscroll, Editor, MultiBufferSnapshot, ToOffset, ToPoint};
use gpui::{impl_actions, ViewContext};
use gpui::{impl_actions, Context, Window};
use language::{Bias, Point};
use schemars::JsonSchema;
use serde::Deserialize;
@ -23,25 +23,31 @@ struct Decrement {
impl_actions!(vim, [Increment, Decrement]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, action: &Increment, cx| {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, action: &Increment, window, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
let step = if action.step { 1 } else { 0 };
vim.increment(count as i64, step, cx)
vim.increment(count as i64, step, window, cx)
});
Vim::action(editor, cx, |vim, action: &Decrement, cx| {
Vim::action(editor, cx, |vim, action: &Decrement, window, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
let step = if action.step { -1 } else { 0 };
vim.increment(-(count as i64), step, cx)
vim.increment(-(count as i64), step, window, cx)
});
}
impl Vim {
fn increment(&mut self, mut delta: i64, step: i32, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
fn increment(
&mut self,
mut delta: i64,
step: i32,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
let mut edits = Vec::new();
let mut new_anchors = Vec::new();
@ -76,11 +82,11 @@ impl Vim {
}
}
}
editor.transact(cx, |editor, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.edit(edits, cx);
let snapshot = editor.buffer().read(cx).snapshot(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
let mut new_ranges = Vec::new();
for (visual, anchor) in new_anchors.iter() {
let mut point = anchor.to_point(&snapshot);
@ -94,7 +100,7 @@ impl Vim {
})
});
});
self.switch_mode(Mode::Normal, true, cx)
self.switch_mode(Mode::Normal, true, window, cx)
}
}

View file

@ -6,7 +6,7 @@ use editor::{
scroll::Autoscroll,
Anchor, Bias, DisplayPoint,
};
use gpui::ViewContext;
use gpui::{Context, Window};
use language::SelectionGoal;
use crate::{
@ -16,8 +16,14 @@ use crate::{
};
impl Vim {
pub fn create_mark(&mut self, text: Arc<str>, tail: bool, cx: &mut ViewContext<Self>) {
let Some(anchors) = self.update_editor(cx, |_, editor, _| {
pub fn create_mark(
&mut self,
text: Arc<str>,
tail: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(anchors) = self.update_editor(window, cx, |_, editor, _, _| {
editor
.selections
.disjoint_anchors()
@ -28,23 +34,28 @@ impl Vim {
return;
};
self.marks.insert(text.to_string(), anchors);
self.clear_operator(cx);
self.clear_operator(window, cx);
}
// When handling an action, you must create visual marks if you will switch to normal
// mode without the default selection behavior.
pub(crate) fn store_visual_marks(&mut self, cx: &mut ViewContext<Self>) {
pub(crate) fn store_visual_marks(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.mode.is_visual() {
self.create_visual_marks(self.mode, cx);
self.create_visual_marks(self.mode, window, cx);
}
}
pub(crate) fn create_visual_marks(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
pub(crate) fn create_visual_marks(
&mut self,
mode: Mode,
window: &mut Window,
cx: &mut Context<Self>,
) {
let mut starts = vec![];
let mut ends = vec![];
let mut reversed = vec![];
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, _, cx| {
let (map, selections) = editor.selections.all_display(cx);
for selection in selections {
let end = movement::saturating_left(&map, selection.end);
@ -65,11 +76,17 @@ impl Vim {
self.stored_visual_mode.replace((mode, reversed));
}
pub fn jump(&mut self, text: Arc<str>, line: bool, cx: &mut ViewContext<Self>) {
self.pop_operator(cx);
pub fn jump(
&mut self,
text: Arc<str>,
line: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.pop_operator(window, cx);
let anchors = match &*text {
"{" | "}" => self.update_editor(cx, |_, editor, cx| {
"{" | "}" => self.update_editor(window, cx, |_, editor, _, cx| {
let (map, selections) = editor.selections.all_display(cx);
selections
.into_iter()
@ -98,12 +115,13 @@ impl Vim {
anchor: *anchor,
line,
},
window,
cx,
)
}
} else {
self.update_editor(cx, |_, editor, cx| {
let map = editor.snapshot(cx);
self.update_editor(window, cx, |_, editor, window, cx| {
let map = editor.snapshot(window, cx);
let mut ranges: Vec<Range<Anchor>> = Vec::new();
for mut anchor in anchors {
if line {
@ -118,7 +136,7 @@ impl Vim {
ranges.push(anchor..anchor);
}
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_anchor_ranges(ranges)
})
});

View file

@ -1,5 +1,5 @@
use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, DisplayPoint, RowExt};
use gpui::{impl_actions, ViewContext};
use gpui::{impl_actions, Context, Window};
use language::{Bias, SelectionGoal};
use schemars::JsonSchema;
use serde::Deserialize;
@ -22,14 +22,14 @@ pub struct Paste {
impl_actions!(vim, [Paste]);
impl Vim {
pub fn paste(&mut self, action: &Paste, cx: &mut ViewContext<Self>) {
pub fn paste(&mut self, action: &Paste, window: &mut Window, cx: &mut Context<Self>) {
self.record_current_action(cx);
self.store_visual_marks(cx);
self.store_visual_marks(window, cx);
let count = Vim::take_count(cx).unwrap_or(1);
self.update_editor(cx, |vim, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let selected_register = vim.selected_register.take();
@ -159,7 +159,7 @@ impl Vim {
// and put the cursor on the first non-blank character of the first inserted line (or at the end if the first line is blank).
// otherwise vim will insert the next text at (or before) the current cursor position,
// the cursor will go to the last (or first, if is_multiline) inserted character.
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.replace_cursors_with(|map| {
let mut cursors = Vec::new();
for (anchor, line_mode, is_multiline) in &new_selections {
@ -190,7 +190,7 @@ impl Vim {
})
});
});
self.switch_mode(Mode::Normal, true, cx);
self.switch_mode(Mode::Normal, true, window, cx);
}
}

View file

@ -8,8 +8,7 @@ use crate::{
Vim,
};
use editor::Editor;
use gpui::{actions, Action, ViewContext, WindowContext};
use util::ResultExt;
use gpui::{actions, Action, App, Context, Window};
use workspace::Workspace;
actions!(vim, [Repeat, EndRepeat, ToggleRecord, ReplayLastRecording]);
@ -45,28 +44,30 @@ fn repeatable_insert(action: &ReplayableAction) -> Option<Box<dyn Action>> {
}
}
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &EndRepeat, cx| {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &EndRepeat, window, cx| {
Vim::globals(cx).dot_replaying = false;
vim.switch_mode(Mode::Normal, false, cx)
vim.switch_mode(Mode::Normal, false, window, cx)
});
Vim::action(editor, cx, |vim, _: &Repeat, cx| vim.repeat(false, cx));
Vim::action(editor, cx, |vim, _: &Repeat, window, cx| {
vim.repeat(false, window, cx)
});
Vim::action(editor, cx, |vim, _: &ToggleRecord, cx| {
Vim::action(editor, cx, |vim, _: &ToggleRecord, window, cx| {
let globals = Vim::globals(cx);
if let Some(char) = globals.recording_register.take() {
globals.last_recorded_register = Some(char)
} else {
vim.push_operator(Operator::RecordRegister, cx);
vim.push_operator(Operator::RecordRegister, window, cx);
}
});
Vim::action(editor, cx, |vim, _: &ReplayLastRecording, cx| {
Vim::action(editor, cx, |vim, _: &ReplayLastRecording, window, cx| {
let Some(register) = Vim::globals(cx).last_recorded_register else {
return;
};
vim.replay_register(register, cx)
vim.replay_register(register, window, cx)
});
}
@ -88,7 +89,7 @@ impl Replayer {
})))
}
pub fn replay(&mut self, actions: Vec<ReplayableAction>, cx: &mut WindowContext) {
pub fn replay(&mut self, actions: Vec<ReplayableAction>, window: &mut Window, cx: &mut App) {
let mut lock = self.0.borrow_mut();
let range = lock.ix..lock.ix;
lock.actions.splice(range, actions);
@ -97,14 +98,14 @@ impl Replayer {
}
lock.running = true;
let this = self.clone();
cx.defer(move |cx| this.next(cx))
window.defer(cx, move |window, cx| this.next(window, cx))
}
pub fn stop(self) {
self.0.borrow_mut().actions.clear()
}
pub fn next(self, cx: &mut WindowContext) {
pub fn next(self, window: &mut Window, cx: &mut App) {
let mut lock = self.0.borrow_mut();
let action = if lock.ix < 10000 {
lock.actions.get(lock.ix).cloned()
@ -121,7 +122,7 @@ impl Replayer {
match action {
ReplayableAction::Action(action) => {
if should_replay(&*action) {
cx.dispatch_action(action.boxed_clone());
window.dispatch_action(action.boxed_clone(), cx);
cx.defer(move |cx| Vim::globals(cx).observe_action(action.boxed_clone()));
}
}
@ -129,41 +130,47 @@ impl Replayer {
text,
utf16_range_to_replace,
} => {
cx.window_handle()
.update(cx, |handle, cx| {
let Ok(workspace) = handle.downcast::<Workspace>() else {
return;
};
let Some(editor) = workspace
.read(cx)
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
else {
return;
};
editor.update(cx, |editor, cx| {
editor.replay_insert_event(&text, utf16_range_to_replace.clone(), cx)
})
})
.log_err();
let Some(Some(workspace)) = window.root_model::<Workspace>() else {
return;
};
let Some(editor) = workspace
.read(cx)
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))
else {
return;
};
editor.update(cx, |editor, cx| {
editor.replay_insert_event(&text, utf16_range_to_replace.clone(), window, cx)
})
}
}
cx.defer(move |cx| self.next(cx));
window.defer(cx, move |window, cx| self.next(window, cx));
}
}
impl Vim {
pub(crate) fn record_register(&mut self, register: char, cx: &mut ViewContext<Self>) {
pub(crate) fn record_register(
&mut self,
register: char,
window: &mut Window,
cx: &mut Context<Self>,
) {
let globals = Vim::globals(cx);
globals.recording_register = Some(register);
globals.recordings.remove(&register);
globals.ignore_current_insertion = true;
self.clear_operator(cx)
self.clear_operator(window, cx)
}
pub(crate) fn replay_register(&mut self, mut register: char, cx: &mut ViewContext<Self>) {
pub(crate) fn replay_register(
&mut self,
mut register: char,
window: &mut Window,
cx: &mut Context<Self>,
) {
let mut count = Vim::take_count(cx).unwrap_or(1);
self.clear_operator(cx);
self.clear_operator(window, cx);
let globals = Vim::globals(cx);
if register == '@' {
@ -184,11 +191,17 @@ impl Vim {
globals.last_replayed_register = Some(register);
let mut replayer = globals.replayer.get_or_insert_with(Replayer::new).clone();
replayer.replay(repeated_actions, cx);
replayer.replay(repeated_actions, window, cx);
}
pub(crate) fn repeat(&mut self, from_insert_mode: bool, cx: &mut ViewContext<Self>) {
pub(crate) fn repeat(
&mut self,
from_insert_mode: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count = Vim::take_count(cx);
let Some((mut actions, selection, mode)) = Vim::update_globals(cx, |globals, _| {
let actions = globals.recorded_actions.clone();
if actions.is_empty() {
@ -231,13 +244,13 @@ impl Vim {
return;
};
if let Some(mode) = mode {
self.switch_mode(mode, false, cx)
self.switch_mode(mode, false, window, cx)
}
match selection {
RecordedSelection::SingleLine { cols } => {
if cols > 1 {
self.visual_motion(Motion::Right, Some(cols as usize - 1), cx)
self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx)
}
}
RecordedSelection::Visual { rows, cols } => {
@ -246,6 +259,7 @@ impl Vim {
display_lines: false,
},
Some(rows as usize),
window,
cx,
);
self.visual_motion(
@ -253,10 +267,11 @@ impl Vim {
display_lines: false,
},
None,
window,
cx,
);
if cols > 1 {
self.visual_motion(Motion::Right, Some(cols as usize - 1), cx)
self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx)
}
}
RecordedSelection::VisualBlock { rows, cols } => {
@ -265,10 +280,11 @@ impl Vim {
display_lines: false,
},
Some(rows as usize),
window,
cx,
);
if cols > 1 {
self.visual_motion(Motion::Right, Some(cols as usize - 1), cx);
self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx);
}
}
RecordedSelection::VisualLine { rows } => {
@ -277,6 +293,7 @@ impl Vim {
display_lines: false,
},
Some(rows as usize),
window,
cx,
);
}
@ -321,7 +338,8 @@ impl Vim {
let globals = Vim::globals(cx);
globals.dot_replaying = true;
let mut replayer = globals.replayer.get_or_insert_with(Replayer::new).clone();
replayer.replay(actions, cx);
replayer.replay(actions, window, cx);
}
}
@ -380,9 +398,9 @@ mod test {
cx.simulate_keystrokes("i");
// simulate brazilian input for ä.
cx.update_editor(|editor, cx| {
editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), cx);
editor.replace_text_in_range(None, "ä", cx);
cx.update_editor(|editor, window, cx| {
editor.replace_and_mark_text_in_range(None, "\"", Some(1..1), window, cx);
editor.replace_text_in_range(None, "ä", window, cx);
});
cx.simulate_keystrokes("escape");
cx.assert_state("hˇällo", Mode::Normal);

View file

@ -4,7 +4,7 @@ use editor::{
scroll::ScrollAmount,
DisplayPoint, Editor, EditorSettings,
};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use language::Bias;
use settings::Settings;
@ -13,21 +13,21 @@ actions!(
[LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown]
);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &LineDown, cx| {
vim.scroll(false, cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &LineDown, window, cx| {
vim.scroll(false, window, cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
});
Vim::action(editor, cx, |vim, _: &LineUp, cx| {
vim.scroll(false, cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
Vim::action(editor, cx, |vim, _: &LineUp, window, cx| {
vim.scroll(false, window, cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
});
Vim::action(editor, cx, |vim, _: &PageDown, cx| {
vim.scroll(false, cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
Vim::action(editor, cx, |vim, _: &PageDown, window, cx| {
vim.scroll(false, window, cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
});
Vim::action(editor, cx, |vim, _: &PageUp, cx| {
vim.scroll(false, cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
Vim::action(editor, cx, |vim, _: &PageUp, window, cx| {
vim.scroll(false, window, cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
});
Vim::action(editor, cx, |vim, _: &ScrollDown, cx| {
vim.scroll(true, cx, |c| {
Vim::action(editor, cx, |vim, _: &ScrollDown, window, cx| {
vim.scroll(true, window, cx, |c| {
if let Some(c) = c {
ScrollAmount::Line(c)
} else {
@ -35,8 +35,8 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
}
})
});
Vim::action(editor, cx, |vim, _: &ScrollUp, cx| {
vim.scroll(true, cx, |c| {
Vim::action(editor, cx, |vim, _: &ScrollUp, window, cx| {
vim.scroll(true, window, cx, |c| {
if let Some(c) = c {
ScrollAmount::Line(-c)
} else {
@ -50,12 +50,13 @@ impl Vim {
fn scroll(
&mut self,
move_cursor: bool,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
by: fn(c: Option<f32>) -> ScrollAmount,
) {
let amount = by(Vim::take_count(cx).map(|c| c as f32));
self.update_editor(cx, |_, editor, cx| {
scroll_editor(editor, move_cursor, &amount, cx)
self.update_editor(window, cx, |_, editor, window, cx| {
scroll_editor(editor, move_cursor, &amount, window, cx)
});
}
}
@ -64,12 +65,13 @@ fn scroll_editor(
editor: &mut Editor,
preserve_cursor_position: bool,
amount: &ScrollAmount,
cx: &mut ViewContext<Editor>,
window: &mut Window,
cx: &mut Context<Editor>,
) {
let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
let old_top_anchor = editor.scroll_manager.anchor().anchor;
if editor.scroll_hover(amount, cx) {
if editor.scroll_hover(amount, window, cx) {
return;
}
@ -85,7 +87,7 @@ fn scroll_editor(
_ => amount.clone(),
};
editor.scroll_screen(&amount, cx);
editor.scroll_screen(&amount, window, cx);
if !should_move_cursor {
return;
}
@ -97,7 +99,7 @@ fn scroll_editor(
let top_anchor = editor.scroll_manager.anchor().anchor;
let vertical_scroll_margin = EditorSettings::get_global(cx).vertical_scroll_margin;
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let mut head = selection.head();
let top = top_anchor.to_display_point(map);
@ -161,7 +163,7 @@ mod test {
test::{NeovimBackedTestContext, VimTestContext},
};
use editor::{EditorSettings, ScrollBeyondLastLine};
use gpui::{point, px, size, Context};
use gpui::{point, px, size, AppContext as _};
use indoc::indoc;
use language::Point;
use settings::SettingsStore;
@ -183,21 +185,21 @@ mod test {
async fn test_scroll(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
let (line_height, visible_line_count) = cx.editor(|editor, cx| {
let (line_height, visible_line_count) = cx.editor(|editor, window, _cx| {
(
editor
.style()
.unwrap()
.text
.line_height_in_pixels(cx.rem_size()),
.line_height_in_pixels(window.rem_size()),
editor.visible_line_count().unwrap(),
)
});
let window = cx.window;
let margin = cx
.update_window(window, |_, cx| {
cx.viewport_size().height - line_height * visible_line_count
.update_window(window, |_, window, _cx| {
window.viewport_size().height - line_height * visible_line_count
})
.unwrap();
cx.simulate_window_resize(
@ -224,30 +226,33 @@ mod test {
Mode::Normal,
);
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
});
cx.simulate_keystrokes("ctrl-e");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 1.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 1.))
});
cx.simulate_keystrokes("2 ctrl-e");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 3.))
});
cx.simulate_keystrokes("ctrl-y");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 2.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 2.))
});
// does not select in normal mode
cx.simulate_keystrokes("g g");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
});
cx.simulate_keystrokes("ctrl-d");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
cx.update_editor(|editor, window, cx| {
assert_eq!(
editor.snapshot(window, cx).scroll_position(),
point(0., 3.0)
);
assert_eq!(
editor.selections.newest(cx).range(),
Point::new(6, 0)..Point::new(6, 0)
@ -256,12 +261,15 @@ mod test {
// does select in visual mode
cx.simulate_keystrokes("g g");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 0.))
cx.update_editor(|editor, window, cx| {
assert_eq!(editor.snapshot(window, cx).scroll_position(), point(0., 0.))
});
cx.simulate_keystrokes("v ctrl-d");
cx.update_editor(|editor, cx| {
assert_eq!(editor.snapshot(cx).scroll_position(), point(0., 3.0));
cx.update_editor(|editor, window, cx| {
assert_eq!(
editor.snapshot(window, cx).scroll_position(),
point(0., 3.0)
);
assert_eq!(
editor.selections.newest(cx).range(),
Point::new(0, 0)..Point::new(6, 1)

View file

@ -1,5 +1,5 @@
use editor::Editor;
use gpui::{actions, impl_actions, impl_internal_actions, ViewContext};
use gpui::{actions, impl_actions, impl_internal_actions, Context, Window};
use language::Point;
use schemars::JsonSchema;
use search::{buffer_search, BufferSearchBar, SearchOptions};
@ -69,7 +69,7 @@ actions!(vim, [SearchSubmit, MoveToNextMatch, MoveToPrevMatch]);
impl_actions!(vim, [FindCommand, Search, MoveToPrev, MoveToNext]);
impl_internal_actions!(vim, [ReplaceCommand]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, Vim::move_to_next);
Vim::action(editor, cx, Vim::move_to_prev);
Vim::action(editor, cx, Vim::move_to_next_match);
@ -81,36 +81,48 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
}
impl Vim {
fn move_to_next(&mut self, action: &MoveToNext, cx: &mut ViewContext<Self>) {
fn move_to_next(&mut self, action: &MoveToNext, window: &mut Window, cx: &mut Context<Self>) {
self.move_to_internal(
Direction::Next,
action.case_sensitive,
!action.partial_word,
action.regex,
window,
cx,
)
}
fn move_to_prev(&mut self, action: &MoveToPrev, cx: &mut ViewContext<Self>) {
fn move_to_prev(&mut self, action: &MoveToPrev, window: &mut Window, cx: &mut Context<Self>) {
self.move_to_internal(
Direction::Prev,
action.case_sensitive,
!action.partial_word,
action.regex,
window,
cx,
)
}
fn move_to_next_match(&mut self, _: &MoveToNextMatch, cx: &mut ViewContext<Self>) {
self.move_to_match_internal(self.search.direction, cx)
fn move_to_next_match(
&mut self,
_: &MoveToNextMatch,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.move_to_match_internal(self.search.direction, window, cx)
}
fn move_to_prev_match(&mut self, _: &MoveToPrevMatch, cx: &mut ViewContext<Self>) {
self.move_to_match_internal(self.search.direction.opposite(), cx)
fn move_to_prev_match(
&mut self,
_: &MoveToPrevMatch,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.move_to_match_internal(self.search.direction.opposite(), window, cx)
}
fn search(&mut self, action: &Search, cx: &mut ViewContext<Self>) {
let Some(pane) = self.pane(cx) else {
fn search(&mut self, action: &Search, window: &mut Window, cx: &mut Context<Self>) {
let Some(pane) = self.pane(window, cx) else {
return;
};
let direction = if action.backwards {
@ -119,17 +131,17 @@ impl Vim {
Direction::Next
};
let count = Vim::take_count(cx).unwrap_or(1);
let prior_selections = self.editor_selections(cx);
let prior_selections = self.editor_selections(window, cx);
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |search_bar, cx| {
if !search_bar.show(cx) {
if !search_bar.show(window, cx) {
return;
}
let query = search_bar.query(cx);
search_bar.select_query(cx);
cx.focus_self();
search_bar.select_query(window, cx);
cx.focus_self(window);
search_bar.set_replacement(None, cx);
let mut options = SearchOptions::NONE;
@ -157,14 +169,16 @@ impl Vim {
}
// hook into the existing to clear out any vim search state on cmd+f or edit -> find.
fn search_deploy(&mut self, _: &buffer_search::Deploy, cx: &mut ViewContext<Self>) {
fn search_deploy(&mut self, _: &buffer_search::Deploy, _: &mut Window, cx: &mut Context<Self>) {
self.search = Default::default();
cx.propagate();
}
pub fn search_submit(&mut self, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
let Some(pane) = self.pane(cx) else { return };
pub fn search_submit(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.store_visual_marks(window, cx);
let Some(pane) = self.pane(window, cx) else {
return;
};
let result = pane.update(cx, |pane, cx| {
let search_bar = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()?;
search_bar.update(cx, |search_bar, cx| {
@ -178,8 +192,8 @@ impl Vim {
count = count.saturating_sub(1)
}
self.search.count = 1;
search_bar.select_match(direction, count, cx);
search_bar.focus_editor(&Default::default(), cx);
search_bar.select_match(direction, count, window, cx);
search_bar.focus_editor(&Default::default(), window, cx);
let prior_selections: Vec<_> = self.search.prior_selections.drain(..).collect();
let prior_mode = self.search.prior_mode;
@ -195,12 +209,13 @@ impl Vim {
return;
};
let new_selections = self.editor_selections(cx);
let new_selections = self.editor_selections(window, cx);
// If the active editor has changed during a search, don't panic.
if prior_selections.iter().any(|s| {
self.update_editor(cx, |_, editor, cx| {
!s.start.is_valid(&editor.snapshot(cx).buffer_snapshot)
self.update_editor(window, cx, |_, editor, window, cx| {
!s.start
.is_valid(&editor.snapshot(window, cx).buffer_snapshot)
})
.unwrap_or(true)
}) {
@ -208,34 +223,42 @@ impl Vim {
}
if prior_mode != self.mode {
self.switch_mode(prior_mode, true, cx);
self.switch_mode(prior_mode, true, window, cx);
}
if let Some(operator) = prior_operator {
self.push_operator(operator, cx);
self.push_operator(operator, window, cx);
};
self.search_motion(
Motion::ZedSearchResult {
prior_selections,
new_selections,
},
window,
cx,
);
}
pub fn move_to_match_internal(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
let Some(pane) = self.pane(cx) else { return };
pub fn move_to_match_internal(
&mut self,
direction: Direction,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(pane) = self.pane(window, cx) else {
return;
};
let count = Vim::take_count(cx).unwrap_or(1);
let prior_selections = self.editor_selections(cx);
let prior_selections = self.editor_selections(window, cx);
let success = pane.update(cx, |pane, cx| {
let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
return false;
};
search_bar.update(cx, |search_bar, cx| {
if !search_bar.has_active_match() || !search_bar.show(cx) {
if !search_bar.has_active_match() || !search_bar.show(window, cx) {
return false;
}
search_bar.select_match(direction, count, cx);
search_bar.select_match(direction, count, window, cx);
true
})
});
@ -243,12 +266,13 @@ impl Vim {
return;
}
let new_selections = self.editor_selections(cx);
let new_selections = self.editor_selections(window, cx);
self.search_motion(
Motion::ZedSearchResult {
prior_selections,
new_selections,
},
window,
cx,
);
}
@ -259,12 +283,15 @@ impl Vim {
case_sensitive: bool,
whole_word: bool,
regex: bool,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(pane) = self.pane(cx) else { return };
let Some(pane) = self.pane(window, cx) else {
return;
};
let count = Vim::take_count(cx).unwrap_or(1);
let prior_selections = self.editor_selections(cx);
let vim = cx.view().clone();
let prior_selections = self.editor_selections(window, cx);
let vim = cx.model().clone();
let searched = pane.update(cx, |pane, cx| {
self.search.direction = direction;
@ -282,32 +309,33 @@ impl Vim {
if whole_word {
options |= SearchOptions::WHOLE_WORD;
}
if !search_bar.show(cx) {
if !search_bar.show(window, cx) {
return None;
}
let Some(query) = search_bar.query_suggestion(cx) else {
drop(search_bar.search("", None, cx));
let Some(query) = search_bar.query_suggestion(window, cx) else {
drop(search_bar.search("", None, window, cx));
return None;
};
let query = regex::escape(&query);
Some(search_bar.search(&query, Some(options), cx))
Some(search_bar.search(&query, Some(options), window, cx))
});
let Some(search) = search else { return false };
let search_bar = search_bar.downgrade();
cx.spawn(|_, mut cx| async move {
cx.spawn_in(window, |_, mut cx| async move {
search.await?;
search_bar.update(&mut cx, |search_bar, cx| {
search_bar.select_match(direction, count, cx);
search_bar.update_in(&mut cx, |search_bar, window, cx| {
search_bar.select_match(direction, count, window, cx);
vim.update(cx, |vim, cx| {
let new_selections = vim.editor_selections(cx);
let new_selections = vim.editor_selections(window, cx);
vim.search_motion(
Motion::ZedSearchResult {
prior_selections,
new_selections,
},
window,
cx,
)
});
@ -318,20 +346,22 @@ impl Vim {
true
});
if !searched {
self.clear_operator(cx)
self.clear_operator(window, cx)
}
if self.mode.is_visual() {
self.switch_mode(Mode::Normal, false, cx)
self.switch_mode(Mode::Normal, false, window, cx)
}
}
fn find_command(&mut self, action: &FindCommand, cx: &mut ViewContext<Self>) {
let Some(pane) = self.pane(cx) else { return };
fn find_command(&mut self, action: &FindCommand, window: &mut Window, cx: &mut Context<Self>) {
let Some(pane) = self.pane(window, cx) else {
return;
};
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
let search = search_bar.update(cx, |search_bar, cx| {
if !search_bar.show(cx) {
if !search_bar.show(window, cx) {
return None;
}
let mut query = action.query.clone();
@ -347,7 +377,7 @@ impl Vim {
);
}
Some(search_bar.search(&query, Some(options), cx))
Some(search_bar.search(&query, Some(options), window, cx))
});
let Some(search) = search else { return };
let search_bar = search_bar.downgrade();
@ -356,10 +386,10 @@ impl Vim {
} else {
Direction::Next
};
cx.spawn(|_, mut cx| async move {
cx.spawn_in(window, |_, mut cx| async move {
search.await?;
search_bar.update(&mut cx, |search_bar, cx| {
search_bar.select_match(direction, 1, cx)
search_bar.update_in(&mut cx, |search_bar, window, cx| {
search_bar.select_match(direction, 1, window, cx)
})?;
anyhow::Ok(())
})
@ -368,16 +398,23 @@ impl Vim {
})
}
fn replace_command(&mut self, action: &ReplaceCommand, cx: &mut ViewContext<Self>) {
fn replace_command(
&mut self,
action: &ReplaceCommand,
window: &mut Window,
cx: &mut Context<Self>,
) {
let replacement = action.replacement.clone();
let Some(((pane, workspace), editor)) =
self.pane(cx).zip(self.workspace(cx)).zip(self.editor())
let Some(((pane, workspace), editor)) = self
.pane(window, cx)
.zip(self.workspace(window))
.zip(self.editor())
else {
return;
};
if let Some(result) = self.update_editor(cx, |vim, editor, cx| {
let range = action.range.buffer_range(vim, editor, cx)?;
let snapshot = &editor.snapshot(cx).buffer_snapshot;
if let Some(result) = self.update_editor(window, cx, |vim, editor, window, cx| {
let range = action.range.buffer_range(vim, editor, window, cx)?;
let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
let end_point = Point::new(range.end.0, snapshot.line_len(range.end));
let range = snapshot.anchor_before(Point::new(range.start.0, 0))
..snapshot.anchor_after(end_point);
@ -388,13 +425,13 @@ impl Vim {
result.notify_err(workspace, cx);
})
}
let vim = cx.view().clone();
let vim = cx.model().clone();
pane.update(cx, |pane, cx| {
let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() else {
return;
};
let search = search_bar.update(cx, |search_bar, cx| {
if !search_bar.show(cx) {
if !search_bar.show(window, cx) {
return None;
}
@ -414,16 +451,16 @@ impl Vim {
);
}
search_bar.set_replacement(Some(&replacement.replacement), cx);
Some(search_bar.search(&search, Some(options), cx))
Some(search_bar.search(&search, Some(options), window, cx))
});
let Some(search) = search else { return };
let search_bar = search_bar.downgrade();
cx.spawn(|_, mut cx| async move {
cx.spawn_in(window, |_, mut cx| async move {
search.await?;
search_bar.update(&mut cx, |search_bar, cx| {
search_bar.update_in(&mut cx, |search_bar, window, cx| {
if replacement.should_replace_all {
search_bar.select_last_match(cx);
search_bar.replace_all(&Default::default(), cx);
search_bar.select_last_match(window, cx);
search_bar.replace_all(&Default::default(), window, cx);
cx.spawn(|_, mut cx| async move {
cx.background_executor()
.timer(Duration::from_millis(200))
@ -439,6 +476,7 @@ impl Vim {
display_lines: false,
},
None,
window,
cx,
)
});
@ -621,7 +659,7 @@ mod test {
cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
cx.simulate_keystrokes("/ c c");
let search_bar = cx.workspace(|workspace, cx| {
let search_bar = cx.workspace(|workspace, _, cx| {
workspace
.active_pane()
.read(cx)
@ -631,14 +669,14 @@ mod test {
.expect("Buffer search bar should be deployed")
});
cx.update_view(search_bar, |bar, cx| {
cx.update_model(search_bar, |bar, _window, cx| {
assert_eq!(bar.query(cx), "cc");
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
let highlights = editor.all_text_background_highlights(cx);
cx.update_editor(|editor, window, cx| {
let highlights = editor.all_text_background_highlights(window, cx);
assert_eq!(3, highlights.len());
assert_eq!(
DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
@ -679,7 +717,9 @@ mod test {
cx.simulate_keystrokes("/ d");
cx.simulate_keystrokes("enter");
cx.assert_state("aa\nbb\nˇdd\ncc\nbb\n", Mode::Normal);
cx.update_editor(|editor, cx| editor.move_to_beginning(&Default::default(), cx));
cx.update_editor(|editor, window, cx| {
editor.move_to_beginning(&Default::default(), window, cx)
});
cx.assert_state("ˇaa\nbb\ndd\ncc\nbb\n", Mode::Normal);
cx.simulate_keystrokes("/ b");
cx.simulate_keystrokes("enter");

View file

@ -1,25 +1,25 @@
use editor::{movement, Editor};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use language::Point;
use crate::{motion::Motion, Mode, Vim};
actions!(vim, [Substitute, SubstituteLine]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Substitute, cx| {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &Substitute, window, cx| {
vim.start_recording(cx);
let count = Vim::take_count(cx);
vim.substitute(count, vim.mode == Mode::VisualLine, cx);
vim.substitute(count, vim.mode == Mode::VisualLine, window, cx);
});
Vim::action(editor, cx, |vim, _: &SubstituteLine, cx| {
Vim::action(editor, cx, |vim, _: &SubstituteLine, window, cx| {
vim.start_recording(cx);
if matches!(vim.mode, Mode::VisualBlock | Mode::Visual) {
vim.switch_mode(Mode::VisualLine, false, cx)
vim.switch_mode(Mode::VisualLine, false, window, cx)
}
let count = Vim::take_count(cx);
vim.substitute(count, true, cx)
vim.substitute(count, true, window, cx)
});
}
@ -28,14 +28,15 @@ impl Vim {
&mut self,
count: Option<usize>,
line_mode: bool,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
editor.transact(cx, |editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.change_selections(None, cx, |s| {
editor.transact(window, cx, |editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
if selection.start == selection.end {
Motion::Right.expand_selection(
@ -80,7 +81,7 @@ impl Vim {
editor.edit(edits, cx);
});
});
self.switch_mode(Mode::Insert, true, cx);
self.switch_mode(Mode::Insert, true, window, cx);
}
}

View file

@ -1,30 +1,31 @@
use crate::{motion::Motion, object::Object, Vim};
use collections::HashMap;
use editor::{display_map::ToDisplayPoint, Bias};
use gpui::{Context, Window};
use language::SelectionGoal;
use ui::ViewContext;
impl Vim {
pub fn toggle_comments_motion(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let mut selection_starts: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
selection_starts.insert(selection.id, anchor);
motion.expand_selection(map, selection, times, false, &text_layout_details);
});
});
editor.toggle_comments(&Default::default(), cx);
editor.change_selections(None, cx, |s| {
editor.toggle_comments(&Default::default(), window, cx);
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = selection_starts.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
@ -38,21 +39,22 @@ impl Vim {
&mut self,
object: Object,
around: bool,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
original_positions.insert(selection.id, anchor);
object.expand_selection(map, selection, around);
});
});
editor.toggle_comments(&Default::default(), cx);
editor.change_selections(None, cx, |s| {
editor.toggle_comments(&Default::default(), window, cx);
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = original_positions.remove(&selection.id).unwrap();
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);

View file

@ -8,7 +8,8 @@ use crate::{
};
use collections::HashMap;
use editor::{ClipboardSelection, Editor};
use gpui::ViewContext;
use gpui::Context;
use gpui::Window;
use language::Point;
use multi_buffer::MultiBufferRow;
use settings::Settings;
@ -20,14 +21,15 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(cx, |vim, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let original_position = (selection.head(), selection.goal);
original_positions.insert(selection.id, original_position);
@ -35,7 +37,7 @@ impl Vim {
});
});
vim.yank_selections_content(editor, motion.linewise(), cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|_, selection| {
let (head, goal) = original_positions.remove(&selection.id).unwrap();
selection.collapse_to(head, goal);
@ -43,15 +45,21 @@ impl Vim {
});
});
});
self.exit_temporary_normal(cx);
self.exit_temporary_normal(window, cx);
}
pub fn yank_object(&mut self, object: Object, around: bool, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
pub fn yank_object(
&mut self,
object: Object,
around: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let original_position = (selection.head(), selection.goal);
object.expand_selection(map, selection, around);
@ -59,7 +67,7 @@ impl Vim {
});
});
vim.yank_selections_content(editor, false, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|_, selection| {
let (head, goal) = original_positions.remove(&selection.id).unwrap();
selection.collapse_to(head, goal);
@ -67,14 +75,14 @@ impl Vim {
});
});
});
self.exit_temporary_normal(cx);
self.exit_temporary_normal(window, cx);
}
pub fn yank_selections_content(
&mut self,
editor: &mut Editor,
linewise: bool,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) {
self.copy_ranges(
editor,
@ -94,7 +102,7 @@ impl Vim {
&mut self,
editor: &mut Editor,
linewise: bool,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) {
self.copy_ranges(
editor,
@ -116,7 +124,7 @@ impl Vim {
linewise: bool,
is_yank: bool,
selections: Vec<Range<Point>>,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) {
let buffer = editor.buffer().read(cx).snapshot(cx);
let mut text = String::new();

View file

@ -10,12 +10,13 @@ use editor::{
movement::{self, FindRange},
Bias, DisplayPoint, Editor,
};
use gpui::{actions, impl_actions, ViewContext};
use gpui::{actions, impl_actions, Window};
use itertools::Itertools;
use language::{BufferSnapshot, CharKind, Point, Selection, TextObject, TreeSitterOptions};
use multi_buffer::MultiBufferRow;
use schemars::JsonSchema;
use serde::Deserialize;
use ui::Context;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema)]
pub enum Object {
@ -84,84 +85,88 @@ actions!(
]
);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(
editor,
cx,
|vim, &Word { ignore_punctuation }: &Word, cx| {
vim.object(Object::Word { ignore_punctuation }, cx)
|vim, &Word { ignore_punctuation }: &Word, window, cx| {
vim.object(Object::Word { ignore_punctuation }, window, cx)
},
);
Vim::action(
editor,
cx,
|vim, &Subword { ignore_punctuation }: &Subword, cx| {
vim.object(Object::Subword { ignore_punctuation }, cx)
|vim, &Subword { ignore_punctuation }: &Subword, window, cx| {
vim.object(Object::Subword { ignore_punctuation }, window, cx)
},
);
Vim::action(editor, cx, |vim, _: &Tag, cx| vim.object(Object::Tag, cx));
Vim::action(editor, cx, |vim, _: &Sentence, cx| {
vim.object(Object::Sentence, cx)
Vim::action(editor, cx, |vim, _: &Tag, window, cx| {
vim.object(Object::Tag, window, cx)
});
Vim::action(editor, cx, |vim, _: &Paragraph, cx| {
vim.object(Object::Paragraph, cx)
Vim::action(editor, cx, |vim, _: &Sentence, window, cx| {
vim.object(Object::Sentence, window, cx)
});
Vim::action(editor, cx, |vim, _: &Quotes, cx| {
vim.object(Object::Quotes, cx)
Vim::action(editor, cx, |vim, _: &Paragraph, window, cx| {
vim.object(Object::Paragraph, window, cx)
});
Vim::action(editor, cx, |vim, _: &BackQuotes, cx| {
vim.object(Object::BackQuotes, cx)
Vim::action(editor, cx, |vim, _: &Quotes, window, cx| {
vim.object(Object::Quotes, window, cx)
});
Vim::action(editor, cx, |vim, _: &AnyQuotes, cx| {
vim.object(Object::AnyQuotes, cx)
Vim::action(editor, cx, |vim, _: &AnyQuotes, window, cx| {
vim.object(Object::AnyQuotes, window, cx)
});
Vim::action(editor, cx, |vim, _: &DoubleQuotes, cx| {
vim.object(Object::DoubleQuotes, cx)
Vim::action(editor, cx, |vim, _: &DoubleQuotes, window, cx| {
vim.object(Object::DoubleQuotes, window, cx)
});
Vim::action(editor, cx, |vim, _: &Parentheses, cx| {
vim.object(Object::Parentheses, cx)
Vim::action(editor, cx, |vim, _: &DoubleQuotes, window, cx| {
vim.object(Object::DoubleQuotes, window, cx)
});
Vim::action(editor, cx, |vim, _: &SquareBrackets, cx| {
vim.object(Object::SquareBrackets, cx)
Vim::action(editor, cx, |vim, _: &Parentheses, window, cx| {
vim.object(Object::Parentheses, window, cx)
});
Vim::action(editor, cx, |vim, _: &CurlyBrackets, cx| {
vim.object(Object::CurlyBrackets, cx)
Vim::action(editor, cx, |vim, _: &SquareBrackets, window, cx| {
vim.object(Object::SquareBrackets, window, cx)
});
Vim::action(editor, cx, |vim, _: &AngleBrackets, cx| {
vim.object(Object::AngleBrackets, cx)
Vim::action(editor, cx, |vim, _: &CurlyBrackets, window, cx| {
vim.object(Object::CurlyBrackets, window, cx)
});
Vim::action(editor, cx, |vim, _: &VerticalBars, cx| {
vim.object(Object::VerticalBars, cx)
Vim::action(editor, cx, |vim, _: &AngleBrackets, window, cx| {
vim.object(Object::AngleBrackets, window, cx)
});
Vim::action(editor, cx, |vim, _: &Argument, cx| {
vim.object(Object::Argument, cx)
Vim::action(editor, cx, |vim, _: &VerticalBars, window, cx| {
vim.object(Object::VerticalBars, window, cx)
});
Vim::action(editor, cx, |vim, _: &Method, cx| {
vim.object(Object::Method, cx)
Vim::action(editor, cx, |vim, _: &Argument, window, cx| {
vim.object(Object::Argument, window, cx)
});
Vim::action(editor, cx, |vim, _: &Class, cx| {
vim.object(Object::Class, cx)
Vim::action(editor, cx, |vim, _: &Method, window, cx| {
vim.object(Object::Method, window, cx)
});
Vim::action(editor, cx, |vim, _: &Comment, cx| {
Vim::action(editor, cx, |vim, _: &Class, window, cx| {
vim.object(Object::Class, window, cx)
});
Vim::action(editor, cx, |vim, _: &Comment, window, cx| {
if !matches!(vim.active_operator(), Some(Operator::Object { .. })) {
vim.push_operator(Operator::Object { around: true }, cx);
vim.push_operator(Operator::Object { around: true }, window, cx);
}
vim.object(Object::Comment, cx)
vim.object(Object::Comment, window, cx)
});
Vim::action(
editor,
cx,
|vim, &IndentObj { include_below }: &IndentObj, cx| {
vim.object(Object::IndentObj { include_below }, cx)
|vim, &IndentObj { include_below }: &IndentObj, window, cx| {
vim.object(Object::IndentObj { include_below }, window, cx)
},
);
}
impl Vim {
fn object(&mut self, object: Object, cx: &mut ViewContext<Self>) {
fn object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Self>) {
match self.mode {
Mode::Normal => self.normal_object(object, cx),
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => self.visual_object(object, cx),
Mode::Normal => self.normal_object(object, window, cx),
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
self.visual_object(object, window, cx)
}
Mode::Insert | Mode::Replace | Mode::HelixNormal => {
// Shouldn't execute a text object in insert mode. Ignoring
}

View file

@ -4,35 +4,40 @@ use crate::{
Vim,
};
use editor::{display_map::ToDisplayPoint, Bias, Editor, ToPoint};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use language::Point;
use std::ops::Range;
use std::sync::Arc;
actions!(vim, [ToggleReplace, UndoReplace]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &ToggleReplace, cx| {
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &ToggleReplace, window, cx| {
vim.replacements = vec![];
vim.start_recording(cx);
vim.switch_mode(Mode::Replace, false, cx);
vim.switch_mode(Mode::Replace, false, window, cx);
});
Vim::action(editor, cx, |vim, _: &UndoReplace, cx| {
Vim::action(editor, cx, |vim, _: &UndoReplace, window, cx| {
if vim.mode != Mode::Replace {
return;
}
let count = Vim::take_count(cx);
vim.undo_replace(count, cx)
vim.undo_replace(count, window, cx)
});
}
impl Vim {
pub(crate) fn multi_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
pub(crate) fn multi_replace(
&mut self,
text: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let map = editor.snapshot(cx);
let map = editor.snapshot(window, cx);
let display_selections = editor.selections.all::<Point>(cx);
// Handles all string that require manipulation, including inserts and replaces
@ -60,7 +65,7 @@ impl Vim {
editor.edit_with_block_indent(edits.clone(), Vec::new(), cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.select_anchor_ranges(edits.iter().map(|(range, _)| range.end..range.end));
});
editor.set_clip_at_line_ends(true, cx);
@ -68,11 +73,16 @@ impl Vim {
});
}
fn undo_replace(&mut self, maybe_times: Option<usize>, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
fn undo_replace(
&mut self,
maybe_times: Option<usize>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let map = editor.snapshot(cx);
let map = editor.snapshot(window, cx);
let selections = editor.selections.all::<Point>(cx);
let mut new_selections = vec![];
let edits: Vec<(Range<Point>, String)> = selections
@ -107,7 +117,7 @@ impl Vim {
editor.edit(edits, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.select_ranges(new_selections);
});
editor.set_clip_at_line_ends(true, cx);

View file

@ -1,22 +1,21 @@
use crate::{motion::Motion, object::Object, state::Mode, Vim};
use collections::HashMap;
use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias, Editor, IsVimMode};
use gpui::actions;
use gpui::{actions, Context, Window};
use language::SelectionGoal;
use ui::ViewContext;
actions!(vim, [Rewrap]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Rewrap, cx| {
pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &Rewrap, window, cx| {
vim.record_current_action(cx);
Vim::take_count(cx);
vim.store_visual_marks(cx);
vim.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
vim.store_visual_marks(window, cx);
vim.update_editor(window, cx, |vim, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let mut positions = vim.save_selection_starts(editor, cx);
editor.rewrap_impl(IsVimMode::Yes, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
if let Some(anchor) = positions.remove(&selection.id) {
let mut point = anchor.to_display_point(map);
@ -28,7 +27,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
});
});
if vim.mode.is_visual() {
vim.switch_mode(Mode::Normal, true, cx)
vim.switch_mode(Mode::Normal, true, window, cx)
}
});
}
@ -38,14 +37,15 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
let mut selection_starts: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
selection_starts.insert(selection.id, anchor);
@ -53,7 +53,7 @@ impl Vim {
});
});
editor.rewrap_impl(IsVimMode::Yes, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = selection_starts.remove(&selection.id).unwrap();
let mut point = anchor.to_display_point(map);
@ -69,13 +69,14 @@ impl Vim {
&mut self,
object: Object,
around: bool,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let mut original_positions: HashMap<_, _> = Default::default();
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = map.display_point_to_anchor(selection.head(), Bias::Right);
original_positions.insert(selection.id, anchor);
@ -83,7 +84,7 @@ impl Vim {
});
});
editor.rewrap_impl(IsVimMode::Yes, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
let anchor = original_positions.remove(&selection.id).unwrap();
let mut point = anchor.to_display_point(map);

View file

@ -7,7 +7,7 @@ use collections::HashMap;
use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
use editor::{Anchor, ClipboardSelection, Editor};
use gpui::{
Action, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, Global, View, WeakView,
Action, App, BorrowAppContext, ClipboardEntry, ClipboardItem, Entity, Global, WeakEntity,
};
use language::Point;
use schemars::JsonSchema;
@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use std::borrow::BorrowMut;
use std::{fmt::Display, ops::Range, sync::Arc};
use ui::{SharedString, ViewContext};
use ui::{Context, SharedString};
use workspace::searchable::Direction;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)]
@ -199,15 +199,15 @@ pub struct VimGlobals {
pub registers: HashMap<char, Register>,
pub recordings: HashMap<char, Vec<ReplayableAction>>,
pub focused_vim: Option<WeakView<Vim>>,
pub focused_vim: Option<WeakEntity<Vim>>,
}
impl Global for VimGlobals {}
impl VimGlobals {
pub(crate) fn register(cx: &mut AppContext) {
pub(crate) fn register(cx: &mut App) {
cx.set_global(VimGlobals::default());
cx.observe_keystrokes(|event, cx| {
cx.observe_keystrokes(|event, _, cx| {
let Some(action) = event.action.as_ref().map(|action| action.boxed_clone()) else {
return;
};
@ -242,7 +242,7 @@ impl VimGlobals {
register: Option<char>,
is_yank: bool,
linewise: bool,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) {
if let Some(register) = register {
let lower = register.to_lowercase().next().unwrap_or(register);
@ -316,7 +316,7 @@ impl VimGlobals {
&mut self,
register: Option<char>,
editor: Option<&mut Editor>,
cx: &mut ViewContext<Editor>,
cx: &mut Context<Editor>,
) -> Option<Register> {
let Some(register) = register.filter(|reg| *reg != '"') else {
let setting = VimSettings::get_global(cx).use_system_clipboard;
@ -361,7 +361,7 @@ impl VimGlobals {
}
}
fn system_clipboard_is_newer(&self, cx: &ViewContext<Editor>) -> bool {
fn system_clipboard_is_newer(&self, cx: &mut Context<Editor>) -> bool {
cx.read_from_clipboard().is_some_and(|item| {
if let Some(last_state) = &self.last_yank {
Some(last_state.as_ref()) != item.text().as_deref()
@ -418,19 +418,19 @@ impl VimGlobals {
}
}
pub fn focused_vim(&self) -> Option<View<Vim>> {
pub fn focused_vim(&self) -> Option<Entity<Vim>> {
self.focused_vim.as_ref().and_then(|vim| vim.upgrade())
}
}
impl Vim {
pub fn globals(cx: &mut AppContext) -> &mut VimGlobals {
pub fn globals(cx: &mut App) -> &mut VimGlobals {
cx.global_mut::<VimGlobals>()
}
pub fn update_globals<C, R>(cx: &mut C, f: impl FnOnce(&mut VimGlobals, &mut C) -> R) -> R
where
C: BorrowMut<AppContext>,
C: BorrowMut<App>,
{
cx.update_global(f)
}

View file

@ -5,10 +5,10 @@ use crate::{
Vim,
};
use editor::{movement, scroll::Autoscroll, Bias};
use gpui::{Context, Window};
use language::BracketPair;
use std::sync::Arc;
use ui::ViewContext;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SurroundsType {
@ -22,14 +22,15 @@ impl Vim {
&mut self,
text: Arc<str>,
target: SurroundsType,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
let count = Vim::take_count(cx);
let mode = self.mode;
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let pair = match find_surround_pair(&all_support_surround_pair(), &text) {
@ -111,7 +112,7 @@ impl Vim {
editor.edit(edits, cx);
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
if mode == Mode::VisualBlock {
s.select_anchor_ranges(anchors.into_iter().take(1))
} else {
@ -120,10 +121,15 @@ impl Vim {
});
});
});
self.switch_mode(Mode::Normal, false, cx);
self.switch_mode(Mode::Normal, false, window, cx);
}
pub fn delete_surrounds(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
pub fn delete_surrounds(
&mut self,
text: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
// only legitimate surrounds can be removed
@ -137,8 +143,8 @@ impl Vim {
};
let surround = pair.end != *text;
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (display_map, display_selections) = editor.selections.all_display(cx);
@ -204,7 +210,7 @@ impl Vim {
}
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges(anchors);
});
edits.sort_by_key(|(range, _)| range.start);
@ -214,11 +220,17 @@ impl Vim {
});
}
pub fn change_surrounds(&mut self, text: Arc<str>, target: Object, cx: &mut ViewContext<Self>) {
pub fn change_surrounds(
&mut self,
text: Arc<str>,
target: Object,
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(will_replace_pair) = object_to_bracket_pair(target) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let pair = match find_surround_pair(&all_support_surround_pair(), &text) {
@ -308,7 +320,7 @@ impl Vim {
edits.sort_by_key(|(range, _)| range.start);
editor.edit(edits, cx);
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_anchor_ranges(stable_anchors);
});
});
@ -326,12 +338,13 @@ impl Vim {
pub fn check_and_move_to_valid_bracket_pair(
&mut self,
object: Object,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) -> bool {
let mut valid = false;
if let Some(pair) = object_to_bracket_pair(object) {
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
let mut anchors = Vec::new();
@ -365,7 +378,7 @@ impl Vim {
anchors.push(start..start)
}
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges(anchors);
});
editor.set_clip_at_line_ends(true, cx);
@ -733,7 +746,7 @@ mod test {
async fn test_add_surrounds_visual(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"shift-s",
PushOperator(Operator::AddSurrounds { target: None }),

View file

@ -58,9 +58,9 @@ async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
// Selections aren't changed if editor is blurred but vim-mode is still disabled.
cx.cx.set_state("«hjklˇ»");
cx.assert_editor_state("«hjklˇ»");
cx.update_editor(|_, cx| cx.blur());
cx.update_editor(|_, window, _cx| window.blur());
cx.assert_editor_state("«hjklˇ»");
cx.update_editor(|_, cx| cx.focus_self());
cx.update_editor(|_, window, cx| cx.focus_self(window));
cx.assert_editor_state("«hjklˇ»");
// Enabling dynamically sets vim mode again and restores normal mode
@ -116,7 +116,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
);
cx.simulate_keystrokes("/");
let search_bar = cx.workspace(|workspace, cx| {
let search_bar = cx.workspace(|workspace, _, cx| {
workspace
.active_pane()
.read(cx)
@ -126,7 +126,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
.expect("Buffer search bar should be deployed")
});
cx.update_view(search_bar, |bar, cx| {
cx.update_model(search_bar, |bar, _, cx| {
assert_eq!(bar.query(cx), "");
})
}
@ -229,10 +229,12 @@ async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
cx.set_state("aˇbc\n", Mode::Normal);
cx.simulate_keystrokes("i cmd-shift-p");
assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
assert!(cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
cx.simulate_keystrokes("escape");
cx.run_until_parked();
assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
assert!(
!cx.workspace(|workspace, _, cx| workspace.active_modal::<CommandPalette>(cx).is_some())
);
cx.assert_state("aˇbc\n", Mode::Insert);
}
@ -253,7 +255,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
cx.simulate_keystrokes("/ c c");
let search_bar = cx.workspace(|workspace, cx| {
let search_bar = cx.workspace(|workspace, _, cx| {
workspace
.active_pane()
.read(cx)
@ -263,12 +265,12 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
.expect("Buffer search bar should be deployed")
});
cx.update_view(search_bar, |bar, cx| {
cx.update_model(search_bar, |bar, _, cx| {
assert_eq!(bar.query(cx), "cc");
});
cx.update_editor(|editor, cx| {
let highlights = editor.all_text_background_highlights(cx);
cx.update_editor(|editor, window, cx| {
let highlights = editor.all_text_background_highlights(window, cx);
assert_eq!(3, highlights.len());
assert_eq!(
DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
@ -843,7 +845,7 @@ async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
async fn test_jk(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"j k",
NormalBefore,
@ -861,7 +863,7 @@ async fn test_jk(cx: &mut gpui::TestAppContext) {
async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"j k",
NormalBefore,
@ -885,7 +887,7 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
async fn test_comma_w(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
", w",
motion::Down {
@ -952,7 +954,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
// test moving the cursor
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g z",
workspace::SendKeystrokes("l l l l".to_string()),
@ -964,7 +966,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
cx.assert_state("1234ˇ56789", Mode::Normal);
// test switching modes
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g y",
workspace::SendKeystrokes("i f o o escape l".to_string()),
@ -976,7 +978,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
cx.assert_state("fooˇ123456789", Mode::Normal);
// test recursion
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g x",
workspace::SendKeystrokes("g z g y".to_string()),
@ -990,7 +992,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
cx.executor().allow_parking();
// test command
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g w",
workspace::SendKeystrokes(": j enter".to_string()),
@ -1002,7 +1004,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
cx.assert_state("1234ˇ 56789", Mode::Normal);
// test leaving command
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g u",
workspace::SendKeystrokes("g w g z".to_string()),
@ -1014,7 +1016,7 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
cx.assert_state("1234 567ˇ89", Mode::Normal);
// test leaving command
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"g t",
workspace::SendKeystrokes("i space escape".to_string()),
@ -1341,7 +1343,7 @@ async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
async fn test_sneak(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|cx| {
cx.update(|_window, cx| {
cx.bind_keys([
KeyBinding::new(
"s",
@ -1437,7 +1439,7 @@ async fn test_command_alias(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([
KeyBinding::new(
"d o g",
@ -1470,7 +1472,7 @@ async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([
KeyBinding::new(
"p i n",
@ -1513,7 +1515,7 @@ async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"x",
workspace::SendKeystrokes("\" _ x".to_string()),
@ -1547,7 +1549,7 @@ async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.update(|cx| {
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
});
cx.neovim.exec("map <c-w> D").await;

View file

@ -1,4 +1,4 @@
use gpui::{px, size, Context, UpdateGlobal};
use gpui::{px, size, AppContext as _, UpdateGlobal};
use indoc::indoc;
use settings::SettingsStore;
use std::{
@ -222,7 +222,7 @@ impl NeovimBackedTestContext {
.set_option(&format!("columns={}", columns))
.await;
self.update(|cx| {
self.update(|_, cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.defaults.soft_wrap = Some(SoftWrap::PreferredLineLength);
@ -237,21 +237,21 @@ impl NeovimBackedTestContext {
self.neovim.set_option(&format!("scrolloff={}", 3)).await;
// +2 to account for the vim command UI at the bottom.
self.neovim.set_option(&format!("lines={}", rows + 2)).await;
let (line_height, visible_line_count) = self.editor(|editor, cx| {
let (line_height, visible_line_count) = self.editor(|editor, window, _cx| {
(
editor
.style()
.unwrap()
.text
.line_height_in_pixels(cx.rem_size()),
.line_height_in_pixels(window.rem_size()),
editor.visible_line_count().unwrap(),
)
});
let window = self.window;
let margin = self
.update_window(window, |_, cx| {
cx.viewport_size().height - line_height * visible_line_count
.update_window(window, |_, window, _cx| {
window.viewport_size().height - line_height * visible_line_count
})
.unwrap();
@ -286,7 +286,7 @@ impl NeovimBackedTestContext {
register,
state: self.shared_state().await,
neovim: self.neovim.read_register(register).await,
editor: self.update(|cx| {
editor: self.update(|_, cx| {
cx.global::<VimGlobals>()
.registers
.get(&register)

View file

@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
use assets::Assets;
use editor::test::editor_lsp_test_context::EditorLspTestContext;
use gpui::{Context, SemanticVersion, UpdateGlobal, View, VisualContext};
use gpui::{Context, Entity, SemanticVersion, UpdateGlobal};
use search::{project_search::ProjectSearchBar, BufferSearchBar};
use crate::{state::Operator, *};
@ -57,7 +57,7 @@ impl VimTestContext {
}
pub fn new_with_lsp(mut cx: EditorLspTestContext, enabled: bool) -> VimTestContext {
cx.update(|cx| {
cx.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
});
@ -75,44 +75,46 @@ impl VimTestContext {
});
// Setup search toolbars and keypress hook
cx.update_workspace(|workspace, cx| {
cx.update_workspace(|workspace, window, cx| {
workspace.active_pane().update(cx, |pane, cx| {
pane.toolbar().update(cx, |toolbar, cx| {
let buffer_search_bar = cx.new_view(BufferSearchBar::new);
toolbar.add_item(buffer_search_bar, cx);
let buffer_search_bar = cx.new(|cx| BufferSearchBar::new(window, cx));
toolbar.add_item(buffer_search_bar, window, cx);
let project_search_bar = cx.new_view(|_| ProjectSearchBar::new());
toolbar.add_item(project_search_bar, cx);
let project_search_bar = cx.new(|_| ProjectSearchBar::new());
toolbar.add_item(project_search_bar, window, cx);
})
});
workspace.status_bar().update(cx, |status_bar, cx| {
let vim_mode_indicator = cx.new_view(ModeIndicator::new);
status_bar.add_right_item(vim_mode_indicator, cx);
let vim_mode_indicator = cx.new(|cx| ModeIndicator::new(window, cx));
status_bar.add_right_item(vim_mode_indicator, window, cx);
});
});
Self { cx }
}
pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
pub fn update_model<F, T, R>(&mut self, model: Entity<T>, update: F) -> R
where
T: 'static,
F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
F: FnOnce(&mut T, &mut Window, &mut Context<T>) -> R + 'static,
{
let window = self.window;
self.update_window(window, move |_, cx| view.update(cx, update))
.unwrap()
self.update_window(window, move |_, window, cx| {
model.update(cx, |t, cx| update(t, window, cx))
})
.unwrap()
}
pub fn workspace<F, T>(&mut self, update: F) -> T
where
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
F: FnOnce(&mut Workspace, &mut Window, &mut Context<Workspace>) -> T,
{
self.cx.update_workspace(update)
}
pub fn enable_vim(&mut self) {
self.cx.update(|cx| {
self.cx.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(true));
});
@ -120,7 +122,7 @@ impl VimTestContext {
}
pub fn disable_vim(&mut self) {
self.cx.update(|cx| {
self.cx.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(false));
});
@ -128,15 +130,15 @@ impl VimTestContext {
}
pub fn mode(&mut self) -> Mode {
self.update_editor(|editor, cx| editor.addon::<VimAddon>().unwrap().view.read(cx).mode)
self.update_editor(|editor, _, cx| editor.addon::<VimAddon>().unwrap().model.read(cx).mode)
}
pub fn active_operator(&mut self) -> Option<Operator> {
self.update_editor(|editor, cx| {
self.update_editor(|editor, _, cx| {
editor
.addon::<VimAddon>()
.unwrap()
.view
.model
.read(cx)
.operator_stack
.last()
@ -146,11 +148,12 @@ impl VimTestContext {
pub fn set_state(&mut self, text: &str, mode: Mode) {
self.cx.set_state(text);
let vim = self.update_editor(|editor, _cx| editor.addon::<VimAddon>().cloned().unwrap());
let vim =
self.update_editor(|editor, _window, _cx| editor.addon::<VimAddon>().cloned().unwrap());
self.update(|cx| {
vim.view.update(cx, |vim, cx| {
vim.switch_mode(mode, true, cx);
self.update(|window, cx| {
vim.model.update(cx, |vim, cx| {
vim.switch_mode(mode, true, window, cx);
});
});
self.cx.cx.cx.run_until_parked();

View file

@ -26,8 +26,8 @@ use editor::{
Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
};
use gpui::{
actions, impl_actions, Action, AppContext, Axis, Entity, EventEmitter, KeyContext,
KeystrokeEvent, Render, Subscription, Task, View, ViewContext, WeakView,
actions, impl_actions, Action, App, AppContext as _, Axis, Context, Entity, EventEmitter,
KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window,
};
use insert::{NormalBefore, TemporaryNormal};
use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
@ -42,7 +42,7 @@ use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
use std::{mem, ops::Range, sync::Arc};
use surrounds::SurroundsType;
use theme::ThemeSettings;
use ui::{px, IntoElement, SharedString, VisualContext};
use ui::{px, IntoElement, SharedString};
use vim_mode_setting::VimModeSetting;
use workspace::{self, Pane, ResizeIntent, Workspace};
@ -96,16 +96,15 @@ impl_actions!(
);
/// Initializes the `vim` crate.
pub fn init(cx: &mut AppContext) {
pub fn init(cx: &mut App) {
vim_mode_setting::init(cx);
VimSettings::register(cx);
VimGlobals::register(cx);
cx.observe_new_views(|editor: &mut Editor, cx| Vim::register(editor, cx))
.detach();
cx.observe_new(Vim::register).detach();
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &ToggleVimMode, cx| {
cx.observe_new(|workspace: &mut Workspace, _, _| {
workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
let fs = workspace.app_state().fs.clone();
let currently_enabled = Vim::enabled(cx);
update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
@ -113,7 +112,7 @@ pub fn init(cx: &mut AppContext) {
})
});
workspace.register_action(|_, _: &OpenDefaultKeymap, cx| {
workspace.register_action(|_, _: &OpenDefaultKeymap, _, cx| {
cx.emit(workspace::Event::OpenBundledFile {
text: settings::vim_keymap(),
title: "Default Vim Bindings",
@ -121,11 +120,11 @@ pub fn init(cx: &mut AppContext) {
});
});
workspace.register_action(|workspace, _: &ResetPaneSizes, cx| {
workspace.register_action(|workspace, _: &ResetPaneSizes, _, cx| {
workspace.reset_pane_sizes(cx);
});
workspace.register_action(|workspace, _: &MaximizePane, cx| {
workspace.register_action(|workspace, _: &MaximizePane, _, cx| {
let pane = workspace.active_pane();
let Some(size) = workspace.bounding_box_for_pane(&pane) else {
return;
@ -142,13 +141,13 @@ pub fn init(cx: &mut AppContext) {
workspace.resize_pane(Axis::Vertical, desired_size - size.size.height, cx)
});
workspace.register_action(|workspace, action: &ResizePane, cx| {
workspace.register_action(|workspace, action: &ResizePane, window, cx| {
let count = Vim::take_count(cx).unwrap_or(1) as f32;
let theme = ThemeSettings::get_global(cx);
let Ok(font_id) = cx.text_system().font_id(&theme.buffer_font) else {
let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
return;
};
let Ok(width) = cx
let Ok(width) = window
.text_system()
.advance(font_id, theme.buffer_font_size(), 'm')
else {
@ -166,16 +165,17 @@ pub fn init(cx: &mut AppContext) {
workspace.resize_pane(axis, amount * count, cx);
});
workspace.register_action(|workspace, _: &SearchSubmit, cx| {
workspace.register_action(|workspace, _: &SearchSubmit, window, cx| {
let vim = workspace
.focused_pane(cx)
.focused_pane(window, cx)
.read(cx)
.active_item()
.and_then(|item| item.act_as::<Editor>(cx))
.and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned());
let Some(vim) = vim else { return };
vim.view
.update(cx, |_, cx| cx.defer(|vim, cx| vim.search_submit(cx)))
vim.model.update(cx, |_, cx| {
cx.defer_in(window, |vim, window, cx| vim.search_submit(window, cx))
})
});
})
.detach();
@ -183,12 +183,12 @@ pub fn init(cx: &mut AppContext) {
#[derive(Clone)]
pub(crate) struct VimAddon {
pub(crate) view: View<Vim>,
pub(crate) model: Entity<Vim>,
}
impl editor::Addon for VimAddon {
fn extend_key_context(&self, key_context: &mut KeyContext, cx: &AppContext) {
self.view.read(cx).extend_key_context(key_context, cx)
fn extend_key_context(&self, key_context: &mut KeyContext, cx: &App) {
self.model.read(cx).extend_key_context(key_context, cx)
}
fn to_any(&self) -> &dyn std::any::Any {
@ -219,7 +219,7 @@ pub(crate) struct Vim {
selected_register: Option<char>,
pub search: SearchState,
editor: WeakView<Editor>,
editor: WeakEntity<Editor>,
last_command: Option<String>,
running_command: Option<Task<()>>,
@ -230,7 +230,7 @@ pub(crate) struct Vim {
// This means it needs a VisualContext. The easiest way to satisfy that constraint is
// to make Vim a "View" that is just never actually rendered.
impl Render for Vim {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
gpui::Empty
}
}
@ -244,10 +244,10 @@ impl Vim {
/// The namespace for Vim actions.
const NAMESPACE: &'static str = "vim";
pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
let editor = cx.view().clone();
pub fn new(window: &mut Window, cx: &mut Context<Editor>) -> Entity<Self> {
let editor = cx.model().clone();
cx.new_view(|cx| Vim {
cx.new(|cx| Vim {
mode: Mode::Normal,
last_mode: Mode::Normal,
temp_mode: false,
@ -273,28 +273,32 @@ impl Vim {
editor: editor.downgrade(),
_subscriptions: vec![
cx.observe_keystrokes(Self::observe_keystrokes),
cx.subscribe(&editor, |this, _, event, cx| {
this.handle_editor_event(event, cx)
cx.subscribe_in(&editor, window, |this, _, event, window, cx| {
this.handle_editor_event(event, window, cx)
}),
],
})
}
fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
fn register(editor: &mut Editor, window: Option<&mut Window>, cx: &mut Context<Editor>) {
let Some(window) = window else {
return;
};
if !editor.use_modal_editing() {
return;
}
let mut was_enabled = Vim::enabled(cx);
let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
cx.observe_global::<SettingsStore>(move |editor, cx| {
cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
let enabled = Vim::enabled(cx);
let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
if enabled && was_enabled && (toggle != was_toggle) {
if toggle {
let is_relative = editor
.addon::<VimAddon>()
.map(|vim| vim.view.read(cx).mode != Mode::Insert);
.map(|vim| vim.model.read(cx).mode != Mode::Insert);
editor.set_relative_line_number(is_relative, cx)
} else {
editor.set_relative_line_number(None, cx)
@ -306,42 +310,42 @@ impl Vim {
}
was_enabled = enabled;
if enabled {
Self::activate(editor, cx)
Self::activate(editor, window, cx)
} else {
Self::deactivate(editor, cx)
}
})
.detach();
if was_enabled {
Self::activate(editor, cx)
Self::activate(editor, window, cx)
}
}
fn activate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
let vim = Vim::new(cx);
fn activate(editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>) {
let vim = Vim::new(window, cx);
editor.register_addon(VimAddon { view: vim.clone() });
editor.register_addon(VimAddon { model: vim.clone() });
vim.update(cx, |_, cx| {
Vim::action(editor, cx, |vim, action: &SwitchMode, cx| {
vim.switch_mode(action.0, false, cx)
Vim::action(editor, cx, |vim, action: &SwitchMode, window, cx| {
vim.switch_mode(action.0, false, window, cx)
});
Vim::action(editor, cx, |vim, action: &PushOperator, cx| {
vim.push_operator(action.0.clone(), cx)
Vim::action(editor, cx, |vim, action: &PushOperator, window, cx| {
vim.push_operator(action.0.clone(), window, cx)
});
Vim::action(editor, cx, |vim, _: &ClearOperators, cx| {
vim.clear_operator(cx)
Vim::action(editor, cx, |vim, _: &ClearOperators, window, cx| {
vim.clear_operator(window, cx)
});
Vim::action(editor, cx, |vim, n: &Number, cx| {
vim.push_count_digit(n.0, cx);
Vim::action(editor, cx, |vim, n: &Number, window, cx| {
vim.push_count_digit(n.0, window, cx);
});
Vim::action(editor, cx, |vim, _: &Tab, cx| {
vim.input_ignored(" ".into(), cx)
Vim::action(editor, cx, |vim, _: &Tab, window, cx| {
vim.input_ignored(" ".into(), window, cx)
});
Vim::action(editor, cx, |vim, _: &Enter, cx| {
vim.input_ignored("\n".into(), cx)
Vim::action(editor, cx, |vim, _: &Enter, window, cx| {
vim.input_ignored("\n".into(), window, cx)
});
normal::register(editor, cx);
@ -357,13 +361,13 @@ impl Vim {
change_list::register(editor, cx);
digraph::register(editor, cx);
cx.defer(|vim, cx| {
vim.focused(false, cx);
cx.defer_in(window, |vim, window, cx| {
vim.focused(false, window, cx);
})
})
}
fn deactivate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
fn deactivate(editor: &mut Editor, cx: &mut Context<Editor>) {
editor.set_cursor_shape(CursorShape::Bar, cx);
editor.set_clip_at_line_ends(false, cx);
editor.set_collapse_matches(false);
@ -373,7 +377,7 @@ impl Vim {
editor.unregister_addon::<VimAddon>();
editor.set_relative_line_number(None, cx);
if let Some(vim) = Vim::globals(cx).focused_vim() {
if vim.entity_id() == cx.view().entity_id() {
if vim.entity_id() == cx.model().entity_id() {
Vim::globals(cx).focused_vim = None;
}
}
@ -382,35 +386,38 @@ impl Vim {
/// Register an action on the editor.
pub fn action<A: Action>(
editor: &mut Editor,
cx: &mut ViewContext<Vim>,
f: impl Fn(&mut Vim, &A, &mut ViewContext<Vim>) + 'static,
cx: &mut Context<Vim>,
f: impl Fn(&mut Vim, &A, &mut Window, &mut Context<Vim>) + 'static,
) {
let subscription = editor.register_action(cx.listener(f));
cx.on_release(|_, _, _| drop(subscription)).detach();
cx.on_release(|_, _| drop(subscription)).detach();
}
pub fn editor(&self) -> Option<View<Editor>> {
pub fn editor(&self) -> Option<Entity<Editor>> {
self.editor.upgrade()
}
pub fn workspace(&self, cx: &mut ViewContext<Self>) -> Option<View<Workspace>> {
cx.window_handle()
.downcast::<Workspace>()
.and_then(|handle| handle.root(cx).ok())
pub fn workspace(&self, window: &mut Window) -> Option<Entity<Workspace>> {
window.root_model::<Workspace>().flatten()
}
pub fn pane(&self, cx: &mut ViewContext<Self>) -> Option<View<Pane>> {
self.workspace(cx)
.map(|workspace| workspace.read(cx).focused_pane(cx))
pub fn pane(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Entity<Pane>> {
self.workspace(window)
.map(|workspace| workspace.read(cx).focused_pane(window, cx))
}
pub fn enabled(cx: &mut AppContext) -> bool {
pub fn enabled(cx: &mut App) -> bool {
VimModeSetting::get_global(cx).0
}
/// Called whenever an keystroke is typed so vim can observe all actions
/// and keystrokes accordingly.
fn observe_keystrokes(&mut self, keystroke_event: &KeystrokeEvent, cx: &mut ViewContext<Self>) {
fn observe_keystrokes(
&mut self,
keystroke_event: &KeystrokeEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.exit_temporary_mode {
self.exit_temporary_mode = false;
// Don't switch to insert mode if the action is temporary_normal.
@ -419,24 +426,30 @@ impl Vim {
return;
}
}
self.switch_mode(Mode::Insert, false, cx)
self.switch_mode(Mode::Insert, false, window, cx)
}
if let Some(action) = keystroke_event.action.as_ref() {
// Keystroke is handled by the vim system, so continue forward
if action.name().starts_with("vim::") {
return;
}
} else if cx.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress() {
} else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
{
return;
}
if let Some(operator) = self.active_operator() {
match operator {
Operator::Literal { prefix } => {
self.handle_literal_keystroke(keystroke_event, prefix.unwrap_or_default(), cx);
self.handle_literal_keystroke(
keystroke_event,
prefix.unwrap_or_default(),
window,
cx,
);
}
_ if !operator.is_waiting(self.mode) => {
self.clear_operator(cx);
self.clear_operator(window, cx);
self.stop_recording_immediately(Box::new(ClearOperators), cx)
}
_ => {}
@ -444,15 +457,20 @@ impl Vim {
}
}
fn handle_editor_event(&mut self, event: &EditorEvent, cx: &mut ViewContext<Self>) {
fn handle_editor_event(
&mut self,
event: &EditorEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
match event {
EditorEvent::Focused => self.focused(true, cx),
EditorEvent::Blurred => self.blurred(cx),
EditorEvent::Focused => self.focused(true, window, cx),
EditorEvent::Blurred => self.blurred(window, cx),
EditorEvent::SelectionsChanged { local: true } => {
self.local_selections_changed(cx);
self.local_selections_changed(window, cx);
}
EditorEvent::InputIgnored { text } => {
self.input_ignored(text.clone(), cx);
self.input_ignored(text.clone(), window, cx);
Vim::globals(cx).observe_insertion(text, None)
}
EditorEvent::InputHandled {
@ -460,19 +478,19 @@ impl Vim {
utf16_range_to_replace: range_to_replace,
} => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
EditorEvent::TransactionBegun { transaction_id } => {
self.transaction_begun(*transaction_id, cx)
self.transaction_begun(*transaction_id, window, cx)
}
EditorEvent::TransactionUndone { transaction_id } => {
self.transaction_undone(transaction_id, cx)
self.transaction_undone(transaction_id, window, cx)
}
EditorEvent::Edited { .. } => self.push_to_change_list(cx),
EditorEvent::FocusedIn => self.sync_vim_settings(cx),
EditorEvent::CursorShapeChanged => self.cursor_shape_changed(cx),
EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
_ => {}
}
}
fn push_operator(&mut self, operator: Operator, cx: &mut ViewContext<Self>) {
fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
if matches!(
operator,
Operator::Change
@ -503,14 +521,20 @@ impl Vim {
}
};
self.operator_stack.push(operator);
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
}
pub fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut ViewContext<Self>) {
pub fn switch_mode(
&mut self,
mode: Mode,
leave_selections: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.temp_mode && mode == Mode::Normal {
self.temp_mode = false;
self.switch_mode(Mode::Normal, leave_selections, cx);
self.switch_mode(Mode::Insert, false, cx);
self.switch_mode(Mode::Normal, leave_selections, window, cx);
self.switch_mode(Mode::Insert, false, window, cx);
return;
} else if self.temp_mode
&& !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
@ -526,7 +550,7 @@ impl Vim {
self.mode = mode;
self.operator_stack.clear();
self.selected_register.take();
self.cancel_running_command(cx);
self.cancel_running_command(window, cx);
if mode == Mode::Normal || mode != last_mode {
self.current_tx.take();
self.current_anchor.take();
@ -536,13 +560,13 @@ impl Vim {
}
// Sync editor settings like clip mode
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
if VimSettings::get_global(cx).toggle_relative_line_numbers
&& self.mode != self.last_mode
&& (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
{
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, _, cx| {
let is_relative = vim.mode != Mode::Insert;
editor.set_relative_line_number(Some(is_relative), cx)
});
@ -553,14 +577,16 @@ impl Vim {
}
if !mode.is_visual() && last_mode.is_visual() {
self.create_visual_marks(last_mode, cx);
self.create_visual_marks(last_mode, window, cx);
}
// Adjust selections
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
{
vim.visual_block_motion(true, editor, cx, |_, point, goal| Some((point, goal)))
vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
Some((point, goal))
})
}
if last_mode == Mode::Insert || last_mode == Mode::Replace {
if let Some(prior_tx) = prior_tx {
@ -568,7 +594,7 @@ impl Vim {
}
}
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
// we cheat with visual block mode and use multiple cursors.
// the cost of this cheat is we need to convert back to a single
// cursor whenever vim would.
@ -612,7 +638,7 @@ impl Vim {
});
}
pub fn take_count(cx: &mut AppContext) -> Option<usize> {
pub fn take_count(cx: &mut App) -> Option<usize> {
let global_state = cx.global_mut::<VimGlobals>();
if global_state.dot_replaying {
return global_state.recorded_count;
@ -697,7 +723,7 @@ impl Vim {
}
}
pub fn extend_key_context(&self, context: &mut KeyContext, cx: &AppContext) {
pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
let mut mode = match self.mode {
Mode::Normal => "normal",
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
@ -736,7 +762,7 @@ impl Vim {
context.set("vim_operator", operator_id);
}
fn focused(&mut self, preserve_selection: bool, cx: &mut ViewContext<Self>) {
fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
let Some(editor) = self.editor() else {
return;
};
@ -753,11 +779,11 @@ impl Vim {
&& editor.leader_peer_id().is_none()
{
if preserve_selection {
self.switch_mode(Mode::Visual, true, cx);
self.switch_mode(Mode::Visual, true, window, cx);
} else {
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|_, selection| {
selection.collapse_to(selection.start, selection.goal)
})
@ -767,58 +793,63 @@ impl Vim {
}
cx.emit(VimEvent::Focused);
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
if VimSettings::get_global(cx).toggle_relative_line_numbers {
if let Some(old_vim) = Vim::globals(cx).focused_vim() {
if old_vim.entity_id() != cx.view().entity_id() {
if old_vim.entity_id() != cx.model().entity_id() {
old_vim.update(cx, |vim, cx| {
vim.update_editor(cx, |_, editor, cx| {
vim.update_editor(window, cx, |_, editor, _, cx| {
editor.set_relative_line_number(None, cx)
});
});
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, _, cx| {
let is_relative = vim.mode != Mode::Insert;
editor.set_relative_line_number(Some(is_relative), cx)
});
}
} else {
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, _, cx| {
let is_relative = vim.mode != Mode::Insert;
editor.set_relative_line_number(Some(is_relative), cx)
});
}
}
Vim::globals(cx).focused_vim = Some(cx.view().downgrade());
Vim::globals(cx).focused_vim = Some(cx.model().downgrade());
}
fn blurred(&mut self, cx: &mut ViewContext<Self>) {
fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
self.store_visual_marks(cx);
self.clear_operator(cx);
self.update_editor(cx, |_, editor, cx| {
self.store_visual_marks(window, cx);
self.clear_operator(window, cx);
self.update_editor(window, cx, |_, editor, _, cx| {
editor.set_cursor_shape(language::CursorShape::Hollow, cx);
});
}
fn cursor_shape_changed(&mut self, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |vim, editor, cx| {
fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.update_editor(window, cx, |vim, editor, _, cx| {
editor.set_cursor_shape(vim.cursor_shape(), cx);
});
}
fn update_editor<S>(
&mut self,
cx: &mut ViewContext<Self>,
update: impl FnOnce(&mut Self, &mut Editor, &mut ViewContext<Editor>) -> S,
window: &mut Window,
cx: &mut Context<Self>,
update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
) -> Option<S> {
let editor = self.editor.upgrade()?;
Some(editor.update(cx, |editor, cx| update(self, editor, cx)))
Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
}
fn editor_selections(&mut self, cx: &mut ViewContext<Self>) -> Vec<Range<Anchor>> {
self.update_editor(cx, |_, editor, _| {
fn editor_selections(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Vec<Range<Anchor>> {
self.update_editor(window, cx, |_, editor, _, _| {
editor
.selections
.disjoint_anchors()
@ -831,7 +862,7 @@ impl Vim {
/// When doing an action that modifies the buffer, we start recording so that `.`
/// will replay the action.
pub fn start_recording(&mut self, cx: &mut ViewContext<Self>) {
pub fn start_recording(&mut self, cx: &mut Context<Self>) {
Vim::update_globals(cx, |globals, cx| {
if !globals.dot_replaying {
globals.dot_recording = true;
@ -874,7 +905,7 @@ impl Vim {
})
}
pub fn stop_replaying(&mut self, cx: &mut ViewContext<Self>) {
pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
let globals = Vim::globals(cx);
globals.dot_replaying = false;
if let Some(replayer) = globals.replayer.take() {
@ -885,7 +916,7 @@ impl Vim {
/// When finishing an action that modifies the buffer, stop recording.
/// as you usually call this within a keystroke handler we also ensure that
/// the current action is recorded.
pub fn stop_recording(&mut self, cx: &mut ViewContext<Self>) {
pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
let globals = Vim::globals(cx);
if globals.dot_recording {
globals.stop_recording_after_next_action = true;
@ -897,11 +928,7 @@ impl Vim {
/// next action to stop recording.
///
/// This doesn't include the current action.
pub fn stop_recording_immediately(
&mut self,
action: Box<dyn Action>,
cx: &mut ViewContext<Self>,
) {
pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
let globals = Vim::globals(cx);
if globals.dot_recording {
globals
@ -915,12 +942,12 @@ impl Vim {
}
/// Explicitly record one action (equivalents to start_recording and stop_recording)
pub fn record_current_action(&mut self, cx: &mut ViewContext<Self>) {
pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
self.start_recording(cx);
self.stop_recording(cx);
}
fn push_count_digit(&mut self, number: usize, cx: &mut ViewContext<Self>) {
fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
if self.active_operator().is_some() {
let post_count = Vim::globals(cx).post_count.unwrap_or(0);
@ -941,41 +968,46 @@ impl Vim {
)
}
// update the keymap so that 0 works
self.sync_vim_settings(cx)
self.sync_vim_settings(window, cx)
}
fn select_register(&mut self, register: Arc<str>, cx: &mut ViewContext<Self>) {
fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
if register.chars().count() == 1 {
self.selected_register
.replace(register.chars().next().unwrap());
}
self.operator_stack.clear();
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
}
fn maybe_pop_operator(&mut self) -> Option<Operator> {
self.operator_stack.pop()
}
fn pop_operator(&mut self, cx: &mut ViewContext<Self>) -> Operator {
fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
let popped_operator = self.operator_stack.pop()
.expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
popped_operator
}
fn clear_operator(&mut self, cx: &mut ViewContext<Self>) {
fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
Vim::take_count(cx);
self.selected_register.take();
self.operator_stack.clear();
self.sync_vim_settings(cx);
self.sync_vim_settings(window, cx);
}
fn active_operator(&self) -> Option<Operator> {
self.operator_stack.last().cloned()
}
fn transaction_begun(&mut self, transaction_id: TransactionId, _: &mut ViewContext<Self>) {
fn transaction_begun(
&mut self,
transaction_id: TransactionId,
_window: &mut Window,
_: &mut Context<Self>,
) {
let mode = if (self.mode == Mode::Insert
|| self.mode == Mode::Replace
|| self.mode == Mode::Normal)
@ -991,12 +1023,17 @@ impl Vim {
}
}
fn transaction_undone(&mut self, transaction_id: &TransactionId, cx: &mut ViewContext<Self>) {
fn transaction_undone(
&mut self,
transaction_id: &TransactionId,
window: &mut Window,
cx: &mut Context<Self>,
) {
match self.mode {
Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
self.update_editor(cx, |vim, editor, cx| {
self.update_editor(window, cx, |vim, editor, window, cx| {
let original_mode = vim.undo_modes.get(transaction_id);
editor.change_selections(None, cx, |s| match original_mode {
editor.change_selections(None, window, cx, |s| match original_mode {
Some(Mode::VisualLine) => {
s.move_with(|map, selection| {
selection.collapse_to(
@ -1020,11 +1057,11 @@ impl Vim {
}
});
});
self.switch_mode(Mode::Normal, true, cx)
self.switch_mode(Mode::Normal, true, window, cx)
}
Mode::Normal => {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(None, cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
selection
.collapse_to(map.clip_at_line_end(selection.end), selection.goal)
@ -1036,7 +1073,7 @@ impl Vim {
}
}
fn local_selections_changed(&mut self, cx: &mut ViewContext<Self>) {
fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some(editor) = self.editor() else { return };
if editor.read(cx).leader_peer_id().is_some() {
@ -1050,26 +1087,26 @@ impl Vim {
self.current_anchor = Some(newest);
} else if self.current_anchor.as_ref().unwrap() != &newest {
if let Some(tx_id) = self.current_tx.take() {
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, _, cx| {
editor.group_until_transaction(tx_id, cx)
});
}
}
} else if self.mode == Mode::Normal && newest.start != newest.end {
if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
self.switch_mode(Mode::VisualBlock, false, cx);
self.switch_mode(Mode::VisualBlock, false, window, cx);
} else {
self.switch_mode(Mode::Visual, false, cx)
self.switch_mode(Mode::Visual, false, window, cx)
}
} else if newest.start == newest.end
&& !is_multicursor
&& [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
{
self.switch_mode(Mode::Normal, true, cx);
self.switch_mode(Mode::Normal, true, window, cx);
}
}
fn input_ignored(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
if text.is_empty() {
return;
}
@ -1087,7 +1124,7 @@ impl Vim {
smartcase: VimSettings::get_global(cx).use_smartcase_find,
};
Vim::globals(cx).last_find = Some(find.clone());
self.motion(find, cx)
self.motion(find, window, cx)
}
Some(Operator::FindBackward { after }) => {
let find = Motion::FindBackward {
@ -1101,7 +1138,7 @@ impl Vim {
smartcase: VimSettings::get_global(cx).use_smartcase_find,
};
Vim::globals(cx).last_find = Some(find.clone());
self.motion(find, cx)
self.motion(find, window, cx)
}
Some(Operator::Sneak { first_char }) => {
if let Some(first_char) = first_char {
@ -1112,12 +1149,12 @@ impl Vim {
smartcase: VimSettings::get_global(cx).use_smartcase_find,
};
Vim::globals(cx).last_find = Some((&sneak).clone());
self.motion(sneak, cx)
self.motion(sneak, window, cx)
}
} else {
let first_char = text.chars().next();
self.pop_operator(cx);
self.push_operator(Operator::Sneak { first_char }, cx);
self.pop_operator(window, cx);
self.push_operator(Operator::Sneak { first_char }, window, cx);
}
}
Some(Operator::SneakBackward { first_char }) => {
@ -1129,74 +1166,74 @@ impl Vim {
smartcase: VimSettings::get_global(cx).use_smartcase_find,
};
Vim::globals(cx).last_find = Some((&sneak).clone());
self.motion(sneak, cx)
self.motion(sneak, window, cx)
}
} else {
let first_char = text.chars().next();
self.pop_operator(cx);
self.push_operator(Operator::SneakBackward { first_char }, cx);
self.pop_operator(window, cx);
self.push_operator(Operator::SneakBackward { first_char }, window, cx);
}
}
Some(Operator::Replace) => match self.mode {
Mode::Normal => self.normal_replace(text, cx),
Mode::Normal => self.normal_replace(text, window, cx),
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
self.visual_replace(text, cx)
self.visual_replace(text, window, cx)
}
_ => self.clear_operator(cx),
_ => self.clear_operator(window, cx),
},
Some(Operator::Digraph { first_char }) => {
if let Some(first_char) = first_char {
if let Some(second_char) = text.chars().next() {
self.insert_digraph(first_char, second_char, cx);
self.insert_digraph(first_char, second_char, window, cx);
}
} else {
let first_char = text.chars().next();
self.pop_operator(cx);
self.push_operator(Operator::Digraph { first_char }, cx);
self.pop_operator(window, cx);
self.push_operator(Operator::Digraph { first_char }, window, cx);
}
}
Some(Operator::Literal { prefix }) => {
self.handle_literal_input(prefix.unwrap_or_default(), &text, cx)
self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
}
Some(Operator::AddSurrounds { target }) => match self.mode {
Mode::Normal => {
if let Some(target) = target {
self.add_surrounds(text, target, cx);
self.clear_operator(cx);
self.add_surrounds(text, target, window, cx);
self.clear_operator(window, cx);
}
}
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
self.add_surrounds(text, SurroundsType::Selection, cx);
self.clear_operator(cx);
self.add_surrounds(text, SurroundsType::Selection, window, cx);
self.clear_operator(window, cx);
}
_ => self.clear_operator(cx),
_ => self.clear_operator(window, cx),
},
Some(Operator::ChangeSurrounds { target }) => match self.mode {
Mode::Normal => {
if let Some(target) = target {
self.change_surrounds(text, target, cx);
self.clear_operator(cx);
self.change_surrounds(text, target, window, cx);
self.clear_operator(window, cx);
}
}
_ => self.clear_operator(cx),
_ => self.clear_operator(window, cx),
},
Some(Operator::DeleteSurrounds) => match self.mode {
Mode::Normal => {
self.delete_surrounds(text, cx);
self.clear_operator(cx);
self.delete_surrounds(text, window, cx);
self.clear_operator(window, cx);
}
_ => self.clear_operator(cx),
_ => self.clear_operator(window, cx),
},
Some(Operator::Mark) => self.create_mark(text, false, cx),
Some(Operator::Mark) => self.create_mark(text, false, window, cx),
Some(Operator::RecordRegister) => {
self.record_register(text.chars().next().unwrap(), cx)
self.record_register(text.chars().next().unwrap(), window, cx)
}
Some(Operator::ReplayRegister) => {
self.replay_register(text.chars().next().unwrap(), cx)
self.replay_register(text.chars().next().unwrap(), window, cx)
}
Some(Operator::Register) => match self.mode {
Mode::Insert => {
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
if let Some(register) = Vim::update_globals(cx, |globals, cx| {
globals.read_register(text.chars().next(), Some(editor), cx)
}) {
@ -1204,26 +1241,28 @@ impl Vim {
&register.text.to_string(),
register.clipboard_selections.clone(),
false,
window,
cx,
)
}
});
self.clear_operator(cx);
self.clear_operator(window, cx);
}
_ => {
self.select_register(text, cx);
self.select_register(text, window, cx);
}
},
Some(Operator::Jump { line }) => self.jump(text, line, cx),
Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
_ => {
if self.mode == Mode::Replace {
self.multi_replace(text, cx)
self.multi_replace(text, window, cx)
}
if self.mode == Mode::Normal {
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.accept_inline_completion(
&editor::actions::AcceptInlineCompletion {},
window,
cx,
);
});
@ -1232,8 +1271,8 @@ impl Vim {
}
}
fn sync_vim_settings(&mut self, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |vim, editor, cx| {
fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.update_editor(window, cx, |vim, editor, _, cx| {
editor.set_cursor_shape(vim.cursor_shape(), cx);
editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
editor.set_collapse_matches(true);
@ -1291,7 +1330,7 @@ impl Settings for VimSettings {
type FileContent = VimSettingsContent;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
sources.json_merge()
}
}

View file

@ -7,7 +7,7 @@ use editor::{
scroll::Autoscroll,
Bias, DisplayPoint, Editor, ToOffset,
};
use gpui::{actions, ViewContext};
use gpui::{actions, Context, Window};
use language::{Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow;
use search::BufferSearchBar;
@ -44,62 +44,66 @@ actions!(
]
);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &ToggleVisual, cx| {
vim.toggle_mode(Mode::Visual, cx)
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, _: &ToggleVisual, window, cx| {
vim.toggle_mode(Mode::Visual, window, cx)
});
Vim::action(editor, cx, |vim, _: &ToggleVisualLine, cx| {
vim.toggle_mode(Mode::VisualLine, cx)
Vim::action(editor, cx, |vim, _: &ToggleVisualLine, window, cx| {
vim.toggle_mode(Mode::VisualLine, window, cx)
});
Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, cx| {
vim.toggle_mode(Mode::VisualBlock, cx)
Vim::action(editor, cx, |vim, _: &ToggleVisualBlock, window, cx| {
vim.toggle_mode(Mode::VisualBlock, window, cx)
});
Vim::action(editor, cx, Vim::other_end);
Vim::action(editor, cx, Vim::visual_insert_end_of_line);
Vim::action(editor, cx, Vim::visual_insert_first_non_white_space);
Vim::action(editor, cx, |vim, _: &VisualDelete, cx| {
Vim::action(editor, cx, |vim, _: &VisualDelete, window, cx| {
vim.record_current_action(cx);
vim.visual_delete(false, cx);
vim.visual_delete(false, window, cx);
});
Vim::action(editor, cx, |vim, _: &VisualDeleteLine, cx| {
Vim::action(editor, cx, |vim, _: &VisualDeleteLine, window, cx| {
vim.record_current_action(cx);
vim.visual_delete(true, cx);
vim.visual_delete(true, window, cx);
});
Vim::action(editor, cx, |vim, _: &VisualYank, cx| {
vim.visual_yank(false, cx)
Vim::action(editor, cx, |vim, _: &VisualYank, window, cx| {
vim.visual_yank(false, window, cx)
});
Vim::action(editor, cx, |vim, _: &VisualYankLine, cx| {
vim.visual_yank(true, cx)
Vim::action(editor, cx, |vim, _: &VisualYankLine, window, cx| {
vim.visual_yank(true, window, cx)
});
Vim::action(editor, cx, Vim::select_next);
Vim::action(editor, cx, Vim::select_previous);
Vim::action(editor, cx, |vim, _: &SelectNextMatch, cx| {
vim.select_match(Direction::Next, cx);
Vim::action(editor, cx, |vim, _: &SelectNextMatch, window, cx| {
vim.select_match(Direction::Next, window, cx);
});
Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, cx| {
vim.select_match(Direction::Prev, cx);
Vim::action(editor, cx, |vim, _: &SelectPreviousMatch, window, cx| {
vim.select_match(Direction::Prev, window, cx);
});
Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, cx| {
Vim::action(editor, cx, |vim, _: &SelectLargerSyntaxNode, window, cx| {
let count = Vim::take_count(cx).unwrap_or(1);
for _ in 0..count {
vim.update_editor(cx, |_, editor, cx| {
editor.select_larger_syntax_node(&Default::default(), cx);
vim.update_editor(window, cx, |_, editor, window, cx| {
editor.select_larger_syntax_node(&Default::default(), window, cx);
});
}
});
Vim::action(editor, cx, |vim, _: &SelectSmallerSyntaxNode, cx| {
let count = Vim::take_count(cx).unwrap_or(1);
for _ in 0..count {
vim.update_editor(cx, |_, editor, cx| {
editor.select_smaller_syntax_node(&Default::default(), cx);
});
}
});
Vim::action(
editor,
cx,
|vim, _: &SelectSmallerSyntaxNode, window, cx| {
let count = Vim::take_count(cx).unwrap_or(1);
for _ in 0..count {
vim.update_editor(window, cx, |_, editor, window, cx| {
editor.select_smaller_syntax_node(&Default::default(), window, cx);
});
}
},
);
Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, cx| {
Vim::action(editor, cx, |vim, _: &RestoreVisualSelection, window, cx| {
let Some((stored_mode, reversed)) = vim.stored_visual_mode.take() else {
return;
};
@ -114,11 +118,11 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
.collect::<Vec<_>>();
if vim.mode.is_visual() {
vim.create_visual_marks(vim.mode, cx);
vim.create_visual_marks(vim.mode, window, cx);
}
vim.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
vim.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
let map = s.display_map();
let ranges = ranges
.into_iter()
@ -136,7 +140,7 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
s.select(ranges);
})
});
vim.switch_mode(stored_mode, true, cx)
vim.switch_mode(stored_mode, true, window, cx)
});
}
@ -145,10 +149,11 @@ impl Vim {
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(cx, |vim, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
let text_layout_details = editor.text_layout_details(window);
if vim.mode == Mode::VisualBlock
&& !matches!(
motion,
@ -158,11 +163,11 @@ impl Vim {
)
{
let is_up_or_down = matches!(motion, Motion::Up { .. } | Motion::Down { .. });
vim.visual_block_motion(is_up_or_down, editor, cx, |map, point, goal| {
vim.visual_block_motion(is_up_or_down, editor, window, cx, |map, point, goal| {
motion.move_point(map, point, goal, times, &text_layout_details)
})
} else {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let was_reversed = selection.reversed;
let mut current_head = selection.head();
@ -225,15 +230,16 @@ impl Vim {
&mut self,
preserve_goal: bool,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
window: &mut Window,
cx: &mut Context<Editor>,
mut move_selection: impl FnMut(
&DisplaySnapshot,
DisplayPoint,
SelectionGoal,
) -> Option<(DisplayPoint, SelectionGoal)>,
) {
let text_layout_details = editor.text_layout_details(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
let text_layout_details = editor.text_layout_details(window);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
let map = &s.display_map();
let mut head = s.newest_anchor().head().to_display_point(map);
let mut tail = s.oldest_anchor().tail().to_display_point(map);
@ -329,17 +335,17 @@ impl Vim {
})
}
pub fn visual_object(&mut self, object: Object, cx: &mut ViewContext<Vim>) {
pub fn visual_object(&mut self, object: Object, window: &mut Window, cx: &mut Context<Vim>) {
if let Some(Operator::Object { around }) = self.active_operator() {
self.pop_operator(cx);
self.pop_operator(window, cx);
let current_mode = self.mode;
let target_mode = object.target_visual_mode(current_mode, around);
if target_mode != current_mode {
self.switch_mode(target_mode, true, cx);
self.switch_mode(target_mode, true, window, cx);
}
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let mut mut_selection = selection.clone();
@ -398,27 +404,33 @@ impl Vim {
}
}
fn visual_insert_end_of_line(&mut self, _: &VisualInsertEndOfLine, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |_, editor, cx| {
editor.split_selection_into_lines(&Default::default(), cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
fn visual_insert_end_of_line(
&mut self,
_: &VisualInsertEndOfLine,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.split_selection_into_lines(&Default::default(), window, cx);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(next_line_end(map, cursor, 1), SelectionGoal::None)
});
});
});
self.switch_mode(Mode::Insert, false, cx);
self.switch_mode(Mode::Insert, false, window, cx);
}
fn visual_insert_first_non_white_space(
&mut self,
_: &VisualInsertFirstNonWhiteSpace,
cx: &mut ViewContext<Self>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
editor.split_selection_into_lines(&Default::default(), cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.split_selection_into_lines(&Default::default(), window, cx);
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
(
first_non_whitespace(map, false, cursor),
@ -428,20 +440,20 @@ impl Vim {
});
});
self.switch_mode(Mode::Insert, false, cx);
self.switch_mode(Mode::Insert, false, window, cx);
}
fn toggle_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
fn toggle_mode(&mut self, mode: Mode, window: &mut Window, cx: &mut Context<Self>) {
if self.mode == mode {
self.switch_mode(Mode::Normal, false, cx);
self.switch_mode(Mode::Normal, false, window, cx);
} else {
self.switch_mode(mode, false, cx);
self.switch_mode(mode, false, window, cx);
}
}
pub fn other_end(&mut self, _: &OtherEnd, cx: &mut ViewContext<Self>) {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
pub fn other_end(&mut self, _: &OtherEnd, window: &mut Window, cx: &mut Context<Self>) {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|_, selection| {
selection.reversed = !selection.reversed;
})
@ -449,14 +461,14 @@ impl Vim {
});
}
pub fn visual_delete(&mut self, line_mode: bool, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
pub fn visual_delete(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
let mut original_columns: HashMap<_, _> = Default::default();
let line_mode = line_mode || editor.selections.line_mode;
editor.transact(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.transact(window, cx, |editor, window, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
if line_mode {
let mut position = selection.head();
@ -488,11 +500,11 @@ impl Vim {
});
});
vim.copy_selections_content(editor, line_mode, cx);
editor.insert("", cx);
editor.insert("", window, cx);
// Fixup cursor position after the deletion
editor.set_clip_at_line_ends(true, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.move_with(|map, selection| {
let mut cursor = selection.head().to_point(map);
@ -508,16 +520,16 @@ impl Vim {
});
})
});
self.switch_mode(Mode::Normal, true, cx);
self.switch_mode(Mode::Normal, true, window, cx);
}
pub fn visual_yank(&mut self, line_mode: bool, cx: &mut ViewContext<Self>) {
self.store_visual_marks(cx);
self.update_editor(cx, |vim, editor, cx| {
pub fn visual_yank(&mut self, line_mode: bool, window: &mut Window, cx: &mut Context<Self>) {
self.store_visual_marks(window, cx);
self.update_editor(window, cx, |vim, editor, window, cx| {
let line_mode = line_mode || editor.selections.line_mode;
editor.selections.line_mode = line_mode;
vim.yank_selections_content(editor, line_mode, cx);
editor.change_selections(None, cx, |s| {
editor.change_selections(None, window, cx, |s| {
s.move_with(|map, selection| {
if line_mode {
selection.start = start_of_line(map, false, selection.start);
@ -529,13 +541,18 @@ impl Vim {
}
});
});
self.switch_mode(Mode::Normal, true, cx);
self.switch_mode(Mode::Normal, true, window, cx);
}
pub(crate) fn visual_replace(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
pub(crate) fn visual_replace(
&mut self,
text: Arc<str>,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
editor.transact(cx, |editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.transact(window, cx, |editor, window, cx| {
let (display_map, selections) = editor.selections.all_adjusted_display(cx);
// Selections are biased right at the start. So we need to store
@ -565,20 +582,20 @@ impl Vim {
}
editor.edit(edits, cx);
editor.change_selections(None, cx, |s| s.select_ranges(stable_anchors));
editor.change_selections(None, window, cx, |s| s.select_ranges(stable_anchors));
});
});
self.switch_mode(Mode::Normal, false, cx);
self.switch_mode(Mode::Normal, false, window, cx);
}
pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
pub fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
let count =
Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
editor.set_clip_at_line_ends(false, cx);
for _ in 0..count {
if editor
.select_next(&Default::default(), cx)
.select_next(&Default::default(), window, cx)
.log_err()
.is_none()
{
@ -588,13 +605,18 @@ impl Vim {
});
}
pub fn select_previous(&mut self, _: &SelectPrevious, cx: &mut ViewContext<Self>) {
pub fn select_previous(
&mut self,
_: &SelectPrevious,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count =
Vim::take_count(cx).unwrap_or_else(|| if self.mode.is_visual() { 1 } else { 2 });
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
for _ in 0..count {
if editor
.select_previous(&Default::default(), cx)
.select_previous(&Default::default(), window, cx)
.log_err()
.is_none()
{
@ -604,16 +626,21 @@ impl Vim {
});
}
pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
pub fn select_match(
&mut self,
direction: Direction,
window: &mut Window,
cx: &mut Context<Self>,
) {
let count = Vim::take_count(cx).unwrap_or(1);
let Some(pane) = self.pane(cx) else {
let Some(pane) = self.pane(window, cx) else {
return;
};
let vim_is_normal = self.mode == Mode::Normal;
let mut start_selection = 0usize;
let mut end_selection = 0usize;
self.update_editor(cx, |_, editor, _| {
self.update_editor(window, cx, |_, editor, _, _| {
editor.set_collapse_matches(false);
});
if vim_is_normal {
@ -621,17 +648,17 @@ impl Vim {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>()
{
search_bar.update(cx, |search_bar, cx| {
if !search_bar.has_active_match() || !search_bar.show(cx) {
if !search_bar.has_active_match() || !search_bar.show(window, cx) {
return;
}
// without update_match_index there is a bug when the cursor is before the first match
search_bar.update_match_index(cx);
search_bar.select_match(direction.opposite(), 1, cx);
search_bar.update_match_index(window, cx);
search_bar.select_match(direction.opposite(), 1, window, cx);
});
}
});
}
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, _, cx| {
let latest = editor.selections.newest::<usize>(cx);
start_selection = latest.start;
end_selection = latest.end;
@ -641,18 +668,18 @@ impl Vim {
pane.update(cx, |pane, cx| {
if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::<BufferSearchBar>() {
search_bar.update(cx, |search_bar, cx| {
search_bar.update_match_index(cx);
search_bar.select_match(direction, count, cx);
match_exists = search_bar.match_exists(cx);
search_bar.update_match_index(window, cx);
search_bar.select_match(direction, count, window, cx);
match_exists = search_bar.match_exists(window, cx);
});
}
});
if !match_exists {
self.clear_operator(cx);
self.clear_operator(window, cx);
self.stop_replaying(cx);
return;
}
self.update_editor(cx, |_, editor, cx| {
self.update_editor(window, cx, |_, editor, window, cx| {
let latest = editor.selections.newest::<usize>(cx);
if vim_is_normal {
start_selection = latest.start;
@ -664,19 +691,19 @@ impl Vim {
if direction == Direction::Prev {
std::mem::swap(&mut start_selection, &mut end_selection);
}
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
s.select_ranges([start_selection..end_selection]);
});
editor.set_collapse_matches(true);
});
match self.maybe_pop_operator() {
Some(Operator::Change) => self.substitute(None, false, cx),
Some(Operator::Change) => self.substitute(None, false, window, cx),
Some(Operator::Delete) => {
self.stop_recording(cx);
self.visual_delete(false, cx)
self.visual_delete(false, window, cx)
}
Some(Operator::Yank) => self.visual_yank(false, cx),
Some(Operator::Yank) => self.visual_yank(false, window, cx),
_ => {} // Ignoring other operators
}
}
@ -701,7 +728,7 @@ mod test {
the lazy dog"
})
.await;
let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
// entering visual mode should select the character
// under cursor
@ -711,7 +738,7 @@ mod test {
.assert_eq(indoc! { "The «qˇ»uick brown
fox jumps over
the lazy dog"});
cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
// forwards motions should extend the selection
cx.simulate_shared_keystrokes("w j").await;
@ -739,14 +766,14 @@ mod test {
b
"})
.await;
let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
cx.simulate_shared_keystrokes("v").await;
cx.shared_state().await.assert_eq(indoc! {"
a
«
ˇ»b
"});
cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
// toggles off again
cx.simulate_shared_keystrokes("v").await;
@ -858,13 +885,13 @@ mod test {
b
ˇ"})
.await;
let cursor = cx.update_editor(|editor, cx| editor.pixel_position_of_cursor(cx));
let cursor = cx.update_editor(|editor, _, cx| editor.pixel_position_of_cursor(cx));
cx.simulate_shared_keystrokes("shift-v").await;
cx.shared_state().await.assert_eq(indoc! {"
a
b
ˇ"});
cx.update_editor(|editor, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
cx.update_editor(|editor, _, cx| assert_eq!(cursor, editor.pixel_position_of_cursor(cx)));
cx.simulate_shared_keystrokes("x").await;
cx.shared_state().await.assert_eq(indoc! {"
a