{ config, pkgs, lib, ... }: { imports = [ ./options.nix ]; /* VIM config backup: syntax on au FileType markdown set colorcolumn=73 textwidth=72 au FileType gitcommit set colorcolumn=73 highlight NormalFloat guibg=NONE au BufReadPre * set foldmethod=syntax au BufReadPost * folddoc foldopen! autocmd BufReadPost * if @% !~# '\.git[\/\\]COMMIT_EDITMSG$' && line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif */ # welcome to my cursed DSL programs.neovim = let notlua = config.notlua; notlua-nvim = notlua.neovim { inherit (config.programs.neovim) plugins extraLuaPackages; }; inherit (notlua.keywords) CALL RAW PROP SET LET DEFUN IF APPLY OR EQ RETURN ELSE ATTR MCALL LETREC; inherit (notlua.utils) compile; inherit (notlua-nvim.stdlib) vim string require print; inherit (notlua-nvim.keywords) REQ REQ' REQLET; in let vimcmd = name: CALL (RAW "vim.cmd.${name}"); vimg = name: PROP vim.g name; keymapSetSingle = opts@{ mode, lhs, rhs, noremap ? true, silent ? true, ... }: let opts'' = opts // { inherit noremap silent; }; opts' = lib.filterAttrs (k: v: k != "keys" && k != "mode" && k != "lhs" && k != "rhs" && k != "desc" # defaults to false && ((k != "silent" && k != "noremap") || (builtins.isBool v && v))) opts''; in vim.keymap.set mode lhs rhs opts'; keymapSetMulti = opts@{ keys, mode, noremap ? true, silent ? true, ... }: let opts'' = opts // { inherit noremap silent; }; opts' = lib.filterAttrs (k: v: k != "keys" && k != "lhs" && k != "rhs" && k != "desc" # defaults to false && ((k != "silent" && k != "noremap") || (builtins.isBool v && v))) opts''; in (lib.mapAttrsToList (k: {rhs, desc}: keymapSetSingle (opts' // { lhs = k; inherit rhs; })) keys) ++ [ (which-key.register (lib.mapAttrs (k: v: [v.rhs v.desc]) keys) opts') ]; keymapSetNs = args: keymapSetMulti (args // { mode = "n"; }); kmSetNs = keys: keymapSetNs { inherit keys; }; keymapSetVs = args: keymapSetMulti (args // { mode = "v"; }); kmSetVs = keys: keymapSetVs { inherit keys; }; which-key = REQ "which-key"; luasnip = REQ "luasnip"; cmp = REQ "cmp"; in { enable = true; defaultEditor = true; package = pkgs.neovim-unwrapped; extraPackages = with pkgs; [ rust-analyzer nodePackages_latest.bash-language-server shellcheck nodePackages_latest.typescript-language-server nodePackages_latest.svelte-language-server clang-tools_latest nodePackages_latest.vscode-langservers-extracted nil marksman taplo ripgrep (python3.withPackages (p: with p; [ python-lsp-server pylsp-mypy python-lsp-server.optional-dependencies.pyflakes python-lsp-server.optional-dependencies.mccabe python-lsp-server.optional-dependencies.pycodestyle ])) ]; # extraPython3Packages = pyPkgs: with pyPkgs; [ # ]; viAlias = true; vimAlias = true; vimdiffAlias = true; extraLuaConfig = (compile "main" [ (SET (vimg "vimsyn_embed") "l") (LET (vim.api.nvim_create_augroup "nvimrc" { clear = true; }) (group: lib.mapAttrsToList (k: v: vim.api.nvim_create_autocmd k { inherit group; callback = v; }) { BufReadPre = DEFUN (SET vim.o.foldmethod "syntax"); BufEnter = { buf, ... }: (LET (vim.filetype.match { inherit buf; }) (filetype: [ (IF (APPLY OR (map (EQ filetype) [ "gitcommit" "markdown" ])) (LET vim.o.colorcolumn (old_colorcolumn: [ (SET vim.o.colorcolumn "73") (vim.api.nvim_create_autocmd "BufLeave" { callback = DEFUN [ (SET vim.o.colorcolumn old_colorcolumn) # return true = delete autocommand (RETURN true) ]; }) ]))) (IF (EQ filetype "markdown") (LET vim.o.textwidth (old_textwidth: [ (SET vim.o.textwidth 72) (vim.api.nvim_create_autocmd "BufLeave" { callback = DEFUN [ (SET vim.o.textwidth old_textwidth) (RETURN true) ]; }) ]))) ])); BufWinEnter = { buf, ... }: (LET (vim.filetype.match { inherit buf; }) (filetype: [ (vimcmd "folddoc" "foldopen!") (IF (EQ filetype "gitcommit") (CALL vim.cmd { cmd = "normal"; bang = true; args = [ "gg" ]; }) ELSE (CALL vim.cmd { cmd = "normal"; bang = true; args = [ "g`\"" ]; }) ) ])); } )) ]); plugins = let ps = pkgs.vimPlugins; in map (x: if x?config && x?plugin then { type = "lua"; } // x else x) [ ps.vim-svelte # TODO remove on next nvim update (0.8.3/0.9) ps.vim-nix { plugin = pkgs.vimUtils.buildVimPluginFrom2Nix { pname = "vscode-nvim"; version = "2023-02-10"; src = pkgs.fetchFromGitHub { owner = "Mofiqul"; repo = "vscode.nvim"; rev = "db9ee339b5556aa832ca58871fd18f9467a18520"; sha256 = "sha256-X2IgIjO5NNq7vJdl09hBY1TFqHlsfF1xfllKr4osILI="; }; }; config = compile "vscode_nvim" [ ((REQ "vscode").setup { transparent = true; color_overrides = { vscGray = "#745b5f"; vscViolet = "#${config.colors.magenta}"; vscBlue = "#6ddfd8"; vscDarkBlue = "#${config.colors.blue}"; vscGreen = "#${config.colors.green}"; vscBlueGreen = "#73bf88"; vscLightGreen = "#6acf6e"; vscRed = "#${config.colors.red}"; vscOrange = "#e89666"; vscLightRed = "#e64e4e"; vscYellowOrange = "#e8b166"; vscYellow = "#${config.colors.yellow}"; vscPink = "#cf83c4"; }; }) (vim.api.nvim_set_hl 0 "NormalFloat" { bg = "NONE"; }) ]; } { plugin = ps.nvim-web-devicons; config = compile "nvim_web_devicons" ((REQ "nvim-web-devicons").setup {}); } { plugin = ps.nvim-tree-lua; config = compile "nvim_tree_lua" (REQLET "nvim-tree" "nvim-tree.api" (nvim-tree: nvim-tree-api: [ (SET (vimg "loaded_netrw") 1) (SET (vimg "loaded_netrwPlugin") 1) (SET vim.o.termguicolors true) (nvim-tree.setup {}) # :help nvim-tree-setup (kmSetNs { "" = { rhs = nvim-tree-api.tree.toggle; desc = "Toggle NvimTree"; }; }) ])); } ps.vim-sleuth ps.luasnip { plugin = ps.nvim-cmp; config = let border = (name: [ [ "╭" name ] [ "─" name ] [ "╮" name ] [ "│" name ] [ "╯" name ] [ "─" name ] [ "╰" name ] [ "│" name ] ]); in compile "nvim_cmp" (REQLET "cmp" "lspkind" (cmp: lspkind: # call is required because cmp.setup is a table (CALL cmp.setup { snippet = { expand = { body, ... }: luasnip.lsp_expand body {}; }; view = { }; window = { completion = { border = border "CmpBorder"; winhighlight = "Normal:CmpPmenu,CursorLine:PmenuSel,Search:None"; }; documentation = { border = border "CmpDocBorder"; }; }; formatting = { format = _: vim_item: let kind = PROP vim_item "kind"; in [ (SET kind (string.format "%s %s" (ATTR lspkind kind) kind)) (RETURN vim_item) ]; }; mapping = { "" = cmp.mapping.select_prev_item {}; "" = cmp.mapping.select_next_item {}; "" = cmp.mapping.complete {}; "" = cmp.mapping.close {}; "" = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace; select = false; }; "" = CALL cmp.mapping (fallback: (IF (CALL cmp.visible) (CALL cmp.select_next_item) (CALL luasnip.expand_or_jumpable) (vim.api.nvim_feedkeys (vim.api.nvim_replace_termcodes "luasnip-expand-or-jump" true true true) "" false) ELSE (CALL fallback))) [ "i" "s" ]; "" = CALL cmp.mapping (fallback: (IF (CALL cmp.visible) (CALL cmp.select_prev_item) (luasnip.jumpable (-1)) (vim.api.nvim_feedkeys (vim.api.nvim_replace_termcodes "luasnip-jump-prev" true true true) "" false) ELSE (CALL fallback))) [ "i" "s" ]; }; sources = cmp.config.sources [ { name = "nvim_lsp"; } { name = "luasnip"; } ]; }) )); } ps.lspkind-nvim ps.cmp_luasnip ps.cmp-nvim-lsp { plugin = ps.nvim-autopairs; config = compile "nvim_autopairs" (REQLET "nvim-autopairs.completion.cmp" "nvim-autopairs" (cmp-autopairs: nvim-autopairs: [ (nvim-autopairs.setup { disable_filetype = [ "TelescopePrompt" "vim" ]; }) (MCALL cmp.event "on" "confirm_done" (cmp-autopairs.on_confirm_done {})) ])); } { plugin = ps.comment-nvim; config = compile "comment_nvim" [ ((REQ "Comment").setup {}) (kmSetNs { "/" = { rhs = PROP (REQ "Comment.api").toggle "linewise.current"; desc = "Comment current line"; }; }) (kmSetVs { "/" = { rhs = "lua require('Comment.api').toggle.linewise(vim.fn.visualmode())"; desc = "Comment selection"; }; }) ]; } { plugin = ps.nvim-lspconfig; config = compile "nvim_lspconfig" ( let lsp = name: builtins.seq # ensure an lsp exists (otherwise lspconfig will still create an empty config for some reason) (REQ "lspconfig.server_configurations.${name}") # metatables, son! they harden in response to physical trauma (REQ' (PROP (require "lspconfig") name)); in [ # See `:help vim.diagnostic.*` for documentation on any of the below functions (kmSetNs { "e" = { rhs = vim.diagnostic.open_float; desc = "Show diagnostics in a floating window."; }; "[d" = { rhs = vim.diagnostic.goto_prev; desc = "Move to the previous diagnostic in the current buffer."; }; "]d" = { rhs = vim.diagnostic.goto_next; desc = "Get the next diagnostic closest to the cursor position."; }; "q" = { rhs = vim.diagnostic.setloclist; desc = "Add buffer diagnostics to the location list."; }; }) (LET # LET on_attach (client: bufnr: [ # Enable completion triggered by (vim.api.nvim_buf_set_option bufnr "omnifunc" "v:lua.vim.lsp.omnifunc") # Mappings. # See `:help vim.lsp.*` for documentation on any of the below functions (kmSetNs { "gD" = { rhs = vim.lsp.buf.declaration; desc = "Jumps to the declaration of the symbol under the cursor."; }; "gd" = { rhs = vim.lsp.buf.definition; desc = "Jumps to the definition of the symbol under the cursor."; }; "K" = { rhs = vim.lsp.buf.hover; desc = "Displays hover information about the symbol under the cursor in a floating window."; }; "gi" = { rhs = vim.lsp.buf.implementation; desc = "Lists all the implementations for the symbol under the cursor in the quickfix window."; }; "" = { rhs = vim.lsp.buf.signature_help; desc = "Displays signature information about the symbol under the cursor in a floating window."; }; "wa" = { rhs = vim.lsp.buf.add_workspace_folder; desc = "Add a folder to the workspace folders."; }; "wr" = { rhs = vim.lsp.buf.remove_workspace_folder; desc = "Remove a folder from the workspace folders."; }; "wl" = { rhs = DEFUN (print (CALL vim.inspect (CALL vim.lsp.buf.list_workspace_folders))); desc = "List workspace folders."; }; "D" = { rhs = vim.lsp.buf.type_definition; desc = "Jumps to the definition of the type of the symbol under the cursor."; }; "rn" = { rhs = vim.lsp.buf.rename; desc = "Rename old_fname to new_fname"; }; "ca" = { rhs = vim.lsp.buf.code_action; desc = "Selects a code action available at the current cursor position."; }; "gr" = { rhs = vim.lsp.buf.references; desc = "Lists all the references to the symbol under the cursor in the quickfix window."; }; "f" = { rhs = (DEFUN (vim.lsp.buf.format {async = true;})); desc = "Formats a buffer."; }; }) ]) # LET rust_settings { rust-analyzer = { assist.emitMustUse = true; cargo.buildScripts.enable = true; check.command = "clippy"; procMacro.enable = true; }; } # LET capabilities (vim.tbl_extend "keep" ((REQ "cmp_nvim_lsp").default_capabilities {}) (CALL vim.lsp.protocol.make_client_capabilities)) # BEGIN (on_attach: rust_settings: capabilities: [ (LETREC # LETREC on_attach_rust (on_attach_rust: client: bufnr: [ (vim.api.nvim_create_user_command "RustAndroid" (opts: [ (vim.lsp.set_log_level "debug") ((lsp "rust_analyzer").setup { on_attach = on_attach_rust; inherit capabilities; settings = vim.tbl_deep_extend "keep" config.rustAnalyzerAndroidSettings rust_settings; }) ]) {}) (CALL on_attach client bufnr) ]) # BEGIN (let setupLsp' = { name, settings ? {} }: (lsp name).setup { inherit on_attach capabilities settings; }; setupLsp = args: setupLsp' (if builtins.isString args then { name = args; } else args); in (on_attach_rust: [ # (vim.lsp.set_log_level "debug") (map setupLsp [ # see https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md "bashls" "clangd" # https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md { name = "pylsp"; settings = { pylsp.plugins.pylsp_mypy.enabled = true; }; } "svelte" "html" "cssls" "tsserver" "jsonls" "nil_ls" "taplo" "marksman" ]) ((lsp "rust_analyzer").setup { on_attach = on_attach_rust; settings = rust_settings; inherit capabilities; }) ]))) # END ])) # END ]); } { plugin = ps.which-key-nvim; config = compile "which_key_nvim" [ (SET vim.o.timeout true) (SET vim.o.timeoutlen 500) (which-key.setup {}) ]; } ]; }; }