Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,17 @@ require('cokeline').setup({
},
{
text = function(buffer) return buffer.filename .. ' ' end,
style = function(buffer)
if buffer.is_hovered and not buffer.is_focused then
return 'underline'
end
end
},
{
text = '',
delete_buffer_on_left_click = true,
on_click = function(_, _, _, _, buffer)
buffer:delete()
end
},
{
text = ' ',
Expand Down Expand Up @@ -367,10 +374,9 @@ them:

![unique-prefix](https://user-images.githubusercontent.com/38540736/226447822-3315ad2f-35c9-4fc3-a777-c01cd8f2fe46.gif)

### Clickable buffers
### Clickable and hoverable buffers

You can switch focus between buffers with a left click and you can delete
them with a right click:
Each component can be given custom click and hover handlers, allowing for implementations of close buttons, diagnostic previews, and more.

![clickable-buffers](https://user-images.githubusercontent.com/38540736/226447799-e845d266-0658-44e3-bd89-f706577844bf.gif)

Expand All @@ -384,10 +390,9 @@ them with a right click:

## :mountain: Plans and Ideas

- Statusline and Winbar
- Statusline
- More mouse events
- Reorder with drag
- Hover events
- Group buffers by tabpage
- Non-buffer custom components
- Right-side tabline components
Expand All @@ -406,9 +411,19 @@ plugin and a patched font (see [Nerd Fonts](https://www.nerdfonts.com/)).

#### Lua

If you ported your Neovim config to Lua and use
[packer.nvim](https://github.com/wbthomason/packer.nvim) as your plugin
manager you can install this plugin with:
##### With lazy.nvim

```lua
{
"willothy/nvim-cokeline",
dependencies = {
"kyazdani42/nvim-web-devicons",
},
config = true
}
```

##### With packer.nvim

```lua
vim.opt.termguicolors = true
Expand Down Expand Up @@ -586,6 +601,13 @@ buffer = {
-- The buffer is the last visible buffer in the tab bar
is_last = true | false,

-- The mouse is hovering over the buffer
-- This is a special variable in that it will only be true for the hovered *component*
-- on render. This is to allow components to respond to hover events individually without managing
-- component state.
-- If you just need the hovered bufnr, you can use `require('cokeline.hover').hovered().bufnr`
is_hovered = true | false

-- The buffer's type as reported by `:echo &buftype`.
type = 'string',

Expand Down Expand Up @@ -721,6 +743,12 @@ Every component passed to the `components` list has to be a table of the form:
-- buffer is a Buffer object, not a bufnr
on_click = nil | function(idx, clicks, buttons, modifiers, buffer)

-- Called on a component when hovered
on_mouse_enter = nil | function(buffer)

-- Called on a component when unhovered
on_mouse_leave = nil | function(buffer)

truncation = {
-- default: index of the component in the `components` table (1 for the
-- first component, 2 for the second, etc.).
Expand Down
27 changes: 16 additions & 11 deletions lua/cokeline/buffers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ Buffer.new = function(b)
is_last = false,
is_modified = opts.modified,
is_readonly = opts.readonly,
is_hovered = false,
path = b.name,
unique_prefix = "",
filename = filename,
Expand Down Expand Up @@ -348,8 +349,9 @@ local move_buffer = function(buffer, target_valid_index)
cmd("redrawtabline")
end

---@param unsorted bool
---@return Buffer[]
local get_valid_buffers = function()
local get_valid_buffers = function(unsorted)
local buffers = map(function(b)
return Buffer.new(b)
end, fn.getbufinfo({ buflisted = 1 }))
Expand All @@ -364,14 +366,16 @@ local get_valid_buffers = function()

buffers = compute_unique_prefixes(buffers)

if _G.cokeline.config.buffers.new_buffers_position == "last" then
sort(buffers, sort_by_new_after_last)
elseif _G.cokeline.config.buffers.new_buffers_position == "next" then
sort(buffers, sort_by_new_after_current)
elseif _G.cokeline.config.buffers.new_buffers_position == "directory" then
sort(buffers, sort_by_directory)
elseif _G.cokeline.config.buffers.new_buffers_position == "number" then
sort(buffers, sort_by_number)
if not unsorted then
if _G.cokeline.config.buffers.new_buffers_position == "last" then
sort(buffers, sort_by_new_after_last)
elseif _G.cokeline.config.buffers.new_buffers_position == "next" then
sort(buffers, sort_by_new_after_current)
elseif _G.cokeline.config.buffers.new_buffers_position == "directory" then
sort(buffers, sort_by_directory)
elseif _G.cokeline.config.buffers.new_buffers_position == "number" then
sort(buffers, sort_by_number)
end
end

order = {}
Expand All @@ -386,9 +390,10 @@ local get_valid_buffers = function()
return buffers
end

---@param unsorted boolean
---@return Buffer[]
local get_visible_buffers = function()
_G.cokeline.valid_buffers = get_valid_buffers()
local get_visible_buffers = function(unsorted)
_G.cokeline.valid_buffers = get_valid_buffers(unsorted)

_G.cokeline.visible_buffers = not _G.cokeline.config.buffers.filter_visible
and _G.cokeline.valid_buffers
Expand Down
7 changes: 6 additions & 1 deletion lua/cokeline/components.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ local map = vim.tbl_map
---@field fg string|fun(buffer:Buffer): string
---@field bg string|fun(buffer:Buffer): string
---@field on_click ClickHandler | nil
---@field on_mouse_enter MouseEnterHandler | nil
---@field on_mouse_leave MouseLeaveHandler | nil
---@field delete_buffer_on_left_click boolean Use the component as a close button ()
---@field truncation table
---@field idx number
Expand All @@ -37,7 +39,9 @@ Component.new = function(c, i, default_hl)
bg = c.bg or default_hl.bg or "NONE",
style = c.style or default_hl.style or "NONE",
delete_buffer_on_left_click = c.delete_buffer_on_left_click or false,
on_click = c.on_click or nil,
on_click = c.on_click,
on_mouse_enter = c.on_mouse_enter,
on_mouse_leave = c.on_mouse_leave,
truncation = {
priority = c.truncation and c.truncation.priority or i,
direction = c.truncation and c.truncation.direction or "right",
Expand All @@ -57,6 +61,7 @@ end
---@param context RenderContext
---@return Component
Component.render = function(self, context)
---@return string
local evaluate = function(field)
return (type(field) == "string" and field)
or (type(field) == "function" and field(context.provider))
Expand Down
15 changes: 13 additions & 2 deletions lua/cokeline/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,23 @@ local defaults = {
},
{
text = function(buffer)
return buffer.filename .. " "
return buffer.filename
end,
style = function(buffer)
if buffer.is_hovered and not buffer.is_focused then
return "underline"
end
return nil
end,
},
{
text = " ",
},
{
text = "",
delete_buffer_on_left_click = true,
on_click = function(_, _, _, _, buffer)
buffer:delete()
end,
},
{
text = " ",
Expand Down
24 changes: 7 additions & 17 deletions lua/cokeline/handlers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ local buffers = require("cokeline/buffers")

---@alias ClickHandler fun(button_id: number, clicks: number, button: string, modifiers: string, buffer: Buffer): void
---@alias WrappedClickHandler fun(buffer: Buffer): ClickHandler
---@alias MouseEnterHandler fun(buffer: Buffer)
---@alias MouseLeaveHandler fun(buffer: Buffer)

---@class Handlers Singleton event handler manager
---@field click WrappedClickHandler[]
Expand All @@ -14,22 +16,6 @@ local Handlers = {
},
}

---@param handler ClickHandler
---@param kind string
---@param idx number
local function register(handler, kind, idx)
-- for global handlers
if idx == nil then
idx = kind
kind = nil
end
rawset(Handlers.private[kind], idx, function(buffer)
return function(minwid, clicks, button, modifiers)
return handler(minwid, clicks, button, modifiers, buffer)
end
end)
end

---@param kind string
---@param idx number
local function unregister(kind, idx)
Expand All @@ -44,7 +30,11 @@ end
---@param idx number
---@param handler ClickHandler
function Handlers.click:register(idx, handler)
register(handler, "click", idx)
rawset(Handlers.private.click, idx, function(buffer)
return function(minwid, clicks, button, modifiers)
return handler(minwid, clicks, button, modifiers, buffer)
end
end)
end

---Unregister a click handler
Expand Down
92 changes: 92 additions & 0 deletions lua/cokeline/hover.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
local M = {}

local version = vim.version()

local buffers = require("cokeline.buffers")
local rendering = require("cokeline.rendering")

function M.hovered()
return _G.cokeline.__hovered
end

function M.get_current(col)
local bufs = buffers.get_visible(true)
if not bufs then
return
end
local components = rendering.prepare(buffers.get_visible())

local current_width = 0
for _, component in ipairs(components) do
current_width = current_width + component.width
if current_width >= col then
return component
end
end
end

local function on_hover(current)
local hovered = _G.cokeline.__hovered
if vim.o.showtabline == 0 then
return
end
if current.screenrow == 1 then
local component = M.get_current(current.screencol)

if not component then
if hovered ~= nil then
local buf = buffers.get_buffer(hovered.bufnr)
if buf then
buf.is_hovered = false
if hovered.on_mouse_leave then
hovered.on_mouse_leave(buf)
end
end
_G.cokeline.__hovered = nil
end
vim.cmd.redrawtabline()
return
end

local buf = buffers.get_buffer(component.bufnr)
if buf then
buf.is_hovered = true
if component.on_mouse_enter then
component.on_mouse_enter(buf)
end
_G.cokeline.__hovered = {
index = component.index,
bufnr = buf.number,
on_mouse_leave = component.on_mouse_leave,
}
end
vim.cmd.redrawtabline()
elseif hovered ~= nil then
local buf = buffers.get_buffer(hovered.bufnr)
if buf then
buf.is_hovered = false
if hovered.on_mouse_leave then
hovered.on_mouse_leave(buf)
end
end
_G.cokeline.__hovered = nil
vim.cmd.redrawtabline()
end
end

function M.setup()
if version.minor < 8 then
return
end

vim.keymap.set({ "", "i" }, "<MouseMove>", function()
local ok, pos = pcall(vim.fn.getmousepos)
if not ok then
return
end
on_hover(pos)
return "<MouseMove>"
end, { expr = true })
end

return M
2 changes: 2 additions & 0 deletions lua/cokeline/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local buffers = require("cokeline/buffers")
local config = require("cokeline/config")
local mappings = require("cokeline/mappings")
local rendering = require("cokeline/rendering")
local hover = require("cokeline/hover")

local opt = vim.opt

Expand Down Expand Up @@ -31,6 +32,7 @@ local setup = function(preferences)
_G.cokeline.config = config.get(preferences or {})
augroups.setup()
mappings.setup()
hover.setup()

opt.showtabline = 2
opt.tabline = "%!v:lua.cokeline.tabline()"
Expand Down
Loading