diff --git a/plugins/default.nix b/plugins/default.nix index 91fe2b64..1514b020 100644 --- a/plugins/default.nix +++ b/plugins/default.nix @@ -120,6 +120,7 @@ ./telescope + ./ui/edgy.nix ./ui/headlines.nix ./ui/image.nix ./ui/neoscroll.nix diff --git a/plugins/ui/edgy.nix b/plugins/ui/edgy.nix new file mode 100644 index 00000000..7c5b5b3e --- /dev/null +++ b/plugins/ui/edgy.nix @@ -0,0 +1,321 @@ +{ + lib, + helpers, + config, + pkgs, + ... +}: +with lib; +helpers.neovim-plugin.mkNeovimPlugin config { + name = "edgy"; + originalName = "edgy.nvim"; + defaultPackage = pkgs.vimPlugins.edgy-nvim; + + maintainers = [ maintainers.GaetanLepage ]; + + extraConfig = cfg: { + # Those options are strongly recommended by the plugin author: + # https://github.com/folke/edgy.nvim?tab=readme-ov-file#-installation + opts = { + laststatus = mkDefault 3; + splitkeep = mkDefault "screen"; + }; + }; + + settingsOptions = + let + viewOpts = { + ft = helpers.mkNullOrStr '' + File type of the view. + ''; + + filter = helpers.mkNullOrLuaFn '' + Optional function to filter buffers and windows. + + `fun(buf:buffer, win:window)` + ''; + + title = helpers.mkNullOrStr '' + Optional title of the view. + Defaults to the capitalized filetype. + ''; + + size = helpers.mkNullOrOption types.ints.unsigned '' + Size of the short edge of the edgebar. + For edgebars, this is the minimum width. + For panels, minimum height. + ''; + + pinned = helpers.mkNullOrOption types.bool '' + If true, the view will always be shown in the edgebar even when it has no windows. + ''; + + open = helpers.mkNullOrStr '' + Function or command to open a pinned view. + ''; + + wo = helpers.mkNullOrOption (with types; attrsOf anything) '' + View-specific window options. + ''; + }; + + mkViewOptsOption = + name: + helpers.defaultNullOpts.mkListOf ( + with types; + either str (submodule { + options = viewOpts; + }) + ) [ ] "List of the ${name} edgebar configurations."; + in + { + left = mkViewOptsOption "left"; + bottom = mkViewOptsOption "bottom"; + right = mkViewOptsOption "right"; + top = mkViewOptsOption "top"; + + options = + mapAttrs + (_: defaultSize: { + size = helpers.defaultNullOpts.mkUnsignedInt defaultSize '' + Size of the short edge of the edgebar. + For edgebars, this is the minimum width. + For panels, minimum height. + ''; + + wo = helpers.mkNullOrOption (with types; attrsOf anything) '' + View-specific window options. + ''; + }) + { + left = 30; + bottom = 10; + right = 30; + top = 10; + }; + + animate = { + enabled = helpers.defaultNullOpts.mkBool true '' + Whether to enable animations. + ''; + + fps = helpers.defaultNullOpts.mkUnsignedInt 100 '' + Frames per second. + ''; + + cps = helpers.defaultNullOpts.mkUnsignedInt 120 '' + Cells per second. + ''; + + on_begin = helpers.defaultNullOpts.mkLuaFn '' + function() + vim.g.minianimate_disable = true + end + '' "Callback for the beginning of animations."; + + on_end = helpers.defaultNullOpts.mkLuaFn '' + function() + vim.g.minianimate_disable = false + end + '' "Callback for the ending of animations."; + + # This option accepts an attrs or a lua string. + # Hence, we use `mkOption` to convert the string to raw lua in `apply`. + spinner = + let + defaultFrames = [ + "⠋" + "⠙" + "⠹" + "⠸" + "⠼" + "⠴" + "⠦" + "⠧" + "⠇" + "⠏" + ]; + in + mkOption { + type = + with helpers.nixvimTypes; + nullOr ( + either strLua (submodule { + freeformType = attrsOf anything; + options = { + frames = helpers.defaultNullOpts.mkListOf types.str defaultFrames '' + Frame characters. + ''; + + interval = helpers.defaultNullOpts.mkUnsignedInt 80 '' + Interval time between two consecutive frames. + ''; + }; + }) + ); + default = null; + example = "require('noice.util.spinners').spinners.circleFull"; + apply = v: if isString v then helpers.mkRaw v else v; + description = helpers.defaultNullOpts.mkDesc { + frames = defaultFrames; + interval = 80; + } "Spinner for pinned views that are loading."; + }; + }; + + exit_when_last = helpers.defaultNullOpts.mkBool false '' + Enable this to exit Neovim when only edgy windows are left. + ''; + + close_when_all_hidden = helpers.defaultNullOpts.mkBool true '' + Close edgy when all windows are hidden instead of opening one of them. + Disable to always keep at least one edgy split visible in each open section. + ''; + + wo = helpers.defaultNullOpts.mkAttrsOf types.anything { + winbar = true; + winfixwidth = true; + winfixheight = false; + winhighlight = "WinBar:EdgyWinBar,Normal:EdgyNormal"; + spell = false; + signcolumn = "no"; + } "Global window options for edgebar windows."; + + # This option accepts an attrs or a lua string. + # Hence, we use `mkOption` to convert the string to raw lua in `apply`. + keys = mkOption { + type = with helpers.nixvimTypes; attrsOf (either strLuaFn (enum [ false ])); + default = { }; + apply = mapAttrs (_: v: if isString v then helpers.mkRaw v else v); + description = + helpers.defaultNullOpts.mkDesc + { + q = '' + function(win) + win:close() + end + ''; + "" = '' + function(win) + win:hide() + end + ''; + Q = '' + function(win) + win.view.edgebar:close() + end + ''; + "]w" = '' + function(win) + win:next({ visible = true, focus = true }) + end + ''; + "[w" = '' + function(win) + win:prev({ visible = true, focus = true }) + end + ''; + "]W" = '' + function(win) + win:next({ pinned = false, focus = true }) + end + ''; + "[W" = '' + function(win) + win:prev({ pinned = false, focus = true }) + end + ''; + ">" = '' + function(win) + win:resize("width", 2) + end + ''; + "" = '' + function(win) + win:resize("width", -2) + end + ''; + "+" = '' + function(win) + win:resize("height", 2) + end + ''; + "-" = '' + function(win) + win:resize("height", -2) + end + ''; + "=" = '' + function(win) + win.view.edgebar:equalize() + end + ''; + } + '' + Buffer-local keymaps to be added to edgebar buffers. + Existing buffer-local keymaps will never be overridden. + + Each value is either: + - A function declaration (as a raw lua string) + -> `fun(win:Edgy.Window)` + - `false` to disable this mapping. + ''; + }; + + icons = { + closed = helpers.defaultNullOpts.mkStr " " '' + Icon for closed edgebars. + ''; + + open = helpers.defaultNullOpts.mkStr " " '' + Icon for opened edgebars. + ''; + }; + }; + + settingsExample = { + animate.enabled = false; + wo = { + winbar = false; + winfixwidth = false; + winfixheight = false; + winhighlight = ""; + spell = false; + signcolumn = "no"; + }; + bottom = [ + { + ft = "toggleterm"; + size = 30; + filter = '' + function(buf, win) + return vim.api.nvim_win_get_config(win).relative == "" + end + ''; + } + { + ft = "help"; + size = 20; + filter = '' + function(buf) + return vim.bo[buf].buftype == "help" + end + ''; + } + ]; + left = [ + { + title = "nvimtree"; + ft = "NvimTree"; + size = 30; + } + { + ft = "Outline"; + open = "SymbolsOutline"; + } + { ft = "dapui_scopes"; } + { ft = "dapui_breakpoints"; } + { ft = "dap-repl"; } + ]; + }; +} diff --git a/tests/test-sources/plugins/ui/edgy.nix b/tests/test-sources/plugins/ui/edgy.nix new file mode 100644 index 00000000..69961321 --- /dev/null +++ b/tests/test-sources/plugins/ui/edgy.nix @@ -0,0 +1,194 @@ +{ + empty = { + plugins.edgy.enable = true; + }; + + defaults = { + plugins.edgy = { + enable = true; + + settings = { + left = [ ]; + bottom = [ ]; + right = [ ]; + top = [ ]; + options = { + left = { + size = 30; + wo = null; + }; + bottom = { + size = 10; + wo = null; + }; + right = { + size = 30; + wo = null; + }; + top = { + size = 10; + wo = null; + }; + }; + animate = { + enabled = true; + fps = 100; + cps = 120; + on_begin = '' + function() + vim.g.minianimate_disable = true + end + ''; + on_end = '' + function() + vim.g.minianimate_disable = false + end + ''; + spinner = { + frames = [ + "⠋" + "⠙" + "⠹" + "⠸" + "⠼" + "⠴" + "⠦" + "⠧" + "⠇" + "⠏" + ]; + interval = 80; + }; + }; + exit_when_last = false; + close_when_all_hidden = true; + wo = { + winbar = true; + winfixwidth = true; + winfixheight = false; + winhighlight = "WinBar:EdgyWinBar,Normal:EdgyNormal"; + spell = false; + signcolumn = "no"; + }; + keys = { + q = '' + function(win) + win:close() + end + ''; + "" = '' + function(win) + win:hide() + end + ''; + Q = '' + function(win) + win.view.edgebar:close() + end + ''; + "]w" = '' + function(win) + win:next({ visible = true, focus = true }) + end + ''; + "[w" = '' + function(win) + win:prev({ visible = true, focus = true }) + end + ''; + "]W" = '' + function(win) + win:next({ pinned = false, focus = true }) + end + ''; + "[W" = '' + function(win) + win:prev({ pinned = false, focus = true }) + end + ''; + ">" = '' + function(win) + win:resize("width", 2) + end + ''; + "" = '' + function(win) + win:resize("width", -2) + end + ''; + "+" = '' + function(win) + win:resize("height", 2) + end + ''; + "-" = '' + function(win) + win:resize("height", -2) + end + ''; + "=" = '' + function(win) + win.view.edgebar:equalize() + end + ''; + }; + icons = { + closed = " "; + open = " "; + }; + }; + }; + }; + + example = { + plugins.edgy = { + enable = true; + + settings = { + animate.enabled = false; + wo = { + winbar = false; + winfixwidth = false; + winfixheight = false; + winhighlight = ""; + spell = false; + signcolumn = "no"; + }; + bottom = [ + { + ft = "toggleterm"; + size = 30; + filter = '' + function(buf, win) + return vim.api.nvim_win_get_config(win).relative == "" + end + ''; + } + { + ft = "help"; + size = 20; + filter = '' + function(buf) + return vim.bo[buf].buftype == "help" + end + ''; + } + ]; + left = [ + { + title = "nvimtree"; + ft = "NvimTree"; + size = 30; + } + { + ft = "Outline"; + open = "SymbolsOutline"; + } + { ft = "dapui_scopes"; } + { ft = "dapui_breakpoints"; } + { ft = "dap-repl"; } + ]; + }; + }; + }; +}