547 lines
15 KiB
Nix
547 lines
15 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.programs.neovim;
|
|
toLua = lib.generators.toLua { };
|
|
buildPluginConfig =
|
|
p:
|
|
let
|
|
pluginConfig = if builtins.hasAttr "plugin" p then p else { plugin = p; };
|
|
name = pluginConfig.plugin.name;
|
|
|
|
config = if (builtins.isNull pluginConfig.config or null) then "" else pluginConfig.config;
|
|
mappings =
|
|
if (builtins.isNull pluginConfig.mappings or null) then
|
|
""
|
|
else
|
|
generateMappings pluginConfig.mappings;
|
|
mergedConfig = config + mappings;
|
|
in
|
|
[
|
|
{
|
|
plugin = pluginConfig.plugin;
|
|
config = lib.modules.mkIf (mergedConfig != "") ''
|
|
Plugins[${toLua name}] = {}
|
|
Plugins[${toLua name}].setup = function()
|
|
${
|
|
lib.strings.concatMapStrings (line: if line == "" then "" else " ${line}\n") (
|
|
lib.strings.splitString "\n" mergedConfig
|
|
)
|
|
|
|
}
|
|
end
|
|
|
|
Plugins[${toLua name}].setup()
|
|
'';
|
|
type = "lua";
|
|
}
|
|
]
|
|
++ (lib.lists.concatMap buildPluginConfig pluginConfig.dependencies or [ ]);
|
|
|
|
lspPluginConfig = with pkgs.vimPlugins; {
|
|
plugin = nvim-lspconfig;
|
|
dependencies = [
|
|
nvim-cmp
|
|
cmp-nvim-lsp
|
|
cmp_luasnip
|
|
luasnip
|
|
];
|
|
|
|
config =
|
|
(builtins.readFile ./lsp-config.lua)
|
|
+ ''
|
|
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
|
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true
|
|
local lspconfig = require('lspconfig')
|
|
local util = require('lspconfig.util')
|
|
|
|
local on_attach = function (client, bufnr)
|
|
local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end
|
|
local map = function (lhs, rhs)
|
|
print('set mapping ' .. lhs)
|
|
end
|
|
|
|
-- Enable completion triggered by <c-x><c-o>
|
|
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
|
|
|
|
-- Mappings.
|
|
-- See `:help vim.lsp.*` for documentation on any of the below functions
|
|
|
|
${lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (name: value: ''
|
|
local buf_${name} = vim.lsp.buf[${toLua name}]
|
|
if buf_${name} then
|
|
vim.keymap.set('n', ${toLua value}, buf_${name}, { buffer = bufnr })
|
|
end
|
|
'') cfg.lsp.mappings.buf
|
|
)}
|
|
${lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (name: value: ''
|
|
local diagnostic_${name} = vim.lsp.buf[${toLua name}]
|
|
if diagnostic_${name} then
|
|
vim.keymap.set('n', ${toLua value}, diagnostic_${name}, { buffer = bufnr })
|
|
end
|
|
'') cfg.lsp.mappings.diagnostic
|
|
)}
|
|
end
|
|
|
|
vim.env.PATH = ${
|
|
toLua (
|
|
lib.makeBinPath (
|
|
map (s: s.package) (builtins.filter (s: builtins.hasAttr "package" s) cfg.lsp.servers)
|
|
)
|
|
+ ":"
|
|
)
|
|
} .. vim.env.PATH
|
|
local servers = ${
|
|
toLua (
|
|
map (
|
|
{
|
|
name,
|
|
config ? { },
|
|
rootPattern ? null,
|
|
...
|
|
}:
|
|
{
|
|
inherit name;
|
|
config = config // (if (builtins.isNull rootPattern) then { } else { inherit rootPattern; });
|
|
}
|
|
) cfg.lsp.servers
|
|
)
|
|
}
|
|
|
|
for _, server in ipairs(servers) do
|
|
local server_config = server.config
|
|
server_config.on_attach = on_attach
|
|
server_config.capabilities = capabilities
|
|
server_config.flags = {
|
|
debounce_text_changes = 150
|
|
}
|
|
|
|
if server_config.rootPattern then
|
|
server_config.root_dir = util.root_pattern(
|
|
unpack(server_config.rootPattern)
|
|
)
|
|
end
|
|
|
|
lspconfig[server.name].setup(server_config)
|
|
end
|
|
'';
|
|
|
|
};
|
|
|
|
treesitterPluginConfig = with pkgs.vimPlugins; {
|
|
plugin = nvim-treesitter.withPlugins cfg.withTreesitterPlugins;
|
|
config = ''
|
|
require('nvim-treesitter.configs').setup(${
|
|
toLua {
|
|
highlight = {
|
|
enable = true;
|
|
additional_vim_regex_highlighting = false;
|
|
};
|
|
}
|
|
});
|
|
'';
|
|
};
|
|
|
|
formatterPluginConfig = with pkgs.vimPlugins; {
|
|
plugin = formatter-nvim;
|
|
config = ''
|
|
local formatter = require('formatter')
|
|
local util = require('formatter.util')
|
|
|
|
formatter.setup({
|
|
filetype = {
|
|
${lib.strings.concatMapStringsSep ",\n " (
|
|
{
|
|
exe,
|
|
args,
|
|
stdin,
|
|
no_append,
|
|
filetypes,
|
|
...
|
|
}:
|
|
lib.strings.concatMapStringsSep ",\n " (filetype: ''
|
|
[${toLua filetype}] = {
|
|
function ()
|
|
return {
|
|
exe = ${toLua exe},
|
|
args = {
|
|
${lib.strings.concatMapStringsSep ",\n " (
|
|
arg:
|
|
if arg == "<<FILE>>" then "util.escape_path(util.get_current_buffer_file_path())" else toLua arg
|
|
) (args "<<FILE>>")}
|
|
},
|
|
stdin = ${toLua stdin},
|
|
no_append = ${toLua no_append},
|
|
}
|
|
end
|
|
}
|
|
'') filetypes
|
|
) cfg.formatters}
|
|
}
|
|
})
|
|
${generateAutoCommand {
|
|
event = "BufWritePost";
|
|
pattern = lib.lists.concatMap ({ globs, ... }: globs) cfg.formatters;
|
|
command = "FormatWrite";
|
|
group = "FormatAutogroup";
|
|
}}
|
|
'';
|
|
};
|
|
|
|
customTypes =
|
|
let
|
|
inherit (lib) mkOption mkEnableOption types;
|
|
in
|
|
{
|
|
mapping = types.submodule {
|
|
options = {
|
|
rhs = mkOption { type = types.str; };
|
|
lua = mkEnableOption { };
|
|
options = mkOption {
|
|
type = types.nullOr (
|
|
types.submodule {
|
|
options = {
|
|
buffer = mkOption {
|
|
type = types.nullOr (types.either types.int types.bool);
|
|
default = null;
|
|
};
|
|
nowait = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
silent = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
expr = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
script = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
unique = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
replace_keycodes = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
};
|
|
}
|
|
);
|
|
default = { };
|
|
};
|
|
};
|
|
};
|
|
|
|
modeMappings = types.attrsOf (types.either types.str customTypes.mapping);
|
|
|
|
mappings = types.submodule {
|
|
options = {
|
|
normal = mkOption {
|
|
type = customTypes.modeMappings;
|
|
default = { };
|
|
};
|
|
insert = mkOption {
|
|
type = customTypes.modeMappings;
|
|
default = { };
|
|
};
|
|
visual = mkOption {
|
|
type = customTypes.modeMappings;
|
|
default = { };
|
|
};
|
|
command = mkOption {
|
|
type = customTypes.modeMappings;
|
|
default = { };
|
|
};
|
|
select = mkOption {
|
|
type = customTypes.modeMappings;
|
|
default = { };
|
|
};
|
|
};
|
|
};
|
|
|
|
plugin = types.either types.package (
|
|
types.submodule {
|
|
options = {
|
|
plugin = mkOption { type = types.package; };
|
|
dependencies = mkOption {
|
|
type = types.listOf customTypes.plugin;
|
|
default = [ ];
|
|
};
|
|
mappings = mkOption {
|
|
type = customTypes.mappings;
|
|
default = { };
|
|
};
|
|
config = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
};
|
|
};
|
|
}
|
|
);
|
|
|
|
autoCommand = types.submodule {
|
|
options = {
|
|
event = mkOption { type = types.either types.str (types.listOf types.str); };
|
|
pattern = mkOption { type = types.either types.str (types.listOf types.str); };
|
|
command = mkOption { type = types.str; };
|
|
};
|
|
};
|
|
|
|
lspServer = types.submodule {
|
|
options = {
|
|
name = mkOption { type = types.str; };
|
|
config = mkOption {
|
|
type = types.attrs;
|
|
default = { };
|
|
};
|
|
package = mkOption {
|
|
type = types.nullOr types.package;
|
|
default = null;
|
|
};
|
|
rootPattern = mkOption {
|
|
type = types.nullOr (types.listOf types.str);
|
|
default = null;
|
|
};
|
|
};
|
|
};
|
|
|
|
formatter = types.submodule {
|
|
options = {
|
|
filetypes = mkOption { type = types.listOf types.str; };
|
|
globs = mkOption { type = types.listOf types.str; };
|
|
exe = mkOption { type = types.either types.path types.str; };
|
|
args = mkOption {
|
|
type = types.functionTo (types.listOf types.str);
|
|
default = _: [ ];
|
|
};
|
|
stdin = mkEnableOption { };
|
|
no_append = mkEnableOption { };
|
|
};
|
|
};
|
|
};
|
|
|
|
generateMappings =
|
|
mappings:
|
|
lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (
|
|
mode: modeMappings:
|
|
lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (
|
|
name: value:
|
|
|
|
let
|
|
mapping = {
|
|
lhs = name;
|
|
lua = false;
|
|
options = { };
|
|
} // (if builtins.isString value then { rhs = value; } else value);
|
|
args = [
|
|
(toLua (builtins.substring 0 1 mode))
|
|
(toLua mapping.lhs)
|
|
(if mapping.lua then mapping.rhs else toLua mapping.rhs)
|
|
(toLua (
|
|
builtins.listToAttrs (
|
|
builtins.filter ({ value, ... }: !isNull (value)) (lib.attrsets.attrsToList mapping.options)
|
|
)
|
|
))
|
|
];
|
|
in
|
|
"vim.keymap.set(${lib.strings.concatStringsSep ", " args})"
|
|
) modeMappings
|
|
)
|
|
) mappings
|
|
);
|
|
|
|
generateAutoCommand =
|
|
{
|
|
event,
|
|
pattern,
|
|
command,
|
|
group ? null,
|
|
}:
|
|
''
|
|
vim.api.nvim_create_autocmd(${toLua event}, {
|
|
pattern = ${toLua pattern},
|
|
command = ${toLua command},
|
|
group = ${
|
|
if isNull group then
|
|
toLua group
|
|
else
|
|
''
|
|
vim.api.nvim_create_augroup(${toLua group}, { clear = true })
|
|
''
|
|
},
|
|
})
|
|
'';
|
|
|
|
generateAutoCommands = autoCommands: lib.strings.concatLines (map generateAutoCommand autoCommands);
|
|
|
|
generateSignDefinitions =
|
|
signs:
|
|
lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (
|
|
name: value:
|
|
let
|
|
hl = "DiagnosticSign${
|
|
lib.strings.toUpper (builtins.substring 0 1 name)
|
|
}${builtins.substring 1 (-1) name}";
|
|
in
|
|
"vim.fn.sign_define(${toLua hl}, ${
|
|
toLua {
|
|
text = value;
|
|
texthl = hl;
|
|
numhl = "";
|
|
}
|
|
})"
|
|
) signs
|
|
);
|
|
in
|
|
{
|
|
options.programs.neovim =
|
|
let
|
|
inherit (lib) mkOption types;
|
|
in
|
|
{
|
|
mappings = mkOption {
|
|
type = customTypes.mappings;
|
|
default = { };
|
|
};
|
|
plug = mkOption {
|
|
type = types.listOf customTypes.plugin;
|
|
default = [ ];
|
|
};
|
|
leader = mkOption {
|
|
type = types.str;
|
|
default = "\\";
|
|
};
|
|
options = mkOption {
|
|
type = types.attrs;
|
|
default = { };
|
|
};
|
|
snippets = mkOption {
|
|
type = types.attrsOf types.path;
|
|
default = { };
|
|
};
|
|
autoCommands = mkOption {
|
|
type = types.listOf customTypes.autoCommand;
|
|
default = [ ];
|
|
};
|
|
env = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = { };
|
|
};
|
|
signs = {
|
|
error = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
};
|
|
warning = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
};
|
|
info = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
};
|
|
hint = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
};
|
|
};
|
|
lsp = {
|
|
servers = mkOption {
|
|
type = types.listOf customTypes.lspServer;
|
|
default = [ ];
|
|
};
|
|
mappings = {
|
|
buf = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = { };
|
|
};
|
|
diagnostic = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = { };
|
|
};
|
|
};
|
|
};
|
|
withTreesitterPlugins = mkOption {
|
|
type = types.functionTo (types.listOf types.package);
|
|
default = _: [ ];
|
|
};
|
|
formatters = mkOption {
|
|
type = types.listOf customTypes.formatter;
|
|
default = [ ];
|
|
};
|
|
};
|
|
|
|
config = {
|
|
programs.neovim = {
|
|
extraLuaConfig = lib.strings.concatLines [
|
|
"local Plugins = {}"
|
|
(generateMappings cfg.mappings)
|
|
(generateAutoCommands cfg.autoCommands)
|
|
(lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (name: value: "vim.env.${name} = ${toLua value}") cfg.env
|
|
))
|
|
(lib.strings.concatLines (
|
|
lib.attrsets.mapAttrsToList (name: value: "vim.opt.${name} = ${toLua value}") cfg.options
|
|
))
|
|
(generateSignDefinitions cfg.signs)
|
|
"vim.g.mapleader = ${toLua cfg.leader}"
|
|
''
|
|
local undodir = ${
|
|
toLua (
|
|
if builtins.hasAttr "undodir" cfg.options then
|
|
cfg.options.undodir
|
|
else
|
|
"${config.xdg.cacheHome}/nvim/undo"
|
|
)
|
|
}
|
|
vim.opt.undodir = undodir
|
|
vim.fn.mkdir(undodir, 'p')
|
|
''
|
|
];
|
|
plugins = lib.lists.concatMap buildPluginConfig (
|
|
cfg.plug
|
|
++ [
|
|
lspPluginConfig
|
|
treesitterPluginConfig
|
|
formatterPluginConfig
|
|
]
|
|
);
|
|
vimAlias = lib.mkForce false;
|
|
vimdiffAlias = lib.mkForce false;
|
|
};
|
|
|
|
xdg.configFile = (
|
|
lib.attrsets.mapAttrs' (name: value: {
|
|
name = "nvim/${name}";
|
|
value = {
|
|
source = value;
|
|
};
|
|
}) cfg.snippets
|
|
);
|
|
|
|
home.shellAliases =
|
|
let
|
|
nvim = ''nvim --listen "$XDG_RUNTIME_DIR/nvimsocket"'';
|
|
in
|
|
{
|
|
inherit nvim;
|
|
vim = nvim;
|
|
vimdiff = "${nvim} -d";
|
|
};
|
|
};
|
|
}
|