From 6518711b10bb5c8bfb73e15d8e33d6c4c10cb7b6 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Wed, 7 Jun 2023 14:05:25 -0700 Subject: [PATCH 1/5] feat: use `MouseMoved` autocmd Makes use of neovim/neovim#23947 --- lua/cokeline/hover.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lua/cokeline/hover.lua b/lua/cokeline/hover.lua index a252ac4..f285805 100644 --- a/lua/cokeline/hover.lua +++ b/lua/cokeline/hover.lua @@ -132,14 +132,12 @@ function M.setup() return end - vim.keymap.set({ "", "i" }, "", function() - local ok, pos = pcall(vim.fn.getmousepos) - if not ok then - return - end - on_hover(pos) - return "" - end, { expr = true }) + vim.api.nvim_create_autocmd("MouseMoved", { + callback = function(ev) + on_hover(ev.data) + return "" + end, + }) end return M From 9050f467557aa07521a53aa8b51a72b99027b3ec Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Fri, 9 Jun 2023 18:27:31 -0700 Subject: [PATCH 2/5] feat: reorder tabs with mouse Depends on neovim/neovim##23947 --- lua/cokeline/hover.lua | 57 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/lua/cokeline/hover.lua b/lua/cokeline/hover.lua index f285805..44cd994 100644 --- a/lua/cokeline/hover.lua +++ b/lua/cokeline/hover.lua @@ -127,14 +127,67 @@ local function on_hover(current) last_position = current end +local function on_drag(pos) + if pos.dragging == "l" then + -- TODO: handle drag events + local current = M.get_current(pos.screencol) + if not current or current.kind ~= "buffer" then + return + end + + if not _G.cokeline.__dragging then + local buf = buffers.get_buffer(current.bufnr) + if not buf then + return + end + _G.cokeline.__dragging = { + bufnr = current.bufnr, + index = buf._valid_index, + } + end + if _G.cokeline.__dragging.bufnr == current.bufnr then + return + end + if _G.cokeline.__dragging.bufnr then + local buf = buffers.get_buffer(_G.cokeline.__dragging.bufnr) + if buf and current.index then + buffers.move_buffer( + buf, + buffers.get_buffer(current.bufnr)._valid_index + ) + end + end + end +end + function M.setup() if version.minor < 8 then return end - vim.api.nvim_create_autocmd("MouseMoved", { + vim.api.nvim_create_autocmd("MouseMove", { callback = function(ev) - on_hover(ev.data) + if ev.data.dragging then + local hov = M.hovered() + if hov ~= nil then + local buf = buffers.get_buffer(hov.bufnr) + if buf then + buf.is_hovered = false + end + if hov.kind == "buffer" then + if buf and hov.on_mouse_leave then + hov.on_mouse_leave(buf) + end + elseif hov.on_mouse_leave then + hov.on_mouse_leave() + end + _G.cokeline.__hovered = nil + end + on_drag(ev.data) + else + _G.cokeline.__dragging = nil + on_hover(ev.data) + end return "" end, }) From 0790a892af57972e0fe36e36f6e8c4700322b45c Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Fri, 9 Jun 2023 20:36:25 -0700 Subject: [PATCH 3/5] refactor: smoother dragging Problem: dragging was glitchy because buffers were sized differently. Solution: ensure mouse will still be in the dragged buffer before actually moving it. --- lua/cokeline/hover.lua | 86 +++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/lua/cokeline/hover.lua b/lua/cokeline/hover.lua index 44cd994..c8519d9 100644 --- a/lua/cokeline/hover.lua +++ b/lua/cokeline/hover.lua @@ -21,13 +21,13 @@ function M.get_current(col) for _, component in ipairs(cx.sidebar) do current_width = current_width + component.width if current_width >= col then - return component + return component, cx.sidebar end end for _, component in ipairs(cx.buffers) do current_width = current_width + component.width if current_width >= col then - return component + return component, cx.buffers end end current_width = current_width + cx.gap @@ -37,7 +37,7 @@ function M.get_current(col) for _, component in ipairs(cx.rhs) do current_width = current_width + component.width if current_width >= col then - return component + return component, cx.rhs end end end @@ -127,11 +127,35 @@ local function on_hover(current) last_position = current end +local function width(bufs, buf) + return vim + .iter(bufs) + :filter(function(v) + return v.bufnr == buf + end) + :map(function(v) + return v.width + end) + :fold(0, function(acc, v) + return acc + v + end) +end + +local function start_pos(bufs, buf) + local pos = 0 + for _, v in ipairs(bufs) do + if v.bufnr == buf then + return pos + end + pos = pos + v.width + end + return pos +end + local function on_drag(pos) if pos.dragging == "l" then - -- TODO: handle drag events - local current = M.get_current(pos.screencol) - if not current or current.kind ~= "buffer" then + local current, bufs = M.get_current(pos.screencol) + if not current or current.kind ~= "buffer" or not bufs then return end @@ -140,21 +164,31 @@ local function on_drag(pos) if not buf then return end - _G.cokeline.__dragging = { - bufnr = current.bufnr, - index = buf._valid_index, - } + _G.cokeline.__dragging = current.bufnr end - if _G.cokeline.__dragging.bufnr == current.bufnr then + if _G.cokeline.__dragging == current.bufnr then return end - if _G.cokeline.__dragging.bufnr then - local buf = buffers.get_buffer(_G.cokeline.__dragging.bufnr) - if buf and current.index then - buffers.move_buffer( - buf, - buffers.get_buffer(current.bufnr)._valid_index - ) + if _G.cokeline.__dragging then + local buf = buffers.get_buffer(_G.cokeline.__dragging) + local dragging_start = start_pos(bufs, _G.cokeline.__dragging) + local cur_buf_width = width(bufs, current.bufnr) + if buf then + if dragging_start > pos.screencol then + if pos.screencol + cur_buf_width > dragging_start then + buffers.move_buffer( + buf, + buffers.get_buffer(current.bufnr)._valid_index + ) + end + else + if dragging_start + cur_buf_width <= pos.screencol then + buffers.move_buffer( + buf, + buffers.get_buffer(current.bufnr)._valid_index + ) + end + end end end end @@ -191,6 +225,22 @@ function M.setup() return "" end, }) + + -- vim.keymap.set({ "", "i" }, "", function() + -- local ok, pos = pcall(vim.fn.getmousepos) + -- if ok then + -- on_hover(pos) + -- end + -- return "" + -- end, { expr = true, noremap = true }) + -- vim.keymap.set({ "", "i" }, "", function() + -- local ok, pos = pcall(vim.fn.getmousepos) + -- if ok then + -- pos.dragging = "l" + -- on_drag(pos) + -- end + -- return "" + -- end, { expr = true, noremap = true }) end return M From d9894b7eff0d1c7f83fa16edbd1fcf1582895015 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Fri, 9 Jun 2023 21:34:09 -0700 Subject: [PATCH 4/5] refactor: clean up on_drag function --- lua/cokeline/hover.lua | 90 ++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/lua/cokeline/hover.lua b/lua/cokeline/hover.lua index c8519d9..f40996b 100644 --- a/lua/cokeline/hover.lua +++ b/lua/cokeline/hover.lua @@ -10,6 +10,26 @@ function M.hovered() return _G.cokeline.__hovered end +function M.set_hovered(val) + _G.cokeline.__hovered = val +end + +function M.clear_hovered() + _G.cokeline.__hovered = nil +end + +function M.dragging() + return _G.cokeline.__dragging +end + +function M.set_dragging(val) + _G.cokeline.__dragging = val +end + +function M.clear_dragging() + _G.cokeline.__dragging = nil +end + function M.get_current(col) local bufs = buffers.get_visible() if not bufs then @@ -43,7 +63,7 @@ function M.get_current(col) end local function on_hover(current) - local hovered = _G.cokeline.__hovered + local hovered = M.hovered() if vim.o.showtabline == 0 then return end @@ -80,7 +100,7 @@ local function on_hover(current) hovered.on_mouse_leave(buf) end end - _G.cokeline.__hovered = nil + M.clear_hovered() end if not component then vim.cmd.redrawtabline() @@ -100,12 +120,12 @@ local function on_hover(current) component.on_mouse_enter(buf, current.screencol) end end - _G.cokeline.__hovered = { + M.set_hovered({ index = component.index, bufnr = buf and buf.number or nil, on_mouse_leave = component.on_mouse_leave, kind = component.kind, - } + }) vim.cmd.redrawtabline() elseif hovered ~= nil then local buf = buffers.get_buffer(hovered.bufnr) @@ -121,7 +141,7 @@ local function on_hover(current) hovered.on_mouse_leave(buf) end end - _G.cokeline.__hovered = nil + M.clear_hovered() vim.cmd.redrawtabline() end last_position = current @@ -155,41 +175,43 @@ end local function on_drag(pos) if pos.dragging == "l" then local current, bufs = M.get_current(pos.screencol) - if not current or current.kind ~= "buffer" or not bufs then + if current == nil or bufs == nil or current.kind ~= "buffer" then return end - if not _G.cokeline.__dragging then - local buf = buffers.get_buffer(current.bufnr) - if not buf then - return - end - _G.cokeline.__dragging = current.bufnr + -- if we're not dragging yet or we're dragging the same buffer, start dragging + if M.dragging() == current.bufnr or not M.dragging() then + M.set_dragging(current.bufnr) + return + end + + -- dragged buffer + local dragged_buf = buffers.get_buffer(_G.cokeline.__dragging) + if not dragged_buf then + return end - if _G.cokeline.__dragging == current.bufnr then + + -- current (hovered) buffer + local cur_buf = buffers.get_buffer(current.bufnr) + if not cur_buf then return end - if _G.cokeline.__dragging then - local buf = buffers.get_buffer(_G.cokeline.__dragging) - local dragging_start = start_pos(bufs, _G.cokeline.__dragging) - local cur_buf_width = width(bufs, current.bufnr) - if buf then - if dragging_start > pos.screencol then - if pos.screencol + cur_buf_width > dragging_start then - buffers.move_buffer( - buf, - buffers.get_buffer(current.bufnr)._valid_index - ) - end - else - if dragging_start + cur_buf_width <= pos.screencol then - buffers.move_buffer( - buf, - buffers.get_buffer(current.bufnr)._valid_index - ) - end - end - end + + -- start position of dragged buffer + local dragging_start = start_pos(bufs, _G.cokeline.__dragging) + -- width of the current (hovered) buffer + local cur_buf_width = width(bufs, current.bufnr) + + if + -- buffer is being dragged to the left + ( + dragging_start > pos.screencol + and pos.screencol + cur_buf_width > dragging_start + ) + -- buffer is being dragged to the right + or dragging_start + cur_buf_width <= pos.screencol + then + buffers.move_buffer(dragged_buf, cur_buf._valid_index) end end end From 74e0b6fb21dce0ea0f3683a4d0367cf7a2f9fe59 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Thu, 29 Jun 2023 01:45:25 -0700 Subject: [PATCH 5/5] refactor: use on_key instead of MouseMove --- lua/cokeline/hover.lua | 86 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/lua/cokeline/hover.lua b/lua/cokeline/hover.lua index f40996b..fca1421 100644 --- a/lua/cokeline/hover.lua +++ b/lua/cokeline/hover.lua @@ -186,7 +186,7 @@ local function on_drag(pos) end -- dragged buffer - local dragged_buf = buffers.get_buffer(_G.cokeline.__dragging) + local dragged_buf = buffers.get_buffer(M.dragging()) if not dragged_buf then return end @@ -198,7 +198,7 @@ local function on_drag(pos) end -- start position of dragged buffer - local dragging_start = start_pos(bufs, _G.cokeline.__dragging) + local dragging_start = start_pos(bufs, M.dragging()) -- width of the current (hovered) buffer local cur_buf_width = width(bufs, current.bufnr) @@ -217,52 +217,54 @@ local function on_drag(pos) end function M.setup() - if version.minor < 8 then + if version.minor < 8 or not vim.on_key then return end - vim.api.nvim_create_autocmd("MouseMove", { - callback = function(ev) - if ev.data.dragging then - local hov = M.hovered() - if hov ~= nil then - local buf = buffers.get_buffer(hov.bufnr) - if buf then - buf.is_hovered = false - end - if hov.kind == "buffer" then - if buf and hov.on_mouse_leave then - hov.on_mouse_leave(buf) - end - elseif hov.on_mouse_leave then - hov.on_mouse_leave() + local mouse_move = vim.keycode("") + local drag = { + [vim.keycode("")] = "l", + [vim.keycode("")] = "r", + [vim.keycode("")] = "m", + } + vim.on_key(function(key) + local ok, pos = pcall(vim.fn.getmousepos) + if not ok then + return + end + + local data = { + dragging = drag[key], + screencol = pos.screencol, + screenrow = pos.screenrow, + line = pos.line, + column = pos.column, + winid = pos.winid, + winrow = pos.winrow, + wincol = pos.wincol, + } + if key == mouse_move then + M.clear_dragging() + on_hover(data) + elseif drag[key] then + local hov = M.hovered() + if hov then + local buf = buffers.get_buffer(hov.bufnr) + if buf then + buf.is_hovered = false + end + if hov.kind == "buffer" then + if buf and hov.on_mouse_leave then + hov.on_mouse_leave(buf) end - _G.cokeline.__hovered = nil + elseif hov.on_mouse_leave then + hov.on_mouse_leave() end - on_drag(ev.data) - else - _G.cokeline.__dragging = nil - on_hover(ev.data) + M.clear_hovered() end - return "" - end, - }) - - -- vim.keymap.set({ "", "i" }, "", function() - -- local ok, pos = pcall(vim.fn.getmousepos) - -- if ok then - -- on_hover(pos) - -- end - -- return "" - -- end, { expr = true, noremap = true }) - -- vim.keymap.set({ "", "i" }, "", function() - -- local ok, pos = pcall(vim.fn.getmousepos) - -- if ok then - -- pos.dragging = "l" - -- on_drag(pos) - -- end - -- return "" - -- end, { expr = true, noremap = true }) + on_drag(data) + end + end) end return M