Add MovePageUp and MovePageDown editor commands
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
137a9cefbd
commit
8df84e0341
5 changed files with 213 additions and 10 deletions
|
@ -108,6 +108,18 @@ pub struct SelectToBeginningOfLine {
|
||||||
stop_at_soft_wraps: bool,
|
stop_at_soft_wraps: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Deserialize, PartialEq)]
|
||||||
|
pub struct MovePageUp {
|
||||||
|
#[serde(default)]
|
||||||
|
center_cursor: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Deserialize, PartialEq)]
|
||||||
|
pub struct MovePageDown {
|
||||||
|
#[serde(default)]
|
||||||
|
center_cursor: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq)]
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
pub struct SelectToEndOfLine {
|
pub struct SelectToEndOfLine {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -222,6 +234,8 @@ impl_actions!(
|
||||||
SelectToBeginningOfLine,
|
SelectToBeginningOfLine,
|
||||||
SelectToEndOfLine,
|
SelectToEndOfLine,
|
||||||
ToggleCodeActions,
|
ToggleCodeActions,
|
||||||
|
MovePageUp,
|
||||||
|
MovePageDown,
|
||||||
ConfirmCompletion,
|
ConfirmCompletion,
|
||||||
ConfirmCodeAction,
|
ConfirmCodeAction,
|
||||||
]
|
]
|
||||||
|
@ -5536,6 +5550,49 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vscode style + emacs style
|
||||||
|
pub fn move_page_down(&mut self, _: &MovePageDown, cx: &mut ViewContext<Self>) {
|
||||||
|
let row_count = match self.visible_line_count {
|
||||||
|
Some(row_count) => row_count as u32 - 1,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
|
let line_mode = s.line_mode;
|
||||||
|
s.move_with(|map, selection| {
|
||||||
|
if !selection.is_empty() && !line_mode {
|
||||||
|
selection.goal = SelectionGoal::None;
|
||||||
|
}
|
||||||
|
let (cursor, goal) =
|
||||||
|
movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
|
||||||
|
eprintln!(
|
||||||
|
"{:?} down by rows {} = {:?}",
|
||||||
|
selection.end, row_count, cursor
|
||||||
|
);
|
||||||
|
selection.collapse_to(cursor, goal);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_page_up(&mut self, _: &MovePageUp, cx: &mut ViewContext<Self>) {
|
||||||
|
let row_count = match self.visible_line_count {
|
||||||
|
Some(row_count) => row_count as u32 - 1,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
|
let line_mode = s.line_mode;
|
||||||
|
s.move_with(|map, selection| {
|
||||||
|
if !selection.is_empty() && !line_mode {
|
||||||
|
selection.goal = SelectionGoal::None;
|
||||||
|
}
|
||||||
|
let (cursor, goal) =
|
||||||
|
movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
|
||||||
|
selection.collapse_to(cursor, goal);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext<Self>) {
|
pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext<Self>) {
|
||||||
let lines = match self.visible_line_count {
|
let lines = match self.visible_line_count {
|
||||||
Some(lines) => lines,
|
Some(lines) => lines,
|
||||||
|
|
|
@ -1194,6 +1194,120 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = EditorTestContext::new(cx);
|
||||||
|
|
||||||
|
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||||
|
cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
|
||||||
|
|
||||||
|
cx.set_state(
|
||||||
|
&r#"
|
||||||
|
ˇone
|
||||||
|
two
|
||||||
|
threeˇ
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ten
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
one
|
||||||
|
two
|
||||||
|
three
|
||||||
|
ˇfour
|
||||||
|
five
|
||||||
|
sixˇ
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
tenx
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
one
|
||||||
|
two
|
||||||
|
three
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
ˇseven
|
||||||
|
eight
|
||||||
|
nineˇ
|
||||||
|
ten
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
one
|
||||||
|
two
|
||||||
|
three
|
||||||
|
ˇfour
|
||||||
|
five
|
||||||
|
sixˇ
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ten
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
ˇone
|
||||||
|
two
|
||||||
|
threeˇ
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ten
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test select collapsing
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
editor.move_page_down(&MovePageDown::default(), cx);
|
||||||
|
editor.move_page_down(&MovePageDown::default(), cx);
|
||||||
|
editor.move_page_down(&MovePageDown::default(), cx);
|
||||||
|
});
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
one
|
||||||
|
two
|
||||||
|
three
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ˇten
|
||||||
|
ˇ"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = EditorTestContext::new(cx);
|
let mut cx = EditorTestContext::new(cx);
|
||||||
|
|
|
@ -29,6 +29,25 @@ pub fn up(
|
||||||
start: DisplayPoint,
|
start: DisplayPoint,
|
||||||
goal: SelectionGoal,
|
goal: SelectionGoal,
|
||||||
preserve_column_at_start: bool,
|
preserve_column_at_start: bool,
|
||||||
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
|
up_by_rows(map, start, 1, goal, preserve_column_at_start)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn down(
|
||||||
|
map: &DisplaySnapshot,
|
||||||
|
start: DisplayPoint,
|
||||||
|
goal: SelectionGoal,
|
||||||
|
preserve_column_at_end: bool,
|
||||||
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
|
down_by_rows(map, start, 1, goal, preserve_column_at_end)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn up_by_rows(
|
||||||
|
map: &DisplaySnapshot,
|
||||||
|
start: DisplayPoint,
|
||||||
|
row_count: u32,
|
||||||
|
goal: SelectionGoal,
|
||||||
|
preserve_column_at_start: bool,
|
||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
let mut goal_column = if let SelectionGoal::Column(column) = goal {
|
let mut goal_column = if let SelectionGoal::Column(column) = goal {
|
||||||
column
|
column
|
||||||
|
@ -36,7 +55,7 @@ pub fn up(
|
||||||
map.column_to_chars(start.row(), start.column())
|
map.column_to_chars(start.row(), start.column())
|
||||||
};
|
};
|
||||||
|
|
||||||
let prev_row = start.row().saturating_sub(1);
|
let prev_row = start.row().saturating_sub(row_count);
|
||||||
let mut point = map.clip_point(
|
let mut point = map.clip_point(
|
||||||
DisplayPoint::new(prev_row, map.line_len(prev_row)),
|
DisplayPoint::new(prev_row, map.line_len(prev_row)),
|
||||||
Bias::Left,
|
Bias::Left,
|
||||||
|
@ -62,9 +81,10 @@ pub fn up(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn down(
|
pub fn down_by_rows(
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
start: DisplayPoint,
|
start: DisplayPoint,
|
||||||
|
row_count: u32,
|
||||||
goal: SelectionGoal,
|
goal: SelectionGoal,
|
||||||
preserve_column_at_end: bool,
|
preserve_column_at_end: bool,
|
||||||
) -> (DisplayPoint, SelectionGoal) {
|
) -> (DisplayPoint, SelectionGoal) {
|
||||||
|
@ -74,8 +94,8 @@ pub fn down(
|
||||||
map.column_to_chars(start.row(), start.column())
|
map.column_to_chars(start.row(), start.column())
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_row = start.row() + 1;
|
let new_row = start.row() + row_count;
|
||||||
let mut point = map.clip_point(DisplayPoint::new(next_row, 0), Bias::Right);
|
let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
|
||||||
if point.row() > start.row() {
|
if point.row() > start.row() {
|
||||||
*point.column_mut() = map.column_from_chars(point.row(), goal_column);
|
*point.column_mut() = map.column_from_chars(point.row(), goal_column);
|
||||||
} else if preserve_column_at_end {
|
} else if preserve_column_at_end {
|
||||||
|
|
|
@ -17,10 +17,11 @@ use parking_lot::{Mutex, RwLock};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
executor, keymap::Keystroke, platform, Action, AnyViewHandle, AppContext, Appearance, Entity,
|
executor, geometry::vector::Vector2F, keymap::Keystroke, platform, Action, AnyViewHandle,
|
||||||
Event, FontCache, InputHandler, KeyDownEvent, LeakDetector, ModelContext, ModelHandle,
|
AppContext, Appearance, Entity, Event, FontCache, InputHandler, KeyDownEvent, LeakDetector,
|
||||||
MutableAppContext, Platform, ReadModelWith, ReadViewWith, RenderContext, Task, UpdateModel,
|
ModelContext, ModelHandle, MutableAppContext, Platform, ReadModelWith, ReadViewWith,
|
||||||
UpdateView, View, ViewContext, ViewHandle, WeakHandle, WindowInputHandler,
|
RenderContext, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle, WeakHandle,
|
||||||
|
WindowInputHandler,
|
||||||
};
|
};
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -275,6 +276,17 @@ impl TestAppContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
|
||||||
|
let mut window = self.window_mut(window_id);
|
||||||
|
window.size = size;
|
||||||
|
let mut handlers = mem::take(&mut window.resize_handlers);
|
||||||
|
drop(window);
|
||||||
|
for handler in &mut handlers {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
self.window_mut(window_id).resize_handlers = handlers;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
|
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
|
||||||
let mut handlers = BTreeMap::new();
|
let mut handlers = BTreeMap::new();
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,11 +34,11 @@ pub struct ForegroundPlatform {
|
||||||
struct Dispatcher;
|
struct Dispatcher;
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
size: Vector2F,
|
pub(crate) size: Vector2F,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
current_scene: Option<crate::Scene>,
|
current_scene: Option<crate::Scene>,
|
||||||
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
||||||
resize_handlers: Vec<Box<dyn FnMut()>>,
|
pub(crate) resize_handlers: Vec<Box<dyn FnMut()>>,
|
||||||
close_handlers: Vec<Box<dyn FnOnce()>>,
|
close_handlers: Vec<Box<dyn FnOnce()>>,
|
||||||
fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
|
fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
|
||||||
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
|
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue