neoscroll.nvim icon indicating copy to clipboard operation
neoscroll.nvim copied to clipboard

[Feature request] Scrolling cursor alone, without viewport

Open Rumi152 opened this issue 1 year ago • 12 comments

Could we get option for scrolling only cursor, without viewport, unless the cursor goes out of bounds. It will be really useful if you create custom scrolling with chaining multiple scrolls with hooks. I am doing equivalent of <C-d>zz that looks nice, which is combination of scrolling only cursor (not possible), scrolling viewport, and scrolling both

Rumi152 avatar Jun 02 '24 21:06 Rumi152

For anyone interested here is my config for auto zz after <C-d> and <C-u>, without weird jumping.

local function post_hook(info)
	local neoscroll = require("neoscroll")

	if info == nil then
		return
	end

	if info.move_view ~= nil then
		vim.defer_fn(function()
			neoscroll.scroll(info.move_view.lines, {
				move_cursor = false,
				duration = info.move_view.duration,
			})
		end, 10)
	end

	if info.move_cursor ~= nil then
		vim.defer_fn(function()
			vim.api.nvim_win_set_cursor(0, {
				math.max(1 - vim.fn.line("."), math.min(vim.fn.line("$"), vim.fn.line(".") + info.move_cursor.lines)),
				vim.api.nvim_win_get_cursor(0)[2],
			})
		end, 10 + info.move_cursor.duration)
	end
end

local function scroll_and_center(scroll, half_win_duration)
	local window_height = vim.api.nvim_win_get_height(0)
	local topmost_line = vim.fn.line("w0")
	local current_middle_line = topmost_line + math.ceil(window_height / 2)
	local current_line = vim.fn.line(".")

	local off_target = math.max(-current_line + 1, math.min(scroll, vim.fn.line("$") - current_line))
	local off_center = current_line - current_middle_line

	local both_scroll = 0
	local view_scroll = 0
	local cursor_scroll = 0
	-- both scrolls work together in one direction
	if off_center * off_target >= 0 then
		both_scroll = off_target
		view_scroll = off_center
		cursor_scroll = 0
	else
		both_scroll = off_target + off_center -- off_center is opposite to off target here, so actually its substracting
		view_scroll = 0
		cursor_scroll = -off_center
	end

	local both_scroll_duration = math.floor(half_win_duration * (math.abs(both_scroll) / (window_height / 2)) + 0.5)
	local view_scroll_duration = math.floor(half_win_duration * (math.abs(view_scroll) / (window_height / 2)) + 0.5)
	local cursor_scroll_duration = math.floor(half_win_duration * (math.abs(cursor_scroll) / (window_height / 2)) + 0.5)

	if both_scroll == 0 then
		post_hook({
			move_view = {
				duration = view_scroll_duration,
				lines = view_scroll,
			},
			move_cursor = {
				duration = cursor_scroll_duration,
				lines = cursor_scroll,
			},
		})
	else
		require("neoscroll").scroll(both_scroll, {
			move_cursor = true,
			duration = both_scroll_duration,
			info = {
				move_view = {
					duration = view_scroll_duration,
					lines = view_scroll,
				},
				move_cursor = {
					duration = cursor_scroll_duration,
					lines = cursor_scroll,
				},
			},
		})
	end
end

return {
	"karb94/neoscroll.nvim",
	config = function()
		local neoscroll = require("neoscroll")
		neoscroll.setup({
			mappings = {},
			-- pre_hook = function(info) end,
			post_hook = post_hook,
		})
		local keymap = {
			["<C-u>"] = function()
				scroll_and_center(-vim.wo.scroll, 100)
			end,
			["<C-d>"] = function()
				scroll_and_center(vim.wo.scroll, 100)
			end,
			["<C-y>"] = function()
			    neoscroll.scroll(-0.1, { move_cursor = false, duration = 80 })
			end,
			["<C-e>"] = function()
			    neoscroll.scroll(0.1, { move_cursor = false, duration = 80 })
			end,
			["zz"] = function()
			    neoscroll.zz({ half_win_duration = 100 })
			end,
			-- ["G"] = function()
			-- 	neoscroll.G({ half_win_duration = 100 })
			-- end,
			-- ["gg"] = function()
			-- 	neoscroll.gg({ half_win_duration = 100 })
		}
		local modes = { "n", "v", "x" }
		for key, func in pairs(keymap) do
			vim.keymap.set(modes, key, func)
		end
	end,
}

Rumi152 avatar Jun 02 '24 23:06 Rumi152

Can you explain in more detail what kind of animation are you looking to achieve? I copied your config but I can't really tell what is going on.

