1
Files
flake-nixinator/home/modules/neovim/default.nix

2098 lines
68 KiB
Nix

{
inputs,
system,
username,
hostname,
config,
lib,
mylib,
pkgs,
...
}: let
inherit (config.modules) neovim color;
in {
options.modules.neovim = import ./options.nix {inherit lib mylib;};
config = lib.mkIf neovim.enable {
home = {
file.".config/neovide/config.toml".source = ./neovide_config.ini;
file.".config/vale/.vale.ini".source = ./vale_config.ini;
sessionVariables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
packages = with pkgs;
builtins.concatLists [
(lib.optionals neovim.neovide [neovide])
[
(pkgs.ripgrep.override {withPCRE2 = true;})
# Dependencies
lua54Packages.jsregexp # For tree-sitter
# nodejs_latest
# Language servers
clang-tools
clojure-lsp
cmake-language-server
haskell-language-server
jdt-language-server
ltex-ls # TODO: Only enable on-demand
lua-language-server
nil
nixd
basedpyright
pyrefly
ty
rust-analyzer
svelte-language-server
tailwindcss-language-server
tex-fmt
texlab
typescript
# vscode-langservers-extracted # includes nodejs
# Linters
checkstyle # java
clippy # rust
clj-kondo # clojure
eslint_d # javascript
python313Packages.ruff
python313Packages.flake8
python313Packages.pylint
lua54Packages.luacheck
vale # text
statix # nix
# Formatters
alejandra # nix
cljfmt
python313Packages.black
google-java-format
html-tidy
jq # json
# prettierd # Use prettier instead because of plugins
# nodePackages_latest.prettier # Use local install as plugins change per project
rustfmt
stylua
]
];
};
programs.nixvim = {
defaultEditor = true;
enable = true;
enableMan = false; # Nixvim man pages
luaLoader.enable = true; # NOTE: Experimental
viAlias = neovim.alias;
vimAlias = neovim.alias;
# Configured using plugin
# colorschemes.catppuccin = {
# enable = true;
# settings = {
# flavour = "mocha"; # latte, frappe, macchiato, mocha
# background = {
# light = "latte";
# dark = "mocha";
# };
# };
# };
performance.byteCompileLua = {
enable = true;
configs = true;
initLua = false; # When debugging init.lua turn this off
nvimRuntime = true;
plugins = true;
};
globals = {
mapleader = " ";
maplocalleader = ",";
};
opts = import ./vim_opts.nix {inherit lib mylib;};
extraConfigLuaPost = builtins.readFile ./extraConfigLuaPost.lua;
extraConfigLua = builtins.readFile ./extraConfigLua.lua;
# Those files will be added to the nvim runtimpath
extraFiles = {
# For this its probably important to set the default filetype to tex (see extraConfigLua)
"ftplugin/tex/mappings.lua".text = mylib.generators.toLuaKeymap (import ./mappings_latex.nix {});
"ftplugin/markdown/mappings.lua".text = mylib.generators.toLuaKeymap (import ./mappings_markdown.nix {});
# Luasnip searches the luasnippets folder in the runtimepath
"luasnippets/tex.lua".text = builtins.readFile ./snippets_latex.lua;
};
highlight = {
# Manually define blink.cmp highlight groups until catpuccin supports it. Update: Catpuccin supports it now.
# BlinkCmpMenu = {
# bg = "#${color.hex.dark.base}";
# };
# BlinkCmpMenuBorder = {
# bg = "#${color.hex.dark.base}";
# fg = "#${color.hex.dark.blue}";
# };
# BlinkCmpMenuSelection = {
# bg = "#${color.hex.dark.blue}";
# fg = "#${color.hex.dark.crust}";
# bold = true;
# };
# BlinkCmpLabel = {
# bg = "#${color.hex.dark.base}";
# fg = "#${color.hex.dark.text}";
# bold = false;
# };
};
# extraLuaPackages = with pkgs.lua51Packages; [];
# extraPython3Packages = p: [];
autoCmd = [
# TODO: This only works if neotree has been opened before...
# {
# desc = "Refresh neotree when closing lazygit";
# event = ["BufLeave"];
# pattern = ["*lazygit*"];
# callback.__raw = ''
# function()
# local manager = require("neo-tree.sources.manager")
# local renderer = require("neo-tree.ui.renderer")
# local state = manager.get_state("filesystem")
# local window_exists = renderer.window_exists(state)
# if window_exists then
# require("neo-tree.sources.filesystem.commands").refresh(state)
# end
# end
# '';
# }
{
desc = "Lint the file if autolint is enabled";
event = ["BufWritePost"];
callback.__raw = ''
function()
if not vim.g.disable_autolint then
require("lint").try_lint()
end
end
'';
}
{
desc = "Highlight yanked regions";
event = ["TextYankPost"];
callback.__raw = "function() vim.highlight.on_yank() end";
}
{
desc = "Resize splits when Neovim is resized by the WM";
event = ["VimResized"];
callback.__raw = ''
function()
local current_tab = vim.fn.tabpagenr()
vim.cmd("tabdo wincmd =")
vim.cmd("tabnext " .. current_tab)
end
'';
}
{
desc = "Disable conceal in JSON files";
event = ["FileType"];
pattern = ["json" "jsonc" "json5"]; # Disable conceal for these filetypes
callback.__raw = "function() vim.opt_local.conceallevel = 0 end";
}
{
desc = "Attach JDTLS to Java files";
event = ["FileType"];
pattern = ["Java" "java"];
callback.__raw = ''
function()
local workspace = vim.fs.dirname(vim.fs.find({'gradlew', '.git', 'mvnw'}, { upward = true })[1])
local opts = {
root_dir = workspace,
cmd = {
"jdtls",
"-data",
"/home/christoph/.local/share/eclipse/" .. vim.fn.fnamemodify(workspace, ":p:h:t"),
},
}
require('jdtls').start_or_attach(opts)
end
'';
}
];
keymaps = import ./mappings.nix {inherit lib mylib;};
plugins.lazy = let
mkDefaultConfig = name: ''
function(_, opts)
require("${name}").setup(opts)
end
'';
in {
enable = true;
plugins = let
autopairs = rec {
name = "nvim-autopairs";
pkg = pkgs.vimPlugins.nvim-autopairs;
lazy = true;
event = ["InsertEnter"];
config = mkDefaultConfig name;
opts = {
check_ts = true;
};
};
better-escape = rec {
name = "better_escape";
pkg = pkgs.vimPlugins.better-escape-nvim;
lazy = true;
event = ["InsertEnter"];
config = mkDefaultConfig name;
opts = {
# mapping = ["jk"]; # Deprecated but still the default
default_mappings = true;
timeout = 200; # In ms
};
};
catppuccin = {
name = "catppuccin";
pkg = pkgs.vimPlugins.catppuccin-nvim;
lazy = false;
priority = 1000;
config = ''
function(_, opts)
require("catppuccin").setup(opts)
vim.cmd([[
let $BAT_THEME = "catppuccin"
colorscheme catppuccin
]])
end
'';
opts = {
flavour = "mocha"; # latte, frappe, macchiato, mocha
background = {
light = "latte";
dark = "mocha";
};
# https://github.com/catppuccin/nvim/tree/main?tab=readme-ov-file#integrations
default_integrations = true;
integrations = {
blink_cmp = true;
dashboard = true;
diffview = true;
flash = true;
gitsigns = true;
mini.enabled = true;
neotree = true;
noice = true;
native_lsp = {
enabled = true;
virtual_text = {
errors = ["italic"];
hints = ["italic"];
warnings = ["italic"];
information = ["italic"];
ok = ["italic"];
};
underlines = {
errors = ["underline"];
hints = ["underline"];
warnings = ["underline"];
information = ["underline"];
ok = ["underline"];
};
inlay_hints = {
background = true;
};
};
navic = {
enabled = true;
# custom_bg = "crust";
};
notify = true;
treesitter = true;
ufo = true;
rainbow_delimiters = true;
telescope.enabled = true;
lsp_trouble = true;
illuminate = {
enabled = true;
lsp = true;
};
which_key = true;
};
};
};
# NOTE: In LazyVim require("clang_extensions").setup(opts) is called where opts is the server definition from lspconfig...
clangd-extensions = rec {
name = "clangd_extensions";
pkg = pkgs.vimPlugins.clangd_extensions-nvim;
lazy = true;
config = mkDefaultConfig name;
opts = {
inlay_hints = {
inline = false;
};
};
};
blink-cmp = rec {
name = "blink.cmp";
# pkg = inputs.blink-cmp.packages.${system}.default;
pkg = pkgs.vimPlugins.blink-cmp;
lazy = true;
event = ["InsertEnter"];
config = mkDefaultConfig name;
opts = {
keymap.preset = "enter";
completion = {
keyword = {
range = "full"; # Fuzzy match on text before and after the cursor
};
accept = {
auto_brackets = {
enabled = true; # Insert brackets for functions
};
};
menu = {
enabled = true;
auto_show = true;
scrollbar = true;
border = "rounded";
};
documentation = {
auto_show = true;
auto_show_delay_ms = 250;
treesitter_highlighting = true;
window = {
scrollbar = true;
border = "rounded";
};
};
ghost_text = {
enabled = false;
};
};
signature = {
enabled = true;
window = {
border = "rounded";
};
};
fuzzy = {
use_frecency = true;
use_proximity = true;
};
sources = {
default = ["lsp" "path" "snippets"]; # No "buffer"
};
cmdline = {
enabled = false;
};
term = {
enabled = false;
};
appearance = {
use_nvim_cmp_as_default = true;
nerd_font_variant = "mono";
};
};
};
# colorizer = rec {
# name = "colorizer";
# pkg = pkgs.vimPlugins.nvim-colorizer-lua;
# lazy = true;
# event = ["BufReadPost" "BufNewFile"];
# config = (mkDefaultConfig name);
# opts = {
# filtetypes = null;
# user_default_options = null;
# buftypes = null;
# };
# };
_ts-context-commentstring = rec {
name = "ts_context_commentstring";
pkg = pkgs.vimPlugins.nvim-ts-context-commentstring;
lazy = true;
# NOTE: Init is run before the plugin loads, e.g. for legacy vim.g settings
init = ''
function()
-- Skip compatibility checks
vim.g.skip_ts_context_commentstring_module = true
end
'';
# NOTE: Config is run after the plugin was loaded
config = mkDefaultConfig name;
};
comment = rec {
name = "Comment";
pkg = pkgs.vimPlugins.comment-nvim;
lazy = false;
# keys = ["<C-c>" "<C-b>"]; # NOTE: This list only works in normal mode
dependencies = [
_ts-context-commentstring
];
config = mkDefaultConfig name;
opts = {
pre_hook = {__raw = "function() require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook() end";};
mappings.basic = true; # Apparently required for opleader/toggler config
mappings.extra = false;
opleader.line = "<C-c>";
toggler.line = "<C-c>";
opleader.block = "<C-b>";
toggler.block = "<C-b>";
};
};
# Code formatting
conform = rec {
name = "conform";
pkg = pkgs.vimPlugins.conform-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = mkDefaultConfig name;
opts = {
formatters_by_ft = {
c = ["clang-format"];
h = ["clang-format"];
cpp = ["clang-format"];
hpp = ["clang-format"];
clojure = ["cljfmt"];
css = ["prettierd" "prettier"];
html = ["prettierd" "prettier"];
java = ["google-java-format"];
javascript = ["prettierd" "prettier"];
latex = ["tex-fmt"];
lua = ["stylua"];
markdown = ["prettierd" "prettier"];
nix = ["alejandra"];
python = ["black"];
qml = ["qmlformat"];
svelte = ["prettierd" "prettier"];
typescript = ["prettierd" "prettier"];
rust = ["rustfmt"];
};
default_format_opts = {
lsp_format = "fallback";
stop_after_first = true;
};
# format_on_save formats synchronously, format_after_save asynchronously
format_after_save.__raw = ''
function(bufnr)
-- Disable with a global or buffer-local variable
if vim.g.disable_autoformat then
return
end
return { timeout_ms = 500, lsp_format = "fallback", stop_after_first = true, }
end
'';
log_level.__raw = ''vim.log.levels.DEBUG'';
};
};
direnv = {
name = "direnv";
pkg = pkgs.vimPlugins.direnv-vim;
lazy = false;
};
diffview = {
name = "diffview";
pkg = pkgs.vimPlugins.diffview-nvim;
lazy = true;
cmd = ["DiffviewOpen"];
};
flash = rec {
name = "flash";
pkg = pkgs.vimPlugins.flash-nvim;
lazy = true;
keys = ["f" "F"];
config = mkDefaultConfig name;
};
gitsigns = rec {
name = "gitsigns";
pkg = pkgs.vimPlugins.gitsigns-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = mkDefaultConfig name;
opts = {
numhl = false;
linehl = false;
current_line_blame = true;
current_line_blame_opts = {
delay = 50;
};
};
};
haskell-tools = {
name = "haskell-tools";
pkg = pkgs.vimPlugins.haskell-tools-nvim;
lazy = false; # Recommended by author
# Don't call setup!
};
hover = let
# Display LSP information and Diagnostics at the same time
# https://github.com/lewis6991/hover.nvim/issues/34#issuecomment-1625662866
lspWithDiag = ''
{
name = "LSP",
priority = 1000,
enabled = function()
return true
end,
execute = function(opts, done)
local params = vim.lsp.util.make_position_params()
local ___ = "\n\n"
vim.lsp.buf_request_all(0, 'textDocument/hover', params, function(responses)
local value = ""
for _, response in pairs(responses) do
local result = response.result
if result and result.contents and result.contents.value then
if value ~= "" then
value = value .. ___
end
value = value .. result.contents.value
end
end
local _, row = unpack(vim.fn.getpos("."))
local lineDiag = vim.diagnostic.get(0, { lnum = row - 1 })
for _, d in pairs(lineDiag) do
if d.message then
if value ~= "" then
value = value .. ___
end
value = value .. string.format("*%s* %s", d.source, d.message)
end
end
value = value:gsub("\r", "")
if value ~= "" then
done({ lines = vim.split(value, "\n", true), filetype = "markdown" })
else
done()
end
end)
end,
}
'';
in rec {
name = "hover";
pkg = pkgs.vimPlugins.hover-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = mkDefaultConfig name;
opts = {
init.__raw = ''
function()
-- Register custom providers
require('hover').register(${lspWithDiag})
-- Require providers
-- require("hover.providers.lsp")
-- require('hover.providers.diagnostic')
require('hover.providers.fold_preview')
require('hover.providers.man')
-- require('hover.providers.gh')
-- require('hover.providers.gh_user')
-- require('hover.providers.jira')
-- require('hover.providers.dap')
-- require('hover.providers.dictionary')
-- require('hover.providers.highlight')
end
'';
preview_opts = {
border = "rounded";
};
# Whether the contents of a currently open hover window should be moved
# to a :h preview-window when pressing the hover keymap.
preview_window = false;
title = false;
};
};
illuminate = rec {
name = "illuminate";
pkg = pkgs.vimPlugins.vim-illuminate;
lazy = true;
event = ["BufreadPost" "BufNewFile"];
config = ''
function(_, opts)
require("${name}").configure(opts)
end
'';
opts = {
filetypesDenylist = [
"DressingSelect"
"Outline"
"alpha"
"harpoon"
"toggleterm"
"neo-tree"
"Spectre"
"reason"
];
};
};
_navic = {
name = "navic";
pkg = pkgs.vimPlugins.nvim-navic;
lazy = true;
config = ''
function(_, opts)
navic = require("nvim-navic")
navic.setup(opts)
-- NOTE: Use incline, because the default winbar isn't floating and disappears
-- when leavin the split, which makes the buffer jump
-- Register navic with lualine's winbar
-- TODO: The setup function should only be ran once
-- require("lualine").setup({
-- winbar = {
-- lualine_c = {
-- {
-- function()
-- return navic.get_location()
-- end,
-- cond = function()
-- return navic.is_available()
-- end
-- }
-- }
-- }
-- })
end
'';
opts = {
lsp.auto_attach = true;
click = true;
highlight = true;
depth_limit = 5;
};
};
intellitab = {
name = "intellitab";
pkg = pkgs.vimPlugins.intellitab-nvim; # Prints at each indent :(
# TODO: Build broken
# pkg = pkgs.vimUtils.buildVimPlugin {
# name = "intellitab-nvim";
# src = pkgs.fetchFromGitHub {
# owner = "ChUrl";
# repo = "intellitab.nvim";
# rev = "6d644b7d92198477f2920d0c3b3b22dad470ef10"; # Disable print
# sha256 = "sha256-MwBcsYpyrjoXa7nxcwaci3h0NIWyMoF1NyYfEbFzo0E=";
# };
# };
lazy = true;
event = ["InsertEnter"];
};
jdtls = {
name = "jdtls";
pkg = pkgs.vimPlugins.nvim-jdtls;
lazy = false; # Is only ever loaded in Java buffers anyway
# Additional configuration is done in autoCmd
};
lastplace = rec {
name = "nvim-lastplace";
pkg = pkgs.vimPlugins.nvim-lastplace;
lazy = false;
config = mkDefaultConfig name;
};
lint = {
name = "lint";
pkg = pkgs.vimPlugins.nvim-lint;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = ''
function(_, opts)
local lint = require("lint")
for k, v in pairs(opts) do
lint[k] = v
end
local chktex = lint.linters.chktex
chktex.args = {
'-v0',
'-I0',
'-n3', -- Enclose previous parenthesis with {}
-- '-n8', -- Wrong length of dash may have been used
-- '-n24', -- Delete this space to maintain correct page references
'-s',
':',
'-f',
'%l%b%c%b%d%b%k%b%n%b%m%b%b%b'
}
end
'';
opts = {
linters_by_ft = {
c = ["clangtidy"];
h = ["clangtidy"];
cpp = ["clangtidy"];
hpp = ["clangtidy"];
clojure = ["clj-kondo"];
java = ["checkstyle"];
javascript = ["eslint_d"];
lua = ["luacheck"];
markdown = ["vale"];
nix = ["statix"];
python = ["ruff" "flake8"]; # TODO: "pylint" can't resolve some imports (e.g. PySide6.QtGui)
tex = ["chktex"];
# rust = ["clippy"]; # Not supported, but integrated through rustaceanvim
text = ["vale"];
};
};
};
# Newer alternative to neodev
_lazydev = rec {
name = "lazydev";
pkg = pkgs.vimPlugins.lazydev-nvim;
lazy = true;
ft = ["lua"];
config = mkDefaultConfig name;
# opts = {
# library = [
# "~/NixFlake/config/neovim/store"
# ];
# };
};
# NOTE: This entire thing is rough, I should rewrite...
lspconfig = {
name = "lspconfig";
pkg = pkgs.vimPlugins.nvim-lspconfig;
lazy = true;
cmd = ["LspInfo"];
event = ["BufReadPost" "BufNewFile"];
dependencies = [_lazydev];
config = let
servers = mylib.generators.toLuaObject [
{name = "basedpyright";}
# {name = "pyrefly";} # TODO: Config
# {name = "ty";} # TODO: Config
{
name = "clangd";
extraOptions = {
root_dir.__raw = ''
function(fname)
return require("lspconfig.util").root_pattern(
"Makefile",
"CMakeLists.txt",
".clang-format",
".clang-tidy"
)(fname) or require("lspconfig.util").root_pattern(
"compile_commands.json"
)(fname) or require("lspconfig.util").find_git_ancestor(fname)
end
'';
cmd = [
"clangd"
"--background-index"
"--clang-tidy"
"--header-insertion=iwyu"
"--completion-style=detailed"
"--function-arg-placeholders"
"--fallback-style=llvm"
];
capabilities = {
offsetEncoding = ["utf-16"];
};
init_options = {
usePlaceholders = true;
completeUnimported = true;
clangdFileStatus = true;
};
};
}
{name = "clojure_lsp";}
{name = "cmake";}
{name = "cssls";}
# {name = "html";} # vscode-langservers-extracted
{name = "lua_ls";}
{
name = "ltex";
extraOptions.settings = {
ltex = {
checkFrequency = "save";
enabled = ["markdown" "org" "tex" "latex" "plaintext"];
};
};
}
{name = "nil_ls";}
{
name = "nixd";
extraOptions.cmd = [
"nixd"
"--inlay-hints=false"
"--semantic-tokens=true"
];
extraOptions.settings = {
nixd = {
nixpkgs = {
expr = "import <nixpkgs> { }";
};
formatting = {
command = ["alejandra"];
};
options = {
nixos = {
expr = "(builtins.getFlake \"/home/${username}/NixFlake\").nixosConfigurations.${hostname}.options";
};
# For HM standalone
# home-manager = {
# expr = "(builtins.getFlake \"/home/${username}/NixFlake\").homeConfigurations.\"${username}@${hostname}\".options";
# };
# For HM NixOS module
home-manager = {
expr = "(builtins.getFlake \"/home/${username}/NixFlake\").nixosConfigurations.\"${hostname}\".options.home-manager.users.type.getSubOptions []";
};
};
diagnostic = {
suppress = [
"sema-escaping-with"
"var-bind-to-this"
"escaping-this-with"
];
};
};
};
}
{
name = "qmlls";
extraOptions.cmd = [
"qmlls"
"-E" # Use QML_IMPORT_PATH env variable
];
}
{name = "svelte";}
{name = "tailwindcss";}
{name = "texlab";}
# {name = "jdtls";} # Don't set up when using nvim-jdtls
# {name = "rust_analyzer";} # Don't set up when using rustaceanvim
# {name = "hls";} # Don't set up when using haskell-tools
];
in ''
function(_, opts)
-- Make LspInfo window border rounded
require("lspconfig.ui.windows").default_options.border = "rounded"
local __lspOnAttach = function(client, bufnr)
-- NOTE: The ltex-extra package needs to be loaded in ltex's onAttach.
-- I don't know how to do this more declaratively with the current structure.
if client.name == "ltex" then
require("ltex_extra").setup({})
end
-- NOTE: The svelte-lsp apparently has a bug and doesn't watch files correctly
if client.name == "svelte" then
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = { "*.js", "*.ts" },
group = vim.api.nvim_create_augroup("svelte_ondidchangetsorjsfile", { clear = true }),
callback = function(ctx)
-- Here use ctx.match instead of ctx.file
client.notify("$/onDidChangeTsOrJsFile", { uri = ctx.match })
end,
})
end
end
local __lspCapabilities = function()
capabilities = vim.lsp.protocol.make_client_capabilities()
-- I don't remember where this came from, but without cmp it makes no sense
-- capabilities = vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities())
-- get_lsp_capabilities merges with the existing capabilities
capabilities = require("blink.cmp").get_lsp_capabilities(capabilities)
return capabilities
end
local __setup = {
on_attach = __lspOnAttach,
capabilities = __lspCapabilities(),
}
for i, server in ipairs(${servers}) do
if type(server) == "string" then
require("lspconfig")[server].setup(__setup)
else
local options = server.extraOptions
if options == nil then
options = __setup
else
options = vim.tbl_extend("keep", options, __setup)
end
require("lspconfig")[server.name].setup(options)
end
end
end
'';
};
lualine = {
name = "lualine";
pkg = pkgs.vimPlugins.lualine-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = ''
function(_, opts)
local lualine = require("lualine")
lualine.setup(opts)
-- Disable tabline/winbar sections
lualine.hide({
place = {'tabline', 'winbar'},
unhide = false,
})
end
'';
opts = let
bubbles = ''
{
normal = {
a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.lavender}", gui = "bold" },
b = { fg = "#${color.hex.dark.text}", bg = "#${color.hex.dark.crust}" },
c = { fg = "#${color.hex.dark.text}", bg = "NONE" },
},
insert = {
a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.green}", gui = "bold" },
b = { fg = "#${color.hex.dark.green}", bg = "#${color.hex.dark.crust}" },
},
visual = {
a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.mauve}", gui = "bold" },
b = { fg = "#${color.hex.dark.mauve}", bg = "#${color.hex.dark.crust}" },
},
replace = {
a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.red}", gui = "bold" },
b = { fg = "#${color.hex.dark.red}", bg = "#${color.hex.dark.crust}" },
},
-- terminal = {
-- a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.green}", gui = "bold" },
-- b = { fg = "#${color.hex.dark.green}", bg = "#${color.hex.dark.crust}" },
-- },
command = {
a = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.peach}", gui = "bold" },
b = { fg = "#${color.hex.dark.peach}", bg = "#${color.hex.dark.crust}" },
},
inactive = {
a = { fg = "#${color.hex.dark.text}", bg = "#${color.hex.dark.base}" },
b = { fg = "#${color.hex.dark.text}", bg = "#${color.hex.dark.base}" },
c = { fg = "#${color.hex.dark.text}", bg = "NONE" },
},
}
'';
in {
extensions = ["fzf" "lazy" "quickfix" "toggleterm" "trouble"];
options = {
# theme = "catppuccin";
theme.__raw = bubbles;
always_divide_middle = true;
globalstatus = true;
ignore_focus = ["neo-tree"];
section_separators = {
left = "";
right = "";
};
component_separators = {
left = "";
right = "";
};
};
sections = {
# These often use mixed lists/attrSets, so we have to use __raw alot
lualine_a.__raw = ''{ { "mode", separator = {}, } }'';
lualine_b.__raw = ''
{
{
"branch",
on_click = function() vim.cmd("LazyGit") end,
},
{
"diff",
on_click = function() vim.cmd("DiffviewOpen") end,
},
{
"diagnostics",
on_click = function() vim.cmd("Trouble diagnostics toggle") end,
},
{
"filename",
path = 1,
},
}
'';
lualine_c.__raw = ''{}''; # Use __raw: Nixvim does nothing with "[]", so the default config would be used
lualine_x.__raw = ''{}'';
lualine_y = ["filetype" "encoding" "fileformat"];
lualine_z.__raw = ''{ { "location", separator = {}, } }'';
};
inactive_sections = {
lualine_a = [];
lualine_b = ["filename"];
lualine_c = [];
lualine_x = [];
lualine_y = [];
lualine_z = ["location"];
};
# Using tabby for this
# tabline = {
# lualine_a = ["hostname"];
# lualine_b = ["tabs"]; # buffers
# lualine_x = ["windows"];
# };
};
};
luasnip = {
name = "luasnip";
pkg = pkgs.vimPlugins.luasnip;
lazy = true;
event = ["InsertEnter"];
config = ''
function(_, opts)
require("luasnip").config.set_config(opts)
-- Load snippets. Because we don't set "path", the nvim runtimepath is searched.
-- Snippet files are added through nixvim's extraFiles (see at the top).
require("luasnip.loaders.from_lua").lazy_load({})
end
'';
opts = {
enable_autosnippets = false;
};
};
ltex-extra = {
name = "ltex_extra";
pkg = pkgs.vimPlugins.ltex_extra-nvim;
lazy = true;
ft = ["markdown" "tex"];
dependencies = [lspconfig];
config = ''
-- Do nothing, as we call require("ltex_extra") in ltex's onAttach
function(_, opts) end
'';
};
markview = {
name = "markview";
pkg = pkgs.vimPlugins.markview-nvim;
lazy = true;
ft = ["markdown"];
dependencies = [
treesitter
web-devicons
];
};
navbuddy = {
name = "navbuddy";
pkg = pkgs.vimPlugins.nvim-navbuddy;
dependencies = [_navic];
lazy = true;
cmd = ["Navbuddy"];
config = ''
function(_, opts)
local actions = require("nvim-navbuddy.actions") -- ?
require("nvim-navbuddy").setup(opts)
end
'';
opts = {
lsp.auto_attach = true;
window.border = "rounded";
};
};
neogen = rec {
name = "neogen";
pkg = pkgs.vimPlugins.neogen;
lazy = true;
cmd = ["Neogen"];
config = mkDefaultConfig name;
opts = {
snippet_engine = "luasnip";
};
};
neotree = rec {
name = "neo-tree";
pkg = pkgs.vimPlugins.neo-tree-nvim;
dependencies = [
_plenary
web-devicons
_nui
];
lazy = true;
cmd = ["Neotree"];
config = mkDefaultConfig name;
opts = {
use_default_mappings = false;
popup_border_style = "rounded";
enable_git_status = true;
enable_diagnostics = false;
open_files_do_not_replace_types = ["terminal" "trouble" "qf"];
default_component_configs = {
container = {
enable_character_fade = true;
};
};
filesystem = {
bind_to_cwd = true;
cwd_target.sidebar = "global";
filtered_items = {
visible = false; # Toggle with "H"
};
follow_current_file = {
enabled = true;
leave_dirs_open = false;
};
};
buffers = {
follow_current_file = {
enabled = true;
leave_dirs_open = false;
};
};
window = {
position = "left";
mappings = {
"<CR>" = "open";
"c" = "close_node";
"R" = "refresh";
"q" = "close_window";
"i" = "show_file_details";
"r" = "rename";
"d" = "delete";
"x" = "cut_to_clipboard";
"y" = "copy_to_clipboard";
"p" = "paste_from_clipboard";
"a" = "add";
"." = "set_root";
">" = "navigate_up";
"H" = "toggle_hidden";
"<Esc>" = "cancel";
"/" = "fuzzy_finder";
"?" = "show_help";
};
};
};
};
_nui = {
name = "nui"; # For noice
pkg = pkgs.vimPlugins.nui-nvim;
lazy = true;
};
noice = rec {
name = "noice";
pkg = pkgs.vimPlugins.noice-nvim;
lazy = false;
dependencies = [
_nui
];
config = mkDefaultConfig name;
opts = {
presets = {
bottom_search = false;
command_palette = true;
long_message_to_split = true;
inc_rename = true; # TODO: Doesn't work as I expect (not for LSP rename)
lsp_doc_border = true;
};
lsp = {
progress.enabled = true;
hover.enabled = true;
signature.enabled = false; # Use blink for this
message.enabled = true;
documentation = {
view = "hover";
opts = {
lang = "markdown";
replace = true;
render = "plain";
border = "rounded"; # single or rounded
format = ["{message}"];
win_options = {
concealcursor = "n";
conceallevel = 3;
};
};
};
override = {
"vim.lsp.util.convert_input_to_markdown_lines" = true;
"vim.lsp.util.stylize_markdown" = true;
"cmp.entry.get_documentation" = true;
};
};
notify = {
enabled = true;
};
popupmenu = {
enabled = true;
backend = "nui"; # cmp completion is broken
};
# cmdline.enabled = false;
# messages.enabled = false;
routes = [
# Hide inline search count info
{
filter = {
event = "msg_show";
kind = "search_count";
};
opts = {skip = true;};
}
];
};
};
obsidian = rec {
name = "obsidian";
pkg = pkgs.vimPlugins.obsidian-nvim;
lazy = true;
cmd = ["ObsidianSearch" "ObsidianNew"];
ft = ["markdown"];
dependencies = [
_plenary
];
config = mkDefaultConfig name;
opts = {
workspaces = [
{
name = "Chriphost";
path = "~/Notes/Obsidian/Chriphost";
}
];
};
};
# TODO: Don't autosave, but if a session exists, update it (using should_save)
# TODO: No idea which opts below really exist...
persisted = rec {
name = "persisted";
pkg = pkgs.vimPlugins.persisted-nvim;
lazy = false;
config = mkDefaultConfig name;
opts = {
silent = false;
use_git_branch = false;
autostart = false;
autosave = false;
autoload = false;
follow_cwd = true;
ignored_dirs = [
"/"
"~/"
"~/Projects/"
];
};
};
presence = rec {
name = "presence";
pkg = pkgs.vimPlugins.presence-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
config = mkDefaultConfig name;
opts = {
auto_update = true;
show_time = false;
};
};
quickfix-reflector = {
name = "quickfix-reflector";
pkg = pkgs.vimPlugins.quickfix-reflector-vim;
lazy = false;
};
rainbow-delimiters = {
name = "rainbow-delimiters";
pkg = pkgs.vimPlugins.rainbow-delimiters-nvim;
lazy = false;
};
rustaceanvim = {
name = "rustaceanvim";
pkg = pkgs.vimPlugins.rustaceanvim;
lazy = false; # Recommended by author
# Don't call setup!
init = ''
function()
vim.g.rustaceanvim = {
tools = {
enable_clippy = true,
float_win_config = {
border = "rounded",
},
},
server = {
default_settings = {
["rust-analyzer"] = {
cargo = {
allFeatures = true,
-- features = "all",
-- loadOutDirsFromCheck = true,
-- runBuildScripts = true,
},
-- lint-nvim doesn't support clippy
checkOnSave = {
allFeatures = true,
allTargets = true,
command = "clippy",
extraArgs = {
"--",
"--no-deps",
"-Dclippy::pedantic",
"-Dclippy::nursery",
"-Dclippy::unwrap_used",
"-Dclippy::enum_glob_use",
"-Dclippy::complexity",
"-Dclippy::perf",
},
},
},
},
},
};
end
'';
};
scope = rec {
name = "scope";
pkg = pkgs.vimPlugins.scope-nvim;
lazy = false;
config = mkDefaultConfig name;
};
sleuth = {
name = "sleuth";
pkg = pkgs.vimPlugins.vim-sleuth;
lazy = false;
};
snacks = rec {
name = "snacks";
pkg = pkgs.vimPlugins.snacks-nvim;
dependencies = [
web-devicons
];
lazy = false;
priority = 1000;
config = mkDefaultConfig name;
opts = {
# Disables slow stuff in big files
bigfile = {
enabled = true;
notify = true;
size = 1.5 * 1024 * 1024; # 1.5MB
line_length = 1000;
};
dashboard = {
enabled = true;
preset = {
keys = [
{
icon = " ";
key = "f";
desc = "Find File";
action = "<cmd>lua Snacks.dashboard.pick('files')<cr>";
}
{
icon = " ";
key = "n";
desc = "New File";
action = "<cmd>ene | startinsert<cr>";
}
{
icon = " ";
key = "g";
desc = "Find Text";
action = "<cmd>lua Snacks.dashboard.pick('live_grep')<cr>";
}
{
icon = " ";
key = "r";
desc = "Recent Files";
action = "<cmd>lua Snacks.dashboard.pick('oldfiles')<cr>";
}
{
icon = " ";
key = "s";
desc = "Restore Session";
action = "<cmd>lua require('persisted').select()<cr>";
}
{
icon = "󰒲 ";
key = "L";
desc = "Lazy";
action = "<cmd>Lazy<cr>";
}
{
icon = " ";
key = "q";
desc = "Quit";
action = "<cmd>quitall<cr>";
}
];
sections = [
{section = "header";}
{
section = "keys";
gap = 1;
padding = 1;
}
{section = "startup";}
];
};
};
explorer = {
enabled = false;
replace_netrw = false; # Use yazi for that
};
notifier = {
enabled = true;
};
picker = let
defaultLayout = ''
--- Use the default layout or vertical if the window is too narrow
function()
return vim.o.columns >= 120 and "default" or "vertical"
end
'';
in {
enabled = true;
layout = {
cycle = true;
preset.__raw = defaultLayout;
};
sources = {
lines = {
layout.__raw = defaultLayout;
};
};
};
};
};
tabby = rec {
name = "tabby";
pkg = pkgs.vimPlugins.tabby-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
dependencies = [web-devicons];
config = mkDefaultConfig name;
opts = {
line.__raw = ''
function(line)
local base = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.base}" }
local crust = { fg = "#${color.hex.dark.crust}", bg = "#${color.hex.dark.crust}" }
local text = { fg = "#${color.hex.dark.text}", bg = "#${color.hex.dark.crust}" }
local lavender = { fg = "#${color.hex.dark.lavender}", bg = "#${color.hex.dark.lavender}" }
local numtabs = vim.call("tabpagenr", "$")
return {
-- Head
{
{ " NEOVIM ", hl = { fg = "#${color.hex.dark.base}", bg = "#${color.hex.dark.lavender}", style = "bold" } },
-- The separator gets a foreground and background fill (each have fg + bg).
-- line.sep("", lavender, lavender),
},
-- Tabs
line.tabs().foreach(function(tab)
-- Switch out the start separator instead of the ending one because the last separator is different
local hl = tab.is_current() and { fg = "#${color.hex.dark.lavender}", bg = "#${color.hex.dark.crust}", style = "bold" } or text
local sep_start = tab.number() == 1 and "" or ""
local sep_end = tab.number() == numtabs and "" or ""
return {
line.sep(sep_start, lavender, crust),
tab.number(),
tab.name(),
line.sep(sep_end, crust, base),
hl = hl,
margin = " ",
}
end),
-- Background
hl = base,
}
end
'';
};
};
_plenary = {
name = "plenary";
pkg = pkgs.vimPlugins.plenary-nvim;
lazy = true;
};
todo-comments = rec {
name = "todo-comments";
pkg = pkgs.vimPlugins.todo-comments-nvim;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
dependencies = [
_plenary
];
config = mkDefaultConfig name;
opts = {
signs = true;
keywords = {
FIX = {
icon = " ";
color = "error";
alt = [
"FIXME"
"BUG"
"FIXIT"
"ISSUE"
];
# signs = false; # Configure signs for some keywords individually
};
TODO = {
icon = " ";
color = "info";
alt = [
];
};
HACK = {
icon = " ";
color = "warning";
alt = [
];
};
WARN = {
icon = " ";
color = "warning";
alt = [
"WARNING"
"XXX"
];
};
PERF = {
icon = " ";
alt = [
"OPTIM"
"PERFORMANCE"
"OPTIMIZE"
];
};
NOTE = {
icon = " ";
color = "hint";
alt = [
"INFO"
];
};
TEST = {
icon = " ";
color = "test";
alt = [
"TESTING"
"PASSED"
"FAILED"
];
};
};
};
};
toggleterm = rec {
name = "toggleterm";
pkg = pkgs.vimPlugins.toggleterm-nvim;
lazy = true;
cmd = ["ToggleTerm"];
keys = ["<C-/>"];
config = mkDefaultConfig name;
opts = {
open_mapping.__raw = "[[<C-/>]]";
autochdir = true;
hide_numbers = true;
shade_terminals = false;
shading_factor = 30; # Default is -30 to darken the terminal
start_in_insert = true;
terminal_mappings = true;
size = 20;
persist_size = true;
persist_mode = false; # Don't persist the current mode (insert/normal)
insert_mappings = true;
close_on_exit = true;
shell = "fish";
direction = "horizontal"; # 'vertical' | 'horizontal' | 'window' | 'float'
auto_scroll = true;
float_opts = {
border = "curved"; # 'single' | 'double' | 'shadow' | 'curved'
width = 80;
height = 80;
winblend = 0;
};
};
};
# _treesitter-context = {
# name = "treesitter-context";
# pkg = pkgs.vimPlugins.nvim-treesitter-context;
# lazy = true;
# config = ''
# function(_, opts)
# require("treesitter-context").setup(opts)
# end
# '';
# opts = {
# max_lines = 3;
# line_numbers = false;
# };
# };
# _treesitter-refactor = {
# name = "treesitter-refactor";
# pkg = pkgs.vimPlugins.nvim-treesitter-refactor;
# lazy = true;
# };
treesitter = let
nvim-plugintree = pkgs.vimPlugins.nvim-treesitter.withAllGrammars;
treesitter-parsers = pkgs.symlinkJoin {
name = "treesitter-parsers";
paths = nvim-plugintree.dependencies;
};
in {
name = "treesitter";
pkg = pkgs.vimPlugins.nvim-treesitter;
dependencies = [
# _treesitter-context # Ugly
# _treesitter-refactor # Ugly
];
lazy = true;
cmd = ["TSModuleInfo"];
event = ["BufReadPost" "BufNewFile"];
init = ''
function()
-- Fix treesitter grammars/parsers on nix
vim.opt.runtimepath:append("${nvim-plugintree}")
vim.opt.runtimepath:append("${treesitter-parsers}")
end
'';
config = ''
function(_, opts)
require("nvim-treesitter.configs").setup(opts)
-- GLSL filetypes
vim.filetype.add {
extension = {
vert = "vert",
frag = "frag",
},
}
-- Tell treesitter that those filetypes are GLSL
vim.treesitter.language.register("glsl", "vert")
vim.treesitter.language.register("glsl", "frag")
end
'';
opts = {
auto_install = false;
ensure_installed = [];
parser_install_dir = "${treesitter-parsers}";
indent = {
enable = true;
# disable = ["python" "yaml"]; # NOTE: Check how bad it is
};
highlight = {
enable = true;
# disable = ["yaml"];
additional_vim_regex_highlighting = false;
};
# refactor = {
# highlight_definitions.enable = true;
# highlight_current_scope.enable = false; # Ugly
# };
incremental_selection = {
enable = false;
keymaps = {
"init_selection" = "gnn";
"node_decremental" = "grm";
"node_incremental" = "grn";
"scope_incremental" = "grc";
};
};
};
};
trim = rec {
name = "trim";
pkg = pkgs.vimPlugins.trim-nvim;
lazy = false;
config = mkDefaultConfig name;
};
trouble = rec {
name = "trouble";
pkg = pkgs.vimPlugins.trouble-nvim;
lazy = true;
cmd = ["Trouble" "TroubleToggle"];
config = mkDefaultConfig name;
};
ts-autotag = rec {
name = "nvim-ts-autotag";
pkg = pkgs.vimPlugins.nvim-ts-autotag;
lazy = false;
config = mkDefaultConfig name;
};
typescript-tools = rec {
name = "typescript-tools";
pkg = pkgs.vimPlugins.typescript-tools-nvim;
lazy = true;
ft = ["javascript" "typescript" "svelte" "html"];
dependencies = [_plenary lspconfig];
config = mkDefaultConfig name;
};
_promise = {
name = "promise";
pkg = pkgs.vimPlugins.promise-async;
lazy = true;
};
ufo = rec {
name = "ufo";
pkg = pkgs.vimPlugins.nvim-ufo;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
dependencies = [
_promise
];
config = mkDefaultConfig name;
};
vimtex = {
name = "vimtex";
pkg = pkgs.vimPlugins.vimtex;
init = ''
function()
vim.g.vimtex_mappings_enabled = false
vim.g.vimtex_view_method = "zathura"
vim.g.vimtex_compiler_latexmk = {
options = {
"-shell-escape",
"-file-line-error",
"-synctex=1",
"-interaction=nonstopmode",
},
aux_dir = ".aux",
out_dir = ".out",
}
end
'';
};
wakatime = {
name = "wakatime";
pkg = pkgs.vimPlugins.vim-wakatime;
lazy = true;
event = ["BufReadPost" "BufNewFile"];
};
web-devicons = rec {
name = "nvim-web-devicons";
pkg = pkgs.vimPlugins.nvim-web-devicons;
lazy = true;
config = mkDefaultConfig name;
};
_mini = {
name = "mini";
pkg = pkgs.vimPlugins.mini-nvim;
lazy = true;
};
which-key = rec {
name = "which-key";
pkg = pkgs.vimPlugins.which-key-nvim;
lazy = false;
dependencies = [
_mini
];
priority = 500;
config = mkDefaultConfig name;
opts = {
preset = "helix"; # or "modern"
};
};
window-picker = rec {
name = "window-picker";
pkg = pkgs.vimPlugins.nvim-window-picker;
lazy = true;
event = ["VeryLazy"];
config = mkDefaultConfig name;
opts = {
hint = "floating-big-letter";
show_prompt = false;
filter_rules = {
autoselect_one = false;
include_current_win = false;
bo = {
# Ignored filetypes
filetype = ["NvimTree" "neo-tree" "notify" "TelescopePrompt" "noice"];
# Ignored buffer types
buftype = ["terminal" "quickfix"];
};
};
};
};
winshift = rec {
name = "winshift";
pkg = pkgs.vimPlugins.winshift-nvim;
lazy = true;
cmd = ["WinShift"];
config = mkDefaultConfig name;
opts = {
highlight_moving_win = true;
keymaps = {
disable_defaults = true;
win_move_mode = {
h = "left";
j = "down";
k = "up";
l = "right";
};
};
};
};
yanky = rec {
name = "yanky";
pkg = pkgs.vimPlugins.yanky-nvim;
lazy = true;
cmd = [
"YankyClearHistory"
"YankyRingHistory"
];
config = mkDefaultConfig name;
};
yazi = rec {
name = "yazi";
pkg = pkgs.vimPlugins.yazi-nvim;
lazy = true;
event = ["VeryLazy"];
dependencies = [_plenary];
config = mkDefaultConfig name;
opts = {
open_for_directories = true;
highlight_hovered_buffers_in_same_directory = false;
integrations = {
grep_in_directory.__raw = ''
function(directory)
Snacks.picker.grep({dirs={directory}})
end
'';
picker_add_copy_relative_path_action = "snacks.picker";
};
};
};
in [
autopairs # Automatic closing brackets/parens # NOTE: For now replaced by blink
better-escape # Escape to normal mode using "jk"
catppuccin # Colortheme (also add this here to access palettes)
clangd-extensions # Improved clang LSP support
blink-cmp # Fast as fuck auto completion
# colorizer # Colorize color strings # TODO: Only colorize html/css/scss/sass/js
comment # Toggle line- or block-comments
conform # Auto formatting on save
# dadbod # Database interface # TODO:
# dadbod-ui # Dadbod UI # TODO:
# dap # Debug adapter protocol # TODO:
# dap-ui # Debugger UI # TODO:
diffview # Git diff # TODO: Check the keybindings
direnv # Automatically load local environments
flash # Highlight f/F search results
gitsigns # Show git line additions/deletions/changes in the gutter
haskell-tools # Haskell integration
hover # Multiple hover providers
illuminate # Highlight usages of word under cursor
intellitab # Indent to the correct level on blanklines # TODO: Behaves bit fishy sometimes
jdtls # Eclipse JDT language server integration for Java
lastplace # Reopen a file at the last editing position
lint # Lint documents on save
lspconfig # Language server configurations for different languages
lualine # Status line
luasnip # Snippets
ltex-extra # Additional ltex lsp support, e.g. for add-to-dictionary action
markview # Markdown support # TODO: Disable in help buffers (?) + confiure a bit more
navbuddy # Structural file view
neogen # Generate doc comments
neotree
noice # Modern UI overhaul, e.g. floating cmdline
obsidian # Integration with Obsidian.md
# overseer # Run tasks from within neovim (e.g. cargo) # TODO:
persisted # Session management
presence # Discord rich presence
quickfix-reflector # Make the quickfix list editable and saveable to apply changes
rainbow-delimiters # Bracket/Paren colorization
rustaceanvim # Rust integration
scope # Buffers scoped to tabpages
sleuth # Heuristically set indent depth # TODO: See intellitab
snacks # Lots of QoL
tabby # Nicer tabline (only showing tabpages)
todo-comments # Highlight TODOs
toggleterm # Integrated terminal
treesitter # AST based syntax highlighting + indentation
trim # Trim whitespace
trouble # Diagnostics window
ts-autotag # Automatic html tag insertion/updating
typescript-tools # Typescript tsserver LSP
ufo # Code folding
vimtex # LaTeX support
wakatime # Time tracking
web-devicons # Icons for many plugins
which-key # Live keybinding help
window-picker # Jump to window without multiple <leader-hjkl>
winshift # Move windows around
yanky # Clipboard history
yazi
];
};
};
};
}