diff --git a/README.md b/README.md index 8bdfe1c..1be348c 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,24 @@ Add the following to the `inputs` section of your flake.nix: ```nix - nixvim-config.url = "github:homefree/nixvim-config"; +inputs = { + nixvim-config.url = "github:homefree/nixvim-config"; +}; ``` -And add the following module: +And add the following to the outputs: ```nix - @TODO +outputs = { ... }@inputs: { + nixosConfigurations = { + = inputs.nixpkgs.lib.nixosSystem { + modules = [ + inputs.nixvim-config.nixosModules.default + { + nixvim-config.enable = true; + } + ]; + }; + }; +} ``` diff --git a/autocmd.nix b/autocmd.nix new file mode 100644 index 0000000..30968c7 --- /dev/null +++ b/autocmd.nix @@ -0,0 +1,80 @@ +{ ... }: +{ + programs.nixvim = { + autoCmd = [ + ## Close nvim on last buffer closed, not leaving neovim-tree open + { + event = [ "BufEnter" ]; + pattern = [ "NvimTree_*" ]; + callback = { + __raw = '' + function() + local layout = vim.api.nvim_call_function("winlayout", {}) + if layout[1] == "leaf" and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(layout[2]), "filetype") == "NvimTree" and layout[3] == nil then vim.cmd("confirm quit") end + end + ''; + }; + } + ## Go to same line in file next time it is open + { + event = [ "BufReadPost" ]; + pattern = [ "*" ]; + callback = { + __raw = '' + function() + if vim.fn.line("'\"") > 1 and vim.fn.line("'\"") <= vim.fn.line("$") then + vim.api.nvim_exec("normal! g'\"",false) + end + end + ''; + }; + } + ## Highlight tabs and trailing whitespace + { + event = [ "BufEnter" ]; + pattern = [ "*" ]; + callback = { + __raw = '' + function() + vim.cmd([[ + if exists('w:extratabs') + call matchdelete(w:extratabs) + unlet w:extratabs + endif + if exists('w:trailingwhitespace') + call matchdelete(w:trailingwhitespace) + unlet w:trailingwhitespace + endif + highlight ExtraTabs ctermbg=red guibg=red + highlight TrailingWhitespace ctermbg=red guibg=red + if &ft != 'help' + let w:extratabs=matchadd('ExtraTabs', '\t\+') + let w:trailingwhitespace=matchadd('TrailingWhitespace', '\s\+$') + endif + ]]) + end + ''; + }; + } + ## Trim tailing whitespace on save + { + event = [ "BufWritePre" ]; + pattern = [ "*" ]; + callback = { + __raw = '' + function() + vim.cmd([[ + if &ft =~ 'javascript\|html\|jade\|json\|css\|less\|php\|python\|sh\|c\|cpp\|markdown\|yaml\|vim\|nix' + :%s/\s\+$//e + elseif expand('%:t') =~ '\.gltf$' || expand('%:t') =~ '\.glsl$' + :%s/\s\+$//e + endif + ]]) + end + ''; + }; + } + ]; + }; +} + diff --git a/default.nix b/default.nix index 8391d00..202503f 100644 --- a/default.nix +++ b/default.nix @@ -1,6 +1,8 @@ -{ ... }: +{ inputs, ... }: { imports = [ + inputs.nixvim.nixosModules.nixvim + ./module.nix ./nixvim.nix ]; } diff --git a/flake.lock b/flake.lock index 92295d4..5999137 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,6 @@ { "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1738574474, - "narHash": "sha256-rvyfF49e/k6vkrRTV4ILrWd92W+nmBDfRYZgctOyolQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "fecfeb86328381268e29e998ddd3ebc70bbd7f7c", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-24.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } + "root": {} }, "root": "root", "version": 7 diff --git a/flake.nix b/flake.nix index 0512372..9b96b84 100644 --- a/flake.nix +++ b/flake.nix @@ -3,33 +3,29 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - }; - outputs = { self, nixpkgs, ... } @ inputs: - # let - # system = "x86_64-linux"; - # in - { - packages.x86_64-linux = let - pkgs = import "${nixpkgs}" { - # system = "x86_64-linux"; - }; - - in with pkgs; { - nixvim-config = callPackage ./default.nix { - inherit builtins; - }; + nixvim = { + url = "github:nix-community/nixvim"; + inputs.nixpkgs.follows = "nixpkgs"; }; + }; + + outputs = { nixpkgs, ... } @ inputs: + let + # system = "x86_64-linux"; + pkgs = import "${nixpkgs}" { + # system = "x86_64-linux"; + }; + in + { nixosModules = rec { - homefree = import ./default.nix { - # inherit system; - }; - imports = [ ]; - default = homefree; - lan-client = import ./lan-client.nix { + nixvim-config = import ./default.nix { + inherit inputs; # inherit system; + inherit pkgs; }; + default = nixvim-config; }; }; } diff --git a/keymaps.nix b/keymaps.nix new file mode 100644 index 0000000..572efe0 --- /dev/null +++ b/keymaps.nix @@ -0,0 +1,259 @@ +{ ... }: +{ + programs.nixvim = { + keymaps = [ + # ----------------------------------------------------- + # nvim-tree + # ----------------------------------------------------- + + ## Go to current buffer's file in nvim-tree + { + mode = [ "n" ]; + key = ",n"; + action = ":NvimTreeFindFile"; + } + ## Toggle nvim-tree visibility + { + mode = [ "n" ]; + key = ",m"; + action = ":NvimTreeToggle"; + } + + # ----------------------------------------------------- + # buffer manipulation + # ----------------------------------------------------- + + ## Next Buffer + { + key = ""; + action = ":bn"; + options = { noremap = true; }; + } + ## Previous Buffer + { + key = ""; + action = ":bp"; + options = { noremap = true; }; + } + ## Close Buffer + { + key = ""; + action = ":bd"; + options = { noremap = true; }; + } + ## Force Close Buffer + { + key = ""; + action = ":bd!"; + options = { noremap = true; }; + } + ## New Tab + { + key = "t"; + action = ":tabnew split"; + options = { noremap = true; }; + } + + # ----------------------------------------------------- + # Telescope + # ----------------------------------------------------- + + ## Lists files in your current working directory, respects .gitignore + { + mode = [ "n" ]; + key = "ff"; + action = "Telescope find_files"; + options = { noremap = true; }; + } + ## Finds files by filename + { + mode = [ "n" ]; + key = ""; + action = "Telescope find_files"; + # action = "FzfLua files"; + options = { noremap = true; }; + } + # Search for a string in your current working directory and get results live as you type, respects .gitignore. (Requires ripgrep) + { + mode = [ "n" ]; + key = "fg"; + action = "Telescope live_grep"; + # action = "FzfLua live_grep"; + options = { noremap = true; }; + } + # Search file contents + { + mode = [ "n" ]; + key = ""; + action = "Telescope live_grep"; + # action = "FzfLua live_grep"; + options = { noremap = true; }; + } + # Lists open buffers in current neovim instance + { + mode = [ "n" ]; + key = "db"; + action = "Telescope buffers"; + # action = "FzfLua buffers"; + options = { noremap = true; }; + } + # Lists available help tags and opens a new window with the relevant help info on + { + mode = [ "n" ]; + key = "fh"; + action = "Telescope help_tags"; + # action = "FzfLua helptags"; + options = { noremap = true; }; + } + # Lists manpage entries, opens them in a help window on + { + mode = [ "n" ]; + key = "fm"; + action = "Telescope man_pages"; + # action = "FzfLua manpages"; + options = { noremap = true; }; + } + # Lists previously open files + { + mode = [ "n" ]; + key = "fp"; + action = "Telescope oldfiles"; + # action = "FzfLua oldfiles"; + options = { noremap = true; }; + } + # Lists previously open files, Maps to ctrl-/ + { + mode = [ "n" ]; + key = ""; + action = "Telescope oldfiles"; + # action = "FzfLua oldfiles"; + options = { noremap = true; }; + } + # Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on + { + mode = [ "n" ]; + key = "fs"; + action = "Telescope spell_suggest"; + # action = "FzfLua spell_suggest"; + options = { noremap = true; }; + } + # Lists LSP references for iword under the cursor + { + mode = [ "n" ]; + key = "fr"; + action = "Telescope lsp_references"; + # action = "FzfLua lsp_references"; + options = { noremap = true; }; + } + # Lists LSP incoming calls for word under the cursor + { + mode = [ "n" ]; + key = "fi"; + action = "Telescope lsp_incoming_calls"; + # action = "FzfLua lsp_incoming_calls"; + options = { noremap = true; }; + } + # Lists LSP outgoing calls for word under the cursor + { + mode = [ "n" ]; + key = "fo"; + action = "Telescope lsp_outgoing_calls"; + # action = "FzfLua lsp_outgoing_calls"; + options = { noremap = true; }; + } + # Dynamically Lists LSP for all workspace symbols + { + mode = [ "n" ]; + key = "fw"; + action = "Telescope lsp_dynamic_workspace_symbols"; + # action = "FzfLua lsp_workspace_symbols"; + options = { noremap = true; }; + } + # Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope + { + mode = [ "n" ]; + key = "fd"; + action = "Telescope lsp_definitions"; + options = { noremap = true; }; + } + # Got to previous error + { + mode = [ "n" ]; + key = "[d"; + action = "lua vim.diagnostic.goto_prev()"; + options = { noremap = true; silent = true; }; + } + { + mode = [ "n" ]; + key = ",k"; + action = "lua vim.diagnostic.goto_prev()"; + options = { noremap = true; silent = true; }; + } + # Got to next error + { + mode = [ "n" ]; + key = "]d"; + action = "lua vim.diagnostic.goto_next()"; + options = { noremap = true; silent = true; }; + } + { + mode = [ "n" ]; + key = ",j"; + action = "lua vim.diagnostic.goto_next()"; + options = { noremap = true; silent = true; }; + } + + ## Other Telescope options: + ## git_files search only files in git, respects .gitignore + ## oldfiles previously opened files + ## command_history + ## search_history + ## man_pages + ## resume lists the results including multi-selections of the previous + ## picker + + # ----------------------------------------------------- + # Diff + # ----------------------------------------------------- + + { + mode = [ "n" ]; + key = ",d"; + ## @TODO: This doesn't work + action = '' + function() + if next(require('diffview.lib').views) == nil then + vim.cmd('DiffviewOpen origin') + else + vim.cmd('DiffviewClose') + end + end + ''; + options = { noremap = true; }; + } + + # ----------------------------------------------------- + # Bufferline + # ----------------------------------------------------- + + { + mode = [ "n" ]; + key = ""; + action = ":BufferLineCyclePrev"; + options = { noremap = true; silent = true; }; + } + { + mode = [ "n" ]; + key = ""; + action = ":BufferLineCycleNex"; + options = { noremap = true; silent = true; }; + } + { + mode = [ "n" ]; + key = ""; + action = ":bdelete!"; + options = { noremap = true; silent = true; }; + } + ]; + }; +} diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..326b737 --- /dev/null +++ b/module.nix @@ -0,0 +1,6 @@ +{ lib, ... }: +{ + options.nixvim-config = { + enable = lib.mkEnableOption "Enable nixvim-config"; + }; +} diff --git a/nixvim.nix b/nixvim.nix index 3cec966..3dcf8b8 100644 --- a/nixvim.nix +++ b/nixvim.nix @@ -1,6 +1,13 @@ -{ pkgs, ... }: +{ config, pkgs, ... }: { - environment.systemPackages = with pkgs; [ + imports = [ + ./options.nix + ./keymaps.nix + ./autocmd.nix + ./plugins + ]; + + environment.systemPackages = if config.nixvim-config.enable then with pkgs; [ ripgrep ## To get rid of checkhealth warnings @@ -9,912 +16,10 @@ mercurial ueberzugpp viu - ]; + ] else []; - environment.interactiveShellInit = '' + environment.interactiveShellInit = if config.nixvim-config.enable then '' alias vi='nvim' alias vim='nvim' - ''; - - programs.nixvim = { - enable = true; - - defaultEditor = true; - - ## ------------------------------------------------ - ## Options - ## ------------------------------------------------ - - globals = { - mapleader = " "; # global - maplocalleader = " "; # per buffer, e.g. can change behavior per filetype - ## To appropriately highlight codefences returned from denols - markdown_fenced_languages.__raw = '' - { - "ts=typescript" - } - ''; - }; - - opts = { - number = true; # Show line numbers - relativenumber = true; # Show relative line numbers - ruler = true; # displays line, column, and cursor position at bottom - wrap = false; # don't wrap lines - signcolumn = "yes"; # always show two column sign column on left - cursorline = true; # Highlight line cursor sits on - - undodir.__raw = "vim.fs.normalize('~/.local/share/nvim/undo/')"; - undofile = true; - - # ----------------------------------------------------- - # Backspace settings - # indent allow backspacing over autoindent - # eol allow backspacing over line breaks (join lines) - # start allow backspacing over the start of insert; CTRL-W and CTRL-U - # 0 same as ":set backspace=" (Vi compatible) - # 1 same as ":set backspace=indent,eol" - # 2 same as ":set backspace=indent,eol,start" - # ----------------------------------------------------- - - bs = "2"; - - # ----------------------------------------------------- - # Indentation settings - # ----------------------------------------------------- - - tabstop = 4; # number of spaces a tab counts for - shiftwidth = 4; # control how many columns text is indented with the reindent operations (<< and >>) and automatic C-style indentation. - expandtab = true; # Insert spaces when entering - softtabstop = 4; # Number of spaces that a counts for while performing editing operations, like inserting a or using . It "feels" like a tab though - ai = true; # auto indent - }; - - keymaps = [ - # ----------------------------------------------------- - # nvim-tree - # ----------------------------------------------------- - - ## Go to current buffer's file in nvim-tree - { - mode = [ "n" ]; - key = ",n"; - action = ":NvimTreeFindFile"; - } - ## Toggle nvim-tree visibility - { - mode = [ "n" ]; - key = ",m"; - action = ":NvimTreeToggle"; - } - - # ----------------------------------------------------- - # buffer manipulation - # ----------------------------------------------------- - - ## Next Buffer - { - key = ""; - action = ":bn"; - options = { noremap = true; }; - } - ## Previous Buffer - { - key = ""; - action = ":bp"; - options = { noremap = true; }; - } - ## Close Buffer - { - key = ""; - action = ":bd"; - options = { noremap = true; }; - } - ## Force Close Buffer - { - key = ""; - action = ":bd!"; - options = { noremap = true; }; - } - ## New Tab - { - key = "t"; - action = ":tabnew split"; - options = { noremap = true; }; - } - - # ----------------------------------------------------- - # Telescope - # ----------------------------------------------------- - - ## Lists files in your current working directory, respects .gitignore - { - mode = [ "n" ]; - key = "ff"; - action = "Telescope find_files"; - options = { noremap = true; }; - } - ## Finds files by filename - { - mode = [ "n" ]; - key = ""; - action = "Telescope find_files"; - # action = "FzfLua files"; - options = { noremap = true; }; - } - # Search for a string in your current working directory and get results live as you type, respects .gitignore. (Requires ripgrep) - { - mode = [ "n" ]; - key = "fg"; - action = "Telescope live_grep"; - # action = "FzfLua live_grep"; - options = { noremap = true; }; - } - # Search file contents - { - mode = [ "n" ]; - key = ""; - action = "Telescope live_grep"; - # action = "FzfLua live_grep"; - options = { noremap = true; }; - } - # Lists open buffers in current neovim instance - { - mode = [ "n" ]; - key = "db"; - action = "Telescope buffers"; - # action = "FzfLua buffers"; - options = { noremap = true; }; - } - # Lists available help tags and opens a new window with the relevant help info on - { - mode = [ "n" ]; - key = "fh"; - action = "Telescope help_tags"; - # action = "FzfLua helptags"; - options = { noremap = true; }; - } - # Lists manpage entries, opens them in a help window on - { - mode = [ "n" ]; - key = "fm"; - action = "Telescope man_pages"; - # action = "FzfLua manpages"; - options = { noremap = true; }; - } - # Lists previously open files - { - mode = [ "n" ]; - key = "fp"; - action = "Telescope oldfiles"; - # action = "FzfLua oldfiles"; - options = { noremap = true; }; - } - # Lists previously open files, Maps to ctrl-/ - { - mode = [ "n" ]; - key = ""; - action = "Telescope oldfiles"; - # action = "FzfLua oldfiles"; - options = { noremap = true; }; - } - # Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on - { - mode = [ "n" ]; - key = "fs"; - action = "Telescope spell_suggest"; - # action = "FzfLua spell_suggest"; - options = { noremap = true; }; - } - # Lists LSP references for iword under the cursor - { - mode = [ "n" ]; - key = "fr"; - action = "Telescope lsp_references"; - # action = "FzfLua lsp_references"; - options = { noremap = true; }; - } - # Lists LSP incoming calls for word under the cursor - { - mode = [ "n" ]; - key = "fi"; - action = "Telescope lsp_incoming_calls"; - # action = "FzfLua lsp_incoming_calls"; - options = { noremap = true; }; - } - # Lists LSP outgoing calls for word under the cursor - { - mode = [ "n" ]; - key = "fo"; - action = "Telescope lsp_outgoing_calls"; - # action = "FzfLua lsp_outgoing_calls"; - options = { noremap = true; }; - } - # Dynamically Lists LSP for all workspace symbols - { - mode = [ "n" ]; - key = "fw"; - action = "Telescope lsp_dynamic_workspace_symbols"; - # action = "FzfLua lsp_workspace_symbols"; - options = { noremap = true; }; - } - # Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope - { - mode = [ "n" ]; - key = "fd"; - action = "Telescope lsp_definitions"; - options = { noremap = true; }; - } - # Got to previous error - { - mode = [ "n" ]; - key = "[d"; - action = "lua vim.diagnostic.goto_prev()"; - options = { noremap = true; silent = true; }; - } - { - mode = [ "n" ]; - key = ",k"; - action = "lua vim.diagnostic.goto_prev()"; - options = { noremap = true; silent = true; }; - } - # Got to next error - { - mode = [ "n" ]; - key = "]d"; - action = "lua vim.diagnostic.goto_next()"; - options = { noremap = true; silent = true; }; - } - { - mode = [ "n" ]; - key = ",j"; - action = "lua vim.diagnostic.goto_next()"; - options = { noremap = true; silent = true; }; - } - - ## Other Telescope options: - ## git_files search only files in git, respects .gitignore - ## oldfiles previously opened files - ## command_history - ## search_history - ## man_pages - ## resume lists the results including multi-selections of the previous - ## picker - - # ----------------------------------------------------- - # Diff - # ----------------------------------------------------- - - { - mode = [ "n" ]; - key = ",d"; - ## @TODO: This doesn't work - action = '' - function() - if next(require('diffview.lib').views) == nil then - vim.cmd('DiffviewOpen origin') - else - vim.cmd('DiffviewClose') - end - end - ''; - options = { noremap = true; }; - } - - # ----------------------------------------------------- - # Bufferline - # ----------------------------------------------------- - - { - mode = [ "n" ]; - key = ""; - action = ":BufferLineCyclePrev"; - options = { noremap = true; silent = true; }; - } - { - mode = [ "n" ]; - key = ""; - action = ":BufferLineCycleNex"; - options = { noremap = true; silent = true; }; - } - { - mode = [ "n" ]; - key = ""; - action = ":bdelete!"; - options = { noremap = true; silent = true; }; - } - ]; - - autoCmd = [ - ## Close nvim on last buffer closed, not leaving neovim-tree open - { - event = [ "BufEnter" ]; - pattern = [ "NvimTree_*" ]; - callback = { - __raw = '' - function() - local layout = vim.api.nvim_call_function("winlayout", {}) - if layout[1] == "leaf" and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(layout[2]), "filetype") == "NvimTree" and layout[3] == nil then vim.cmd("confirm quit") end - end - ''; - }; - } - ## Go to same line in file next time it is open - { - event = [ "BufReadPost" ]; - pattern = [ "*" ]; - callback = { - __raw = '' - function() - if vim.fn.line("'\"") > 1 and vim.fn.line("'\"") <= vim.fn.line("$") then - vim.api.nvim_exec("normal! g'\"",false) - end - end - ''; - }; - } - ## Highlight tabs and trailing whitespace - { - event = [ "BufEnter" ]; - pattern = [ "*" ]; - callback = { - __raw = '' - function() - vim.cmd([[ - if exists('w:extratabs') - call matchdelete(w:extratabs) - unlet w:extratabs - endif - if exists('w:trailingwhitespace') - call matchdelete(w:trailingwhitespace) - unlet w:trailingwhitespace - endif - highlight ExtraTabs ctermbg=red guibg=red - highlight TrailingWhitespace ctermbg=red guibg=red - if &ft != 'help' - let w:extratabs=matchadd('ExtraTabs', '\t\+') - let w:trailingwhitespace=matchadd('TrailingWhitespace', '\s\+$') - endif - ]]) - end - ''; - }; - } - ## Trim tailing whitespace on save - { - event = [ "BufWritePre" ]; - pattern = [ "*" ]; - callback = { - __raw = '' - function() - vim.cmd([[ - if &ft =~ 'javascript\|html\|jade\|json\|css\|less\|php\|python\|sh\|c\|cpp\|markdown\|yaml\|vim\|nix' - :%s/\s\+$//e - elseif expand('%:t') =~ '\.gltf$' || expand('%:t') =~ '\.glsl$' - :%s/\s\+$//e - endif - ]]) - end - ''; - }; - } - ]; - - ## ------------------------------------------------ - ## Theme - ## ------------------------------------------------ - - colorschemes.tokyonight.enable = true; - - # colorschemes.gruvbox.enable = true; - ## Or: - # extraPlugins = [ pkgs.vimPlugins.gruvbox ]; - # colorscheme = "gruvbox"; - - ## ------------------------------------------------ - ## Included Plugins - ## ------------------------------------------------ - - plugins.bufferline = { - enable = true; - # extraOptions = { - settings = { - options = { - tabpages = true; - sidebar_filetypes = { - NvimTree = true; - }; - diagnostics = "nvim_lsp"; - always_show_bufferline = true; - }; - highlights = { - buffer_selected = { - # fg = "#ffffff"; - bold = true; - }; - }; - }; - }; - - plugins.comment.enable = true; - - plugins.diffview = { - enable = true; - }; - - plugins.fugitive.enable = true; - - plugins.gitsigns.enable = true; - - plugins.lightline.enable = true; - - plugins.lualine.enable = true; - - plugins.nix.enable = true; - - plugins.noice.enable = true; - - plugins.notify = { - ## disable, very annoying as notifications block content and - ## are part of the buffer rotation - enable = false; - topDown = false; - }; - - plugins.nvim-autopairs.enable = true; - - plugins.nvim-tree = { - enable = true; - extraOptions = { - actions = { - remove_file = { - close_window = false; - }; - }; - ## Keep tree open if already open when opening a tab - tab = { - sync = { - open = true; - close = true; - }; - }; - view = { - width = 30; - }; - renderer = { - group_empty = true; - }; - git = { - enable = true; - ignore = false; - timeout = 500; - }; - }; - }; - - plugins.rainbow-delimiters.enable = true; - - plugins.sleuth.enable = true; - - plugins.telescope = { - enable = true; - extensions.ui-select.enable = true; - settings = { - defaults = { - mappings = { - i = { - # One instead of two esc taps to exit telescope - "" = { - __raw = "require('telescope.actions').close"; - }; - # Ctrl-space is used by Tmux, so remap to Ctrl-e - "" = { - __raw = "require('telescope.actions').to_fuzzy_refine"; - }; - # "" = { - # __raw = "require('trouble.sources.telescope').open"; - # }; - }; - n = { - # "" = { - # __raw = "require('trouble.sources.telescope').open"; - # }; - }; - }; - }; - }; - }; - - plugins.fzf-lua = { - enable = true; - # profile = "telescope"; - settings = { - oldfiles = { - # In Telescope, when I used fr, it would load old buffers. - # fzf lua does the same, but by default buffers visited in the current - # session are not included. I use fr all the time to switch - # back to buffers I was just in. If you missed this from Telescope, - # give it a try. - include_current_session = true; - }; - preview = { - vertical = "down:90%"; - horizontal = "right:90%"; - }; - previewers = { - builtin = { - # fzf-lua is very fast, but it really struggled to preview a couple files - # in a repo. Those files were very big JavaScript files (1MB, minified, all on a single line). - # It turns out it was Treesitter having trouble parsing the files. - # With this change, the previewer will not add syntax highlighting to files larger than 100KB - # (Yes, I know you shouldn't have 100KB minified files in source control.) - syntax_limit_b = 1024 * 100; # 100KB - }; - }; - grep = { - # One thing I missed from Telescope was the ability to live_grep and the - # run a filter on the filenames. - # Ex: Find all occurrences of "enable" but only in the "plugins" directory. - # With this change, I can sort of get the same behaviour in live_grep. - # ex: > enable --*/plugins/* - # I still find this a bit cumbersome. There's probably a better way of doing this. - rg_glob = true; # enable glob parsing - glob_flag = "--iglob"; # case insensitive globs - glob_separator = "%s%-%-"; # query separator pattern (lua): ' --' - }; - }; - keymaps = { - "" = { - action = "git_files"; - options = { - desc = "Fzf-Lua Git Files"; - silent = true; - }; - settings = { - previewers = { - cat = { - cmd = "${pkgs.coreutils-full}/bin/cat"; - }; - }; - # winopts = { - # height = 0.5; - # }; - }; - }; - # "" = "live_grep"; - # "" = "oldfiles"; - # "fd" = "lsp_definitions"; - # "fg" = "live_grep"; - # "fh" = "helptags"; - # "fi" = "lsp_incoming_calls"; - # "fm" = "manpages"; - # "fo" = "lsp_outgoing_calls"; - # "fp" = "oldfiles"; - # "fr" = "lsp_references"; - # "fs" = "spell_suggest"; - # "fw" = "lsp_workspace_symbols"; - # "db" = "buffers"; - # "ch" = "command_history"; - }; - }; - - plugins.treesitter.enable = false; - - plugins.tmux-navigator.enable = true; - - plugins.trouble.enable = true; - - # ## Needed for telescope, nvim-tree, trouble, diffview, bufferline, and other plugins - # ## Only on unstable at the moment - plugins.web-devicons.enable = true; - - ## ------------------------------------------------ - ## LSP / Completion - ## ------------------------------------------------ - - plugins.lsp = { - enable = true; - onAttach = '' - local active_clients = vim.lsp.get_active_clients() - if client.name == "denols" then - for _, client_ in pairs(active_clients) do - -- stop tsserver if denols is already active - if client_.name == "ts_ls" then - client_.stop() - end - end - elseif client.name == "ts_ls" then - for _, client_ in pairs(active_clients) do - -- prevent tsserver from starting if denols is already active - if client_.name == "denols" then - client.stop() - end - end - end - ''; - servers = { - ts_ls = { - enable = true; - rootDir = "require('lspconfig').util.root_pattern('package.json')"; - settings = { - single_file_support = false; - }; - }; - denols = { - enable = true; - rootDir = "require('lspconfig').util.root_pattern('deno.json', 'deno.jsonc')"; - }; - cssls.enable = true; - tailwindcss.enable = true; - html.enable = true; - phpactor.enable = true; - pyright.enable = true; - marksman.enable = true; - nil_ls.enable = true; - ## Using nil_ls - # nixd.enable = true; - dockerls.enable = true; # Docker - bashls.enable = true; # Bash - clangd.enable = true; # C/C++ - csharp_ls.enable = true; # C# - yamlls.enable = true; # YAML - ltex = { - enable = true; - settings = { - enabled = [ "astro" "html" "latex" "markdown" "text" "tex" "gitcommit" ]; - completionEnabled = true; - language = "en-US de-DE nl"; - # dictionary = { - # "nl-NL" = [ - # ":/home/liv/.local/share/nvim/ltex/nl-NL.txt" - # ]; - # "en-US" = [ - # ":/home/liv/.local/share/nvim/ltex/en-US.txt" - # ]; - # "de-DE" = [ - # ":/home/liv/.local/share/nvim/ltex/de-DE.txt" - # ]; - # }; - }; - }; - gopls = { # Golang - enable = true; - autostart = true; - }; - lua_ls = { # Lua - enable = true; - settings.telemetry.enable = false; - }; - # Rust - rust_analyzer = { - enable = true; - installRustc = true; - installCargo = true; - }; - }; - }; - - ## @TODO: Enable once stable - plugins.blink-cmp = { - enable = false; - }; - - plugins.cmp = { - enable = true; - autoEnableSources = true; - settings = { - enabled.__raw = '' - function() - -- local context = require("cmp.config.context") - -- local is_comment = context.in_treesitter_capture("comment") == true or context.in_syntax_group("Comment") - - buftype = vim.api.nvim_buf_get_option(0, "buftype") - if buftype == "prompt" then - -- don't show in Telescope - return false - end - - local col = vim.fn.col('.') - 1 - local line = vim.fn.getline('.') - local char_under_cursor = string.sub(line, col, col) - - if col == 0 or string.match(char_under_cursor, '%s') then - return false - end - return true - end - ''; - - sources = [ - { name = "nvim_lua"; } - { name = "nvim_lsp"; } - { name = "emoji"; } - { - name = "buffer"; # text within current buffer - option.get_bufnrs.__raw = "vim.api.nvim_list_bufs"; - keywordLength = 3; - } - # { name = "copilot"; } # enable/disable copilot - { - name = "path"; # file system paths - keywordLength = 3; - } - { - name = "luasnip"; # snippets - keywordLength = 3; - } - { name = "cmdline"; } - ]; - - formatting = { - fields = [ "kind" "abbr" "menu" ]; - format = '' - function(entry, vim_item) - local kind_icons = { - Text = "󰊄", - Method = "", - Function = "󰡱", - Constructor = "", - Field = "", - Variable = "󱀍", - Class = "", - Interface = "", - Module = "󰕳", - Property = "", - Unit = "", - Value = "", - Enum = "", - Keyword = "", - Snippet = "", - Color = "", - File = "", - Reference = "", - Folder = "", - EnumMember = "", - Constant = "", - Struct = "", - Event = "", - Operator = "", - TypeParameter = "", - } - vim_item.kind = string.format("%s", kind_icons[vim_item.kind]) - vim_item.menu = ({ - path = "[Path]", - nvim_lua = "[NVIM_LUA]", - nvim_lsp = "[LSP]", - luasnip = "[Snippet]", - buffer = "[Buffer]", - })[entry.source.name] - return vim_item - end - ''; - }; - - completion = { - completeopt = "menuone,noselect"; - }; - - autoEnableSources = true; - - experimental = { ghost_text = true; }; - - performance = { - debounce = 60; - fetchingTimeout = 200; - maxViewEntries = 30; - }; - - snippet = { - expand = '' - function(args) - require('luasnip').lsp_expand(args.body) - end - ''; - }; - - window = { - completion = { border = "solid"; }; - documentation = { border = "solid"; }; - }; - - mapping = { - "" = "cmp.mapping.select_next_item()"; - "" = "cmp.mapping.select_next_item()"; - "" = "cmp.mapping.select_prev_item()"; - "" = "cmp.mapping.select_prev_item()"; - "" = "cmp.mapping.abort()"; - "" = "cmp.mapping.scroll_docs(-4)"; - "" = "cmp.mapping.scroll_docs(4)"; - "" = "cmp.mapping.complete()"; - "" = "cmp.mapping.confirm({ select = true })"; - # "" = '' - # cmp.mapping(function(fallback) - # -- local context = require("cmp.config.context") - # -- local is_comment = context.in_treesitter_capture("comment") == true or context.in_syntax_group("Comment") - # - # local col = vim.fn.col('.') - 1 - # local line = vim.fn.getline('.') - # local char_under_cursor = string.sub(line, col, col) - # - # if col == 0 or string.match(char_under_cursor, '%s') then - # fallback() - # elseif cmp.visible() then - # cmp.confirm({ select = true }) - # else - # fallback() - # end - # end, { "i", "s" }) - # ''; - "" = "cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true })"; - "" = '' - cmp.mapping(function() - if luasnip.expand_or_locally_jumpable() then - luasnip.expand_or_jump() - end - end, { 'i', 's' }) - ''; - "" = '' - cmp.mapping(function() - if luasnip.locally_jumpable(-1) then - luasnip.jump(-1) - end - end, { 'i', 's' }) - ''; - }; - }; - }; - - plugins.nvim-lightbulb = { - enable = true; - }; - # config = '' - # lua << EOF - # require('nvim-lightbulb').setup({ - # float = { - # -- "true" causes "invalid buffer id" error - # enabled = false, - # }, - # autocmd = { - # enabled = true, - # }, - # }) - # EOF - # ''; - plugins.lsp-signature = { - enable = true; - }; - # config = '' - # lua << EOF - # require("lsp_signature").setup() - # EOF - # ''; - - ## ------------------------------------------------ - ## Extra Plugins - ## ------------------------------------------------ - - extraPlugins = with pkgs.vimPlugins; [ - vim-dirdiff - { - plugin = vim-signify; - config = '' - let g:signify_vcs_cmds = { 'git': 'git diff --no-color --no-ext-diff -U0 master -- %f' } - let g:signify_priority = 1 - highlight SignColumn ctermbg=237 - ''; - } - vim-surround - - ## focus-nvim only in unstable - # (pkgs.vimUtils.buildVimPlugin { - # name = "focus-nvim"; - # src = pkgs.fetchFromGitHub { - # owner = "nvim-focus"; - # repo = "focus.nvim"; - # rev = "3841a38df972534567e85840d7ead20d3a26faa6"; - # sha256 = "sha256-mgHk4u0ab2uSUNE+7DU22IO/xS5uop9iATfFRk6l6hs="; - # }; - # }) - ]; - }; + '' else ""; } diff --git a/openconnect-pulse-launcher.py b/openconnect-pulse-launcher.py deleted file mode 100755 index 1d830e9..0000000 --- a/openconnect-pulse-launcher.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python - -import getopt -import inspect -import logging -import os -import psutil -import signal -import shutil -import subprocess -import sys -import time -import urllib - -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.support.ui import WebDriverWait -from xdg_base_dirs import xdg_config_home - -script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - -class OpenconnectPulseLauncher: - - def signal_handler(self, _sig, _frame): - subprocess.run(['sudo', 'route', 'del', 'default', 'gw', self.vpn_gateway_ip]) - while 'openconnect' in (i.name() for i in psutil.process_iter()): - subprocess.run(['sudo', 'pkill', '-SIGINT', 'openconnect']) - ps = subprocess.Popen( - ['getent', 'hosts', self.hostname], - stdout=subprocess.PIPE, - ) - output = subprocess.check_output( - ['awk', '{print $1}'], - stdin=ps.stdout - ) - ps.wait() - vpn_ip = output.decode().rstrip() - # This is normally deleted when the VPN is killed, but sometimes is left behind as there are two entries - subprocess.run(['sudo', 'route', 'del', vpn_ip]) - sys.exit(0) - - def __init__(self): - self.is_root = os.geteuid() == 0 - self.chrome_profile_dir = os.path.join(xdg_config_home(), 'chromedriver', 'pulsevpn') - if not os.path.exists(self.chrome_profile_dir): - os.makedirs(self.chrome_profile_dir) - - self.vpn_gateway_ip = None - - signal.signal(signal.SIGINT, self.signal_handler) - - def is_dsid_valid(self, dsid): - # Expiry is set to Session - return dsid is not None and 'value' in dsid - - def connect(self, vpn_url, chromedriver_path, chromium_path, debug=False, script=None): - self.hostname = urllib.parse.urlparse(vpn_url).hostname - - dsid = None - returncode = 0 - while True: - if self.is_dsid_valid(dsid) and returncode != 2: - logging.info('Launching openconnect.') - - ## Run in background - - ## openconnect is built to already point to a pre-packaged vpnc-script, so no need to specify - # p = subprocess.run(['sudo', 'openconnect', '-b', '-C', dsid['value'], '--protocol=pulse', vpn_url, '-s', '${pkgs.unstable.vpnc-scripts}/bin/vpnc-script']) - - ## --no-dtls addresses VPN dying with "ESP detected dead peer", and also "ESP receive error: Message too long" error - ## See: https://gitlab.com/openconnect/openconnect/-/issues/647 - ## Downside: lots of console spam - ## Also, seems to die often with this error: - ## Short packet received (2 bytes) - ## Unrecoverable I/O error; exiting. - # p = subprocess.run(['sudo', 'openconnect', '--no-dtls', '-b', '-C', dsid['value'], '--protocol=pulse', vpn_url]) - command_line = ['sudo', 'openconnect'] - if debug: - command_line.extend(['-vvvv']) - if script is not None: - command_line.extend(['-s', script]) - command_line.extend(['-b', '-C', dsid['value'], '--protocol=pulse', vpn_url]) - if debug: - print('Command line:') - print(' {}'.format(' '.join(command_line))) - print('') - p = subprocess.run(command_line) - - returncode = p.returncode - - ## Get tun0 IP and set as default GW (vpnc-script doesn't do this for some reason) - ## Probably due to something like this: - ## https://github.com/dlenski/openconnect/issues/125#issuecomment-426032102 - ## There is an error on the command line when openconnect is run: - ## Error: argument "via" is wrong: use nexthop syntax to specify multiple via - - ## sleep to make sure tun0 is available - time.sleep(3) - ps = subprocess.Popen( - ['ifconfig', 'tun0'], - stdout=subprocess.PIPE - ) - output = subprocess.check_output( - ['awk', '-F', ' *|:', '/inet /{print $3}'], - stdin=ps.stdout - ) - ps.wait() - self.vpn_gateway_ip = output.decode().rstrip() - print('VPN IP: '+self.vpn_gateway_ip) - p = subprocess.run(['sudo', 'route', 'add', 'default', 'gw', self.vpn_gateway_ip]) - - # Wait for ctrl-c - signal.pause() - else: - returncode = 0 - service = Service(executable_path=chromedriver_path) - options = webdriver.ChromeOptions() - options.binary_location = chromium_path - options.add_argument('--window-size=800,900') - # options.add_argument('--remote-debugging-pipe') - # options.add_argument('--remote-debugging-port=9222') - options.add_argument('user-data-dir=' + self.chrome_profile_dir) - - logging.info('Starting browser.') - driver = webdriver.Chrome(service=service, options=options) - - driver.get(vpn_url) - dsid = WebDriverWait(driver, float('inf')).until(lambda driver: driver.get_cookie('DSID')) - driver.quit() - logging.info('DSID cookie: %s', dsid) - -def main(argv): - script_name = os.path.basename(__file__) - chromedriver_path = shutil.which('chromedriver') - chromium_path = shutil.which('chromium') or shutil.which('google-chrome') - help_message = '{} '.format(script_name) - - try: - opts, args = getopt.getopt(argv, 'hds:c:', ['help', 'debug', 'script=', 'chromedriver-path']) - except getopt.GetoptError: - print(help_message) - sys.exit(2) - if len(args) != 1: - print(help_message) - sys.exit(2) - debug = False - script = None - for o, a in opts: - if o in ('-h', '--help'): - print(help_message) - sys.exit() - elif o in ('-d', '--debug'): - debug = True - elif o in ('-s', '--script'): - if len(a): - script = a - elif o in ('-c', '--chromedriver-path'): - if len(a): - chromedriver_path = a - vpn_url = args[0] - - launcher = OpenconnectPulseLauncher() - launcher.connect(vpn_url, chromedriver_path=chromedriver_path, chromium_path=chromium_path, debug=debug, script=script) - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/options.nix b/options.nix new file mode 100644 index 0000000..ae36237 --- /dev/null +++ b/options.nix @@ -0,0 +1,97 @@ +{ config, ... }: +{ + programs.nixvim = { + enable = config.nixvim-config.enable; + + defaultEditor = true; + + globals = { + mapleader = " "; # global + maplocalleader = " "; # per buffer, e.g. can change behavior per filetype + ## To appropriately highlight codefences returned from denols + markdown_fenced_languages.__raw = '' + { + "ts=typescript" + } + ''; + }; + + clipboard = { + register = "unnamedplus"; + }; + + opts = { + undodir.__raw = "vim.fs.normalize('~/.local/share/nvim/undo/')"; + undofile = true; + undolevels = 1000; # Increase undo levels + backup = false; # No file backup + + number = true; # Show line numbers + relativenumber = true; # Show relative line numbers + ruler = true; # displays line, column, and cursor position at bottom + wrap = false; # don't wrap lines + signcolumn = "yes"; # always show two column sign column on left + cursorline = true; # Highlight line cursor sits on + + ## @TODO Review these + completeopt = ""; # Completion options + hidden = true; # Enable modified buffers in background + inccommand = "nosplit"; # Show effects of a command incrementally + joinspaces = false; # No double spaces with join after a dot + scrolloff = 4; # Lines of context + fileencoding = "utf-8"; # Encode files using UTF-8 + termguicolors = true; # True color support + background = "dark"; # Always dark background + wildmenu = true; # Command-line completion mode + wildmode = "longest,list,full"; # Command-line completion mode + cmdheight = 2; # Command-line height + timeoutlen = 500; # to to wait for mapped sequence to complete + + errorbells = false; # No sound on errors + visualbell = false; + mouse = ""; + splitright = true; + + winaltkeys = "no"; # Disable ALT keys for menu + + autoindent = true; + smartindent = true; + colorcolumn = "121"; + + laststatus = 3; + + # ----------------------------------------------------- + # Backspace settings + # indent allow backspacing over autoindent + # eol allow backspacing over line breaks (join lines) + # start allow backspacing over the start of insert; CTRL-W and CTRL-U + # 0 same as ":set backspace=" (Vi compatible) + # 1 same as ":set backspace=indent,eol" + # 2 same as ":set backspace=indent,eol,start" + # ----------------------------------------------------- + + bs = "2"; + + # ----------------------------------------------------- + # Indentation settings + # ----------------------------------------------------- + + tabstop = 4; # number of spaces a tab counts for + shiftwidth = 4; # control how many columns text is indented with the reindent operations (<< and >>) and automatic C-style indentation. + expandtab = true; # Insert spaces when entering + softtabstop = 4; # Number of spaces that a counts for while performing editing operations, like inserting a or using . It "feels" like a tab though + ai = true; # auto indent + }; + + ## ------------------------------------------------ + ## Theme + ## ------------------------------------------------ + + colorschemes.tokyonight.enable = true; + + # colorschemes.gruvbox.enable = true; + ## Or: + # extraPlugins = [ pkgs.vimPlugins.gruvbox ]; + # colorscheme = "gruvbox"; + }; +} diff --git a/plugins/blink-cmp-copilot.nix b/plugins/blink-cmp-copilot.nix new file mode 100644 index 0000000..8c018c8 --- /dev/null +++ b/plugins/blink-cmp-copilot.nix @@ -0,0 +1,8 @@ +{ ... }: +{ + programs.nixvim = { + plugins.blink-cmp-copilot = { + enable = true; + }; + }; +} diff --git a/plugins/blink-cmp-dictionary.nix b/plugins/blink-cmp-dictionary.nix new file mode 100644 index 0000000..17e835a --- /dev/null +++ b/plugins/blink-cmp-dictionary.nix @@ -0,0 +1,33 @@ +{ pkgs, lib, ... }: +let + ## Remove flags from words + processHunspellDict = filename: + let + content = builtins.readFile filename; + # Remove first line, which is a word count, then split on newlines + lines = builtins.tail (lib.splitString "\n" content); + # Remove flags at the end of words + result = builtins.concatStringsSep "\n" (map (line: + builtins.head (lib.splitString "/" line) + ) lines); + in + result; +in +{ + programs.nixvim = { + plugins.blink-cmp-dictionary = { + enable = true; + }; + + extraPackages = with pkgs; [ + wordnet + ]; + }; + + environment.etc = { + "dictionaries/english-words.txt" = { + text = processHunspellDict "${pkgs.hunspellDicts.en-us-large}/share/hunspell/en_US.dic"; + mode = "0444"; + }; + }; +} diff --git a/plugins/blink-cmp.nix b/plugins/blink-cmp.nix new file mode 100644 index 0000000..ee2c849 --- /dev/null +++ b/plugins/blink-cmp.nix @@ -0,0 +1,166 @@ +{ ... }: +{ + programs.nixvim = { + plugins = { + blink-cmp = { + enable = true; + + settings = { + fuzzy.prebuilt_binaries = { + download = false; + ignore_version_mismatch = true; + }; + + keymap = { + preset = "enter"; + }; + + signature = { + # handled by noice + enabled = false; + }; + + sources = { + default = [ + "lsp" + "path" + "snippets" + "buffer" + "cmp_yanky" + "copilot" + "emoji" + "lazydev" + "codecompanion" + "dictionary" + ]; + + providers = { + copilot = { + name = "Copilot"; + async = true; + module = "blink-cmp-copilot"; + score_offset = 50; + opts = { + max_completions = 2; + max_attemps = 3; + }; + }; + + cmp_yanky = { + async = true; + name = "cmp_yanky"; + module = "blink.compat.source"; + score_offset = -5; + }; + + emoji = { + module = "blink-emoji"; + name = "Emoji"; + score_offset = -10; + opts = { + insert = true; + }; + }; + + codecompanion = { + name = "CodeCompanion"; + module = "codecompanion.providers.completion.blink"; + }; + + dictionary = { + module = "blink-cmp-dictionary"; + name = "Dict"; + score_offset = -15; + min_keyword_length = 3; + opts = { + dictionary_directories = { + __unkeyed-1.__raw = ''vim.fn.expand("/etc/dictionaries/")''; + }; + }; + }; + + lazydev = { + name = "LazyDev"; + module = "lazydev.integrations.blink"; + score_offset = 100; + }; + + lsp = { + async = true; + score_offset = 60; + }; + }; + }; + + appearance = { + kind_icons = { + Copilot = ""; + cmp_yanky = "⧉"; + emoji = ""; + }; + use_nvim_cmp_as_default = false; + }; + + completion = { + accept = { + auto_brackets = { + enabled = false; + }; + }; + + list = { + selection = { + preselect.__raw = '' + function(ctx) + return ctx.mode ~= 'cmdline' and not require('blink.cmp').snippet_active({ direction = 1 }) + end + ''; + auto_insert = false; + }; + }; + + documentation = { + auto_show = true; + auto_show_delay_ms = 500; + treesitter_highlighting = true; + }; + + menu = { + auto_show = true; + + draw = { + treesitter = ["lsp"]; + }; + }; + }; + + snippets = { + preset = "luasnip"; + expand.__raw = '' + function(snippet) require('luasnip').lsp_expand(snippet) end + ''; + active.__raw = '' + function(filter) + if filter and filter.direction then + return require('luasnip').jumpable(filter.direction) + end + return require('luasnip').in_snippet() + end + ''; + jump.__raw = '' + function(direction) require('luasnip').jump(direction) end + ''; + }; + }; + }; + + blink-compat = { + enable = true; + }; + }; + + extraConfigLua = '' + vim.api.nvim_set_hl(0, 'BlinkCmpKindDict', { default = false, fg = '#a6e3a1' }) + ''; + }; +} diff --git a/plugins/blink-copilot.nix b/plugins/blink-copilot.nix new file mode 100644 index 0000000..c681707 --- /dev/null +++ b/plugins/blink-copilot.nix @@ -0,0 +1,10 @@ +{ ... }: +{ + programs.nixvim = { + plugins.blink-copilot = { + enable = true; + }; + }; +} + + diff --git a/plugins/blink-emoji.nix b/plugins/blink-emoji.nix new file mode 100644 index 0000000..685f99b --- /dev/null +++ b/plugins/blink-emoji.nix @@ -0,0 +1,8 @@ +{ ... }: +{ + programs.nixvim = { + plugins.blink-emoji = { + enable = true; + }; + }; +} diff --git a/plugins/bufferline.nix b/plugins/bufferline.nix new file mode 100644 index 0000000..8ad51ef --- /dev/null +++ b/plugins/bufferline.nix @@ -0,0 +1,25 @@ +{ ... }: +{ + programs.nixvim = { + plugins.bufferline = { + enable = true; + # extraOptions = { + settings = { + options = { + tabpages = true; + sidebar_filetypes = { + NvimTree = true; + }; + diagnostics = "nvim_lsp"; + always_show_bufferline = true; + }; + highlights = { + buffer_selected = { + # fg = "#ffffff"; + bold = true; + }; + }; + }; + }; + }; +} diff --git a/plugins/cmp.nix b/plugins/cmp.nix new file mode 100644 index 0000000..e20bc14 --- /dev/null +++ b/plugins/cmp.nix @@ -0,0 +1,158 @@ +{ ... }: +{ + programs.nixvim = { + plugins.cmp = { + enable = true; + autoEnableSources = true; + settings = { + enabled.__raw = '' + function() + local filetype = vim.api.nvim_buf_get_option(0, "filetype") + if filetype == "TelescopePrompt" then + return false + end + return true + end + ''; + + sources = [ + { name = "nvim_lua"; } + { name = "nvim_lsp"; } + { name = "emoji"; } + { + name = "buffer"; # text within current buffer + option.get_bufnrs.__raw = "vim.api.nvim_list_bufs"; + keywordLength = 3; + } + # { name = "copilot"; } # enable/disable copilot + { + name = "path"; # file system paths + keywordLength = 3; + } + { + name = "luasnip"; # snippets + keywordLength = 3; + } + { name = "cmdline"; } + ]; + + formatting = { + fields = [ "kind" "abbr" "menu" ]; + format = '' + function(entry, vim_item) + local kind_icons = { + Text = "󰊄", + Method = "", + Function = "󰡱", + Constructor = "", + Field = "", + Variable = "󱀍", + Class = "", + Interface = "", + Module = "󰕳", + Property = "", + Unit = "", + Value = "", + Enum = "", + Keyword = "", + Snippet = "", + Color = "", + File = "", + Reference = "", + Folder = "", + EnumMember = "", + Constant = "", + Struct = "", + Event = "", + Operator = "", + TypeParameter = "", + } + vim_item.kind = string.format("%s", kind_icons[vim_item.kind]) + vim_item.menu = ({ + path = "[Path]", + nvim_lua = "[NVIM_LUA]", + nvim_lsp = "[LSP]", + luasnip = "[Snippet]", + buffer = "[Buffer]", + })[entry.source.name] + return vim_item + end + ''; + }; + + completion = { + # completeopt = "menuone,noselect"; + completeopt = ""; + }; + + autoEnableSources = true; + + experimental = { ghost_text = true; }; + + performance = { + debounce = 60; + fetchingTimeout = 200; + maxViewEntries = 30; + }; + + snippet = { + expand = '' + function(args) + require('luasnip').lsp_expand(args.body) + end + ''; + }; + + window = { + completion = { border = "solid"; }; + documentation = { border = "solid"; }; + }; + + mapping = { + "" = "cmp.mapping.select_next_item()"; + "" = "cmp.mapping.select_next_item()"; + "" = "cmp.mapping.select_prev_item()"; + "" = "cmp.mapping.select_prev_item()"; + "" = "cmp.mapping.abort()"; + "" = "cmp.mapping.scroll_docs(-4)"; + "" = "cmp.mapping.scroll_docs(4)"; + "" = "cmp.mapping.complete()"; + "" = "cmp.mapping.confirm({ select = true })"; + # "" = '' + # cmp.mapping(function(fallback) + # -- local context = require("cmp.config.context") + # -- local is_comment = context.in_treesitter_capture("comment") == true or context.in_syntax_group("Comment") + # + # local col = vim.fn.col('.') - 1 + # local line = vim.fn.getline('.') + # local char_under_cursor = string.sub(line, col, col) + # + # if col == 0 or string.match(char_under_cursor, '%s') then + # fallback() + # elseif cmp.visible() then + # cmp.confirm({ select = true }) + # else + # fallback() + # end + # end, { "i", "s" }) + # ''; + "" = "cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true })"; + "" = '' + cmp.mapping(function() + if luasnip.expand_or_locally_jumpable() then + luasnip.expand_or_jump() + end + end, { 'i', 's' }) + ''; + "" = '' + cmp.mapping(function() + if luasnip.locally_jumpable(-1) then + luasnip.jump(-1) + end + end, { 'i', 's' }) + ''; + }; + }; + }; + }; +} diff --git a/plugins/codecompanion.nix b/plugins/codecompanion.nix new file mode 100644 index 0000000..51abadd --- /dev/null +++ b/plugins/codecompanion.nix @@ -0,0 +1,94 @@ +{ pkgs, ... }: +{ + programs.nixvim.plugins = { + codecompanion = { + enable = true; + + settings = { + adapters = { + anthropic.__raw = '' + function() + return require('codecompanion.adapters').extend('anthropic', { + env = { + api_key = "cmd:${pkgs.passage}/bin/passage /apis/ai/anthropic" + } + }) + end + ''; + + copilot.__raw = '' + function() + return require('codecompanion.adapters').extend('copilot', { + env = { + -- api_key = "cmd:${pkgs.passage}/bin/passage /apis/ai/anthropic" + } + }) + end + ''; + + openai.__raw = '' + function() + return require('codecompanion.adapters').extend('copilot', { + env = { + api_key = "cmd:${pkgs.passage}/bin/passage /apis/ai/openai" + } + }) + end + ''; + + deepseek.__raw = '' + function() + return require('codecompanion.adapters').extend('deepseek', { + env = { + api_key = "cmd:${pkgs.passage}/bin/passage /apis/ai/deepseek" + }, + schema = { + model = { + default = "deepseek-reasoner", + }, + }, + }) + end + ''; + + ollama.__raw = '' + function() + return require('codecompanion.adapters').extend('ollama', { + env = { + url = "http://127.0.0.1:11434", + }, + schema = { + model = { + default = 'hf.co/unsloth/DeepSeek-R1-Distill-Qwen-1.5B-GGUF', + }, + temperature = { + default = 0.6, + }, + num_ctx = { + default = 32768, + }, + }, + }) + end + ''; + }; + opts = { + send_code = true; + use_default_actions = true; + use_default_prompts = true; + }; + strategies = { + agent = { + adapter = "copilot"; + }; + chat = { + adapter = "copilot"; + }; + inline = { + adapter = "copilot"; + }; + }; + }; + }; + }; +} diff --git a/plugins/copilot.nix b/plugins/copilot.nix new file mode 100644 index 0000000..2da60cd --- /dev/null +++ b/plugins/copilot.nix @@ -0,0 +1,14 @@ +{ ... }: +{ + programs.nixvim = { + plugins.copilot-lua = { + enable = true; + + settings = { + suggestion.enabled = false; + panel.enabled = false; + }; + }; + }; +} + diff --git a/plugins/default.nix b/plugins/default.nix new file mode 100644 index 0000000..fe9337a --- /dev/null +++ b/plugins/default.nix @@ -0,0 +1,71 @@ +{ ... }: +{ + imports = [ + # ./blink-cmp.nix + # ./blink-cmp-copilot.nix + # ./blink-cmp-dictionary.nix + # ./blink-copilot.nix + # ./blink-emoji.nixd + ./bufferline.nix + ./cmp.nix + ./codecompanion.nix + ./copilot.nix + ./fzf-lua.nix + ./lazydev.nix + ./lsp.nix + ./luasnip.nix + ./nvim-tree.nix + ./telescope.nix + ./vim-signify.nix + ]; + + programs.nixvim.plugins = { + comment.enable = true; + diffview.enable = true; + fugitive.enable = true; + gitsigns.enable = true; + indent-blankline.enable = true; + lightline.enable = true; + lsp-signature.enable = true; + # config = '' + # lua << EOF + # require("lsp_signature").setup() + # EOF + # ''; + + lualine.enable = true; + nix.enable = true; + noice.enable = true; + ## disable, very annoying as notifications block content and + ## are part of the buffer rotation + notify = { + enable = false; + topDown = false; + }; + nvim-autopairs.enable = true; + nvim-lightbulb.enable = true; + # config = '' + # lua << EOF + # require('nvim-lightbulb').setup({ + # float = { + # -- "true" causes "invalid buffer id" error + # enabled = false, + # }, + # autocmd = { + # enabled = true, + # }, + # }) + # EOF + # ''; + + rainbow-delimiters.enable = true; + sleuth.enable = true; + treesitter.enable = false; + tmux-navigator.enable = true; + trouble.enable = true; + # ## Needed for telescope, nvim-tree, trouble, diffview, bufferline, and other plugins + # ## Only on unstable at the moment + web-devicons.enable = true; + which-key.enable = true; + }; +} diff --git a/plugins/fzf-lua.nix b/plugins/fzf-lua.nix new file mode 100644 index 0000000..773ba89 --- /dev/null +++ b/plugins/fzf-lua.nix @@ -0,0 +1,77 @@ +{ pkgs, ... }: +{ + programs.nixvim = { + plugins.fzf-lua = { + enable = true; + # profile = "telescope"; + settings = { + oldfiles = { + # In Telescope, when I used fr, it would load old buffers. + # fzf lua does the same, but by default buffers visited in the current + # session are not included. I use fr all the time to switch + # back to buffers I was just in. If you missed this from Telescope, + # give it a try. + include_current_session = true; + }; + preview = { + vertical = "down:90%"; + horizontal = "right:90%"; + }; + previewers = { + builtin = { + # fzf-lua is very fast, but it really struggled to preview a couple files + # in a repo. Those files were very big JavaScript files (1MB, minified, all on a single line). + # It turns out it was Treesitter having trouble parsing the files. + # With this change, the previewer will not add syntax highlighting to files larger than 100KB + # (Yes, I know you shouldn't have 100KB minified files in source control.) + syntax_limit_b = 1024 * 100; # 100KB + }; + }; + grep = { + # One thing I missed from Telescope was the ability to live_grep and the + # run a filter on the filenames. + # Ex: Find all occurrences of "enable" but only in the "plugins" directory. + # With this change, I can sort of get the same behaviour in live_grep. + # ex: > enable --*/plugins/* + # I still find this a bit cumbersome. There's probably a better way of doing this. + rg_glob = true; # enable glob parsing + glob_flag = "--iglob"; # case insensitive globs + glob_separator = "%s%-%-"; # query separator pattern (lua): ' --' + }; + }; + keymaps = { + "" = { + action = "git_files"; + options = { + desc = "Fzf-Lua Git Files"; + silent = true; + }; + settings = { + previewers = { + cat = { + cmd = "${pkgs.coreutils-full}/bin/cat"; + }; + }; + # winopts = { + # height = 0.5; + # }; + }; + }; + # "" = "live_grep"; + # "" = "oldfiles"; + # "fd" = "lsp_definitions"; + # "fg" = "live_grep"; + # "fh" = "helptags"; + # "fi" = "lsp_incoming_calls"; + # "fm" = "manpages"; + # "fo" = "lsp_outgoing_calls"; + # "fp" = "oldfiles"; + # "fr" = "lsp_references"; + # "fs" = "spell_suggest"; + # "fw" = "lsp_workspace_symbols"; + # "db" = "buffers"; + # "ch" = "command_history"; + }; + }; + }; +} diff --git a/plugins/lazydev.nix b/plugins/lazydev.nix new file mode 100644 index 0000000..da3b99f --- /dev/null +++ b/plugins/lazydev.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: +{ + programs.nixvim = { + extraConfigLua = '' + require("lazydev").setup({}) + ''; + + extraPlugins = with pkgs.vimPlugins; [ + lazydev-nvim + ]; + }; +} diff --git a/plugins/lsp.nix b/plugins/lsp.nix new file mode 100644 index 0000000..d7aed20 --- /dev/null +++ b/plugins/lsp.nix @@ -0,0 +1,89 @@ +{ ... }: +{ + programs.nixvim = { + plugins.lsp = { + enable = true; + onAttach = '' + -- Set capabilities for the LSP client from blink-cmp + -- client.config.capabilities = require('blink.cmp').get_lsp_capabilities(client.config.capabilities) + + local active_clients = vim.lsp.get_active_clients() + if client.name == "denols" then + for _, client_ in pairs(active_clients) do + -- stop tsserver if denols is already active + if client_.name == "ts_ls" then + client_.stop() + end + end + elseif client.name == "ts_ls" then + for _, client_ in pairs(active_clients) do + -- prevent tsserver from starting if denols is already active + if client_.name == "denols" then + client.stop() + end + end + end + ''; + servers = { + ts_ls = { + enable = true; + rootDir = "require('lspconfig').util.root_pattern('package.json')"; + settings = { + single_file_support = false; + }; + }; + denols = { + enable = true; + rootDir = "require('lspconfig').util.root_pattern('deno.json', 'deno.jsonc')"; + }; + cssls.enable = true; + tailwindcss.enable = true; + html.enable = true; + phpactor.enable = true; + pyright.enable = true; + marksman.enable = true; + nil_ls.enable = true; + ## Using nil_ls + # nixd.enable = true; + dockerls.enable = true; # Docker + bashls.enable = true; # Bash + clangd.enable = true; # C/C++ + csharp_ls.enable = true; # C# + yamlls.enable = true; # YAML + ltex = { + enable = true; + settings = { + enabled = [ "astro" "html" "latex" "markdown" "text" "tex" "gitcommit" ]; + completionEnabled = true; + language = "en-US de-DE nl"; + # dictionary = { + # "nl-NL" = [ + # ":/home/liv/.local/share/nvim/ltex/nl-NL.txt" + # ]; + # "en-US" = [ + # ":/home/liv/.local/share/nvim/ltex/en-US.txt" + # ]; + # "de-DE" = [ + # ":/home/liv/.local/share/nvim/ltex/de-DE.txt" + # ]; + # }; + }; + }; + gopls = { # Golang + enable = true; + autostart = true; + }; + lua_ls = { # Lua + enable = true; + settings.telemetry.enable = false; + }; + # Rust + rust_analyzer = { + enable = true; + installRustc = true; + installCargo = true; + }; + }; + }; + }; +} diff --git a/plugins/luasnip.nix b/plugins/luasnip.nix new file mode 100644 index 0000000..7610225 --- /dev/null +++ b/plugins/luasnip.nix @@ -0,0 +1,41 @@ +{ ... }: +{ + programs.nixvim = { + plugins.luasnip = { + enable = true; + + fromLua = [ + {} + { + paths = ./snippets; + } + ]; + + settings = { + history = true; + updateevents = ["TextChanged" "TextChangedI"]; + region_check_events = "CursorHold"; + delete_check_events = "InsertLeave"; + # ext_opts.__raw = '' + # { + # [require('luasnip.util.types').choiceNode] = { + # active = { + # virt_text = { { 'choice ', 'Comment' } }, + # hl_mode = 'combine', + # }, + # }, + # } + # ''; + }; + }; + + # keymaps = [ + # { + # mode = "i"; + # key = ""; + # action.__raw = ''function() require("luasnip.extras.select_choice")() end''; + # options.desc = "Search"; + # } + # ]; + }; +} diff --git a/plugins/nvim-tree.nix b/plugins/nvim-tree.nix new file mode 100644 index 0000000..c5c4106 --- /dev/null +++ b/plugins/nvim-tree.nix @@ -0,0 +1,33 @@ +{ ... }: +{ + programs.nixvim = { + plugins.nvim-tree = { + enable = true; + extraOptions = { + actions = { + remove_file = { + close_window = false; + }; + }; + ## Keep tree open if already open when opening a tab + tab = { + sync = { + open = true; + close = true; + }; + }; + view = { + width = 30; + }; + renderer = { + group_empty = true; + }; + git = { + enable = true; + ignore = false; + timeout = 500; + }; + }; + }; + }; +} diff --git a/plugins/snippets/all.lua b/plugins/snippets/all.lua new file mode 100644 index 0000000..6e6c56b --- /dev/null +++ b/plugins/snippets/all.lua @@ -0,0 +1,36 @@ +--example: +--inc 20250209T133000Z - 20250209T140000Z # personal test +---@return string +local function get_timestamp() + local line_number = vim.api.nvim_win_get_cursor(vim.api.nvim_get_current_win())[1] + local line = vim.fn.getline(line_number - 1) + return vim.split(line, " ")[4] +end + +---@return string +local function get_date() + local timestamp = get_timestamp() + return vim.split(timestamp, "T")[1] +end + +return { + s( + { + trig = "tw-continue", + name = "Timewarior Continue From Previous Line", + desc = { "Start a new time tracking with end time from previous line." }, + }, + fmt( + [[ + inc {} - {}T{}00Z # {} {} + ]], + { + f(get_timestamp), + f(get_date), + i(1), + c(2, { t("work"), t("meeting"), t("other") }), + i(3), + } + ) + ), +} diff --git a/plugins/snippets/yaml.lua b/plugins/snippets/yaml.lua new file mode 100644 index 0000000..8fcc05c --- /dev/null +++ b/plugins/snippets/yaml.lua @@ -0,0 +1,44 @@ +return { + s( + { + trig = "schema", + name = "Yaml Schema", + desc = { "Adds a yaml-language-server schema to current buffer." }, + }, + fmt( + [[ + # yaml-language-server: $schema={} + ]], + { + i(1), + } + ) + ), + s( + { trig = "kustomize", name = "Kustomize", desc = { "Adds the kustomize boilerplate." } }, + fmt( + [[ + # yaml-language-server: $schema=https://json.schemastore.org/kustomization.json + apiVersion: kustomize.config.k8s.io/v1beta1 + kind: Kustomization + + resources: [] + ]], + {} + ) + ), + s( + { + trig = "m", + name = "directive end marker", + desc = { "Adds directive end parker." }, + }, + fmt( + [[ + --- + + ]], + {} + ) + ), +} diff --git a/plugins/telescope.nix b/plugins/telescope.nix new file mode 100644 index 0000000..0add60f --- /dev/null +++ b/plugins/telescope.nix @@ -0,0 +1,33 @@ +{ ... }: +{ + programs.nixvim = { + plugins.telescope = { + enable = true; + extensions.ui-select.enable = true; + settings = { + defaults = { + mappings = { + i = { + # One instead of two esc taps to exit telescope + "" = { + __raw = "require('telescope.actions').close"; + }; + # Ctrl-space is used by Tmux, so remap to Ctrl-e + "" = { + __raw = "require('telescope.actions').to_fuzzy_refine"; + }; + # "" = { + # __raw = "require('trouble.sources.telescope').open"; + # }; + }; + n = { + # "" = { + # __raw = "require('trouble.sources.telescope').open"; + # }; + }; + }; + }; + }; + }; + }; +} diff --git a/plugins/vim-signify.nix b/plugins/vim-signify.nix new file mode 100644 index 0000000..447fc2d --- /dev/null +++ b/plugins/vim-signify.nix @@ -0,0 +1,17 @@ +{ pkgs, ... }: +{ + programs.nixvim = { + extraPlugins = with pkgs.vimPlugins; [ + vim-dirdiff + { + plugin = vim-signify; + config = '' + let g:signify_vcs_cmds = { 'git': 'git diff --no-color --no-ext-diff -U0 master -- %f' } + let g:signify_priority = 1 + highlight SignColumn ctermbg=237 + ''; + } + vim-surround + ]; + }; +} diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 06d1bc0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -attrs==23.2.0 -certifi==2024.2.2 -h11==0.14.0 -idna==3.6 -outcome==1.3.0.post0 -psutil==5.9.8 -PySocks==1.7.1 -selenium==4.19.0 -sniffio==1.3.1 -sortedcontainers==2.4.0 -trio==0.25.0 -trio-websocket==0.11.1 -typing_extensions==4.11.0 -urllib3==2.2.1 -wsproto==1.2.0 -xdg-base-dirs==6.0.1