Skip to content

Commit

Permalink
Merge pull request #7 from lucobellic/feature/add-picking
Browse files Browse the repository at this point in the history
Add picking support
  • Loading branch information
lucobellic authored Jan 27, 2024
2 parents 34833fb + 895b95f commit e1b7c5e
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 13 deletions.
43 changes: 38 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
</h1>

> [!IMPORTANT]
> This plugin is intended for personal and demonstration purposes only and have a lot of limitations.<br/>
> It is not recommended to use this plugin.
> This plugin is mainly for personal and demonstration purposes and have a lot of limitations.<br/>
> Limited support will be provided for issues and pull requests.<br/>
> I would prefer to have this plugin integrated directly into edgy.nvim.
https://github.com/lucobellic/edgy-group.nvim/assets/6067072/00feeae1-6d6c-486c-a93c-25688ff37766

Expand All @@ -23,7 +24,8 @@ https://github.com/lucobellic/edgy-group.nvim/assets/6067072/00feeae1-6d6c-486c-

- Switch between groups of windows within **edgebar**.
- Add a command to navigate through groups of windows.
- Allow the creation of custom icon indicators in the statusline, bufferline, etc.
- Allow the creation of custom icon indicators in statusline, bufferline, etc.
- Pick mode to select group from statusline

## ⚠️ Limitations

Expand Down Expand Up @@ -79,13 +81,31 @@ local default_options = {
clickable = false, -- enable `open_group` on click
colored = false, -- enable highlight support
colors = { -- highlight colors
active = 'Normal',
inactive = 'Normal',
active = 'Normal', -- highlight color for open group
inactive = 'Normal', -- highlight color for closed group
pick_active = 'PmenuSel', -- highlight color for pick key for open group
pick_inactive = 'PmenuSel', -- highlight color for pick key for closed group
},
},
}
```

#### Groups

```lua
groups = {
right = { -- group position (right, left, bottom, top)
{
icon = '', -- icon used in statusline and vim.ui.select
titles = { 'Neo-Tree', 'Neo-Tree Buffers' }, -- list of titles from edgy.nvim
pick_key = 'f' -- key to pick a group in statusline
},
},
}
```

Groups without pick_key will be assigned to the first available key in alphabetical order.

### 🔌 API

- **EdgyGroupSelect** select group to open with **vim.ui.select**.
Expand All @@ -94,6 +114,7 @@ local default_options = {
- **require('edgy-group').open_group_offset(position, offset)** open group with offset relative to the current group.
- **require('edgy-group').open_group_index(position, index)** open group with index relative to the current position.
- **require('edgy-group.stl.statusline').get_statusline(position)** get a list of string in statusline format for each group icons with optional highlight and click support.
- **require('edgy-group.stl.statusline').pick(callback)** enable picking mode to select group from statusline.

## Example Setup

Expand All @@ -119,6 +140,13 @@ Usage of **edgy-group.nvim** to create three groups for the left **edgebar**:
function() require('edgy-group').open_group_offset('left', -1) end,
desc = 'Edgy Group Prev Left',
},
{
'<c-,>',
function()
require('edgy-group.stl.statusline').pick()
end,
desc = 'Edgy Group Pick',
},
},
opts = {
groups = {
Expand Down Expand Up @@ -183,5 +211,10 @@ Examples of how to use **edgy-group.nvim** with [bufferline.nvim](https://github
},
},
}
```

##### Picking

```lua
require('edgy-group.stl.statusline').pick(function() require('lualine').refresh() end)
```
5 changes: 5 additions & 0 deletions lua/edgy-group/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local Groups = require('edgy-group.groups')
---@class EdgyGroup
---@field icon number
---@field titles string[]
---@field pick_key? string Key to use for group pick.

---@class EdgyGroup.Statusline.Opts
---@field separators string[]
Expand All @@ -15,6 +16,8 @@ local Groups = require('edgy-group.groups')
---@class EdgyGroup.Statusline.Colors
---@field active string
---@field inactive string
---@field pick_active string
---@field pick_inactive string

---@class EdgyGroup.Opts
---@field groups table<Edgy.Pos, EdgyGroup[]>
Expand All @@ -35,6 +38,8 @@ local default_options = {
colors = {
active = 'Normal',
inactive = 'Normal',
pick_active = 'PmenuSel',
pick_inactive = 'PmenuSel',
},
},
}
Expand Down
51 changes: 47 additions & 4 deletions lua/edgy-group/stl/cache.lua
Original file line number Diff line number Diff line change
@@ -1,22 +1,65 @@
local Group = require('edgy-group')

-- TODO: Extend cache to not recompute highlight if selected_group didn't change
-- Otherwise update the statusline directly after group change

---@class EdgyGroup.Statusline.Cache.GroupIndex
---@field position Edgy.Pos
---@field index number

---@class EdgyGroup.Statusline.Cache
---@field opts EdgyGroup.Statusline.Opts
---@field status_lines table<Edgy.Pos, string[]>
---@field pick_keys table<Edgy.Pos, string[]> pick keys for each position and group
---@field key_to_group table<string, EdgyGroup.Statusline.Cache.GroupIndex> associate a key to a group
local Cache = {}

---@param groups table<Edgy.Pos, EdgyGroup.IndexedGroups>
---@param opts EdgyGroup.Statusline.Opts
function Cache.new(groups, opts)
local self = setmetatable({}, { __index = Cache })
self.opts = opts
self.status_lines = self:build_cache(groups)
self.status_lines = self:build_status_lines(groups)
self:build_keys(groups)
return self
end

