Merge branch 'main' into language-api-docs

This commit is contained in:
Max Brunsfeld 2024-01-18 15:04:54 -08:00
commit b65cf6d2d9
382 changed files with 12764 additions and 7823 deletions

View file

@ -1,5 +1,5 @@
use command_palette::CommandInterceptResult;
use editor::{SortLinesCaseInsensitive, SortLinesCaseSensitive};
use editor::actions::{SortLinesCaseInsensitive, SortLinesCaseSensitive};
use gpui::{impl_actions, Action, AppContext, ViewContext};
use serde_derive::Deserialize;
use workspace::{SaveIntent, Workspace};
@ -204,25 +204,31 @@ pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option<CommandInt
// quickfix / loclist (merged together for now)
"cl" | "cli" | "clis" | "clist" => ("clist", diagnostics::Deploy.boxed_clone()),
"cc" => ("cc", editor::Hover.boxed_clone()),
"ll" => ("ll", editor::Hover.boxed_clone()),
"cn" | "cne" | "cnex" | "cnext" => ("cnext", editor::GoToDiagnostic.boxed_clone()),
"lne" | "lnex" | "lnext" => ("cnext", editor::GoToDiagnostic.boxed_clone()),
"cc" => ("cc", editor::actions::Hover.boxed_clone()),
"ll" => ("ll", editor::actions::Hover.boxed_clone()),
"cn" | "cne" | "cnex" | "cnext" => ("cnext", editor::actions::GoToDiagnostic.boxed_clone()),
"lne" | "lnex" | "lnext" => ("cnext", editor::actions::GoToDiagnostic.boxed_clone()),
"cpr" | "cpre" | "cprev" | "cprevi" | "cprevio" | "cpreviou" | "cprevious" => {
("cprevious", editor::GoToPrevDiagnostic.boxed_clone())
"cpr" | "cpre" | "cprev" | "cprevi" | "cprevio" | "cpreviou" | "cprevious" => (
"cprevious",
editor::actions::GoToPrevDiagnostic.boxed_clone(),
),
"cN" | "cNe" | "cNex" | "cNext" => {
("cNext", editor::actions::GoToPrevDiagnostic.boxed_clone())
}
"cN" | "cNe" | "cNex" | "cNext" => ("cNext", editor::GoToPrevDiagnostic.boxed_clone()),
"lp" | "lpr" | "lpre" | "lprev" | "lprevi" | "lprevio" | "lpreviou" | "lprevious" => {
("lprevious", editor::GoToPrevDiagnostic.boxed_clone())
"lp" | "lpr" | "lpre" | "lprev" | "lprevi" | "lprevio" | "lpreviou" | "lprevious" => (
"lprevious",
editor::actions::GoToPrevDiagnostic.boxed_clone(),
),
"lN" | "lNe" | "lNex" | "lNext" => {
("lNext", editor::actions::GoToPrevDiagnostic.boxed_clone())
}
"lN" | "lNe" | "lNex" | "lNext" => ("lNext", editor::GoToPrevDiagnostic.boxed_clone()),
// modify the buffer (should accept [range])
"j" | "jo" | "joi" | "join" => ("join", JoinLines.boxed_clone()),
"d" | "de" | "del" | "dele" | "delet" | "delete" | "dl" | "dell" | "delel" | "deletl"
| "deletel" | "dp" | "dep" | "delp" | "delep" | "deletp" | "deletep" => {
("delete", editor::DeleteLine.boxed_clone())
("delete", editor::actions::DeleteLine.boxed_clone())
}
"sor" | "sor " | "sort" | "sort " => ("sort", SortLinesCaseSensitive.boxed_clone()),
"sor i" | "sort i" => ("sort i", SortLinesCaseInsensitive.boxed_clone()),

View file

@ -1,6 +1,6 @@
use crate::Vim;
use crate::{insert::NormalBefore, Vim};
use editor::{Editor, EditorEvent};
use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext};
use gpui::{Action, AppContext, Entity, EntityId, View, ViewContext, WindowContext};
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|_, cx: &mut ViewContext<Editor>| {
@ -34,8 +34,7 @@ fn focused(editor: View<Editor>, cx: &mut WindowContext) {
fn blurred(editor: View<Editor>, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| {
vim.workspace_state.recording = false;
vim.workspace_state.recorded_actions.clear();
vim.stop_recording_immediately(NormalBefore.boxed_clone());
if let Some(previous_editor) = vim.active_editor.clone() {
if previous_editor
.upgrade()
@ -69,7 +68,7 @@ fn released(entity_id: EntityId, cx: &mut AppContext) {
mod test {
use crate::{test::VimTestContext, Vim};
use editor::Editor;
use gpui::{Context, Entity};
use gpui::{Context, Entity, VisualTestContext};
use language::Buffer;
// regression test for blur called with a different active editor
@ -82,11 +81,13 @@ mod test {
let editor2 = cx
.update(|cx| {
window2.update(cx, |_, cx| {
cx.activate_window();
cx.focus_self();
cx.view().clone()
})
})
.unwrap();
cx.run_until_parked();
cx.update(|cx| {
let vim = Vim::read(cx);
@ -101,4 +102,41 @@ mod test {
editor1.handle_blur(cx);
});
}
// regression test for focus_in/focus_out being called on window activation
#[gpui::test]
async fn test_focus_across_windows(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
let mut cx1 = VisualTestContext::from_window(cx.window, &cx);
let editor1 = cx.editor.clone();
let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
let (editor2, cx2) = cx.add_window_view(|cx| Editor::for_buffer(buffer, None, cx));
editor2.update(cx2, |_, cx| {
cx.focus_self();
cx.activate_window();
});
cx.run_until_parked();
cx1.update(|cx| {
assert_eq!(
Vim::read(cx).active_editor.as_ref().unwrap().entity_id(),
editor2.entity_id(),
)
});
cx1.update(|cx| {
cx.activate_window();
});
cx.run_until_parked();
cx.update(|cx| {
assert_eq!(
Vim::read(cx).active_editor.as_ref().unwrap().entity_id(),
editor1.entity_id(),
)
});
}
}

View file

@ -1,5 +1,5 @@
use crate::{normal::repeat, state::Mode, Vim};
use editor::{scroll::autoscroll::Autoscroll, Bias};
use editor::{scroll::Autoscroll, Bias};
use gpui::{actions, Action, ViewContext};
use language::SelectionGoal;
use workspace::Workspace;

View file

@ -4,12 +4,14 @@ use workspace::{item::ItemHandle, ui::prelude::*, StatusItemView};
use crate::{state::Mode, Vim};
/// The ModeIndicator displays the current mode in the status bar.
pub struct ModeIndicator {
pub mode: Option<Mode>,
pub(crate) mode: Option<Mode>,
_subscriptions: Vec<Subscription>,
}
impl ModeIndicator {
/// Construct a new mode indicator in this window.
pub fn new(cx: &mut ViewContext<Self>) -> Self {
let _subscriptions = vec![
cx.observe_global::<Vim>(|this, cx| this.update_mode(cx)),
@ -26,24 +28,16 @@ impl ModeIndicator {
fn update_mode(&mut self, cx: &mut ViewContext<Self>) {
// Vim doesn't exist in some tests
if !cx.has_global::<Vim>() {
let Some(vim) = cx.try_global::<Vim>() else {
return;
}
};
let vim = Vim::read(cx);
if vim.enabled {
self.mode = Some(vim.state().mode);
} else {
self.mode = None;
}
}
pub fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
if self.mode != Some(mode) {
self.mode = Some(mode);
cx.notify();
}
}
}
impl Render for ModeIndicator {

View file

@ -1,17 +1,17 @@
use editor::{
char_kind,
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
movement::{self, find_boundary, find_preceding_boundary, FindRange, TextLayoutDetails},
Bias, CharKind, DisplayPoint, ToOffset,
Bias, DisplayPoint, ToOffset,
};
use gpui::{actions, impl_actions, px, ViewContext, WindowContext};
use language::{Point, Selection, SelectionGoal};
use language::{char_kind, CharKind, Point, Selection, SelectionGoal};
use serde::Deserialize;
use workspace::Workspace;
use crate::{
normal::normal_motion,
state::{Mode, Operator},
utils::coerce_punctuation,
visual::visual_motion,
Vim,
};
@ -712,7 +712,7 @@ fn next_word_end(
}
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = ccoerce_punctuation(har_kind(&scope, right), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});
@ -952,14 +952,6 @@ pub(crate) fn next_line_end(
end_of_line(map, false, point)
}
fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> Self {
if treat_punctuation_as_word && kind == CharKind::Punctuation {
CharKind::Word
} else {
kind
}
}
#[cfg(test)]
mod test {

View file

@ -18,7 +18,7 @@ use crate::{
Vim,
};
use collections::HashSet;
use editor::scroll::autoscroll::Autoscroll;
use editor::scroll::Autoscroll;
use editor::{Bias, DisplayPoint};
use gpui::{actions, ViewContext, WindowContext};
use language::SelectionGoal;

View file

@ -1,4 +1,4 @@
use editor::scroll::autoscroll::Autoscroll;
use editor::scroll::Autoscroll;
use gpui::ViewContext;
use language::{Bias, Point};
use workspace::Workspace;

View file

@ -1,13 +1,18 @@
use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
use crate::{
motion::Motion,
object::Object,
state::Mode,
utils::{coerce_punctuation, copy_selections_content},
Vim,
};
use editor::{
char_kind,
display_map::DisplaySnapshot,
movement::{self, FindRange, TextLayoutDetails},
scroll::autoscroll::Autoscroll,
CharKind, DisplayPoint,
scroll::Autoscroll,
DisplayPoint,
};
use gpui::WindowContext;
use language::Selection;
use language::{char_kind, CharKind, Selection};
pub fn change_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
// Some motions ignore failure when switching to normal mode
@ -103,9 +108,9 @@ fn expand_changed_word_selection(
if in_word {
selection.end =
movement::find_boundary(map, selection.end, FindRange::MultiLine, |left, right| {
let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind =
char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});

View file

@ -1,6 +1,6 @@
use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim};
use collections::{HashMap, HashSet};
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Bias};
use editor::{display_map::ToDisplayPoint, scroll::Autoscroll, Bias};
use gpui::WindowContext;
use language::Point;

View file

@ -1,6 +1,6 @@
use std::ops::Range;
use editor::{scroll::autoscroll::Autoscroll, MultiBufferSnapshot, ToOffset, ToPoint};
use editor::{scroll::Autoscroll, MultiBufferSnapshot, ToOffset, ToPoint};
use gpui::{impl_actions, ViewContext, WindowContext};
use language::{Bias, Point};
use serde::Deserialize;

View file

@ -1,8 +1,7 @@
use std::{borrow::Cow, cmp};
use editor::{
display_map::ToDisplayPoint, movement, scroll::autoscroll::Autoscroll, ClipboardSelection,
DisplayPoint,
display_map::ToDisplayPoint, movement, scroll::Autoscroll, ClipboardSelection, DisplayPoint,
};
use gpui::{impl_actions, ViewContext};
use language::{Bias, SelectionGoal};

View file

@ -12,7 +12,7 @@ actions!(vim, [Repeat, EndRepeat]);
fn should_replay(action: &Box<dyn Action>) -> bool {
// skip so that we don't leave the character palette open
if editor::ShowCharacterPalette.partial_eq(&**action) {
if editor::actions::ShowCharacterPalette.partial_eq(&**action) {
return false;
}
true
@ -152,7 +152,7 @@ pub(crate) fn repeat(cx: &mut WindowContext, from_insert_mode: bool) {
let mut count = Vim::read(cx).workspace_state.recorded_count.unwrap_or(1);
// if we came from insert mode we're just doing repititions 2 onwards.
// if we came from insert mode we're just doing repetitions 2 onwards.
if from_insert_mode {
count -= 1;
new_actions[0] = actions[0].clone();
@ -493,4 +493,17 @@ mod test {
cx.simulate_keystrokes(["escape"]);
cx.assert_state("ˇjhello\n", Mode::Normal);
}
#[gpui::test]
async fn test_repeat_over_blur(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state("ˇhello hello hello\n").await;
cx.simulate_shared_keystrokes(["c", "f", "o", "x", "escape"])
.await;
cx.assert_shared_state("ˇx hello hello\n").await;
cx.simulate_shared_keystrokes([":", "escape"]).await;
cx.simulate_shared_keystrokes(["."]).await;
cx.assert_shared_state("ˇx hello\n").await;
}
}

View file

@ -1,7 +1,7 @@
use crate::Vim;
use editor::{
display_map::ToDisplayPoint,
scroll::{scroll_amount::ScrollAmount, VERTICAL_SCROLL_MARGIN},
scroll::{ScrollAmount, VERTICAL_SCROLL_MARGIN},
DisplayPoint, Editor,
};
use gpui::{actions, ViewContext};

View file

@ -278,7 +278,7 @@ fn parse_replace_all(query: &str) -> Replacement {
return Replacement::default();
}
let Some(delimeter) = chars.next() else {
let Some(delimiter) = chars.next() else {
return Replacement::default();
};
@ -301,13 +301,13 @@ fn parse_replace_all(query: &str) -> Replacement {
buffer.push('$')
// unescape escaped parens
} else if phase == 0 && c == '(' || c == ')' {
} else if c != delimeter {
} else if c != delimiter {
buffer.push('\\')
}
buffer.push(c)
} else if c == '\\' {
escaped = true;
} else if c == delimeter {
} else if c == delimiter {
if phase == 0 {
buffer = &mut replacement;
phase = 1;

View file

@ -1,17 +1,19 @@
use std::ops::Range;
use editor::{
char_kind,
display_map::{DisplaySnapshot, ToDisplayPoint},
movement::{self, FindRange},
Bias, CharKind, DisplayPoint,
Bias, DisplayPoint,
};
use gpui::{actions, impl_actions, ViewContext, WindowContext};
use language::Selection;
use language::{char_kind, CharKind, Selection};
use serde::Deserialize;
use workspace::Workspace;
use crate::{motion::right, normal::normal_object, state::Mode, visual::visual_object, Vim};
use crate::{
motion::right, normal::normal_object, state::Mode, utils::coerce_punctuation,
visual::visual_object, Vim,
};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Object {
@ -213,14 +215,14 @@ fn in_word(
right(map, relative_to, 1),
movement::FindRange::SingleLine,
|left, right| {
char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
!= char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
!= coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
},
);
let end = movement::find_boundary(map, relative_to, FindRange::SingleLine, |left, right| {
char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
!= char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
!= coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
});
Some(start..end)
@ -282,15 +284,15 @@ fn around_next_word(
right(map, relative_to, 1),
FindRange::SingleLine,
|left, right| {
char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
!= char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
!= coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
},
);
let mut word_found = false;
let end = movement::find_boundary(map, relative_to, FindRange::MultiLine, |left, right| {
let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';

View file

@ -71,6 +71,30 @@ async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
assert_eq!(cx.mode(), Mode::Normal);
}
#[gpui::test]
async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {"The quick brown fox juˇmps over the lazy dog"},
Mode::Normal,
);
// jumps
cx.simulate_keystrokes(["v", "l", "l"]);
cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
cx.simulate_keystrokes(["escape"]);
cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
// go back to the same selection state
cx.simulate_keystrokes(["v", "h", "h"]);
cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
// Ctrl-[ should behave like Esc
cx.simulate_keystrokes(["ctrl-["]);
cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
}
#[gpui::test]
async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;

View file

@ -62,6 +62,8 @@ pub struct NeovimBackedTestContext {
impl NeovimBackedTestContext {
pub async fn new(cx: &mut gpui::TestAppContext) -> NeovimBackedTestContext {
#[cfg(feature = "neovim")]
cx.executor().allow_parking();
// rust stores the name of the test on the current thread.
// We use this to automatically name a file that will store
// the neovim connection's requests/responses so that we can

View file

@ -359,7 +359,7 @@ impl NeovimConnection {
// to add one to the end in visual mode.
match mode {
Some(Mode::VisualBlock) if selection_row != cursor_row => {
// in zed we fake a block selecrtion by using multiple cursors (one per line)
// in zed we fake a block selection by using multiple cursors (one per line)
// this code emulates that.
// to deal with casees where the selection is not perfectly rectangular we extract
// the content of the selection via the "a register to get the shape correctly.

View file

@ -6,7 +6,7 @@ use editor::test::{
use futures::Future;
use gpui::{Context, View, VisualContext};
use lsp::request;
use search::BufferSearchBar;
use search::{project_search::ProjectSearchBar, BufferSearchBar};
use crate::{state::Operator, *};
@ -17,7 +17,6 @@ pub struct VimTestContext {
impl VimTestContext {
pub fn init(cx: &mut gpui::TestAppContext) {
if cx.has_global::<Vim>() {
dbg!("OOPS");
return;
}
cx.update(|cx| {
@ -59,9 +58,9 @@ impl VimTestContext {
pane.toolbar().update(cx, |toolbar, cx| {
let buffer_search_bar = cx.new_view(BufferSearchBar::new);
toolbar.add_item(buffer_search_bar, cx);
// todo!();
// let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
// toolbar.add_item(project_search_bar, cx);
let project_search_bar = cx.new_view(|_| ProjectSearchBar::new());
toolbar.add_item(project_search_bar, cx);
})
});
workspace.status_bar().update(cx, |status_bar, cx| {

View file

@ -1,6 +1,6 @@
use editor::{ClipboardSelection, Editor};
use gpui::{AppContext, ClipboardItem};
use language::Point;
use language::{CharKind, Point};
pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) {
let selections = editor.selections.all_adjusted(cx);
@ -48,3 +48,11 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
}
pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
if treat_punctuation_as_word && kind == CharKind::Punctuation {
CharKind::Word
} else {
kind
}
}

View file

@ -1,3 +1,5 @@
//! Vim support for Zed.
#[cfg(test)]
mod test;
@ -38,12 +40,18 @@ use crate::state::ReplayableAction;
/// Default: false
pub struct VimModeSetting(pub bool);
/// An Action to Switch between modes
#[derive(Clone, Deserialize, PartialEq)]
pub struct SwitchMode(pub Mode);
/// PushOperator is used to put vim into a "minor" mode,
/// where it's waiting for a specific next set of keystrokes.
/// For example 'd' needs a motion to complete.
#[derive(Clone, Deserialize, PartialEq)]
pub struct PushOperator(pub Operator);
/// Number is used to manage vim's count. Pushing a digit
/// multiplis the current value by 10 and adds the digit.
#[derive(Clone, Deserialize, PartialEq)]
struct Number(usize);
@ -51,11 +59,13 @@ actions!(
vim,
[Tab, Enter, Object, InnerObject, FindForward, FindBackward]
);
// in the workspace namespace so it's not filtered out when vim is disabled.
actions!(workspace, [ToggleVimMode]);
impl_actions!(vim, [SwitchMode, PushOperator, Number]);
/// Initializes the `vim` crate.
pub fn init(cx: &mut AppContext) {
cx.set_global(Vim::default());
VimModeSetting::register(cx);
@ -119,6 +129,7 @@ fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
visual::register(workspace, cx);
}
/// Registers a keystroke observer to observe keystrokes for the Vim integration.
pub fn observe_keystrokes(cx: &mut WindowContext) {
cx.observe_keystrokes(|keystroke_event, cx| {
if let Some(action) = keystroke_event
@ -160,6 +171,7 @@ pub fn observe_keystrokes(cx: &mut WindowContext) {
.detach()
}
/// The state pertaining to Vim mode. Stored as a global.
#[derive(Default)]
pub struct Vim {
active_editor: Option<WeakView<Editor>>,
@ -251,6 +263,8 @@ impl Vim {
Some(editor.update(cx, update))
}
/// 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 WindowContext) {
if !self.workspace_state.replaying {
self.workspace_state.recording = true;
@ -295,12 +309,19 @@ 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) {
if self.workspace_state.recording {
self.workspace_state.stop_recording_after_next_action = true;
}
}
/// Stops recording actions immediately rather than waiting until after the
/// next action to stop recording.
///
/// This doesn't include the current action.
pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>) {
if self.workspace_state.recording {
self.workspace_state
@ -311,6 +332,7 @@ impl Vim {
}
}
/// Explicitly record one action (equivalents to start_recording and stop_recording)
pub fn record_current_action(&mut self, cx: &mut WindowContext) {
self.start_recording(cx);
self.stop_recording();
@ -516,6 +538,7 @@ impl Vim {
}
}
/// Returns the state of the active editor.
pub fn state(&self) -> &EditorState {
if let Some(active_editor) = self.active_editor.as_ref() {
if let Some(state) = self.editor_states.get(&active_editor.entity_id()) {
@ -526,6 +549,7 @@ impl Vim {
&self.default_state
}
/// Updates the state of the active editor.
pub fn update_state<T>(&mut self, func: impl FnOnce(&mut EditorState) -> T) -> T {
let mut state = self.state().clone();
let ret = func(&mut state);

View file

@ -5,7 +5,7 @@ use collections::HashMap;
use editor::{
display_map::{DisplaySnapshot, ToDisplayPoint},
movement,
scroll::autoscroll::Autoscroll,
scroll::Autoscroll,
Bias, DisplayPoint, Editor,
};
use gpui::{actions, ViewContext, WindowContext};
@ -201,15 +201,13 @@ pub fn visual_block_motion(
let mut row = tail.row();
loop {
let layed_out_line = map.layout_row(row, &text_layout_details);
let laid_out_line = map.layout_row(row, &text_layout_details);
let start = DisplayPoint::new(
row,
layed_out_line.closest_index_for_x(positions.start) as u32,
);
let mut end = DisplayPoint::new(
row,
layed_out_line.closest_index_for_x(positions.end) as u32,
laid_out_line.closest_index_for_x(positions.start) as u32,
);
let mut end =
DisplayPoint::new(row, laid_out_line.closest_index_for_x(positions.end) as u32);
if end <= start {
if start.column() == map.line_len(start.row()) {
end = start;
@ -218,7 +216,7 @@ pub fn visual_block_motion(
}
}
if positions.start <= layed_out_line.width {
if positions.start <= laid_out_line.width {
let selection = Selection {
id: s.new_selection_id(),
start: start.to_point(map),