diff --git a/README.md b/README.md index 8e36387..404ec62 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ > [!IMPORTANT] -> This plugin is intended for personal and demonstration purposes only and have a lot of limitations.
-> It is not recommended to use this plugin. +> This plugin is mainly for personal and demonstration purposes and have a lot of limitations.
+> Limited support will be provided for issues and pull requests.
+> 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 @@ -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 @@ -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**. @@ -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 @@ -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', }, + { + '', + function() + require('edgy-group.stl.statusline').pick() + end, + desc = 'Edgy Group Pick', + }, }, opts = { groups = { @@ -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) ``` diff --git a/lua/edgy-group/options.lua b/lua/edgy-group/options.lua index 9384b4b..b992081 100644 --- a/lua/edgy-group/options.lua +++ b/lua/edgy-group/options.lua @@ -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[] @@ -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 @@ -35,6 +38,8 @@ local default_options = { colors = { active = 'Normal', inactive = 'Normal', + pick_active = 'PmenuSel', + pick_inactive = 'PmenuSel', }, }, } diff --git a/lua/edgy-group/stl/cache.lua b/lua/edgy-group/stl/cache.lua index f157083..dcf94b2 100644 --- a/lua/edgy-group/stl/cache.lua +++ b/lua/edgy-group/stl/cache.lua @@ -1,11 +1,15 @@ -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 +---@field pick_keys table pick keys for each position and group +---@field key_to_group table associate a key to a group local Cache = {} ---@param groups table @@ -13,10 +17,49 @@ local Cache = {} 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 +---@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 +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 @@ -51,7 +94,7 @@ end ---@private ---@param groups table ---@return table -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 {}) diff --git a/lua/edgy-group/stl/statusline.lua b/lua/edgy-group/stl/statusline.lua index d52db56..47df837 100644 --- a/lua/edgy-group/stl/statusline.lua +++ b/lua/edgy-group/stl/statusline.lua @@ -3,16 +3,18 @@ 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 ---@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) @@ -20,11 +22,28 @@ function M.get_highlight(is_visible) 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 -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 @@ -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