Add scrolling functionality to markdown preview mode (#44585)

Closes #21324

Adds four new commands:
- `markdown::MoveUp`, `markdown::MoveDown` - these scroll up and down in
markdown preview mode, by no more than the height of a large headline.

- `markdown::MoveUpByItem`, and `markdown::MoveDownByItem` - these
scroll up and down by the height of the item at the top of the markdown
preview window. So headlines and large codeblocks, for instance, scroll
further than individual paragraph lines.

Also attempts to create sensible defaults:
`down` -> `markdown::ScrollDown`
`up` -> `markdown::ScrollUp`
`alt-down` -> `markdown::ScrollDownByItem`
`alt-up` -> `markdown::ScrollUpByItem`

And in Vim:

`ctrl-u` -> `markdown::ScrollPageUp`
`ctrl-d` -> `markdown::ScrollPageDown`
`ctrl-e` -> `markdown::ScrollDown`
`ctrl-y` -> `markdown::ScrollUp`


Release Notes:

- Added commands `markdown::ScrollUp`, `markdown::ScrollDown`,
`markdown::ScrollUpByItem`, and `markdown::ScrollDownByItem`
- Changed commands `markdown::MovePageUp` to `markdown::ScrollPageUp`
and `markdown::MovePageDown` to `markdown::ScrollPageDown`
This commit is contained in:
KyleBarton
2025-12-11 22:18:38 +00:00
committed by GitHub
parent 37f2ac24b8
commit 07748b7bae
9 changed files with 106 additions and 11 deletions
Generated
+1
View File
@@ -18097,6 +18097,7 @@ dependencies = [
"language",
"log",
"lsp",
"markdown_preview",
"menu",
"multi_buffer",
"nvim-rs",
+6 -2
View File
@@ -1192,8 +1192,12 @@
{
"context": "MarkdownPreview",
"bindings": {
"pageup": "markdown::MovePageUp",
"pagedown": "markdown::MovePageDown"
"pageup": "markdown::ScrollPageUp",
"pagedown": "markdown::ScrollPageDown",
"up": "markdown::ScrollUp",
"down": "markdown::ScrollDown",
"alt-up": "markdown::ScrollUpByItem",
"alt-down": "markdown::ScrollDownByItem"
}
},
{
+6 -2
View File
@@ -1296,8 +1296,12 @@
{
"context": "MarkdownPreview",
"bindings": {
"pageup": "markdown::MovePageUp",
"pagedown": "markdown::MovePageDown"
"pageup": "markdown::ScrollPageUp",
"pagedown": "markdown::ScrollPageDown",
"up": "markdown::ScrollUp",
"down": "markdown::ScrollDown",
"alt-up": "markdown::ScrollUpByItem",
"alt-down": "markdown::ScrollDownByItem"
}
},
{
+6 -2
View File
@@ -1220,8 +1220,12 @@
"context": "MarkdownPreview",
"use_key_equivalents": true,
"bindings": {
"pageup": "markdown::MovePageUp",
"pagedown": "markdown::MovePageDown"
"pageup": "markdown::ScrollPageUp",
"pagedown": "markdown::ScrollPageDown",
"up": "markdown::ScrollUp",
"down": "markdown::ScrollDown",
"alt-up": "markdown::ScrollUpByItem",
"alt-down": "markdown::ScrollDownByItem"
}
},
{
+9
View File
@@ -1046,5 +1046,14 @@
"g g": "settings_editor::FocusFirstNavEntry",
"shift-g": "settings_editor::FocusLastNavEntry"
}
},
{
"context": "MarkdownPreview",
"bindings": {
"ctrl-u": "markdown::ScrollPageUp",
"ctrl-d": "markdown::ScrollPageDown",
"ctrl-y": "markdown::ScrollUp",
"ctrl-e": "markdown::ScrollDown"
}
}
]
@@ -11,9 +11,19 @@ actions!(
markdown,
[
/// Scrolls up by one page in the markdown preview.
MovePageUp,
#[action(deprecated_aliases = ["markdown::MovePageUp"])]
ScrollPageUp,
/// Scrolls down by one page in the markdown preview.
MovePageDown,
#[action(deprecated_aliases = ["markdown::MovePageDown"])]
ScrollPageDown,
/// Scrolls up by approximately one visual line.
ScrollUp,
/// Scrolls down by approximately one visual line.
ScrollDown,
/// Scrolls up by one markdown element in the markdown preview
ScrollUpByItem,
/// Scrolls down by one markdown element in the markdown preview
ScrollDownByItem,
/// Opens a markdown preview for the current file.
OpenPreview,
/// Opens a markdown preview in a split pane.
@@ -1,3 +1,4 @@
use std::cmp::min;
use std::sync::Arc;
use std::time::Duration;
use std::{ops::Range, path::PathBuf};
@@ -20,11 +21,12 @@ use workspace::{Pane, Workspace};
use crate::markdown_elements::ParsedMarkdownElement;
use crate::markdown_renderer::CheckboxClickedEvent;
use crate::{
MovePageDown, MovePageUp, OpenFollowingPreview, OpenPreview, OpenPreviewToTheSide,
OpenFollowingPreview, OpenPreview, OpenPreviewToTheSide, ScrollPageDown, ScrollPageUp,
markdown_elements::ParsedMarkdown,
markdown_parser::parse_markdown,
markdown_renderer::{RenderContext, render_markdown_block},
};
use crate::{ScrollDown, ScrollDownByItem, ScrollUp, ScrollUpByItem};
const REPARSE_DEBOUNCE: Duration = Duration::from_millis(200);
@@ -425,7 +427,7 @@ impl MarkdownPreviewView {
!(current_block.is_list_item() && next_block.map(|b| b.is_list_item()).unwrap_or(false))
}
fn scroll_page_up(&mut self, _: &MovePageUp, _window: &mut Window, cx: &mut Context<Self>) {
fn scroll_page_up(&mut self, _: &ScrollPageUp, _window: &mut Window, cx: &mut Context<Self>) {
let viewport_height = self.list_state.viewport_bounds().size.height;
if viewport_height.is_zero() {
return;
@@ -435,7 +437,12 @@ impl MarkdownPreviewView {
cx.notify();
}
fn scroll_page_down(&mut self, _: &MovePageDown, _window: &mut Window, cx: &mut Context<Self>) {
fn scroll_page_down(
&mut self,
_: &ScrollPageDown,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let viewport_height = self.list_state.viewport_bounds().size.height;
if viewport_height.is_zero() {
return;
@@ -444,6 +451,56 @@ impl MarkdownPreviewView {
self.list_state.scroll_by(viewport_height);
cx.notify();
}
fn scroll_up(&mut self, _: &ScrollUp, window: &mut Window, cx: &mut Context<Self>) {
let scroll_top = self.list_state.logical_scroll_top();
if let Some(bounds) = self.list_state.bounds_for_item(scroll_top.item_ix) {
let item_height = bounds.size.height;
// Scroll no more than the rough equivalent of a large headline
let max_height = window.rem_size() * 2;
let scroll_height = min(item_height, max_height);
self.list_state.scroll_by(-scroll_height);
}
cx.notify();
}
fn scroll_down(&mut self, _: &ScrollDown, window: &mut Window, cx: &mut Context<Self>) {
let scroll_top = self.list_state.logical_scroll_top();
if let Some(bounds) = self.list_state.bounds_for_item(scroll_top.item_ix) {
let item_height = bounds.size.height;
// Scroll no more than the rough equivalent of a large headline
let max_height = window.rem_size() * 2;
let scroll_height = min(item_height, max_height);
self.list_state.scroll_by(scroll_height);
}
cx.notify();
}
fn scroll_up_by_item(
&mut self,
_: &ScrollUpByItem,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let scroll_top = self.list_state.logical_scroll_top();
if let Some(bounds) = self.list_state.bounds_for_item(scroll_top.item_ix) {
self.list_state.scroll_by(-bounds.size.height);
}
cx.notify();
}
fn scroll_down_by_item(
&mut self,
_: &ScrollDownByItem,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let scroll_top = self.list_state.logical_scroll_top();
if let Some(bounds) = self.list_state.bounds_for_item(scroll_top.item_ix) {
self.list_state.scroll_by(bounds.size.height);
}
cx.notify();
}
}
impl Focusable for MarkdownPreviewView {
@@ -496,6 +553,10 @@ impl Render for MarkdownPreviewView {
.track_focus(&self.focus_handle(cx))
.on_action(cx.listener(MarkdownPreviewView::scroll_page_up))
.on_action(cx.listener(MarkdownPreviewView::scroll_page_down))
.on_action(cx.listener(MarkdownPreviewView::scroll_up))
.on_action(cx.listener(MarkdownPreviewView::scroll_down))
.on_action(cx.listener(MarkdownPreviewView::scroll_up_by_item))
.on_action(cx.listener(MarkdownPreviewView::scroll_down_by_item))
.size_full()
.bg(cx.theme().colors().editor_background)
.p_4()
+1
View File
@@ -63,6 +63,7 @@ indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
markdown_preview.workspace = true
parking_lot.workspace = true
project_panel.workspace = true
release_channel.workspace = true
+1
View File
@@ -28,6 +28,7 @@ impl VimTestContext {
search::init(cx);
theme::init(theme::LoadThemes::JustBase, cx);
settings_ui::init(cx);
markdown_preview::init(cx);
});
}