6 Commits a3fb902167 ... 803dd55955

Autor SHA1 Mensaje Fecha
  Thomas Dy 803dd55955 nixpkgs/neovim: further reorganize configuration hace 1 mes
  Thomas Dy 18f38ee87f nixpkgs/neovim: add lua-language-server and format hace 1 mes
  Thomas Dy 64dfa3c9f6 nixpkgs/neovim: copy queries from out-of-tree treesitter plugins hace 1 mes
  Thomas Dy 4e35e82fcd nixpkgs/neovim: make kulala lazy hace 1 mes
  Thomas Dy b8024fb314 nixpkgs/neovim: remove extra-treesitter-textobjects hace 1 mes
  Thomas Dy d4433ff3b2 nixpkgs/neovim: reorganize configuration hace 1 mes
Se han modificado 36 ficheros con 864 adiciones y 840 borrados
  1. 4 0
      .config/nixpkgs/neovim/.editorconfig
  2. 8 0
      .config/nixpkgs/neovim/.luarc.json
  3. 0 92
      .config/nixpkgs/neovim/autocmd.lua
  4. 6 0
      .config/nixpkgs/neovim/config/after/compiler/typescript.lua
  5. 0 0
      .config/nixpkgs/neovim/config/after/ftplugin/javascript.vim
  6. 0 0
      .config/nixpkgs/neovim/config/after/ftplugin/typescript.vim
  7. 0 0
      .config/nixpkgs/neovim/config/after/ftplugin/typescriptreact.vim
  8. 0 0
      .config/nixpkgs/neovim/config/after/syntax/git.vim
  9. 1 0
      .config/nixpkgs/neovim/config/ftplugin/http.lua
  10. 1 0
      .config/nixpkgs/neovim/config/ftplugin/markdown.lua
  11. 0 0
      .config/nixpkgs/neovim/config/lua/user/commands.lua
  12. 6 0
      .config/nixpkgs/neovim/config/lua/user/init.lua
  13. 189 0
      .config/nixpkgs/neovim/config/lua/user/lsp.lua
  14. 3 0
      .config/nixpkgs/neovim/config/lua/user/luadev.lua
  15. 36 0
      .config/nixpkgs/neovim/config/lua/user/plugins/completion.lua
  16. 82 0
      .config/nixpkgs/neovim/config/lua/user/plugins/editing.lua
  17. 41 0
      .config/nixpkgs/neovim/config/lua/user/plugins/git.lua
  18. 12 0
      .config/nixpkgs/neovim/config/lua/user/plugins/init.lua
  19. 28 0
      .config/nixpkgs/neovim/config/lua/user/plugins/navigation.lua
  20. 167 0
      .config/nixpkgs/neovim/config/lua/user/plugins/telescope.lua
  21. 39 0
      .config/nixpkgs/neovim/config/lua/user/plugins/treesitter.lua
  22. 63 0
      .config/nixpkgs/neovim/config/lua/user/plugins/ui.lua
  23. 6 0
      .config/nixpkgs/neovim/config/lua/user/rest.lua
  24. 35 5
      .config/nixpkgs/neovim/config/lua/user/settings.lua
  25. 41 0
      .config/nixpkgs/neovim/config/lua/user/terminal.lua
  26. 2 2
      .config/nixpkgs/neovim/config/lua/user/theme.lua
  27. 40 0
      .config/nixpkgs/neovim/config/lua/user/util.lua
  28. 6 0
      .config/nixpkgs/neovim/config/plugin/elvish.lua
  29. 6 1
      .config/nixpkgs/neovim/flake.nix
  30. 0 124
      .config/nixpkgs/neovim/lsp.lua
  31. 0 119
      .config/nixpkgs/neovim/mappings.lua
  32. 35 58
      .config/nixpkgs/neovim/neovim.nix
  33. 0 6
      .config/nixpkgs/neovim/overrides/after/compiler/typescript.lua
  34. 0 433
      .config/nixpkgs/neovim/plugins.lua
  35. 1 0
      .config/nixpkgs/neovim/plugins/sources.json
  36. 6 0
      .config/nixpkgs/neovim/treesitter/default.nix

+ 4 - 0
.config/nixpkgs/neovim/.editorconfig

@@ -0,0 +1,4 @@
+[*.lua]
+indent_style = space
+indent_size = 2
+quote_style = single

+ 8 - 0
.config/nixpkgs/neovim/.luarc.json

@@ -0,0 +1,8 @@
+{
+  "diagnostics.globals": [
+    "vim"
+  ],
+  "diagnostics.globalsRegex": [
+    "^Mini"
+  ]
+}

+ 0 - 92
.config/nixpkgs/neovim/autocmd.lua

@@ -1,92 +0,0 @@
--- apply default terminal settings
-vim.api.nvim_create_autocmd('TermOpen', {
-  callback = function()
-    vim.bo.scrollback = 10000
-    vim.opt_local.number = false
-    vim.opt_local.relativenumber = false
-    vim.b.miniindentscope_disable = true
-  end
-})
-
--- preserve window structure when exiting terminal via C-d
-vim.api.nvim_create_autocmd('TermClose', {
-  callback = function(opts)
-    -- don't trigger when force deleting
-    if vim.api.nvim_buf_get_option(opts.buf, 'modified') then
-      return
-    end
-    MiniBufremove.delete(opts.buf)
-  end,
-  -- needed so statusline properly updates
-  nested = true,
-})
-
--- automatically enter/leave terminal mode
-vim.api.nvim_create_autocmd('TermOpen', { command = 'startinsert' })
-vim.api.nvim_create_autocmd({'WinEnter','BufWinEnter'}, {
-  pattern = 'term://*',
-  command = 'startinsert',
-})
-vim.api.nvim_create_autocmd('BufLeave', {
-  pattern = 'term://*',
-  command = 'stopinsert',
-})
-
--- rename
-vim.api.nvim_create_autocmd('User', {
-  pattern = 'MiniFilesActionRename',
-  callback = function(opts)
-    local params = {
-      files = {{
-        oldUri = vim.uri_from_fname(opts.data.from),
-        newUri = vim.uri_from_fname(opts.data.to),
-      }}
-    }
-    local bufnr = vim.fn.bufadd(opts.data.to)
-
-    local clients = vim.lsp.get_clients({ bufnr = bufnr })
-    for _, client in ipairs(clients) do
-      if client:supports_method('workspace/willRenameFiles') then
-        local resp = client:request_sync('workspace/willRenameFiles', params, 5000, bufnr)
-        if resp and resp.result ~= nil then
-          vim.lsp.util.apply_workspace_edit(resp.result, client.offset_encoding)
-        end
-      end
-    end
-
-    for _, client in ipairs(clients) do
-      if client:supports_method('workspace/didRenameFiles') then
-        client:notify('workspace/didRenameFiles', params)
-      end
-    end
-  end,
-  nested = true,
-})
-
--- formatting
-vim.api.nvim_create_autocmd('BufWritePre', {
-  callback = function(opts)
-    if vim.bo.filetype == 'diff' then
-      return
-    end
-
-    if not vim.g.no_lsp_format then
-      -- check if can LSP format
-      local clients = vim.lsp.get_clients({ bufnr = opts.buf, method = 'textDocument/formatting' })
-      if #clients > 0 then
-        vim.lsp.buf.format({ bufnr = opts.buf })
-      end
-    end
-
-    -- otherwise strip trailing whitespace
-    vim.cmd('%s/\\s\\+$//e')
-  end,
-})
-
--- filetype specific options
-vim.api.nvim_create_autocmd('FileType', {
-  pattern = 'markdown',
-  callback = function()
-    vim.bo.textwidth = 80
-  end,
-})

+ 6 - 0
.config/nixpkgs/neovim/config/after/compiler/typescript.lua

@@ -0,0 +1,6 @@
+if vim.fn.executable('tsgo') == 1 then
+  vim.bo.makeprg = 'tsgo --noEmit --pretty false $*'
+else
+  vim.bo.makeprg = 'tsc --noEmit --incremental $*'
+end
+vim.bo.errorformat = '%+A %#%f %#(%l\\,%c): %m,%C%m'

+ 0 - 0
.config/nixpkgs/neovim/overrides/after/ftplugin/javascript.vim → .config/nixpkgs/neovim/config/after/ftplugin/javascript.vim


+ 0 - 0
.config/nixpkgs/neovim/overrides/after/ftplugin/typescript.vim → .config/nixpkgs/neovim/config/after/ftplugin/typescript.vim


+ 0 - 0
.config/nixpkgs/neovim/overrides/after/ftplugin/typescriptreact.vim → .config/nixpkgs/neovim/config/after/ftplugin/typescriptreact.vim


