Detailed changes
@@ -255,6 +255,8 @@
/// 2. "indent_aware"
"background_coloring": "disabled"
},
+ // Whether the editor will scroll beyond the last line.
+ "scroll_beyond_last_line": "one_page",
// The number of lines to keep above/below the cursor when scrolling.
"vertical_scroll_margin": 3,
// Scroll sensitivity multiplier. This multiplier is applied
@@ -15,6 +15,7 @@ pub struct EditorSettings {
pub toolbar: Toolbar,
pub scrollbar: Scrollbar,
pub gutter: Gutter,
+ pub scroll_beyond_last_line: ScrollBeyondLastLine,
pub vertical_scroll_margin: f32,
pub scroll_sensitivity: f32,
pub relative_line_numbers: bool,
@@ -116,6 +117,22 @@ pub enum MultiCursorModifier {
CmdOrCtrl,
}
+/// Whether the editor will scroll beyond the last line.
+///
+/// Default: one_page
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum ScrollBeyondLastLine {
+ /// The editor will not scroll beyond the last line.
+ Off,
+
+ /// The editor will scroll beyond the last line by one page.
+ OnePage,
+
+ /// The editor will scroll beyond the last line by the same number of lines as vertical_scroll_margin.
+ VerticalScrollMargin,
+}
+
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct EditorSettingsContent {
/// Whether the cursor blinks in the editor.
@@ -158,6 +175,10 @@ pub struct EditorSettingsContent {
pub scrollbar: Option<ScrollbarContent>,
/// Gutter related settings
pub gutter: Option<GutterContent>,
+ /// Whether the editor will scroll beyond the last line.
+ ///
+ /// Default: one_page
+ pub scroll_beyond_last_line: Option<ScrollBeyondLastLine>,
/// The number of lines to keep above/below the cursor when auto-scrolling.
///
/// Default: 3.
@@ -1,3 +1,4 @@
+use crate::editor_settings::ScrollBeyondLastLine;
use crate::{
blame_entry_tooltip::{blame_entry_relative_timestamp, BlameEntryTooltip},
display_map::{
@@ -1089,11 +1090,17 @@ impl EditorElement {
point(bounds.lower_right().x, bounds.lower_left().y),
);
+ let settings = EditorSettings::get_global(cx);
+ let scroll_beyond_last_line: f32 = match settings.scroll_beyond_last_line {
+ ScrollBeyondLastLine::OnePage => rows_per_page,
+ ScrollBeyondLastLine::Off => 1.0,
+ ScrollBeyondLastLine::VerticalScrollMargin => 1.0 + settings.vertical_scroll_margin,
+ };
+ let total_rows = snapshot.max_point().row().as_f32() + scroll_beyond_last_line;
let height = bounds.size.height;
- let total_rows = snapshot.max_point().row().as_f32() + rows_per_page;
let px_per_row = height / total_rows;
let thumb_height = (rows_per_page * px_per_row).max(ScrollbarLayout::MIN_THUMB_HEIGHT);
- let row_height = (height - thumb_height) / snapshot.max_point().row().as_f32();
+ let row_height = (height - thumb_height) / (total_rows - rows_per_page).max(0.0);
Some(ScrollbarLayout {
hitbox: cx.insert_hitbox(track_bounds, false),
@@ -4805,9 +4812,22 @@ impl Element for EditorElement {
cx,
);
+ let settings = EditorSettings::get_global(cx);
+ let scroll_max_row = max_row.as_f32();
+ let scroll_max_row = match settings.scroll_beyond_last_line {
+ ScrollBeyondLastLine::OnePage => scroll_max_row,
+ ScrollBeyondLastLine::Off => {
+ (scroll_max_row - height_in_lines + 1.0).max(0.0)
+ }
+ ScrollBeyondLastLine::VerticalScrollMargin => (scroll_max_row
+ - height_in_lines
+ + 1.0
+ + settings.vertical_scroll_margin)
+ .max(0.0),
+ };
let scroll_max = point(
((scroll_width - text_hitbox.size.width) / em_width).max(0.0),
- max_row.as_f32(),
+ scroll_max_row,
);
self.editor.update(cx, |editor, cx| {
@@ -2,6 +2,7 @@ mod actions;
pub(crate) mod autoscroll;
pub(crate) mod scroll_amount;
+use crate::editor_settings::ScrollBeyondLastLine;
use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
@@ -199,8 +200,20 @@ impl ScrollManager {
0,
)
} else {
+ let scroll_top = scroll_position.y;
+ let scroll_top = match EditorSettings::get_global(cx).scroll_beyond_last_line {
+ ScrollBeyondLastLine::OnePage => scroll_top,
+ ScrollBeyondLastLine::Off => scroll_top
+ .min((map.max_buffer_row().as_f32()) - self.visible_line_count.unwrap() + 1.0),
+ ScrollBeyondLastLine::VerticalScrollMargin => scroll_top.min(
+ (map.max_buffer_row().as_f32()) - self.visible_line_count.unwrap()
+ + 1.0
+ + self.vertical_scroll_margin,
+ ),
+ };
+
let scroll_top_buffer_point =
- DisplayPoint::new(DisplayRow(scroll_position.y as u32), 0).to_point(&map);
+ DisplayPoint::new(DisplayRow(scroll_top as u32), 0).to_point(&map);
let top_anchor = map
.buffer_snapshot
.anchor_at(scroll_top_buffer_point, Bias::Right);
@@ -210,7 +223,7 @@ impl ScrollManager {
anchor: top_anchor,
offset: point(
scroll_position.x.max(0.),
- scroll_position.y - top_anchor.to_display_point(&map).row().as_f32(),
+ scroll_top - top_anchor.to_display_point(&map).row().as_f32(),
),
},
scroll_top_buffer_point.row,