karb94 avatar Jun 06 '24 22:06 karb94

When the view is at the bottom of a file the cursor keeps scrolling until it hits eof (At least when this option is set in config). I would love to have this possibility whenever i want. My config makes it so C-d is basically C-D + zz, but doign these two commands one after another makes view go down first and then back up after (If cursor is at the top half of screen). I wanted to do it, so view scrolls down just enough to be centered around line that is vim.wo.scroll lines down.

Behavior for C-d (analogous to C-u)

  • Cursor at the center of screen: scroll both cursor and view half a page (scroll with move_cursor = true)
  • Cursor at the bottom half of screen: scroll both cursor and view half a page, and scroll only view (move_cursor=false) additional amount required to center the screen
  • Cursor at the top half of screen: scroll both cursor and view a little amount, and cursor only, with view staying in place (not possible, right now just teleporting the cursor) the rest of half page movement. This makes sure that cursor moves half a page and view is centered after movement

If cursor is on to half, for example first line of window: cursor scrolls half a page, but the view doesnt need to scroll at all. This is problematic because plugin doesn't allow for smooth scrolling cursor, without moving view. Let me know if I need to explain more, english isn't my first language and my lua skills are not great either so it's kind of messy

Rumi152 avatar Jun 08 '24 19:06 Rumi152

Thanks for sharing your config @Rumi152 -- I was just trying to customize C-u and C-d so that it keeps the cursor in the middle of the screen.

To piggyback on this request, it would be great if this case was natively supported as an option on the plugin itself. My default remap for <C-d> used to be "<C-d>zz" (based on ThePrimeagen's dotfiles). Feels less disorienting than the default vim behaviour personally.

In any case, big thanks for neoscroll @karb94!

at-karan-sharma avatar Jun 16 '24 23:06 at-karan-sharma

This is just a thought but what about using the pre_hook functionality to move the cursor to the centre before starting the scrolling animation? You could even hide the cursor so that the transition is seamless.

karb94 avatar Jul 05 '24 19:07 karb94

I mean yeah, it kind of works right now. Just the option of smoothly moving the cursor instead of hiding it and showing again is not possible. Even without that the plugin is great and I love using similiar solution, it just doesn't seamlessly integrate with defaults (Where both the view and cursor scroll together, not the one where cursor is hidden during animation).

Rumi152 avatar Jul 06 '24 10:07 Rumi152

I wanted to achieve the same effect and also couldn't manage with neoscroll, so I wrote my own animation and it works well. I thought I would share it here in case it helps, as an inspiration.

https://gist.github.com/Lcchy/a438997093438785db4796ebcb97b873

ps: it uses coop.nvim for async execution

Lcchy avatar Nov 16 '24 16:11 Lcchy

I tried to create a good scrolling behavior for myself using "Hzz" and "Lzz" as mappings, but the zz smooth scrolling doesn't work with this implementation. Any ideas as to why? I am decently new to NVim, so I don't konw if there is some implicit reason as to why this isn't registered as a normal "zz". Because when I use the command manually, aka, H then zz, it works just fine.

Pairadux avatar Dec 05 '24 17:12 Pairadux

I wanted to achieve the same effect and also couldn't manage with neoscroll, so I wrote my own animation and it works well. I thought I would share it here in case it helps, as an inspiration.

https://gist.github.com/Lcchy/a438997093438785db4796ebcb97b873

ps: it uses coop.nvim for async execution

wow! doing zz doesn't bounce anymore. thanks

idelice avatar Dec 10 '24 13:12 idelice

You're welcome! Although I have noticed it being slower with multiple panes, see https://gist.github.com/Lcchy/a438997093438785db4796ebcb97b873?permalink_comment_id=5334532#gistcomment-5334532

Lcchy avatar Dec 12 '24 14:12 Lcchy

@Rumi152 this works really well! thank you for putting this together, the only issue for me is that gg<C-d> doesn't do anything - as in when the cursor is on start of file <C-d> doesn't work for some reason..?

pashashocky avatar Apr 05 '25 22:04 pashashocky

Thanks for sharing your config @Rumi152 -- I was just trying to customize C-u and C-d so that it keeps the cursor in the middle of the screen.

To piggyback on this request, it would be great if this case was natively supported as an option on the plugin itself. My default remap for <C-d> used to be "<C-d>zz" (based on ThePrimeagen's dotfiles). Feels less disorienting than the default vim behaviour personally.

In any case, big thanks for neoscroll @karb94!

Yeah I was also trying to accomplish this behavior. But haven't been able to make it work.

adnanaq avatar May 15 '25 14:05 adnanaq