Add support for resizing panes using vim motions (#21038)

Closes #8628

Release Notes:

- Added support for resizing the current pane using vim keybinds with
the intention to follow the functionality of vim
  - "ctrl-w +" to make a pane taller 
  - "ctrl-w -" to make the pane shorter
  - "ctrl-w >" to make a pane wider
  - "ctrl-w <" to make the pane narrower
- Changed vim pre_count and post_count to globals to allow for other
crates to use the vim count. In this case, it allows for resizing by
more than one unit. For example, "10 ctrl-w -" will decrease the height
of the pane 10 times more than "ctrl-w -"
- This pr does **not** add keybinds for making all panes in an axis
equal size and does **not** add support for resizing docks. This is
mentioned because these could be implied by the original issue

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
AidanV 2024-11-26 16:24:29 -08:00 committed by GitHub
parent d75d34576a
commit f702575255
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 251 additions and 68 deletions

View file

@ -25,8 +25,8 @@ use editor::{
Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
};
use gpui::{
actions, impl_actions, Action, AppContext, Entity, EventEmitter, KeyContext, KeystrokeEvent,
Render, Subscription, View, ViewContext, WeakView,
actions, impl_actions, Action, AppContext, Axis, Entity, EventEmitter, KeyContext,
KeystrokeEvent, Render, Subscription, View, ViewContext, WeakView,
};
use insert::{NormalBefore, TemporaryNormal};
use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
@ -40,12 +40,17 @@ use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
use std::{mem, ops::Range, sync::Arc};
use surrounds::SurroundsType;
use theme::ThemeSettings;
use ui::{IntoElement, VisualContext};
use vim_mode_setting::VimModeSetting;
use workspace::{self, Pane, Workspace};
use workspace::{self, Pane, ResizeIntent, Workspace};
use crate::state::ReplayableAction;
/// Used to resize the current pane
#[derive(Clone, Deserialize, PartialEq)]
pub struct ResizePane(pub ResizeIntent);
/// An Action to Switch between modes
#[derive(Clone, Deserialize, PartialEq)]
pub struct SwitchMode(pub Mode);
@ -81,7 +86,10 @@ actions!(
// in the workspace namespace so it's not filtered out when vim is disabled.
actions!(workspace, [ToggleVimMode]);
impl_actions!(vim, [SwitchMode, PushOperator, Number, SelectRegister]);
impl_actions!(
vim,
[ResizePane, SwitchMode, PushOperator, Number, SelectRegister]
);
/// Initializes the `vim` crate.
pub fn init(cx: &mut AppContext) {
@ -109,6 +117,30 @@ pub fn init(cx: &mut AppContext) {
});
});
workspace.register_action(|workspace, action: &ResizePane, cx| {
let count = Vim::take_count(cx.window_context()).unwrap_or(1) as f32;
let theme = ThemeSettings::get_global(cx);
let Ok(font_id) = cx.text_system().font_id(&theme.buffer_font) else {
return;
};
let Ok(width) = cx
.text_system()
.advance(font_id, theme.buffer_font_size(cx), 'm')
else {
return;
};
let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
let (axis, amount) = match action.0 {
ResizeIntent::Lengthen => (Axis::Vertical, height),
ResizeIntent::Shorten => (Axis::Vertical, height * -1.),
ResizeIntent::Widen => (Axis::Horizontal, width.width),
ResizeIntent::Narrow => (Axis::Horizontal, width.width * -1.),
};
workspace.resize_pane(axis, amount * count, cx);
});
workspace.register_action(|workspace, _: &SearchSubmit, cx| {
let vim = workspace
.focused_pane(cx)
@ -131,7 +163,7 @@ pub(crate) struct VimAddon {
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)
self.view.read(cx).extend_key_context(key_context, cx)
}
fn to_any(&self) -> &dyn std::any::Any {
@ -146,11 +178,6 @@ pub(crate) struct Vim {
pub temp_mode: bool,
pub exit_temporary_mode: bool,
/// pre_count is the number before an operator is specified (3 in 3d2d)
pre_count: Option<usize>,
/// post_count is the number after an operator is specified (2 in 3d2d)
post_count: Option<usize>,
operator_stack: Vec<Operator>,
pub(crate) replacements: Vec<(Range<editor::Anchor>, String)>,
@ -197,8 +224,6 @@ impl Vim {
last_mode: Mode::Normal,
temp_mode: false,
exit_temporary_mode: false,
pre_count: None,
post_count: None,
operator_stack: Vec::new(),
replacements: Vec::new(),
@ -471,7 +496,7 @@ impl Vim {
self.current_anchor.take();
}
if mode != Mode::Insert && mode != Mode::Replace {
self.take_count(cx);
Vim::take_count(cx);
}
// Sync editor settings like clip mode
@ -551,22 +576,24 @@ impl Vim {
});
}
fn take_count(&mut self, cx: &mut ViewContext<Self>) -> Option<usize> {
pub fn take_count(cx: &mut AppContext) -> Option<usize> {
let global_state = cx.global_mut::<VimGlobals>();
if global_state.dot_replaying {
return global_state.recorded_count;
}
let count = if self.post_count.is_none() && self.pre_count.is_none() {
let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
return None;
} else {
Some(self.post_count.take().unwrap_or(1) * self.pre_count.take().unwrap_or(1))
Some(
global_state.post_count.take().unwrap_or(1)
* global_state.pre_count.take().unwrap_or(1),
)
};
if global_state.dot_recording {
global_state.recorded_count = count;
}
self.sync_vim_settings(cx);
count
}
@ -613,7 +640,7 @@ impl Vim {
}
}
pub fn extend_key_context(&self, context: &mut KeyContext) {
pub fn extend_key_context(&self, context: &mut KeyContext, cx: &AppContext) {
let mut mode = match self.mode {
Mode::Normal => "normal",
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
@ -625,8 +652,8 @@ impl Vim {
let mut operator_id = "none";
let active_operator = self.active_operator();
if active_operator.is_none() && self.pre_count.is_some()
|| active_operator.is_some() && self.post_count.is_some()
if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
|| active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
{
context.add("VimCount");
}
@ -837,18 +864,18 @@ impl Vim {
fn push_count_digit(&mut self, number: usize, cx: &mut ViewContext<Self>) {
if self.active_operator().is_some() {
let post_count = self.post_count.unwrap_or(0);
let post_count = Vim::globals(cx).post_count.unwrap_or(0);
self.post_count = Some(
Vim::globals(cx).post_count = Some(
post_count
.checked_mul(10)
.and_then(|post_count| post_count.checked_add(number))
.unwrap_or(post_count),
)
} else {
let pre_count = self.pre_count.unwrap_or(0);
let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
self.pre_count = Some(
Vim::globals(cx).pre_count = Some(
pre_count
.checked_mul(10)
.and_then(|pre_count| pre_count.checked_add(number))
@ -880,7 +907,7 @@ impl Vim {
}
fn clear_operator(&mut self, cx: &mut ViewContext<Self>) {
self.take_count(cx);
Vim::take_count(cx);
self.selected_register.take();
self.operator_stack.clear();
self.sync_vim_settings(cx);