-- Get a list of all keys not used by the user
---@private
---@param groups table<Edgy.Pos, EdgyGroup.IndexedGroups>
---@return string[] available_keys keys not used by the user
function Cache:get_available_keys(groups)
local user_keys = {}
for _, group in ipairs(groups) do
if group.pick_key then table.insert(user_keys, group.pick_key) end
end

local pick_keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
local keys_table = {}
for i = 1, #pick_keys do
keys_table[i] = pick_keys:sub(i, i)
end

return vim.tbl_filter(function(key)
return not vim.tbl_contains(user_keys, key)
end, keys_table)
end

-- Build picking keys for each positions and group and associate them to the group
---@private
---@param groups table<Edgy.Pos, EdgyGroup.IndexedGroups>
function Cache:build_keys(groups)
local available_keys = self:get_available_keys(groups)
self.pick_keys = {}
self.key_to_group = {}
for _, pos in ipairs({ 'right', 'left', 'bottom', 'top' }) do
self.pick_keys[pos] = {}
for i, group in ipairs(groups[pos] and groups[pos].groups or {}) do
local key = group.pick_key or table.remove(available_keys, 1)
self.pick_keys[pos][i] = key
self.key_to_group[key] = { position = pos, index = i }
end
end
end

-- Create callback function on click if clickable
---@private
---@param position Edgy.Pos
Expand Down Expand Up @@ -51,7 +94,7 @@ end
---@private
---@param groups table<Edgy.Pos, EdgyGroup.IndexedGroups>
---@return table<Edgy.Pos, string[]>
function Cache:build_cache(groups)
function Cache:build_status_lines(groups)
local status_lines = {}
for _, pos in ipairs({ 'right', 'left', 'bottom', 'top' }) do
status_lines[pos] = self:build_statusline(pos, groups[pos] and groups[pos].groups or {})
Expand Down
66 changes: 62 additions & 4 deletions lua/edgy-group/stl/statusline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,47 @@ local Group = require('edgy-group')

---@class EdgyGroup.Statusline
---@field private cache EdgyGroup.Statusline.Cache
---@diagnostic disable-next-line: missing-fields
---@field private pick_mode boolean True when pick is active, false otherwise
------@diagnostic disable-next-line: missing-fields
local M = {}

---@param groups table<Edgy.Pos, EdgyGroup.IndexedGroups>
---@param opts EdgyGroup.Statusline.Opts
function M.setup(groups, opts)
M.cache = require('edgy-group.stl.cache').new(groups, opts)
M.pick_mode = false
end

---@private
---@package
---@param is_visible boolean
---@return string
function M.get_highlight(is_visible)
local highlight = is_visible and M.cache.opts.colors.active or M.cache.opts.colors.inactive
return M.cache.opts.colored and '%#' .. highlight .. '#' or ''
end

---@package
---@param is_visible boolean
---@param position Edgy.Pos
---@param index number
function M.get_pick_text(is_visible, position, index)
local pick = ''
if M.pick_mode then
local pick_per_pos = M.cache.pick_keys[position]
local character = pick_per_pos[index] or ''
local highlight = is_visible and M.cache.opts.colors.pick_active or M.cache.opts.colors.pick_inactive
local pick_highlight = M.cache.opts.colored and '%#' .. highlight .. '#' or ''
pick = pick_highlight .. character
end
return pick
end

-- Get a list of statusline at the given position for each group icons
-- Also provide optional highlight and click support
---@public
---@param position Edgy.Pos The position to get the statusline for
---@return table<string>
M.get_statusline = function(position)
function M.get_statusline(position)
local statusline = {}
local edgebar = Config and Config.layout and Config.layout[position]
local g = Group.groups_by_pos
Expand All @@ -33,10 +52,49 @@ M.get_statusline = function(position)
for index, group_line in ipairs(M.cache.status_lines[position]) do
local is_visible = edgebar.visible ~= 0 and index == indexed_groups.selected_index
local highlight = M.get_highlight(is_visible)
table.insert(statusline, highlight .. group_line)
local pick = M.get_pick_text(is_visible, position, index)
table.insert(statusline, pick .. highlight .. group_line)
end
end
return statusline
end

-- Find the group position and index for the given key
---@private
---@param key string
---@return EdgyGroup.Statusline.Cache.GroupIndex?
function M.find_pos_index(key)
return M.cache.key_to_group[key] or {}
end

-- Enable pick mode and wait for a key to be pressed
-- Redraw statusline and tabline before and after pick
-- Optional callback could be used to trigger external plugin refresh/update
---@public
---@param callback? function Callback function to call after pick mode have been enabled
M.pick = function(callback)
M.pick_mode = true

-- callback function before redraw
if callback then pcall(callback) end
vim.schedule(function()
vim.cmd.redrawtabline()
vim.cmd.redrawstatus()
end)

-- Wait for key and open the corresponding group
local key = vim.fn.getcharstr()
if key then
local group_index = M.find_pos_index(key)
if group_index then pcall(Group.open_group_index, group_index.position, group_index.index) end
end

-- Disable pick mode and redraw
M.pick_mode = false
vim.schedule(function()
vim.cmd.redrawtabline()
vim.cmd.redrawstatus()
end)
end

return M

0 comments on commit e1b7c5e

Please sign in to comment.