+ 0 - 0
.config/nixpkgs/neovim/overrides/after/syntax/git.vim → .config/nixpkgs/neovim/config/after/syntax/git.vim


+ 1 - 0
.config/nixpkgs/neovim/config/ftplugin/http.lua

@@ -0,0 +1 @@
+require('user.rest')

+ 1 - 0
.config/nixpkgs/neovim/config/ftplugin/markdown.lua

@@ -0,0 +1 @@
+vim.bo.textwidth = 80

+ 0 - 0
.config/nixpkgs/neovim/commands.lua → .config/nixpkgs/neovim/config/lua/user/commands.lua


+ 6 - 0
.config/nixpkgs/neovim/config/lua/user/init.lua

@@ -0,0 +1,6 @@
+require('user.settings')
+require('user.plugins')
+require('user.terminal')
+require('user.lsp')
+require('user.commands')
+require('user.theme')

+ 189 - 0
.config/nixpkgs/neovim/config/lua/user/lsp.lua

@@ -0,0 +1,189 @@
+local util = require('user.util')
+
+vim.diagnostic.config({
+  -- only show virtual text for WARN and higher
+  virtual_text = { severity = { min = vim.diagnostic.severity.WARN } },
+})
+
+vim.lsp.config('*', {
+  flags = {
+    debounce_text_changes = 250,
+  }
+})
+
+vim.lsp.enable('bashls');
+vim.lsp.enable('eslint');
+
+if vim.fn.executable('dprint') == 1 then
+  local version = vim.version.parse(vim.fn.system('dprint --version'))
+  if vim.version.cmp(version, { 0, 45, 0 }) >= 0 then
+    vim.lsp.enable('dprint')
+  end
+end
+
+if vim.fn.executable('deno') == 1 then
+  vim.lsp.enable('deno');
+else
+  local preferences = {
+    importModuleSpecifierPreference = 'non-relative',
+    -- this prevents renames from aliasing when destructuring
+    providePrefixAndSuffixTextForRename = false,
+  }
+
+  local helix_preferences = vim.tbl_get(
+    util.read_helix_config(),
+    'language-server',
+    'typescript-language-server',
+    'config',
+    'preferences'
+  )
+  if helix_preferences ~= nil then
+    preferences = vim.tbl_deep_extend('force', preferences, helix_preferences)
+  end
+
+  local function make_settings()
+    -- we disable formatting but these are still used when performing some code
+    -- actions
+    local format = {
+      indentSize = vim.bo.shiftwidth,
+      convertTabsToSpaces = vim.o.expandtab,
+    }
+    return {
+      javascript = { format = format },
+      typescript = { format = format },
+    }
+  end
+
+  vim.lsp.config('ts_ls', {
+    init_options = {
+      completionDisableFilterText = true,
+      preferences = preferences,
+    },
+    settings = make_settings(),
+    handlers = {
+      ['$/typescriptVersion'] = function(err, result, ctx, config)
+        vim.notify(string.format('Typescript %s', result.version))
+      end,
+    },
+    on_init = function(client)
+      -- mark tsserver as not having formatting available as we rely on
+      -- eslint and dprint for that
+      client.server_capabilities.documentFormattingProvider = false
+      client.server_capabilities.documentRangeFormattingProvider = false
+
+      -- we only really know our settings once we've opened a file so report
+      -- the new formatting settings
+      client:notify(vim.lsp.protocol.Methods.workspace_didChangeConfiguration, {
+        settings = make_settings(),
+      })
+    end,
+  })
+  vim.lsp.enable('ts_ls')
+end
+
+vim.lsp.enable('gopls')
+
+vim.lsp.config('ruby_lsp', {
+  init_options = {
+    enabledFeatures = {
+      formatting = false,
+    },
+  }
+})
+vim.lsp.enable('ruby_lsp')
+
+vim.lsp.enable('nil_ls')
+
+vim.lsp.config('jdtls', {
+  handlers = {
+    ['$/progress'] = function()
+      -- this is quite noisy so just disable it
+    end
+  },
+})
+vim.lsp.enable('jdtls')
+
+vim.lsp.enable('lua_ls')
+
+-- custom LSP servers
+vim.lsp.config('elvish', {
+  cmd = { 'elvish', '--lsp' },
+  filetypes = { 'elvish' },
+  settings = {},
+})
+vim.lsp.enable('elvish')
+
+-- diagnostics
+vim.keymap.set('n', '<space>e', function() vim.diagnostic.open_float() end)
+vim.keymap.set('n', '[d',
+  function() vim.diagnostic.jump({ count = -1, float = true, severity = vim.diagnostic.severity.ERROR }) end)
+vim.keymap.set('n', ']d',
+  function() vim.diagnostic.jump({ count = 1, float = true, severity = vim.diagnostic.severity.ERROR }) end)
+vim.keymap.set('n', '[D', function() vim.diagnostic.jump({ count = -1, float = true }) end)
+vim.keymap.set('n', ']D', function() vim.diagnostic.jump({ count = 1, float = true }) end)
+vim.keymap.set('n', '<space>qe', function() vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR }) end)
+vim.keymap.set('n', '<space>qw',
+  function() vim.diagnostic.setqflist({ severity = { min = vim.diagnostic.severity.WARN } }) end)
+vim.keymap.set('n', '<space>qd', function() vim.diagnostic.setqflist({}) end)
+vim.keymap.set('n', '<space>qc', '<cmd>cclose<CR>')
+
+-- LSP-specific
+vim.api.nvim_create_autocmd('LspAttach', {
+  callback = function(args)
+    local client = vim.lsp.get_client_by_id(args.data.client_id)
+
+    if client.name == 'dprint' or client.name == 'eslint' then
+      -- mappings should have been attached by typescript and re-attaching can
+      -- overwrite the typescript specific overrides
+      return
+    end
+
+    local opts = { buffer = args.buf }
+
+    vim.keymap.set('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
+    vim.keymap.set('n', 'gd', '<cmd>Telescope lsp_definitions<CR>', opts)
+    vim.keymap.set('n', '<space>D', '<cmd>Telescope lsp_type_definitions<CR>', opts)
+    -- as much as possible we try to use the default bindings, replacing them
+    -- with Telescope if appropriate
+    vim.keymap.set('n', 'gri', '<cmd>Telescope lsp_implementations<CR>', opts)
+    vim.keymap.set('n', 'grr', '<cmd>Telescope lsp_references<CR>', opts)
+    vim.keymap.set('n', 'gO', '<cmd>Telescope lsp_document_symbols<CR>', opts)
+
+    if client.name == 'ts_ls' then
+      -- exclude import statements from reference search (may have false positives)
+      vim.keymap.set('n', 'grr', '<cmd>Telescope lsp_references default_text=!import\\ <CR>', opts)
+      vim.keymap.set('n', 'grA', '<cmd>LspTypescriptSourceAction<CR>', opts)
+    end
+  end,
+})
+
+-- handle file renames
+vim.api.nvim_create_autocmd('User', {
+  pattern = 'MiniFilesActionRename',
+  callback = function(opts)
+    local params = {
+      files = { {
+        oldUri = vim.uri_from_fname(opts.data.from),
+        newUri = vim.uri_from_fname(opts.data.to),
+      } }
+    }
+    local bufnr = vim.fn.bufadd(opts.data.to)
+
+    local clients = vim.lsp.get_clients({ bufnr = bufnr })
+    for _, client in ipairs(clients) do
+      if client:supports_method('workspace/willRenameFiles') then
+        local resp = client:request_sync('workspace/willRenameFiles', params, 5000, bufnr)
+        if resp and resp.result ~= nil then
+          vim.lsp.util.apply_workspace_edit(resp.result, client.offset_encoding)
+        end
+      end
+    end
+
+    for _, client in ipairs(clients) do
+      if client:supports_method('workspace/didRenameFiles') then
+        client:notify('workspace/didRenameFiles', params)
+      end
+    end
+  end,
+  nested = true,
+})

+ 3 - 0
.config/nixpkgs/neovim/luadev.lua → .config/nixpkgs/neovim/config/lua/user/luadev.lua

@@ -1,3 +1,6 @@
+vim.cmd.packadd('nvim-luadev')
+
+local opts = { silent = true }
 vim.keymap.set('n', '<Leader>r', '<Plug>(Luadev-Run)', opts)
 vim.keymap.set('v', '<Leader>r', '<Plug>(Luadev-Run)', opts)
 

+ 36 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/completion.lua

@@ -0,0 +1,36 @@
+local blink_cmp = require('blink.cmp')
+
+blink_cmp.setup({
+  cmdline = {
+    enabled = false,
+  },
+  completion = {
+    accept = {
+      auto_brackets = {
+        enabled = false,
+      },
+    },
+    documentation = {
+      auto_show = true,
+      auto_show_delay_ms = 500,
+    },
+    trigger = {
+      prefetch_on_insert = false,
+      show_on_keyword = false,
+      show_on_trigger_character = false,
+    },
+    menu = {
+      draw = {
+        columns = {
+          { 'label', 'label_description', gap = 1 },
+          { 'kind' },
+        },
+      },
+    },
+  },
+  keymap = {
+    ['<Enter>'] = { 'select_and_accept', 'fallback' },
+    ['<C-u>'] = { 'scroll_documentation_up', 'fallback' },
+    ['<C-d>'] = { 'scroll_documentation_down', 'fallback' },
+  },
+})

+ 82 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/editing.lua

@@ -0,0 +1,82 @@
+local MiniAi = require('mini.ai')
+local MiniAlign = require('mini.align')
+local MiniComment = require('mini.comment')
+local MiniPairs = require('mini.pairs')
+local MiniSurround = require('mini.surround')
+
+-- comment actions
+MiniComment.setup()
+-- keymap: overrides default vim gc / gcc
+
+-- surround actions
+MiniSurround.setup({
+  custom_surroundings = {
+    -- js template string
+    ['$'] = {
+      output = {
+        left = '`${',
+        right = '}`',
+      },
+    },
+  },
+})
+-- keymap:
+-- sa (surround add)
+-- sr (surround replace)
+-- sd (surround delete)
+
+local spec_treesitter = require('mini.ai').gen_spec.treesitter
+
+MiniAi.setup({
+  -- only consider the current location
+  search_method = 'cover',
+  custom_textobjects = {
+    ['.'] = spec_treesitter({
+      a = '@call.outer',
+      i = '@call.inner',
+    }),
+    [','] = spec_treesitter({
+      a = '@parameter.outer',
+      i = '@parameter.inner',
+    }),
+  },
+});
+
+-- align actions
+MiniAlign.setup()
+-- keymap: ga / gA
+
+-- Treesitter-aware split/join
+require('treesj').setup({
+  use_default_keymaps = false,
+})
+
+vim.keymap.set('n', '<Leader>s', '<cmd>TSJSplit<CR>')
+vim.keymap.set('n', '<Leader>j', '<cmd>TSJJoin<CR>')
+
+-- autopair brackets
+MiniPairs.setup({
+  mappings = {
+    -- default config includes (, [ and {
+
+    -- autopair <> if preceded by a character, otherwise it might be a regular
+    -- comparison operation
+    ['<'] = { action = 'open', pair = '<>', neigh_pattern = '%w.' },
+    ['>'] = { action = 'close', pair = '<>' },
+  },
+})
+
+local function toggle_end_char(char)
+  local cursor = vim.api.nvim_win_get_cursor(0)
+  local row = cursor[1] - 1
+  local end_char = vim.api.nvim_buf_get_text(0, row, -2, row, -1, {})[1]
+  if end_char == char then
+    vim.api.nvim_buf_set_text(0, row, -2, row, -1, {})
+  else
+    vim.api.nvim_buf_set_text(0, row, -1, row, -1, { char })
+  end
+end
+
+-- add ,/; to end of line
+vim.keymap.set('i', '<C-,>', function() toggle_end_char(',') end)
+vim.keymap.set('i', '<C-;>', function() toggle_end_char(';') end)

+ 41 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/git.lua

@@ -0,0 +1,41 @@
+local MiniDiff = require('mini.diff')
+local util = require('user.util')
+
+-- shows added/removed/changed lines
+MiniDiff.setup({
+  -- keymap #bracketed
+  -- ]h / [h - navigate by hunks
+  view = {
+    style = 'sign',
+    signs = {
+      delete = '_',
+    },
+  },
+  source = {
+    MiniDiff.gen_source.git(),
+    -- handle fugitive paths
+    {
+      name = 'fugitive',
+      attach = function(buf_id)
+        local resolved = util.resolve_git_path(buf_id)
+        if not resolved or resolved.path == '' then
+          return false
+        end
+
+        local source = vim.fn.FugitiveFind(string.format('%s~1:%s', resolved.commit, resolved.path))
+        local text = vim.fn['fugitive#readfile'](source)
+        MiniDiff.set_ref_text(buf_id, text)
+      end
+    }
+  }
+})
+
+-- open status in current split without special flag (so navigating away closes it)
+-- https://github.com/tpope/vim-fugitive/issues/1296
+vim.keymap.set('n', '<Leader>gs', '<cmd>Gedit :<CR>')
+vim.keymap.set('n', '<Leader>gb', '<cmd>Telescope git_branches<CR>')
+vim.keymap.set('n', '<Leader>gd', MiniDiff.toggle_overlay)
+
+-- conflicts navigation #bracketed
+vim.keymap.set('n', '[x', '?^[<lt>=>]\\{7}<CR>')
+vim.keymap.set('n', ']x', '/^[<lt>=>]\\{7}<CR>')

+ 12 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/init.lua

@@ -0,0 +1,12 @@
+require('user.plugins.completion')
+require('user.plugins.editing')
+require('user.plugins.git')
+require('user.plugins.navigation')
+require('user.plugins.telescope')
+require('user.plugins.treesitter')
+require('user.plugins.ui')
+
+-- misc
+
+-- undotree
+vim.keymap.set('n', '<Leader>ut', '<cmd>UndotreeToggle<CR>')

+ 28 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/navigation.lua

@@ -0,0 +1,28 @@
+local MiniJump = require('mini.jump')
+local Treewalker = require('treewalker')
+
+-- repeatable f/t
+MiniJump.setup({
+  mappings = {
+    repeat_jump = '',
+  },
+  delay = {
+    highlight = 10000000,
+  },
+})
+
+-- Treesitter navigation
+Treewalker.setup()
+
+vim.keymap.set({ 'n', 'v' }, '<C-k>', '<cmd>Treewalker Up<cr>')
+vim.keymap.set({ 'n', 'v' }, '<C-j>', '<cmd>Treewalker Down<cr>')
+vim.keymap.set({ 'n', 'v' }, '<C-h>', '<cmd>Treewalker Left<cr>')
+vim.keymap.set({ 'n', 'v' }, '<C-l>', '<cmd>Treewalker Right<cr>')
+
+-- jumplist navigation #bracketed
+vim.keymap.set('n', '[f', '<C-O>')
+vim.keymap.set('n', ']f', '<C-I>')
+
+-- window navigation #bracketed
+vim.keymap.set('n', '[w', '<C-w>W')
+vim.keymap.set('n', ']w', '<C-w>w')

+ 167 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/telescope.lua

@@ -0,0 +1,167 @@
+-- file/buffer/etc picker
+local telescope = require('telescope')
+local telescope_actions = require('telescope.actions')
+local telescope_builtin = require('telescope.builtin')
+local telescope_pickers = require('telescope.pickers')
+local telescope_finders = require('telescope.finders')
+local telescope_previewers = require('telescope.previewers')
+local telescope_putils = require('telescope.previewers.utils')
+local telescope_conf = require('telescope.config').values
+local action_state = require('telescope.actions.state')
+local util = require('user.util')
+
+local actions = {}
+
+actions.git_show_commit = function(prompt_bufnr)
+  local selection = action_state.get_selected_entry()
+  local path = vim.fn.FugitiveFind(selection.value)
+  telescope_actions.close(prompt_bufnr)
+  vim.cmd.edit(path)
+end
+
+local commit_mappings = {
+  i = {
+    ['<CR>'] = actions.git_show_commit,
+    ['<C-r>c'] = telescope_actions.git_checkout,
+  }
+}
+
+telescope.setup({
+  defaults = {
+    mappings = {
+      i = {
+        ['jj'] = 'close',
+      },
+    },
+    layout_config = {
+      prompt_position = 'top',
+    },
+    sorting_strategy = 'ascending',
+    -- use filename as preview window title
+    dynamic_preview_title = true,
+
+    path_display = {
+      -- shorten directory names of everything but the last 3 parts
+      -- foo/bar/baz/file.txt -> f/boo/bar/file.txt
+      shorten = { len = 1, exclude = { -3, -2, -1 } },
+
+      -- truncate the beginning of the file name if wider than the window
+      truncate = true,
+    },
+
+    preview = {
+      -- don't preview files larger than 1MB
+      filesize_limit = 1,
+      timeout = 500,
+    },
+
+    vimgrep_arguments = {
+      -- defaults
+      'rg',
+      '--color=never',
+      '--no-heading',
+      '--with-filename',
+      '--line-number',
+      '--column',
+      '--smart-case',
+      -- search "hidden" files except git folder
+      '--hidden',
+      '--iglob=!.git'
+    },
+
+    -- ignore things we're likely not to edit
+    file_ignore_patterns = {
+      '%.zip$',
+      '%.yarn/releases/',
+      '%.yarn/plugins/'
+    },
+
+    -- picker history
+    cache_picker = {
+      num_pickers = 10,
+    },
+  },
+  pickers = {
+    buffers = {
+      sort_lastused = true,
+      sort_mru = true,
+      mappings = {
+        i = {
+          ['<C-k>'] = 'delete_buffer'
+        },
+      },
+    },
+    find_files = {
+      find_command = { 'fd', '--type', 'f', '--strip-cwd-prefix' }
+    },
+    git_commits = {
+      mappings = commit_mappings,
+    },
+    git_bcommits = {
+      mappings = commit_mappings,
+    },
+  },
+})
+
+-- use native sorter for better performance
+telescope.load_extension('fzf')
+
+-- custom picker to fallback to files if no git
+local function project_files()
+  local ok = pcall(telescope_builtin.git_files, { show_untracked = true })
+  if not ok then telescope_builtin.find_files({}) end
+end
+
+-- custom picker for files within a commit
+local function commit_files(opts)
+  local resolved = util.resolve_git_path(0)
+  if not resolved then
+    vim.print('current file is not a fugitive path')
+    return
+  end
+
+  opts = opts or {}
+  telescope_pickers.new(opts, {
+    prompt_title = resolved.commit,
+    finder = telescope_finders.new_oneshot_job({ 'git', 'ls-tree', '--name-only', '-r', resolved.commit }, {
+      entry_maker = function(entry)
+        local path = string.format('fugitive://%s//%s/%s', resolved.repo, resolved.commit, entry)
+        return {
+          path = path,
+          value = entry,
+          display = entry,
+          ordinal = entry,
+        }
+      end,
+    }),
+    sorter = telescope_conf.file_sorter(opts),
+
+    -- the builtin previewer has fancy async loading which doesn't work for
+    -- fugitive paths so we have to define our own
+    previewer = telescope_previewers.new_buffer_previewer({
+      title = function(self)
+        return 'Commit Files'
+      end,
+      dyn_title = function(self, entry)
+        return entry.value
+      end,
+      define_preview = function(self, entry, status)
+        -- the builtin previewer does more things like using mime type
+        -- fallbacks as well as binary file detection which ours doesn't do
+        local ft = telescope_putils.filetype_detect(entry.value)
+
+        vim.api.nvim_buf_call(self.state.bufnr, function()
+          vim.cmd('Gread ' .. entry.path)
+          telescope_putils.highlighter(self.state.bufnr, ft, opts)
+        end)
+      end,
+    }),
+  }):find()
+end
+
+vim.keymap.set('n', '<C-P>', project_files)
+vim.keymap.set('n', '<C-O>', '<cmd>Telescope buffers<CR>')
+vim.keymap.set('n', '<Leader>fg', commit_files)
+vim.keymap.set('n', '<Leader>ff', '<cmd>Telescope live_grep<CR>')
+vim.keymap.set('n', '<Leader>fr', '<cmd>Telescope resume<CR>')
+vim.keymap.set('n', '<Leader>fp', '<cmd>Telescope pickers<CR>')

+ 39 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/treesitter.lua

@@ -0,0 +1,39 @@
+-- Use Treesitter for syntax highlighting
+require('nvim-treesitter.configs').setup({
+  highlight = {
+    enable = true,
+  },
+  indent = {
+    enable = true,
+  },
+  incremental_selection = {
+    enable = true,
+    keymaps = {
+      init_selection = ']t',
+      node_incremental = ']t',
+      node_decremental = '[t',
+    },
+  },
+  textobjects = {
+    swap = {
+      enable = true,
+      swap_next = {
+        ['>,'] = '@parameter.inner',
+      },
+      swap_previous = {
+        ['<,'] = '@parameter.inner',
+      },
+    },
+  },
+})
+
+-- set foldexpr to use treesitter
+vim.o.foldmethod = 'expr'
+vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
+vim.o.foldenable = false
+
+-- Treesitter context
+require('treesitter-context').setup({
+  enable = true,
+  multiline_threshold = 5,
+})

+ 63 - 0
.config/nixpkgs/neovim/config/lua/user/plugins/ui.lua

@@ -0,0 +1,63 @@
+local MiniBufremove = require('mini.bufremove')
+local MiniFiles = require('mini.files')
+local MiniIndentscope = require('mini.indentscope')
+local MiniNotify = require('mini.notify')
+local MiniStatusline = require('mini.statusline')
+
+local function section_git(args)
+  if MiniStatusline.is_truncated(args.trunc_width) then return '' end
+  return vim.fn.FugitiveHead()
+end
+
+MiniStatusline.setup({
+  content = {
+    -- copy-pasted from default, we just want to remove the icon
+    active = function()
+      local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
+      local git           = section_git({ trunc_width = 75 })
+      local diagnostics   = MiniStatusline.section_diagnostics({ trunc_width = 75, icon = '' })
+      local filename      = MiniStatusline.section_filename({ trunc_width = 140 })
+      local fileinfo      = MiniStatusline.section_fileinfo({ trunc_width = 120 })
+      local location      = MiniStatusline.section_location({ trunc_width = 75 })
+
+      return MiniStatusline.combine_groups({
+        { hl = mode_hl,                 strings = { mode } },
+        { hl = 'MiniStatuslineDevinfo', strings = { git, diagnostics } },
+        '%<', -- Mark general truncate point
+        { hl = 'MiniStatuslineFilename', strings = { filename } },
+        '%=', -- End left alignment
+        { hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
+        { hl = mode_hl,                  strings = { location } },
+      })
+    end
+  },
+})
+
+-- notifications
+MiniNotify.setup()
+vim.notify = MiniNotify.make_notify()
+
+vim.keymap.set('n', '<Leader>n', MiniNotify.show_history)
+
+-- file explorer
+MiniFiles.setup({
+  content = {
+    -- remove icons
+    prefix = function() end,
+  }
+})
+
+vim.keymap.set('n', '<Leader>ft', function() MiniFiles.open(MiniFiles.get_latest_path()) end)
+vim.keymap.set('n', '<Leader>fc', function() MiniFiles.open(vim.api.nvim_buf_get_name(0)) end)
+
+-- delete buffer while preserving layout
+MiniBufremove.setup()
+
+vim.keymap.set('n', '<Leader>q', MiniBufremove.delete)
+vim.keymap.set('n', '<Leader>Q', function() MiniBufremove.delete(0, true) end)
+
+-- shows a line indicating the current indentation scope
+MiniIndentscope.setup()
+
+-- cursor display
+vim.keymap.set('n', '<Leader>c', '<cmd>set cursorline! cursorcolumn!<CR>')

+ 6 - 0
.config/nixpkgs/neovim/config/lua/user/rest.lua

@@ -0,0 +1,6 @@
+vim.cmd.packadd('kulala.nvim')
+
+require('kulala').setup({
+  global_keymaps = true,
+  global_keymaps_prefix = '<leader>r'
+})

+ 35 - 5
.config/nixpkgs/neovim/settings.lua → .config/nixpkgs/neovim/config/lua/user/settings.lua

@@ -50,9 +50,39 @@ vim.o.inccommand = 'split'
 -- mouse only in visual mode
 vim.o.mouse = 'v'
 
--- set foldexpr to use treesitter
-vim.o.foldmethod = 'expr'
-vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
-vim.o.foldenable = false
-
+-- use POSIX-y shell for !
 vim.o.shell = '/bin/sh'
+
+-- basic keymaps
+-- ; as :
+vim.keymap.set({ 'n', 'v' }, ';', ':')
+
+-- leave insert mode via jj
+vim.keymap.set('i', 'jj', '<ESC>')
+
+-- j/k with wraps
+vim.keymap.set({ 'n', 'v' }, 'j', 'gj')
+vim.keymap.set({ 'n', 'v' }, 'k', 'gk')
+
+-- select pasted text
+vim.keymap.set('n', 'gp', '`[v`]')
+
+-- format on save
+vim.api.nvim_create_autocmd('BufWritePre', {
+  callback = function(opts)
+    if vim.bo.filetype == 'diff' then
+      return
+    end
+
+    if not vim.g.no_lsp_format then
+      -- check if can LSP format
+      local clients = vim.lsp.get_clients({ bufnr = opts.buf, method = 'textDocument/formatting' })
+      if #clients > 0 then
+        vim.lsp.buf.format({ bufnr = opts.buf })
+      end
+    end
+
+    -- otherwise strip trailing whitespace
+    vim.cmd('%s/\\s\\+$//e')
+  end,
+})

+ 41 - 0
.config/nixpkgs/neovim/config/lua/user/terminal.lua

@@ -0,0 +1,41 @@
+-- apply default terminal settings
+vim.api.nvim_create_autocmd('TermOpen', {
+  callback = function()
+    vim.bo.scrollback = 10000
+    vim.opt_local.number = false
+    vim.opt_local.relativenumber = false
+    vim.b.miniindentscope_disable = true
+  end
+})
+
+-- preserve window structure when exiting terminal via C-d
+vim.api.nvim_create_autocmd('TermClose', {
+  callback = function(opts)
+    -- don't trigger when force deleting
+    if vim.api.nvim_buf_get_option(opts.buf, 'modified') then
+      return
+    end
+    MiniBufremove.delete(opts.buf)
+  end,
+  -- needed so statusline properly updates
+  nested = true,
+})
+
+-- automatically enter/leave terminal mode
+vim.api.nvim_create_autocmd('TermOpen', { command = 'startinsert' })
+vim.api.nvim_create_autocmd({ 'WinEnter', 'BufWinEnter' }, {
+  pattern = 'term://*',
+  command = 'startinsert',
+})
+vim.api.nvim_create_autocmd('BufLeave', {
+  pattern = 'term://*',
+  command = 'stopinsert',
+})
+
+-- leave insert mode with <ESC><ESC>
+vim.keymap.set('t', '<ESC><ESC>', '<C-\\><C-N>')
+vim.keymap.set('t', '<C-[><C-[>', '<C-\\><C-N>')
+
+-- opening terminals
+vim.keymap.set('n', '<Leader>tv', '<cmd>vertical term $SHELL<CR>')
+vim.keymap.set('n', '<Leader>to', '<cmd>term $SHELL<CR>')

+ 2 - 2
.config/nixpkgs/neovim/theme.lua → .config/nixpkgs/neovim/config/lua/user/theme.lua

@@ -18,7 +18,7 @@ local active_ns = vim.api.nvim_create_namespace('active')
 
 function init_active_ns()
   local hls = vim.api.nvim_get_hl(0, {})
-  for k,v in ipairs(hls) do
+  for k, v in ipairs(hls) do
     vim.api.nvim_set_hl(active_ns, k, v)
   end
 
@@ -49,7 +49,7 @@ vim.api.nvim_create_autocmd('ColorScheme', {
     vim.api.nvim_set_hl(0, 'MiniIndentscopeSymbol', { link = 'Whitespace' })
 
     local configuration = vim.fn['sonokai#get_configuration']()
-    local palette = vim.fn['sonokai#get_palette'](configuration.style, {[vim.type_idx]=vim.types.dictionary})
+    local palette = vim.fn['sonokai#get_palette'](configuration.style, { [vim.type_idx] = vim.types.dictionary })
 
     vim.fn['sonokai#highlight']('MiniStatuslineModeNormal', palette.black, palette.blue)
     vim.fn['sonokai#highlight']('MiniStatuslineModeCommand', palette.black, palette.yellow)

+ 40 - 0
.config/nixpkgs/neovim/config/lua/user/util.lua

@@ -0,0 +1,40 @@
+local M = {}
+
+function M.resolve_git_path(buf_id)
+  local bufname = vim.api.nvim_buf_get_name(buf_id)
+  if not vim.startswith(bufname, 'fugitive://') then
+    return false
+  end
+
+  local parsed = vim.fn.FugitiveParse(bufname)
+  local resolved_path = parsed[1]
+  local repo = parsed[2]
+
+  if resolved_path == '' then
+    return false
+  end
+
+  local parts = vim.split(resolved_path, ':')
+  local commit = parts[1]
+  local path = parts[2]
+
+  return {
+    repo = repo,
+    commit = commit,
+    path = path,
+  }
+end
+
+function M.read_helix_config()
+  local tinytoml = require('tinytoml')
+  local data = vim.secure.read('.helix/languages.toml')
+  if data ~= nil then
+    local status, data = pcall(tinytoml.parse, data, { load_from_string = true })
+    if status then
+      return data
+    end
+  end
+  return {}
+end
+
+return M

+ 6 - 0
.config/nixpkgs/neovim/config/plugin/elvish.lua

@@ -0,0 +1,6 @@
+-- add extra filetypes for plenary
+require('plenary.filetype').add_table({
+  extension = {
+    ['elv'] = [[elvish]]
+  }
+})

+ 6 - 1
.config/nixpkgs/neovim/flake.nix

@@ -29,9 +29,14 @@
       in
       {
         packages.neovim = pkgs.callPackage ./neovim.nix {};
-        packages.neovim-with-luadev = pkgs.callPackage ./neovim.nix { withLuadev = true; };
         packages.neovim-minimal = (pkgs.callPackage ./neovim.nix {}).minimal;
         packages.default = self.packages.${system}.neovim;
+
+        devShells.default = pkgs.mkShell {
+          packages = with pkgs; [
+            lua-language-server
+          ];
+        };
       }
     );
 }

+ 0 - 124
.config/nixpkgs/neovim/lsp.lua

@@ -1,124 +0,0 @@
-vim.diagnostic.config({
-  -- only show virtual text for WARN and higher
-  virtual_text = { severity = { min = vim.diagnostic.severity.WARN } },
-})
-
-vim.lsp.config("*", {
-  flags = {
-    debounce_text_changes = 250,
-  }
-})
-
-local function read_helix_config()
-  local tinytoml = require("tinytoml")
-  local data = vim.secure.read(".helix/languages.toml")
-  if data ~= nil then
-    local status, data = pcall(tinytoml.parse, data, { load_from_string = true })
-    if status then
-      return data
-    end
-  end
-  return {}
-end
-
-vim.lsp.enable("bashls");
-vim.lsp.enable("eslint");
-
-if vim.fn.executable("dprint") == 1 then
-  local version = vim.version.parse(vim.fn.system("dprint --version"))
-  if vim.version.cmp(version, {0,45,0}) >= 0 then
-    vim.lsp.enable("dprint")
-  end
-end
-
-if vim.fn.executable("deno") == 1 then
-  vim.lsp.enable("deno");
-else
-  local preferences = {
-    importModuleSpecifierPreference = 'non-relative',
-    -- this prevents renames from aliasing when destructuring
-    providePrefixAndSuffixTextForRename = false,
-  }
-
-  local helix_preferences = vim.tbl_get(read_helix_config(), 'language-server', 'typescript-language-server', 'config', 'preferences')
-  if helix_preferences ~= nil then
-    preferences = vim.tbl_deep_extend('force', preferences, helix_preferences)
-  end
-
-  local function make_settings()
-    -- we disable formatting but these are still used when performing some code
-    -- actions
-    local format = {
-      indentSize = vim.bo.shiftwidth,
-      convertTabsToSpaces = vim.o.expandtab,
-    }
-    return {
-      javascript = { format = format },
-      typescript = { format = format },
-    }
-  end
-
-  vim.lsp.config("ts_ls", {
-    init_options = {
-      completionDisableFilterText = true,
-      preferences = preferences,
-    },
-    settings = make_settings(),
-    handlers = {
-      ['$/typescriptVersion'] = function(err, result, ctx, config)
-        vim.notify(string.format('Typescript %s', result.version))
-      end,
-    },
-    on_init = function(client)
-      -- mark tsserver as not having formatting available as we rely on
-      -- eslint and dprint for that
-      client.server_capabilities.documentFormattingProvider = false
-      client.server_capabilities.documentRangeFormattingProvider = false
-
-      -- we only really know our settings once we've opened a file so report
-      -- the new formatting settings
-      client:notify(vim.lsp.protocol.Methods.workspace_didChangeConfiguration, {
-        settings = make_settings(),
-      })
-    end,
-  })
-  vim.lsp.enable("ts_ls")
-end
-
-if vim.fn.executable("gopls") == 1 then
-  vim.lsp.enable("gopls")
-end
-
-if vim.fn.executable("ruby-lsp") == 1 then
-  vim.lsp.config("ruby_lsp", {
-    init_options = {
-      enabledFeatures = {
-        formatting = false,
-      },
-    }
-  })
-  vim.lsp.enable("ruby_lsp")
-end
-
-if vim.fn.executable("nil") == 1 then
-  vim.lsp.enable("nil_ls")
-end
-
-if vim.fn.executable("jdtls") == 1 then
-  vim.lsp.config("jdtls", {
-    handlers = {
-      ["$/progress"] = function()
-        -- this is quite noisy so just disable it
-      end
-    },
-  })
-  vim.lsp.enable("jdtls")
-end
-
--- custom LSP servers
-vim.lsp.config("elvish", {
-  cmd = {'elvish', '--lsp'},
-  filetypes = {'elvish'},
-  settings = {},
-})
-vim.lsp.enable("elvish")

+ 0 - 119
.config/nixpkgs/neovim/mappings.lua

@@ -1,119 +0,0 @@
-local opts = { silent = true }
-
--- ; as :
-vim.keymap.set({'n', 'v'}, ';', ':')
-
--- leave insert mode via jj
-vim.keymap.set('i', 'jj', '<ESC>', opts)
-
--- j/k with wraps
-vim.keymap.set({'n', 'v'}, 'j', 'gj', opts)
-vim.keymap.set({'n', 'v'}, 'k', 'gk', opts)
-
--- treesitter navigation
-vim.keymap.set({ 'n', 'v' }, '<C-k>', '<cmd>Treewalker Up<cr>', opts)
-vim.keymap.set({ 'n', 'v' }, '<C-j>', '<cmd>Treewalker Down<cr>', opts)
-vim.keymap.set({ 'n', 'v' }, '<C-h>', '<cmd>Treewalker Left<cr>', opts)
-vim.keymap.set({ 'n', 'v' }, '<C-l>', '<cmd>Treewalker Right<cr>', opts)
-
--- leave insert mode with <ESC><ESC>
-vim.keymap.set('t', '<ESC><ESC>', '<C-\\><C-N>', opts)
-vim.keymap.set('t', '<C-[><C-[>', '<C-\\><C-N>', opts)
-
--- select pasted text
-vim.keymap.set('n', 'gp', '`[v`]', opts)
-
--- pickers
-vim.keymap.set('n', '<C-P>', '<cmd>lua project_files()<CR>', opts)
-vim.keymap.set('n', '<C-O>', '<cmd>Telescope buffers<CR>', opts)
-vim.keymap.set('n', '<Leader>fg', '<cmd>lua commit_files()<CR>', opts)
-vim.keymap.set('n', '<Leader>ff', '<cmd>Telescope live_grep<CR>', opts)
-vim.keymap.set('n', '<Leader>fr', '<cmd>Telescope resume<CR>', opts)
-vim.keymap.set('n', '<Leader>fp', '<cmd>Telescope pickers<CR>', opts)
-
--- directory tree
-vim.keymap.set('n', '<Leader>ft', function() MiniFiles.open(MiniFiles.get_latest_path()) end, opts)
-vim.keymap.set('n', '<Leader>fc', function() MiniFiles.open(vim.api.nvim_buf_get_name(0)) end, opts)
-
--- jumplist navigation
-vim.keymap.set('n', '[f', '<C-O>', opts)
-vim.keymap.set('n', ']f', '<C-I>', opts)
-
--- conflicts navigation
-vim.keymap.set('n', '[x', '?^[<lt>=>]\\{7}<CR>', opts)
-vim.keymap.set('n', ']x', '/^[<lt>=>]\\{7}<CR>', opts)
-
--- window navigation
-vim.keymap.set('n', '[w', '<C-w>W', opts)
-vim.keymap.set('n', ']w', '<C-w>w', opts)
-
--- cursor display
-vim.keymap.set('n', '<Leader>c', '<cmd>set cursorline! cursorcolumn!<CR>', opts)
-
--- notification history
-vim.keymap.set('n', '<Leader>n', require('mini.notify').show_history, opts)
-
--- git
--- open status in current split without special flag (so navigating away closes it)
--- https://github.com/tpope/vim-fugitive/issues/1296
-vim.keymap.set('n', '<Leader>gs', '<cmd>Gedit :<CR>', opts)
-vim.keymap.set('n', '<Leader>gb', '<cmd>Telescope git_branches<CR>', opts)
-vim.keymap.set('n', '<Leader>gd', function() MiniDiff.toggle_overlay() end, opts)
-
--- undotree
-vim.keymap.set('n', '<Leader>ut', '<cmd>UndotreeToggle<CR>', opts)
-
--- opening terminals
-vim.keymap.set('n', '<Leader>tv', '<cmd>vertical term $SHELL<CR>', opts)
-vim.keymap.set('n', '<Leader>to', '<cmd>term $SHELL<CR>', opts)
-
--- delete buffer
-vim.keymap.set('n', '<Leader>q', '<cmd>lua MiniBufremove.delete()<CR>', opts)
-vim.keymap.set('n', '<Leader>Q', '<cmd>lua MiniBufremove.delete(0, true)<CR>', opts)
-
--- split/join
-vim.keymap.set('n', '<Leader>s', '<cmd>TSJSplit<CR>', opts)
-vim.keymap.set('n', '<Leader>j', '<cmd>TSJJoin<CR>', opts)
-
--- add ,/; to end of line
-vim.keymap.set('i', '<C-,>', function() toggle_end_char(',') end, opts)
-vim.keymap.set('i', '<C-;>', function() toggle_end_char(';') end, opts)
-
--- diagnostics
-vim.keymap.set('n', '<space>e', function() vim.diagnostic.open_float() end, opts)
-vim.keymap.set('n', '[d', function() vim.diagnostic.jump({ count = -1, float = true, severity = vim.diagnostic.severity.ERROR }) end, opts)
-vim.keymap.set('n', ']d', function() vim.diagnostic.jump({ count = 1, float = true, severity = vim.diagnostic.severity.ERROR }) end, opts)
-vim.keymap.set('n', '[D', function() vim.diagnostic.jump({ count = -1, float = true }) end, opts)
-vim.keymap.set('n', ']D', function() vim.diagnostic.jump({ count = 1, float = true }) end, opts)
-vim.keymap.set('n', '<space>qe', function() vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR }) end, opts)
-vim.keymap.set('n', '<space>qw', function() vim.diagnostic.setqflist({ severity = { min = vim.diagnostic.severity.WARN } }) end, opts)
-vim.keymap.set('n', '<space>qd', function() vim.diagnostic.setqflist({}) end, opts)
-vim.keymap.set('n', '<space>qc', '<cmd>cclose<CR>', opts)
-
--- LSP-specific
-vim.api.nvim_create_autocmd('LspAttach', {
-  callback = function(args)
-    local client = vim.lsp.get_client_by_id(args.data.client_id)
-
-    if client.name == 'dprint' or client.name == 'eslint' then
-      -- mappings should have been attached by typescript and re-attaching can
-      -- overwrite the typescript specific overrides
-      return
-    end
-
-    local opts = { silent = true, buffer = args.buf }
-
-    vim.keymap.set('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
-    vim.keymap.set('n', 'gd', '<cmd>Telescope lsp_definitions<CR>', opts)
-    vim.keymap.set('n', 'gri', '<cmd>Telescope lsp_implementations<CR>', opts)
-    vim.keymap.set('n', '<space>D', '<cmd>Telescope lsp_type_definitions<CR>', opts)
-    vim.keymap.set('n', 'grr', '<cmd>Telescope lsp_references<CR>', opts)
-    vim.keymap.set('n', 'gO', '<cmd>Telescope lsp_document_symbols<CR>', opts)
-
-    if client.name == 'ts_ls' then
-      -- exclude import statements from reference search (may have false positives)
-      vim.keymap.set('n', 'grr', '<cmd>Telescope lsp_references default_text=!import\\ <CR>', opts)
-      vim.keymap.set('n', 'grA', '<cmd>LspTypescriptSourceAction<CR>', opts)
-    end
-  end,
-})

+ 35 - 58
.config/nixpkgs/neovim/neovim.nix

@@ -2,7 +2,6 @@
 , stdenv
 , runCommand
 , writeShellScriptBin
-, writeTextFile
 , neovim-unwrapped
 , makeWrapper
 , tree-sitter
@@ -13,7 +12,6 @@
 , blink-cmp
 , fetchFromGitHub
 , fetchpatch
-, withLuadev ? false
 }:
 let
   buildNeovimPlugin = attrs: stdenv.mkDerivation ({
@@ -27,23 +25,6 @@ let
       fi
     '';
   } // attrs);
-  extra-treesitter-textobjects = writeTextFile {
-    name = "extra-treesitter-textobjects";
-    destination = "/queries/ecma/textobjects.scm";
-    text = ''
-      ;; extends
-
-      ; consider array elements as @parameter text objects too
-      (array
-        "," @_start .
-        (_) @parameter.inner
-        (#make-range! "parameter.outer" @_start @parameter.inner))
-      (array
-        . (_) @parameter.inner
-        . ","? @_end
-        (#make-range! "parameter.outer" @parameter.inner @_end))
-    '';
-  };
   tsc = writeShellScriptBin "tsc" ''
     if [ -x "./node_modules/.bin/tsc" ]; then
       exec ./node_modules/.bin/tsc "$@"
@@ -70,27 +51,31 @@ let
 
   plugins = (builtins.filter (p: !p.optional) (lib.attrValues pinnedPlugins))
     ++ (lib.attrValues treesitterPlugins)
-    ++ [
-      extra-treesitter-textobjects
-      blink-cmp
-    ] ++ lib.optionals withLuadev [
-      pinnedPlugins."bfredl/nvim-luadev"
-    ];
-
-  generic = { initText ? "", enabledPlugins ? [], passthru ? {} }: stdenv.mkDerivation {
+    ++ [ blink-cmp ];
+
+  optionalPlugins = builtins.filter (p: p.optional) (lib.attrValues pinnedPlugins);
+
+  generic = { minimal ? false , startPlugins ? [] , optPlugins ? [] , passthru ? {} }: stdenv.mkDerivation {
     pname = "nvim";
     version = neovim-unwrapped.version;
 
-    initVim = ''
-      let g:loaded_python3_provider = 0
-      let g:loaded_ruby_provider = 0
-      let g:loaded_node_provider = 0
-      let g:loaded_perl_provider = 0
+    initLua = ''
+      vim.g.loaded_python3_provider = 0
+      vim.g.loaded_ruby_provider = 0
+      vim.g.loaded_node_provider = 0
+      vim.g.loaded_perl_provider = 0
+
+      vim.o.runtimepath = table.concat({
+        '${placeholder "out"}/lib',
+        vim.env.VIMRUNTIME,
+        '${placeholder "out"}/lib/after',
+      }, ',')
 
-      set runtimepath=${placeholder "out"}/lib,$VIMRUNTIME
-      set packpath=${placeholder "out"}/lib,$VIMRUNTIME
+      vim.o.packpath = table.concat({
+        '${placeholder "out"}/lib',
+        vim.env.VIMRUNTIME,
+      }, ',')
 
-      lua <<EOF
       -- make sure docs in our packpath are marked as help
       vim.filetype.add({
         pattern = {
@@ -98,12 +83,11 @@ let
           ['.*/pack/.*/doc/.*%.txt'] = 'help',
         }
       })
-      EOF
 
-      ${initText}
+      ${if minimal then "" else "require('user')"}
     '';
 
-    passAsFile = [ "initVim" ];
+    passAsFile = [ "initLua" ];
 
     nativeBuildInputs = [ makeWrapper ];
 
@@ -114,15 +98,19 @@ let
       mkdir -p lib/pack/nixpkgs/start
       ${lib.concatMapStringsSep "\n" (p: ''
         ln -s "${p}" "lib/pack/nixpkgs/start/${lib.getName p}"
-      '') enabledPlugins}
+      '') startPlugins}
 
-      # copy in overrides
-      mkdir -p lib/pack/nixpkgs/start
-      cp -r ${./overrides} lib/pack/nixpkgs/start/overrides
+      mkdir -p lib/pack/nixpkgs/opt
+      ${lib.concatMapStringsSep "\n" (p: ''
+        ln -s "${p}" "lib/pack/nixpkgs/opt/${lib.getName p}"
+      '') optPlugins}
+
+      # copy in config
+      cp -r ${./config}/. lib/
 
       # create config file
       mkdir etc
-      cp "$initVimPath" etc/init.vim
+      cp "$initLuaPath" etc/init.lua
 
       # symlink in man pages
       mkdir -p share
@@ -133,7 +121,7 @@ let
       makeWrapper ${neovim-unwrapped}/bin/nvim bin/nvim \
         --prefix PATH : ${lib.makeBinPath extraPath} \
         --add-flags -u \
-        --add-flags $out/etc/init.vim
+        --add-flags $out/etc/init.lua
     '';
 
     inherit passthru;
@@ -145,21 +133,10 @@ let
   };
 in
 generic {
-  initText = ''
-    source ${./settings.lua}
-    source ${./plugins.lua}
-    source ${./mappings.lua}
-    source ${./autocmd.lua}
-    source ${./lsp.lua}
-    source ${./commands.lua}
-    source ${./theme.lua}
-  '' + lib.optionalString withLuadev ''
-    source ${./luadev.lua}
-  '';
-
-  enabledPlugins = plugins;
+  startPlugins = plugins;
+  optPlugins = optionalPlugins;
 
   passthru = {
-    minimal = generic {};
+    minimal = generic { minimal = true; };
   };
 }

+ 0 - 6
.config/nixpkgs/neovim/overrides/after/compiler/typescript.lua

@@ -1,6 +0,0 @@
-if vim.fn.executable("tsgo") == 1 then
-  vim.bo.makeprg = "tsgo --noEmit --pretty false $*"
-else
-  vim.bo.makeprg = "tsc --noEmit --incremental $*"
-end
-vim.bo.errorformat = "%+A %#%f %#(%l\\,%c): %m,%C%m"

+ 0 - 433
.config/nixpkgs/neovim/plugins.lua

@@ -1,433 +0,0 @@
--- add extra filetypes for plenary
-require('plenary.filetype').add_table({
-  extension = {
-    ['elv'] = [[elvish]]
-  }
-})
-
--- add extra builtin filetypes
-vim.filetype.add({
-  pattern = {
-    ['.*%.ts$'] = 'typescript'
-  },
-})
-
--- helpers
-local function resolve_git_path(buf_id)
-  local bufname = vim.api.nvim_buf_get_name(buf_id)
-  if not vim.startswith(bufname, "fugitive://") then
-    return false
-  end
-
-  local parsed = vim.fn.FugitiveParse(bufname)
-  local resolved_path = parsed[1]
-  local repo = parsed[2]
-
-  if resolved_path == "" then
-    return false
-  end
-
-  local parts = vim.split(resolved_path, ':')
-  local commit = parts[1]
-  local path = parts[2]
-
-  return {
-    repo = repo,
-    commit = commit,
-    path = path,
-  }
-end
-
--- file/buffer/etc picker
-local telescope_actions = require('telescope.actions')
-local action_state = require('telescope.actions.state')
-local actions = {}
-
-actions.git_show_commit = function(prompt_bufnr)
-  local selection = action_state.get_selected_entry()
-  local path = vim.fn.FugitiveFind(selection.value)
-  telescope_actions.close(prompt_bufnr)
-  vim.cmd.edit(path)
-end
-
-local commit_mappings = {
-  i = {
-    ['<CR>'] = actions.git_show_commit,
-    ['<C-r>c'] = telescope_actions.git_checkout,
-  }
-}
-
-require('telescope').setup({
-  defaults = {
-    mappings = {
-      i = {
-        ['jj'] = 'close',
-      },
-    },
-    layout_config = {
-      prompt_position = 'top',
-    },
-    sorting_strategy = 'ascending',
-    -- use filename as preview window title
-    dynamic_preview_title = true,
-
-    path_display = {
-      -- shorten directory names of everything but the last 3 parts
-      -- foo/bar/baz/file.txt -> f/boo/bar/file.txt
-      shorten = { len = 1, exclude = { -3, -2, -1 } },
-
-      -- truncate the beginning of the file name if wider than the window
-      truncate = true,
-    },
-
-    preview = {
-      -- don't preview files larger than 1MB
-      filesize_limit = 1,
-      timeout = 500,
-    },
-
-    vimgrep_arguments = {
-      -- defaults
-      "rg",
-      "--color=never",
-      "--no-heading",
-      "--with-filename",
-      "--line-number",
-      "--column",
-      "--smart-case",
-      -- search "hidden" files except git folder
-      "--hidden",
-      "--iglob=!.git"
-    },
-
-    -- ignore things we're likely not to edit
-    file_ignore_patterns = {
-      "%.zip$",
-      "%.yarn/releases/",
-      "%.yarn/plugins/"
-    },
-
-    -- picker history
-    cache_picker = {
-      num_pickers = 10,
-    },
-  },
-  pickers = {
-    buffers = {
-      sort_lastused = true,
-      sort_mru = true,
-      mappings = {
-        i = {
-          ['<C-k>'] = 'delete_buffer'
-        },
-      },
-    },
-    find_files = {
-      find_command = { "fd", "--type", "f", "--strip-cwd-prefix" }
-    },
-    git_commits = {
-      mappings = commit_mappings,
-    },
-    git_bcommits = {
-      mappings = commit_mappings,
-    },
-  },
-})
-
--- use native sorter for better performance
-require('telescope').load_extension('fzf')
-
-local telescope_builtin = require('telescope.builtin')
-local telescope_pickers = require('telescope.pickers')
-local telescope_finders = require('telescope.finders')
-local telescope_previewers = require('telescope.previewers')
-local telescope_putils = require('telescope.previewers.utils')
-local telescope_conf = require('telescope.config').values
-
--- custom picker to fallback to files if no git
-_G.project_files = function()
-  local ok = pcall(telescope_builtin.git_files, { show_untracked = true })
-  if not ok then telescope_builtin.find_files({}) end
-end
-
--- custom picker for files within a commit
-_G.commit_files = function(opts)
-  local resolved = resolve_git_path(0)
-  if not resolved then
-    vim.print("current file is not a fugitive path")
-    return
-  end
-
-  opts = opts or {}
-  telescope_pickers.new(opts, {
-    prompt_title = resolved.commit,
-    finder = telescope_finders.new_oneshot_job({ "git", "ls-tree", "--name-only", "-r", resolved.commit }, {
-      entry_maker = function(entry)
-        local path = string.format("fugitive://%s//%s/%s", resolved.repo, resolved.commit, entry)
-        return {
-          path = path,
-          value = entry,
-          display = entry,
-          ordinal = entry,
-        }
-      end,
-    }),
-    sorter = telescope_conf.file_sorter(opts),
-
-    -- the builtin previewer has fancy async loading which doesn't work for
-    -- fugitive paths so we have to define our own
-    previewer = telescope_previewers.new_buffer_previewer({
-      title = function(self)
-        return 'Commit Files'
-      end,
-      dyn_title = function(self, entry)
-        return entry.value
-      end,
-      define_preview = function(self, entry, status)
-        -- the builtin previewer does more things like using mime type
-        -- fallbacks as well as binary file detection which ours doesn't do
-        local ft = telescope_putils.filetype_detect(entry.value)
-
-        vim.api.nvim_buf_call(self.state.bufnr, function()
-          vim.cmd('Gread ' .. entry.path)
-          telescope_putils.highlighter(self.state.bufnr, ft, opts)
-        end)
-      end,
-    }),
-  }):find()
-end
-
--- shows added/removed/changed lines
-local MiniDiff = require('mini.diff')
-MiniDiff.setup({
-  view = {
-    style = 'sign',
-    signs = {
-      delete = '_',
-    },
-  },
-  source = {
-    MiniDiff.gen_source.git(),
-    -- handle fugitive paths
-    {
-      name = "fugitive",
-      attach = function(buf_id)
-        local resolved = resolve_git_path(buf_id)
-        if not resolved or resolved.path == "" then
-          return false
-        end
-
-        local source = vim.fn.FugitiveFind(string.format("%s~1:%s", resolved.commit, resolved.path))
-        local text = vim.fn['fugitive#readfile'](source)
-        MiniDiff.set_ref_text(buf_id, text)
-      end
-    }
-  }
-})
-
-local function section_git(args)
-  if MiniStatusline.is_truncated(args.trunc_width) then return '' end
-  return vim.fn.FugitiveHead()
-end
-
-require('mini.statusline').setup({
-  content = {
-    -- copy-pasted from default, we just want to remove the icon
-    active = function()
-      local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
-      local git           = section_git({ trunc_width = 75 })
-      local diagnostics   = MiniStatusline.section_diagnostics({ trunc_width = 75, icon = '' })
-      local filename      = MiniStatusline.section_filename({ trunc_width = 140 })
-      local fileinfo      = MiniStatusline.section_fileinfo({ trunc_width = 120 })
-      local location      = MiniStatusline.section_location({ trunc_width = 75 })
-
-      return MiniStatusline.combine_groups({
-        { hl = mode_hl,                  strings = { mode } },
-        { hl = 'MiniStatuslineDevinfo',  strings = { git, diagnostics } },
-        '%<', -- Mark general truncate point
-        { hl = 'MiniStatuslineFilename', strings = { filename } },
-        '%=', -- End left alignment
-        { hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
-        { hl = mode_hl,                  strings = { location } },
-      })
-    end
-  },
-})
-
--- delete buffer while preserving layout
-require('mini.bufremove').setup()
-
--- shows a line indicating the current indentation scope
-require('mini.indentscope').setup()
-
--- comment actions
-require('mini.comment').setup()
-
--- surround actions
-require('mini.surround').setup({
-  custom_surroundings = {
-    -- js template string
-    ['$'] = {
-      output = {
-        left = '`${',
-        right = '}`',
-      },
-    },
-  },
-})
-
-local spec_treesitter = require('mini.ai').gen_spec.treesitter
-
-require('mini.ai').setup({
-  -- only consider the current location
-  search_method = 'cover',
-  custom_textobjects = {
-    ['.'] = spec_treesitter({
-      a = '@call.outer',
-      i = '@call.inner',
-    }),
-    [','] = spec_treesitter({
-      a = '@parameter.outer',
-      i = '@parameter.inner',
-    }),
-  },
-});
-
--- align actions
-require('mini.align').setup()
-
--- repeatable f/t
-require('mini.jump').setup({
-  mappings = {
-    repeat_jump = '',
-  },
-  delay = {
-    highlight = 10000000,
-  },
-})
-
--- autopair brackets
-require('mini.pairs').setup({
-  mappings = {
-    -- default config includes (, [ and {
-
-    -- autopair <> if preceded by a character, otherwise it might be a regular
-    -- comparison operation
-    ['<'] = { action = 'open', pair = '<>', neigh_pattern = '%w.' },
-    ['>'] = { action = 'close', pair = '<>' },
-  },
-})
-
--- notifications
-require('mini.notify').setup()
-vim.notify = require('mini.notify').make_notify()
-
--- file explorer
-require('mini.files').setup({
-  content = {
-    -- remove icons
-    prefix = function() end,
-  }
-})
-
--- rest client
-require('kulala').setup({
-  global_keymaps = true,
-  global_keymaps_prefix = '<leader>r'
-})
-
--- Use Treesitter for syntax highlighting
-require('nvim-treesitter.configs').setup({
-  highlight = {
-    enable = true,
-  },
-  indent = {
-    enable = true,
-  },
-  incremental_selection = {
-    enable = true,
-    keymaps = {
-      init_selection = "]t",
-      node_incremental = "]t",
-      node_decremental = "[t",
-    },
-  },
-  textobjects = {
-    swap = {
-      enable = true,
-      swap_next = {
-        ['>,'] = '@parameter.inner',
-      },
-      swap_previous = {
-        ['<,'] = '@parameter.inner',
-      },
-    },
-  },
-})
-
-local tsj_utils = require('treesj.langs.utils')
-
--- Treesitter-aware split/join
-require('treesj').setup({
-  use_default_keymaps = false,
-})
-
--- Treesitter context
-require('treesitter-context').setup({
-  enable = true,
-  multiline_threshold = 5,
-})
-
--- Treesitter navigation
-require('treewalker').setup()
-
--- completion
-require('blink.cmp').setup({
-  cmdline = {
-    enabled = false,
-  },
-  completion = {
-    accept = {
-      auto_brackets = {
-        enabled = false,
-      },
-    },
-    documentation = {
-      auto_show = true,
-      auto_show_delay_ms = 500,
-    },
-    trigger = {
-      prefetch_on_insert = false,
-      show_on_keyword = false,
-      show_on_trigger_character = false,
-    },
-    menu = {
-      draw = {
-        columns = {
-          { "label", "label_description", gap = 1 },
-          { "kind" },
-        },
-      },
-    },
-  },
-  keymap = {
-    ['<Enter>'] = { 'select_and_accept', 'fallback' },
-    ['<C-u>'] = { 'scroll_documentation_up', 'fallback' },
-    ['<C-d>'] = { 'scroll_documentation_down', 'fallback' },
-  },
-})
-
--- typescript-vim compiler options
-vim.g.typescript_compiler_options = '--incremental --noEmit'
-
--- stuff
-_G.toggle_end_char = function(char)
-  local cursor = vim.api.nvim_win_get_cursor(0)
-  local row = cursor[1] - 1
-  local end_char = vim.api.nvim_buf_get_text(0, row, -2, row, -1, {})[1]
-  if end_char == char then
-    vim.api.nvim_buf_set_text(0, row, -2, row, -1, {})
-  else
-    vim.api.nvim_buf_set_text(0, row, -1, row, -1, { char })
-  end
-end

+ 1 - 0
.config/nixpkgs/neovim/plugins/sources.json

@@ -69,6 +69,7 @@
     "sha256": "1jglqdc6sc593841694sn9p2hswg2iqifwjij6d553l228k8k8pl"
   },
   "mistweaverco/kulala.nvim": {
+    "optional": true,
     "rev": "6b6e1c8b538cce6654cfc5fb3e4a3acfa316ce57",
     "sha256": "07sgyqnlsm0zgxfg5ir3kmxbhy53h7bw14hiylvixdbp1x3xhn3h"
   },

+ 6 - 0
.config/nixpkgs/neovim/treesitter/default.nix

@@ -27,6 +27,12 @@ let
     } ''
       mkdir -p $out/parser
       ln -s ${grammar}/parser $out/parser/${name}.so
+
+      ${lib.optionalString (builtins.hasAttr "branch" spec) ''
+        if [ -d ${grammar}/queries ]; then
+          ln -s ${grammar}/queries $out/queries
+        fi
+      ''}
     '';
 in
 builtins.mapAttrs buildTreesitterPlugin grammars