Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request] Scrolling cursor alone, without viewport #106

Open
Rumi152 opened this issue Jun 2, 2024 · 7 comments
Open

[Feature request] Scrolling cursor alone, without viewport #106

Rumi152 opened this issue Jun 2, 2024 · 7 comments

Comments

@Rumi152
Copy link

Rumi152 commented Jun 2, 2024

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 zz that looks nice, which is combination of scrolling only cursor (not possible), scrolling viewport, and scrolling both

@Rumi152 Rumi152 changed the title [Feature request] [Feature request] Scrolling cursor alone, without viewport Jun 2, 2024
@Rumi152
Copy link
Author

Rumi152 commented Jun 2, 2024

For anyone interested here is my config for auto zz after and , 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,
}

@karb94
Copy link
Owner

karb94 commented Jun 6, 2024

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.

@Rumi152
Copy link
Author

Rumi152 commented Jun 8, 2024

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

@at-karan-sharma
Copy link

at-karan-sharma commented Jun 16, 2024

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!

@karb94
Copy link
Owner

karb94 commented Jul 5, 2024

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.

@Rumi152
Copy link
Author

Rumi152 commented Jul 6, 2024

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).

@Lcchy
Copy link

Lcchy commented Nov 16, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants