Merge pull request #95 from NotAShelf/feature/breadcrumbs

This commit is contained in:
NotAShelf 2023-07-28 17:12:36 +03:00 committed by GitHub
commit 5196a65a43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 728 additions and 6 deletions

View file

@ -54,7 +54,10 @@ inputs: let
nix.enable = true;
html.enable = isMaximal;
clang.enable = isMaximal;
clang = {
enable = isMaximal;
lsp.server = "clangd";
};
sql.enable = isMaximal;
rust = {
enable = isMaximal;
@ -74,7 +77,7 @@ inputs: let
nvimWebDevicons.enable = true;
scrollBar.enable = true;
smoothScroll.enable = true;
cellularAutomaton.enable = true;
cellularAutomaton.enable = isMaximal;
fidget-nvim.enable = true;
indentBlankline = {
enable = true;
@ -155,7 +158,7 @@ inputs: let
};
vim.projects = {
project-nvim.enable = true;
project-nvim.enable = isMaximal;
};
vim.utility = {
@ -173,7 +176,7 @@ inputs: let
vim.notes = {
obsidian.enable = false; # FIXME neovim fails to build if obsidian is enabled
orgmode.enable = false;
mind-nvim.enable = true;
mind-nvim.enable = isMaximal;
todo-comments.enable = true;
};
@ -190,6 +193,10 @@ inputs: let
colorizer.enable = true;
modes-nvim.enable = false; # the theme looks terrible with catppuccin
illuminate.enable = true;
breadcrumbs = {
enable = isMaximal;
navbuddy.enable = isMaximal;
};
smartcolumn = {
enable = true;
columnAt.languages = {

View file

@ -12,7 +12,7 @@ https://github.com/horriblename[horriblename]:
* Fixed a bug where cmp's close and scrollDocs mappings wasn't working.
* Streamlined and simplified extra plugin API with the addition of <<opt-vim.extraPlugins>>.
* Streamlined and simplified extra plugin API with the addition of <<opt-vim.extraPlugins>>
https://github.com/amanse[amanse]:
@ -23,3 +23,7 @@ https://github.com/notashelf[notashelf]:
* Added GitHub Copilot to completion sources.
* Added <<opt-vim.ui.borders>> for global and individual plugin border configuration.
* LSP integrated breadcrumbs with <<opt-vim.ui.breadcrumbs>> through nvim-navic
* LSP navigation helper with nvim-navbuddy, depends on nvim-navic (automatically enabled)

View file

@ -1092,6 +1092,38 @@
"type": "github"
}
},
"nvim-navbuddy": {
"flake": false,
"locked": {
"lastModified": 1688569844,
"narHash": "sha256-011RT/wnQdBR1vMrXFwxbicBAgdcd4eQYPbok/o3CIE=",
"owner": "SmiteshP",
"repo": "nvim-navbuddy",
"rev": "244a4cded6f2b568403684131d148048efe4e8af",
"type": "github"
},
"original": {
"owner": "SmiteshP",
"repo": "nvim-navbuddy",
"type": "github"
}
},
"nvim-navic": {
"flake": false,
"locked": {
"lastModified": 1689447657,
"narHash": "sha256-fcSer6l6oX0qnOcRfNSZImmtkqjdH0WSUkptjaFj0AI=",
"owner": "SmiteshP",
"repo": "nvim-navic",
"rev": "e6da6f74d89de65258ea7e98e22103ff5de6dcf5",
"type": "github"
},
"original": {
"owner": "SmiteshP",
"repo": "nvim-navic",
"type": "github"
}
},
"nvim-neoclip": {
"flake": false,
"locked": {
@ -1413,6 +1445,8 @@
"nvim-dap-ui": "nvim-dap-ui",
"nvim-lightbulb": "nvim-lightbulb",
"nvim-lspconfig": "nvim-lspconfig",
"nvim-navbuddy": "nvim-navbuddy",
"nvim-navic": "nvim-navic",
"nvim-neoclip": "nvim-neoclip",
"nvim-notify": "nvim-notify",
"nvim-session-manager": "nvim-session-manager",

View file

@ -143,6 +143,7 @@
url = "github:tversteeg/registers.nvim";
flake = false;
};
nvim-neoclip = {
url = "github:AckslD/nvim-neoclip.lua";
flake = false;
@ -248,6 +249,7 @@
url = "github:b3nj5m1n/kommentary";
flake = false;
};
comment-nvim = {
url = "github:numToStr/Comment.nvim";
flake = false;
@ -455,6 +457,16 @@
};
# UI
nvim-navbuddy = {
url = "github:SmiteshP/nvim-navbuddy";
flake = false;
};
nvim-navic = {
url = "github:SmiteshP/nvim-navic";
flake = false;
};
noice-nvim = {
url = "github:folke/noice.nvim";
flake = false;

View file

@ -87,6 +87,8 @@ with lib; let
"nvim-surround"
"nvim-dap"
"nvim-dap-ui"
"nvim-navic"
"nvim-navbuddy"
"copilot-cmp"
];
# You can either use the name of the plugin or a package.

View file

@ -81,9 +81,16 @@ in {
end
end
${optionalString (config.vim.ui.breadcrumbs.enable) ''local navic = require("nvim-navic")''}
default_on_attach = function(client, bufnr)
attach_keymaps(client, bufnr)
format_callback(client, bufnr)
${optionalString (config.vim.ui.breadcrumbs.enable) ''
-- let navic attach to buffers
if client.server_capabilities.documentSymbolProvider then
navic.attach(client, bufnr)
end
''}
end
local capabilities = vim.lsp.protocol.make_client_capabilities()

View file

@ -49,6 +49,17 @@ in {
lualine_z = ${cfg.inactiveSection.z},
},
tabline = {},
${optionalString (config.vim.ui.breadcrumbs.source == "nvim-navic") ''
winbar = {
lualine_c = {
{
"navic",
draw_empty = ${toString config.vim.ui.breadcrumbs.alwaysRender}
}
}
},
''}
extensions = {${
if (config.vim.filetree.nvimTreeLua.enable)
then "\"nvim-tree\""

View file

@ -0,0 +1,478 @@
{
lib,
config,
...
}: let
inherit (lib) mkEnableOption mkOption types;
in {
options.vim.ui.breadcrumbs = {
enable = lib.mkEnableOption "breadcrumbs";
source = mkOption {
type = with types; nullOr (enum ["nvim-navic"]); # TODO: lspsaga and dropbar
default = "nvim-navic";
description = ''
The source to be used for breadcrumbs component. Null means no breadcrumbs.
'';
};
# maybe this should be an option to *disable* alwaysRender optionally but oh well
# too late
alwaysRender = mkOption {
type = types.bool;
default = true;
description = "Whether to always display the breadcrumbs component on winbar (always renders winbar)";
};
navbuddy = {
enable = mkEnableOption "navbuddy LSP helper UI. Enabling this option automatically loads and enables nvim-navic";
# this option is interpreted as null if mkEnableOption is used, and therefore cannot be converted to a string in config.nix
useDefaultMappings = mkOption {
type = types.bool;
default = true;
description = "use default Navbuddy keybindings (disables user-specified keybinds)";
};
mappings = {
close = mkOption {
type = types.str;
default = "<esc>";
description = "keybinding to close Navbuddy UI";
};
nextSibling = mkOption {
type = types.str;
default = "j";
description = "keybinding to navigate to the next sibling node";
};
previousSibling = mkOption {
type = types.str;
default = "k";
description = "keybinding to navigate to the previous sibling node";
};
parent = mkOption {
type = types.str;
default = "h";
description = "keybinding to navigate to the parent node";
};
children = mkOption {
type = types.str;
default = "h";
description = "keybinding to navigate to the child node";
};
root = mkOption {
type = types.str;
default = "0";
description = "keybinding to navigate to the root node";
};
visualName = mkOption {
type = types.str;
default = "v";
description = "visual selection of name";
};
visualScope = mkOption {
type = types.str;
default = "V";
description = "visual selection of scope";
};
yankName = mkOption {
type = types.str;
default = "y";
description = "yank the name to system clipboard";
};
yankScope = mkOption {
type = types.str;
default = "Y";
description = "yank the scope to system clipboard";
};
insertName = mkOption {
type = types.str;
default = "i";
description = "insert at start of name";
};
insertScope = mkOption {
type = types.str;
default = "I";
description = "insert at start of scope";
};
appendName = mkOption {
type = types.str;
default = "a";
description = "insert at end of name";
};
appendScope = mkOption {
type = types.str;
default = "A";
description = "insert at end of scope";
};
rename = mkOption {
type = types.str;
default = "r";
description = "rename the node";
};
delete = mkOption {
type = types.str;
default = "d";
description = "delete the node";
};
foldCreate = mkOption {
type = types.str;
default = "f";
description = "create a new fold";
};
foldDelete = mkOption {
type = types.str;
default = "F";
description = "delete the current fold";
};
comment = mkOption {
type = types.str;
default = "c";
description = "comment the node";
};
select = mkOption {
type = types.str;
default = "<enter>";
description = "goto selected symbol";
};
moveDown = mkOption {
type = types.str;
default = "J";
description = "move focused node down";
};
moveUp = mkOption {
type = types.str;
default = "K";
description = "move focused node up";
};
telescope = mkOption {
type = types.str;
default = "t";
description = "fuzzy finder at current level";
};
help = mkOption {
type = types.str;
default = "g?";
description = "open mapping help window";
};
};
window = {
# size = {}
# position = {}
border = mkOption {
# TODO: let this type accept a custom string
type = types.enum ["single" "rounded" "double" "solid" "none"];
default = config.vim.ui.borders.globalStyle;
description = "border style to use";
};
scrolloff = mkOption {
type = with types; nullOr int;
default = null;
description = "Scrolloff value within navbuddy window";
};
sections = {
# left section
left = {
/*
size = {
type = with types; nullOr (intBetween 0 100);
default = null;
description = "size of the left section of Navbuddy UI in percentage (0-100)";
};
*/
border = mkOption {
# TODO: let this type accept a custom string
type = with types; nullOr (enum ["single" "rounded" "double" "solid" "none"]);
default = config.vim.ui.borders.globalStyle;
description = "border style to use for the left section of Navbuddy UI";
};
};
# middle section
mid = {
/*
size = {
type = with types; nullOr (intBetween 0 100);
default = null;
description = "size of the left section of Navbuddy UI in percentage (0-100)";
};
*/
border = mkOption {
# TODO: let this type accept a custom string
type = with types; nullOr (enum ["single" "rounded" "double" "solid" "none"]);
default = config.vim.ui.borders.globalStyle;
description = "border style to use for the middle section of Navbuddy UI";
};
};
# right section
# there is no size option for the right section, it fills the remaining space
right = {
border = mkOption {
# TODO: let this type accept a custom string
type = with types; nullOr (enum ["single" "rounded" "double" "solid" "none"]);
default = config.vim.ui.borders.globalStyle;
description = "border style to use for the right section of Navbuddy UI";
};
preview = mkOption {
type = types.enum ["leaf" "always" "never"];
default = "leaf";
description = "display mode of the preview on the right section";
};
};
};
};
nodeMarkers = {
enable = mkEnableOption "node markers";
icons = {
leaf = mkOption {
type = types.str;
default = " ";
description = "";
};
leafSelected = mkOption {
type = types.str;
default = " ";
description = "";
};
branch = mkOption {
type = types.str;
default = " ";
description = "";
};
};
};
lsp = {
autoAttach = mkOption {
type = types.bool;
default = true;
description = "Whether to attach to LSP server manually";
};
preference = mkOption {
type = with types; nullOr (listOf str);
default = null;
description = "list of lsp server names in order of preference";
};
};
sourceBuffer = {
followNode = mkOption {
type = types.bool;
default = true;
description = "keep the current node in focus on the source buffer";
};
highlight = mkOption {
type = types.bool;
default = true;
description = "highlight the currently focused node";
};
reorient = mkOption {
type = types.enum ["smart" "top" "mid" "none"];
default = "smart";
};
scrolloff = mkOption {
type = with types; nullOr int;
default = null;
description = "scrolloff value when navbuddy is open";
};
};
# there probably is a better way to do this
# alas, I am not a nix wizard
icons = {
file = mkOption {
type = types.str;
default = "󰈙 ";
description = "";
};
module = mkOption {
type = types.str;
default = " ";
description = "";
};
namespace = mkOption {
type = types.str;
default = "󰌗 ";
description = "";
};
package = mkOption {
type = types.str;
default = " ";
description = "";
};
class = mkOption {
type = types.str;
default = "󰌗 ";
description = "";
};
property = mkOption {
type = types.str;
default = " ";
description = "";
};
field = mkOption {
type = types.str;
default = " ";
description = "";
};
constructor = mkOption {
type = types.str;
default = " ";
description = "";
};
enum = mkOption {
type = types.str;
default = "󰕘";
description = "";
};
interface = mkOption {
type = types.str;
default = "󰕘";
description = "";
};
function = mkOption {
type = types.str;
default = "󰊕 ";
description = "";
};
variable = mkOption {
type = types.str;
default = "󰆧 ";
description = "";
};
constant = mkOption {
type = types.str;
default = "󰏿 ";
description = "";
};
string = mkOption {
type = types.str;
default = " ";
description = "";
};
number = mkOption {
type = types.str;
default = "󰎠 ";
description = "";
};
boolean = mkOption {
type = types.str;
default = " ";
description = "";
};
array = mkOption {
type = types.str;
default = "󰅪 ";
description = "";
};
object = mkOption {
type = types.str;
default = "󰅩 ";
description = "";
};
method = mkOption {
type = types.str;
default = "󰆧 ";
description = "";
};
key = mkOption {
type = types.str;
default = "󰌋 ";
description = "";
};
null = mkOption {
type = types.str;
default = "󰟢 ";
description = "";
};
enumMember = mkOption {
type = types.str;
default = "󰕘 ";
description = "";
};
struct = mkOption {
type = types.str;
default = "󰌗 ";
description = "";
};
event = mkOption {
type = types.str;
default = " ";
description = "";
};
operator = mkOption {
type = types.str;
default = "󰆕 ";
description = "";
};
typeParameter = mkOption {
type = types.str;
default = "󰊄 ";
description = "";
};
};
};
};
}

View file

@ -0,0 +1,159 @@
{
config,
lib,
...
}:
with lib;
with builtins; let
cfg = config.vim.ui.breadcrumbs;
nb = cfg.navbuddy;
nilOrStr = v:
if v == null
then "nil"
else toString v;
in {
config = mkIf cfg.enable {
vim.startPlugins =
[
"nvim-lspconfig"
]
++ lib.optionals (config.vim.lsp.lspsaga.enable && cfg.source == "lspsaga") [
"lspsaga"
]
++ lib.optionals (cfg.navbuddy.enable || cfg.source == "nvim-navic") [
"nvim-navbuddy"
"nvim-navic"
];
vim.luaConfigRC.breadcrumbs = nvim.dag.entryAfter ["lspconfig"] ''
local navbuddy = require("nvim-navbuddy")
local navic = require("nvim-navic")
local actions = require("nvim-navbuddy.actions")
-- TODO: wrap this in an optional string with navbuddy as the enable condition
navbuddy.setup {
window = {
border = "${nb.window.border}", -- "rounded", "double", "solid", "none"
size = "60%",
position = "50%",
scrolloff = ${(nilOrStr nb.window.scrolloff)},
sections = {
left = {
size = "20%",
border = ${(nilOrStr nb.window.sections.left.border)},
},
mid = {
size = "40%",
border = ${(nilOrStr nb.window.sections.mid.border)},
},
right = {
border = ${(nilOrStr nb.window.sections.right.border)},
preview = "leaf",
}
},
},
node_markers = {
enabled = ${boolToString nb.nodeMarkers.enable},
icons = {
leaf = "${nb.nodeMarkers.icons.leaf}",
leaf_selected = "${nb.nodeMarkers.icons.leafSelected}",
branch = "${nb.nodeMarkers.icons.branch}",
},
},
lsp = {
auto_attach = ${boolToString nb.lsp.autoAttach},
-- preference = nil, -- TODO: convert list to lua table if not null
},
source_buffer = {
follow_node = ${boolToString nb.sourceBuffer.followNode},
highlight = ${boolToString nb.sourceBuffer.highlight},
reorient = "${nb.sourceBuffer.reorient}",
scrolloff = ${nilOrStr nb.sourceBuffer.scrolloff}
},
icons = {
File = "${cfg.navbuddy.icons.file}",
Module = "${cfg.navbuddy.icons.module}",
Namespace = "${cfg.navbuddy.icons.namespace}",
Package = "${cfg.navbuddy.icons.package}",
Class = "${cfg.navbuddy.icons.class}",
Method = "${cfg.navbuddy.icons.method}",
Property = "${cfg.navbuddy.icons.property}",
Field = "${cfg.navbuddy.icons.field}",
Constructor = "${cfg.navbuddy.icons.constructor}",
Enum = "${cfg.navbuddy.icons.enum}",
Interface = "${cfg.navbuddy.icons.interface}",
Function = "${cfg.navbuddy.icons.function}",
Variable = "${cfg.navbuddy.icons.variable}",
Constant = "${cfg.navbuddy.icons.constant}",
String = "${cfg.navbuddy.icons.string}",
Number = "${cfg.navbuddy.icons.number}",
Boolean = "${cfg.navbuddy.icons.boolean}",
Array = "${cfg.navbuddy.icons.array}",
Object = "${cfg.navbuddy.icons.object}",
Key = "${cfg.navbuddy.icons.key}",
Null = "${cfg.navbuddy.icons.null}",
EnumMember = "${cfg.navbuddy.icons.enumMember}",
Struct = "${cfg.navbuddy.icons.struct}",
Event = "${cfg.navbuddy.icons.event}",
Operator = "${cfg.navbuddy.icons.operator}",
TypeParameter = "${cfg.navbuddy.icons.typeParameter}"
},
-- make those configurable
use_default_mappings = ${toString (cfg.navbuddy.useDefaultMappings)},
mappings = {
["${cfg.navbuddy.mappings.close}"] = actions.close(),
["${cfg.navbuddy.mappings.nextSibling}"] = actions.next_sibling(),
["${cfg.navbuddy.mappings.previousSibling}"] = actions.previous_sibling(),
["${cfg.navbuddy.mappings.close}"] = actions.parent(),
["${cfg.navbuddy.mappings.children}"] = actions.children(),
["${cfg.navbuddy.mappings.root}"] = actions.root(),
["${cfg.navbuddy.mappings.visualName}"] = actions.visual_name(),
["${cfg.navbuddy.mappings.visualScope}"] = actions.visual_scope(),
["${cfg.navbuddy.mappings.yankName}"] = actions.yank_name(),
["${cfg.navbuddy.mappings.yankScope}"] = actions.yank_scope(),
["${cfg.navbuddy.mappings.insertName}"] = actions.insert_name(),
["${cfg.navbuddy.mappings.insertScope}"] = actions.insert_scope(),
["${cfg.navbuddy.mappings.appendName}"] = actions.append_name(),
["${cfg.navbuddy.mappings.appendScope}"] = actions.append_scope(),
["${cfg.navbuddy.mappings.rename}"] = actions.rename(),
["${cfg.navbuddy.mappings.delete}"] = actions.delete(),
["${cfg.navbuddy.mappings.foldCreate}"] = actions.fold_create(),
["${cfg.navbuddy.mappings.foldDelete}"] = actions.fold_delete(),
["${cfg.navbuddy.mappings.comment}"] = actions.comment(),
["${cfg.navbuddy.mappings.select}"] = actions.select(),
["${cfg.navbuddy.mappings.moveDown}"] = actions.move_down(),
["${cfg.navbuddy.mappings.moveUp}"] = actions.move_up(),
["${cfg.navbuddy.mappings.telescope}"] = actions.telescope({
layout_strategy = "horizontal",
layout_config = {
height = 0.60,
width = 0.75,
prompt_position = "top",
preview_width = 0.50
},
}),
["${cfg.navbuddy.mappings.help}"] = actions.help(), -- Open mappings help window
},
}
'';
};
}

View file

@ -0,0 +1,6 @@
_: {
imports = [
./config.nix
./breadcrumbs.nix
];
}

View file

@ -6,6 +6,7 @@ _: {
./smartcolumn
./colorizer
./illuminate
./breadcrumbs
./borders
];
}

View file

@ -54,7 +54,8 @@ in {
'alpha',
'code-action-menu-menu',
'code-action-menu-warning-message',
'notify'
'notify',
'Navbuddy'
},
}
'';