diff --git a/config/neovim/after/ftplugin/gitcommit.lua b/config/neovim/after/ftplugin/gitcommit.lua new file mode 100644 index 0000000..f1aaed5 --- /dev/null +++ b/config/neovim/after/ftplugin/gitcommit.lua @@ -0,0 +1,8 @@ +vim.opt_local.colorcolumn = "50,72" +vim.opt_local.spell = true +vim.opt_local.textwidth = 72 + +-- autocmd FileType gitcommit highlight ColorColumn ctermbg=8 +-- filetype indent on +-- filetype on +-- filetype plugin on diff --git a/config/neovim/after/ftplugin/markdown.lua b/config/neovim/after/ftplugin/markdown.lua new file mode 100644 index 0000000..5d96d74 --- /dev/null +++ b/config/neovim/after/ftplugin/markdown.lua @@ -0,0 +1,4 @@ +-- TODO: Interim fix for https://github.com/nvim-treesitter/nvim-treesitter-context/issues/431. +require("treesitter-context").disable() + +vim.opt_local.wrap = true; diff --git a/config/neovim/after/ftplugin/rst.lua b/config/neovim/after/ftplugin/rst.lua new file mode 100644 index 0000000..7aa8eee --- /dev/null +++ b/config/neovim/after/ftplugin/rst.lua @@ -0,0 +1,13 @@ +local opt = vim.opt_local + +opt.spell = true +opt.wrap = true + +local cmp = require "cmp" +local sources = cmp.get_config().sources + +-- TODO: confirm these aren't aleady in the list of sources to avoid duplicate suggestions. +table.insert(sources, { name = "buffer" }) +table.insert(sources, { name = "path" }) + +cmp.setup.buffer { sources = sources } diff --git a/config/neovim/after/ftplugin/term.vim b/config/neovim/after/ftplugin/term.vim new file mode 100644 index 0000000..16bbc95 --- /dev/null +++ b/config/neovim/after/ftplugin/term.vim @@ -0,0 +1,4 @@ +setlocal norelativenumber +setlocal nonumber + +setlocal scrolloff=0 diff --git a/config/neovim/autoload/opdavies.vim b/config/neovim/autoload/opdavies.vim new file mode 100644 index 0000000..449666b --- /dev/null +++ b/config/neovim/autoload/opdavies.vim @@ -0,0 +1,12 @@ +if !exists('*opdavies#save_and_exec') + function! opdavies#save_and_exec() abort + if &filetype == 'vim' + :silent! write + :source % + elseif &filetype == 'lua' + :silent! write + :luafile % + endif + return + endfunction +endif diff --git a/config/neovim/lua/opdavies/globals.lua b/config/neovim/lua/opdavies/globals.lua new file mode 100644 index 0000000..d6fd70c --- /dev/null +++ b/config/neovim/lua/opdavies/globals.lua @@ -0,0 +1,13 @@ +P = function(v) + print(vim.inspect(v)) + return v +end + +RELOAD = function(...) + return require("plenary.reload").reload_module(...) +end + +R = function(name) + RELOAD(name) + return require(name) +end diff --git a/config/neovim/lua/opdavies/init.lua b/config/neovim/lua/opdavies/init.lua new file mode 100644 index 0000000..7e5bc84 --- /dev/null +++ b/config/neovim/lua/opdavies/init.lua @@ -0,0 +1,6 @@ +pcall("require", impatient) + +require "opdavies.globals" +require "opdavies.keymaps" +require "opdavies.options" +require "opdavies.lsp" diff --git a/config/neovim/lua/opdavies/keymaps.lua b/config/neovim/lua/opdavies/keymaps.lua new file mode 100644 index 0000000..1693bc6 --- /dev/null +++ b/config/neovim/lua/opdavies/keymaps.lua @@ -0,0 +1,109 @@ +local set = vim.keymap.set + +set("n", "so", ":call opdavies#save_and_exec()") + +-- Format paragraphs to an 80 character line length. +set("n", "g", "gqap") +set("x", "g", "gqa") + +-- Make the current file executable +set("n", "x", ":!chmod +x %") + +-- Yank from the current column to the end of the line +set("n", "Y", "yg$") + +-- Keep things centred +set("n", "n", "nzzzv") +set("n", "N", "Nzzzv") + +-- Disable up and down arrow keys. +set("v", "", "") +set("v", "", "") + +-- Use the left and right arrow keys to change tabs. +set("v", "", "gT") +set("v", "", "gt") + +-- Easily switch back to visual mode. +set("i", "jk", "") + +-- Easy insertion of a trailing ; or , from insert mode +set("i", ",,", "A,") +set("i", ";;", "A;") + +set("n", "ga", "(EasyAlign)") +set("x", "ga", "(EasyAlign)") + +-- Focus on the current buffer. +set("n", "-", ":wincmd _:wincmd |", { noremap = true, silent = true }) + +-- Automatically resize buffers. +set("n", "=", ":wincmd =", { noremap = true, silent = true }) + +-- Move line(s) up and down. +local opts = { noremap = true, silent = true } +set("i", "", ":m .+1==gi", opts) +set("i", "", ":m .-2==gi", opts) +set("n", "", ":m .+1==", opts) +set("n", "", ":m .-2==", opts) +set("v", "", ":m '>+1gv=gv", opts) +set("v", "", ":m '<-2gv=gv", opts) + +-- Re-centre when navigating. +set("n", "#", "#zz", opts) +set("n", "%", "%zz", opts) +set("n", "*", "*zz", opts) +set("n", "", "zz", opts) +set("n", "", "zz", opts) +set("n", "", "zz", opts) +set("n", "", "zz", opts) +set("n", "G", "Gzz", opts) +set("n", "N", "Nzz", opts) +set("n", "gg", "ggzz", opts) +set("n", "n", "Nzz", opts) +set("n", "{", "{zz", opts) +set("n", "}", "}zz", opts) + +-- Clears hlsearch after doing a search, otherwise just does normal stuff +vim.cmd [[ nnoremap {-> v:hlsearch ? ":nohl\" : "\"}() ]] + +-- Quicker macro playback. +set("n", "Q", "@qj") +set("x", "Q", ":norm @q") + +-- Easier navigation between splits. +set("n", "", "") +set("n", "", "") +set("n", "", "") +set("n", "", "") + +set("v", "Q", "") + +set("v", "J", ":m '>+1gvrgv") +set("v", "K", ":m '<-2gv=gv") + +set("n", "J", "mzJ`z") +set("n", "", "zz") +set("n", "", "zz") +set("n", "n", "nzzzv") +set("n", "N", "Nzzzv") + +-- Easily access project-specific notes. +set("n", "en", function() + if vim.fn.filereadable ".ignored/notes" == 1 then + vim.cmd "tabnew .ignored/notes" + else + vim.cmd "tabnew notes" + end +end) + +-- Easily access project-specific todos. +set("n", "et", function() + if vim.fn.filereadable ".ignored/todo" == 1 then + vim.cmd "tabnew .ignored/todo" + else + vim.cmd "tabnew todo" + end +end) + +set("n", "ec", ":edit composer.json") diff --git a/config/neovim/lua/opdavies/lsp/handlers.lua b/config/neovim/lua/opdavies/lsp/handlers.lua new file mode 100644 index 0000000..e44e86a --- /dev/null +++ b/config/neovim/lua/opdavies/lsp/handlers.lua @@ -0,0 +1,71 @@ +local M = {} + +local function should_remove_diagnostic(messages_to_filter, message) + for _, filter_message in ipairs(messages_to_filter) do + if message:match(filter_message) then + return true + end + end + + return false +end + +M.definition = function() + local params = vim.lsp.util.make_position_params() + + vim.lsp.buf_request(0, "textDocument/definition", params, function(err, result, ctx, config) + local bufnr = ctx.bufnr + local ft = vim.api.nvim_buf_get_option(bufnr, "filetype") + + local new_result = vim.tbl_filter(function(v) + -- Remove any definitions within the nix store via the .direnv directory. + if string.find(v.targetUri, ".direnv") then + return false + end + + -- Remove definitions within vendor-bin directory for PHP files. + if ft == "php" then + if string.find(v.targetUri, "vendor%-bin") then + return false + end + end + + return true + end, result) + + if #new_result > 0 then + result = new_result + end + + vim.lsp.handlers["textDocument/definition"](err, result, ctx, config) + vim.cmd [[normal! zz]] + end) +end + +M.on_publish_diagnostics = function(_, result, ctx, config) + local client = vim.lsp.get_client_by_id(ctx.client_id) + + if client.name == "cssls" then + local filtered_diagnostics = {} + + local messages_to_filter = { + "Unknown at rule @apply", + "Unknown at rule @tailwind", + } + + -- For each diagnostic, ensure its mesages doesn't match one I want to + -- ignore before adding it to the result. If it matches, don't add it to the + -- result and it won't be shown. + for _, diagnostic in ipairs(result.diagnostics) do + if not should_remove_diagnostic(messages_to_filter, diagnostic.message) then + table.insert(filtered_diagnostics, diagnostic) + end + end + + result.diagnostics = filtered_diagnostics + end + + vim.lsp.diagnostic.on_publish_diagnostics(_, result, ctx, config) +end + +return M diff --git a/config/neovim/lua/opdavies/lsp/init.lua b/config/neovim/lua/opdavies/lsp/init.lua new file mode 100644 index 0000000..6ae90f7 --- /dev/null +++ b/config/neovim/lua/opdavies/lsp/init.lua @@ -0,0 +1,126 @@ +local lspconfig = require "lspconfig" +local nvim_status = require "lsp-status" + +local handlers = require "opdavies.lsp.handlers" + +require("neodev").setup {} + +local servers = { + bashls = true, + + cssls = { + on_attach = function(client, bufnr) + vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(handlers.on_publish_diagnostics, {}) + end, + }, + + gopls = true, + html = true, + + intelephense = { + filetypes = { "php", "module", "test", "inc" }, + }, + + lua_ls = { + settings = { + Lua = { + completion = { + callSnippet = "Replace", + }, + + diagnostics = { + globals = { "vim" }, + }, + + runtime = { + version = "LuaJIT", + }, + + telemetry = { + enabled = false, + }, + + workspace = { + library = vim.api.nvim_get_runtime_file("", true), + }, + }, + }, + }, + + marksman = true, + nil_ls = true, + + tailwindcss = { + filetypes = { "html", "javascript", "twig", "typescript", "vue" }, + + settings = { + init_options = { + userLanguages = { + ["html.twig"] = "html", + }, + }, + }, + }, + + terraformls = true, + tsserver = true, + vuels = true, + + yamlls = { + settings = { + yaml = { + keyOrdering = false, + }, + }, + }, +} + +local capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities()) + +for server_name, config in pairs(servers) do + if config == true then + config = {} + end + + config = vim.tbl_deep_extend("force", {}, { + capabilities = capabilities, + }, config) + + lspconfig[server_name].setup(config) +end + +vim.diagnostic.config { + float = { source = true }, + signs = true, + underline = false, + update_in_insert = false, + virtual_text = { spacing = 2 }, +} + +vim.api.nvim_create_autocmd("LspAttach", { + callback = function() + local builtin = require "telescope.builtin" + + -- buf_inoremap { "", vim.lsp.buf.signature_help } + -- buf_nnoremap { "ca", vim.lsp.buf.code_action } + -- buf_nnoremap { "d", vim.diagnostic.open_float } + -- buf_nnoremap { "rn", vim.lsp.buf.rename } + -- buf_nnoremap { "rr", "LspRestart" } + -- buf_nnoremap { "[d", vim.diagnostic.goto_prev } + -- buf_nnoremap { "]d", vim.diagnostic.goto_next } + + -- buf_nnoremap { "gD", vim.lsp.buf.declaration } + -- buf_nnoremap { "gd", handlers.definition } + -- buf_nnoremap { "gi", vim.lsp.buf.implementation } + -- buf_nnoremap { "gT", vim.lsp.buf.type_definition } + + vim.keymap.set("n", "gd", builtin.lsp_definitions, { buffer = 0 }) + vim.keymap.set("n", "gr", builtin.lsp_references, { buffer = 0 }) + vim.keymap.set("n", "gD", vim.lsp.buf.declaration, { buffer = 0 }) + vim.keymap.set("n", "gT", vim.lsp.buf.type_definition, { buffer = 0 }) + vim.keymap.set("n", "K", vim.lsp.buf.hover, { buffer = 0 }) + + vim.keymap.set("n", "cr", vim.lsp.buf.rename, { buffer = 0 }) + vim.keymap.set("n", "ca", vim.lsp.buf.code_action, { buffer = 0 }) + end, +}) diff --git a/config/neovim/lua/opdavies/options.lua b/config/neovim/lua/opdavies/options.lua new file mode 100644 index 0000000..cb6a589 --- /dev/null +++ b/config/neovim/lua/opdavies/options.lua @@ -0,0 +1,52 @@ +vim.g.mapleader = " " +vim.g.snippets = "luasnip" + +local settings = { + autoindent = true, + backup = false, + breakindent = true, + colorcolumn = "80", + expandtab = true, + foldlevel = 1, + foldlevelstart = 99, + foldmethod = "indent", + formatoptions = "clqjp", + hidden = false, + hlsearch = false, + inccommand = "split", + laststatus = 3, + linebreak = true, + list = true, + mouse = "", + number = true, + pumblend = 10, + pumheight = 10, + relativenumber = true, + scrolloff = 5, + shiftwidth = 2, + showmode = false, + signcolumn = "yes:1", + smartindent = true, + softtabstop = 2, + spellfile = "/home/opdavies/Code/opdavies.nvim/spell/en.utf-8.add", + swapfile = false, + syntax = "on", + tabstop = 2, + termguicolors = true, + textwidth = 0, + undodir = os.getenv "HOME" .. "/.vim/undodir", + undofile = true, + updatetime = 1000, + wrap = false, +} + +for key, value in pairs(settings) do + vim.o[key] = value +end + +vim.opt.backupdir:remove "." -- keep backups out of the current directory +vim.opt.clipboard:append "unnamedplus" +vim.opt.completeopt = { "menu", "menuone", "noinsert", "noselect" } +vim.opt.listchars:append { + trail = "ยท", +} diff --git a/config/neovim/lua/opdavies/snippets/ft/bash.lua b/config/neovim/lua/opdavies/snippets/ft/bash.lua new file mode 100644 index 0000000..4859a80 --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/bash.lua @@ -0,0 +1,31 @@ +local ls = require "luasnip" + +local fmta = require("luasnip.extras.fmt").fmta + +return { + run = fmta( + [=[ + #!/usr/bin/env bash + + set -o errexit + set -o nounset + set -o pipefail + + function help { + printf "%s <> [args]\n\nTasks:\n" "${0}" + + compgen -A function | grep -v "^_" | cat -n + + printf "\nExtended help:\n Each task has comments for general usage\n" + } + + # Include any local tasks. + # https://stackoverflow.com/a/6659698 + [[ -e "${BASH_SOURCE%/*}/run.local" ]] && source "${BASH_SOURCE%/*}/run.local" + + TIMEFORMAT="Task completed in %3lR" + time "${@:-help}" + ]=], + {} + ), +} diff --git a/config/neovim/lua/opdavies/snippets/ft/javascript.lua b/config/neovim/lua/opdavies/snippets/ft/javascript.lua new file mode 100644 index 0000000..d3e795f --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/javascript.lua @@ -0,0 +1,10 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" + +local i = ls.insert_node + +local M = { + log = fmta("console.log(<>);", { i(1, "value") }), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/lua.lua b/config/neovim/lua/opdavies/snippets/ft/lua.lua new file mode 100644 index 0000000..c2abfba --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/lua.lua @@ -0,0 +1,27 @@ +local ls = require "luasnip" + +local fmt = require("luasnip.extras.fmt").fmt +local rep = require("luasnip.extras").rep + +local f, i = ls.function_node, ls.insert_node + +return { + pcall = fmt( + [[ + local status_ok, {} = pcall(require, "{}") + if not status_ok then + return + end + ]], + { i(1), rep(1) } + ), + + req = fmt([[local {} = require "{}"]], { + f(function(import_name) + local parts = vim.split(import_name[1][1], ".", true) + + return parts[#parts] or "" + end, { 1 }), + i(1), + }), +} diff --git a/config/neovim/lua/opdavies/snippets/ft/markdown.lua b/config/neovim/lua/opdavies/snippets/ft/markdown.lua new file mode 100644 index 0000000..b2a76e4 --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/markdown.lua @@ -0,0 +1,20 @@ +local fmt = require("luasnip.extras.fmt").fmt +local ls = require "luasnip" + +local i = ls.insert_node + +local M = { + frontmatter = fmt( + [[ + --- + title: {} + --- + {} + ]], + { i(1), i(0) } + ), + + link = fmt([[[{}]({}){} ]], { i(1), i(2), i(0) }), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/nix.lua b/config/neovim/lua/opdavies/snippets/ft/nix.lua new file mode 100644 index 0000000..1ef44d5 --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/nix.lua @@ -0,0 +1,23 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" + +local c = ls.choice_node +local i = ls.insert_node +local t = ls.text_node + +local M = { + vimplugin = fmta( + [[ + { + plugin = <>.<>; + type = "lua"; + config = '' + <> + ''; + }<> + ]], + { c(1, { t "vimPlugins", t "customVim" }), i(2), i(3), i(0) } + ), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/php.lua b/config/neovim/lua/opdavies/snippets/ft/php.lua new file mode 100644 index 0000000..77ecd67 --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/php.lua @@ -0,0 +1,115 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" + +local c = ls.choice_node +local f = ls.function_node +local i = ls.insert_node +local t = ls.text_node + +local M = { + __construct = fmta( + [[ + public function __construct(<>) { + <> + } + ]], + { i(1), i(0) } + ), + + __invoke = fmta( + [[ + public function __invoke(<>) { + <> + } + ]], + { i(1), i(0) } + ), + + drupalclass = fmta( + [[ + <; + + final class <> { + + <> + + }]], + { + f(function() + local filepath = vim.fn.expand "%:h" + local filepath_parts = vim.fn.split(filepath, "/") + + if not vim.tbl_contains(filepath_parts, "src") then + return "" + end + + local namespace_parts = { "Drupal" } + + local is_test_file = vim.tbl_contains(filepath_parts, "tests") + if is_test_file then + table.insert(namespace_parts, "Tests") + end + + -- Find and add the module name. + for k, v in ipairs(filepath_parts) do + if v == "src" then + if is_test_file then + table.insert(namespace_parts, filepath_parts[k - 2]) + else + table.insert(namespace_parts, filepath_parts[k - 1]) + end + end + end + + -- Add the rest of the namespace. + local namespace = vim.split(filepath, "src/") + local final_part = (namespace[2] or ""):gsub("/", "\\") + table.insert(namespace_parts, final_part) + + return table.concat(namespace_parts, "\\") + end), + f(function() + return vim.fn.expand "%:t:r" + end), + i(0), + } + ), + + func = fmta("function <>(<>)<> {\n <>\n}<>", { i(1), i(2), i(3), i(4), i(0) }), + + met = fmta( + [[ + <> function <>(<>)<> { + <> + }<> + ]], + { c(1, { t "public", t "protected", t "private" }), i(2), i(3), i(4), i(5), i(0) } + ), + + pest = fmta("<>('<>', function() {\n <>\n});", { c(1, { t "it", t "test" }), i(2), i(0) }), + + test = fmta( + [[ + public function test<>(): void { + <> + }<> + ]], + { i(1), i(2), i(0) } + ), + + testa = fmta( + [[ + /** @test */ + public function <>(): void { + <> + }<> + ]], + { i(1), i(2), i(0) } + ), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/rst.lua b/config/neovim/lua/opdavies/snippets/ft/rst.lua new file mode 100644 index 0000000..206bbf1 --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/rst.lua @@ -0,0 +1,49 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" + +local i = ls.insert_node +local f = ls.function_node + +local fill_line = function(char) + return function() + local row = vim.api.nvim_win_get_cursor(0)[1] + local lines = vim.api.nvim_buf_get_lines(0, row - 2, row, false) + return string.rep(char, #lines[1]) + end +end + +local M = { + class = { ".. class:: ", i(1) }, + footer = { ".. footer:: ", i(1) }, + link = { ".. _", i(1), ":" }, + raw = { ".. raw:: ", i(1) }, + + -- TODO: add an optional new line and ":width" property. + image = { ".. image:: ", i(1) }, + + head = f(fill_line "=", {}), + sub = f(fill_line "-", {}), + subsub = f(fill_line "^", {}), + + -- Add a page break with an optional page template. + pb = fmta( + [[ + .. raw:: pdf + + PageBreak<> + ]], + { i(0) } + ), + + -- Add a new speaker note. + ta = fmta( + [[ + .. raw:: pdf + + TextAnnotation "<>" + ]], + { i(0) } + ), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/scss.lua b/config/neovim/lua/opdavies/snippets/ft/scss.lua new file mode 100644 index 0000000..846cbcb --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/scss.lua @@ -0,0 +1,10 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" + +local i = ls.insert_node + +local M = { + bp = fmta("@include breakpoint(<>) {\n <>\n}", { i(1), i(0) }), +} + +return M diff --git a/config/neovim/lua/opdavies/snippets/ft/yaml.lua b/config/neovim/lua/opdavies/snippets/ft/yaml.lua new file mode 100644 index 0000000..4c5b3ce --- /dev/null +++ b/config/neovim/lua/opdavies/snippets/ft/yaml.lua @@ -0,0 +1,39 @@ +local fmta = require("luasnip.extras.fmt").fmta +local ls = require "luasnip" +local rep = require("luasnip.extras").rep + +local c = ls.choice_node +local i = ls.insert_node +local t = ls.text_node + +local M = { + drupal_info = fmta( + [[ + name: + description: + core_version_requirement: ^10 || ^11 + type: + package: + ]], + { module_name = i(1), description = i(2), type = c(3, { t "module", t "theme" }), package = i(0) } + ), + + drupal_route = fmta( + [[ + .: + path: / + defaults: + _controller: Drupal\\Controller\ + # _form: + # _title: + # _title_callback: + methods: [GET] + requirements: + _permission: access content + # _access: TRUE + ]], + { module = i(1), route = i(2), path = i(3), module_same = rep(1), class = i(4), finish = i(0) } + ), +} + +return M diff --git a/config/neovim/plugin/colorscheme.lua b/config/neovim/plugin/colorscheme.lua new file mode 100644 index 0000000..d949f5f --- /dev/null +++ b/config/neovim/plugin/colorscheme.lua @@ -0,0 +1,25 @@ +local status_ok, catppuccin = pcall(require, "catppuccin") +if not status_ok then + return +end + +catppuccin.setup { + flavour = "macchiato", + integrations = { + cmp = true, + gitsigns = true, + mini = { + enabled = true, + indentscope_color = "", + }, + native_lsp = { + enabled = true, + }, + telescope = true, + treesitter = true, + }, + term_colors = true, + transparent_background = true, +} + +vim.cmd.colorscheme "catppuccin" diff --git a/config/neovim/plugin/comment.lua b/config/neovim/plugin/comment.lua new file mode 100644 index 0000000..eb350eb --- /dev/null +++ b/config/neovim/plugin/comment.lua @@ -0,0 +1,19 @@ +local status_ok, comment = pcall(require, "Comment") +if not status_ok then + return +end + +comment.setup { + padding = true, + + opleader = { + line = "gc", + block = "gb", + }, + + mappings = { + basic = true, + extra = true, + extended = false, + }, +} diff --git a/config/neovim/plugin/completion.lua b/config/neovim/plugin/completion.lua new file mode 100644 index 0000000..ab21c53 --- /dev/null +++ b/config/neovim/plugin/completion.lua @@ -0,0 +1,155 @@ +local cmp = require "cmp" +local ls = require "luasnip" + +vim.opt.shortmess:append "c" + +cmp.setup { + snippet = { + expand = function(args) + ls.lsp_expand(args.body) + end, + }, + + mapping = cmp.mapping.preset.insert { + [""] = cmp.mapping.close(), + + [""] = cmp.mapping(function() + if ls.locally_jumpable(-1) then + ls.jump(-1) + end + end, { "i", "s" }), + + [""] = cmp.mapping(function() + if ls.expand_or_locally_jumpable() then + ls.expand_or_jump() + end + end, { "i", "s" }), + + [""] = cmp.mapping.confirm { select = true }, + [""] = cmp.config.disable, + }, + + sources = { + { name = "nvim_lsp" }, + { name = "nvim_lua" }, + { name = "luasnip" }, + { name = "buffer" }, + { name = "calc" }, + }, + + sorting = { + comparators = { + cmp.config.compare.offset, + cmp.config.compare.exact, + cmp.config.compare.score, + cmp.config.compare.kind, + cmp.config.compare.sort_text, + cmp.config.compare.length, + cmp.config.compare.order, + }, + }, + + formatting = { + format = require("lspkind").cmp_format { + with_text = true, + menu = { + buffer = "[buf]", + cmp_tabnine = "[tn]", + luasnip = "[snip]", + nvim_lsp = "[lsp]", + nvim_lua = "[lua]", + path = "[path]", + }, + }, + }, + + experimental = { + ghost_text = true, + native_menu = false, + }, +} + +cmp.setup.filetype({ "mysql", "sql" }, { + sources = { + { name = "vim-dadbod-completion" }, + { name = "buffer" }, + }, +}) + +local snippet = ls.snippet +local i = ls.insert_node +local t = ls.text_node + +local shortcut = function(val) + if type(val) == "string" then + return { t { val }, i(0) } + end + + if type(val) == "table" then + for k, v in ipairs(val) do + if type(v) == "string" then + val[k] = t { v } + end + end + end + + return val +end + +local make = function(tbl) + local result = {} + for k, v in pairs(tbl) do + table.insert(result, (snippet({ trig = k, desc = v.desc }, shortcut(v)))) + end + + return result +end + +local snippets = {} + +for _, ft_path in ipairs(vim.api.nvim_get_runtime_file("lua/opdavies/snippets/ft/*.lua", true)) do + local ft = vim.fn.fnamemodify(ft_path, ":t:r") + snippets[ft] = make(loadfile(ft_path)()) + + ls.add_snippets(ft, snippets[ft]) +end + +ls.add_snippets("js", snippets.javascript) +ls.add_snippets("typescript", snippets.javascript) +ls.add_snippets("vue", snippets.javascript) + +-- Include any snippets to use in presentations. +for _, ft_path in ipairs(vim.api.nvim_get_runtime_file("lua/opdavies/snippets/talks/*.lua", true)) do + loadfile(ft_path)() +end + +require("luasnip.loaders.from_vscode").lazy_load() + +ls.config.set_config { + enable_autosnippets = true, + history = true, + updateevents = "TextChanged,TextChangedI", +} + +-- Expand the current item or just to the next item within the snippet. +vim.keymap.set({ "i", "s" }, "", function() + if ls.expand_or_jumpable() then + ls.expand_or_jump() + end +end, { silent = true }) + +-- Jump backwards. +vim.keymap.set({ "i", "s" }, "", function() + if ls.jumpable(-1) then + ls.jump(-1) + end +end, { silent = true }) + +-- Select within a list of options. +vim.keymap.set("i", "", function() + if ls.choice_active() then + ls.change_choice(1) + end +end) + +vim.keymap.set("n", "s", "source ~/Code/opdavies.nvim/after/plugin/luasnip.lua") diff --git a/config/neovim/plugin/conform.lua b/config/neovim/plugin/conform.lua new file mode 100644 index 0000000..fc5ef78 --- /dev/null +++ b/config/neovim/plugin/conform.lua @@ -0,0 +1,45 @@ +local conform = require "conform" + +conform.setup { + formatters_by_ft = { + bash = { "shellcheck" }, + javascript = { { "prettierd", "prettier" } }, + just = { "just" }, + lua = { "stylua" }, + nix = { { "nixfmt" } }, + php = { { "php_cs_fixer", "phpcbf" } }, + terraform = { "terraform_fmt" }, + yaml = { "yamlfmt" }, + }, + + format_on_save = function(bufnr) + -- Disable with a global or buffer-local variable. + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + + return { + lsp_fallback = false, + quiet = true, + } + end, +} + +vim.api.nvim_create_user_command("FormatDisable", function(args) + if args.bang then + -- FormatDisable! will disable formatting just for this buffer + vim.b.disable_autoformat = true + else + vim.g.disable_autoformat = true + end +end, { + desc = "Disable autoformat-on-save", + bang = true, +}) + +vim.api.nvim_create_user_command("FormatEnable", function() + vim.b.disable_autoformat = false + vim.g.disable_autoformat = false +end, { + desc = "Re-enable autoformat-on-save", +}) diff --git a/config/neovim/plugin/dap.lua b/config/neovim/plugin/dap.lua new file mode 100644 index 0000000..f9c1f3c --- /dev/null +++ b/config/neovim/plugin/dap.lua @@ -0,0 +1,69 @@ +local dap = require "dap" +local ui = require "dapui" + +dap.adapters.php = { + type = "executable", + command = "node", + args = { os.getenv "HOME" .. "/build/vscode-php-debug/out/phpDebug.js" }, +} + +dap.configurations.php = { + { + type = "php", + request = "launch", + name = "Listen for Xdebug", + port = 9003, + pathMappings = { + ["/app"] = "${workspaceFolder}", + ["/var/www/html"] = "${workspaceFolder}", + }, + }, +} + +dap.listeners.after.event_initialized["ui_config"] = function() + ui.open() +end + +dap.listeners.before.event_terminated["ui_config"] = function() + ui.close() +end + +dap.listeners.before.event_exited["ui_config"] = function() + ui.close() +end + +ui.setup { + layouts = { + { + elements = { + { id = "scopes", size = 0.25 }, + "breakpoints", + "stacks", + "watches", + }, + size = 40, -- 40 columns + position = "right", + }, + { + elements = { + "repl", + "console", + }, + size = 0.25, -- 25% of total lines + position = "bottom", + }, + }, +} + +require("nvim-dap-virtual-text").setup { + commented = true, +} + +vim.keymap.set("n", "b", dap.toggle_breakpoint) +vim.keymap.set("n", "gb", dap.run_to_cursor) + +vim.keymap.set("n", "", dap.continue) +vim.keymap.set("n", "", dap.step_into) +vim.keymap.set("n", "", dap.step_over) +vim.keymap.set("n", "", dap.step_out) +vim.keymap.set("n", "", dap.step_back) diff --git a/config/neovim/plugin/dial.lua b/config/neovim/plugin/dial.lua new file mode 100644 index 0000000..10f33cf --- /dev/null +++ b/config/neovim/plugin/dial.lua @@ -0,0 +1,45 @@ +local augend = require "dial.augend" +local dial_config = require "dial.config" + +dial_config.augends:register_group { + visual = { + augend.integer.alias.decimal, + augend.integer.alias.hex, + augend.date.alias["%Y/%m/%d"], + augend.constant.alias.alpha, + augend.constant.alias.Alpha, + }, + + mygroup = { + augend.constant.new { + elements = { "TRUE", "FALSE" }, + word = true, + cyclic = true, + }, + + augend.constant.new { + elements = { "public", "protected", "private" }, + word = true, + cyclic = true, + }, + + augend.constant.new { + elements = { "&&", "||" }, + word = false, + cyclic = true, + }, + + augend.date.alias["%d/%m/%Y"], + augend.constant.alias.bool, -- boolean value (true <-> false) + augend.integer.alias.decimal, + augend.integer.alias.hex, + augend.semver.alias.semver, + }, +} + +local dial_map = require "dial.map" + +vim.keymap.set("n", "", dial_map.inc_normal "mygroup") +vim.keymap.set("n", "", dial_map.dec_normal "mygroup") +vim.keymap.set("v", "", dial_map.inc_normal "visual") +vim.keymap.set("v", "", dial_map.dec_normal "visual") diff --git a/config/neovim/plugin/edit_alternate.lua b/config/neovim/plugin/edit_alternate.lua new file mode 100644 index 0000000..293a28b --- /dev/null +++ b/config/neovim/plugin/edit_alternate.lua @@ -0,0 +1,40 @@ +vim.fn["edit_alternate#rule#add"]("php", function(filename) + if filename:find "Test.php$" then + filename = filename:gsub("Test.php$", ".php") + + if filename:find "tests/src/" then + -- Drupal tests. Remove the `src/{type}` from the path. + return filename:gsub("tests/src/(.-)/", "src/") + else + return filename:gsub("tests/", "src/") + end + else + filename = filename:gsub(".php$", "Test.php") + + if filename:find "modules/custom" then + -- Drupal test types. + local test_types = { "Functional", "FunctionalJavaScript", "Kernel", "Unit" } + + for _, test_type in ipairs(test_types) do + local filename_with_test_type = filename:gsub("src/", string.format("tests/src/%s/", test_type)) + + -- Return the first matching test file that exists. + if vim.fn.filereadable(filename_with_test_type) == 1 then + return filename_with_test_type + end + end + end + end +end) + +if vim.fn.filereadable "fractal.config.js" == 1 then + vim.fn["edit_alternate#rule#add"]("twig", function(filename) + return (filename:gsub("%.twig$", ".config.yml")) + end) + + vim.fn["edit_alternate#rule#add"]("yml", function(filename) + return (filename:gsub("%.config.yml$", ".twig")) + end) +end + +vim.keymap.set("n", "ea", "EditAlternate", { silent = true }) diff --git a/config/neovim/plugin/fidget.lua b/config/neovim/plugin/fidget.lua new file mode 100644 index 0000000..2a0375f --- /dev/null +++ b/config/neovim/plugin/fidget.lua @@ -0,0 +1,7 @@ +require("fidget").setup { + notification = { + window = { + winblend = 0, + }, + }, +} diff --git a/config/neovim/plugin/filetype.lua b/config/neovim/plugin/filetype.lua new file mode 100644 index 0000000..81ec0a1 --- /dev/null +++ b/config/neovim/plugin/filetype.lua @@ -0,0 +1,9 @@ +vim.filetype.add { + extension = { + inc = "php", + install = "php", + module = "php", + pcss = "scss", + theme = "php", + }, +} diff --git a/config/neovim/plugin/fugitive.lua b/config/neovim/plugin/fugitive.lua new file mode 100644 index 0000000..4aded48 --- /dev/null +++ b/config/neovim/plugin/fugitive.lua @@ -0,0 +1,25 @@ +vim.keymap.set("n", "gc", "Git commitK") + +-- Open the ":Git" window in its own buffer, not a split. +vim.keymap.set("n", "gs", "0Git") + +vim.api.nvim_create_autocmd("BufWinEnter", { + pattern = "*", + + callback = function() + if vim.bo.ft ~= "fugitive" then + return + end + + local bufnr = vim.api.nvim_get_current_buf() + local opts = { buffer = bufnr, remap = false } + + vim.keymap.set("n", "p", function() + vim.cmd.Git "push" + end, opts) + + vim.keymap.set("n", "P", function() + vim.cmd.Git { "pull", "--rebase" } + end, opts) + end, +}) diff --git a/config/neovim/plugin/gitsigns.lua b/config/neovim/plugin/gitsigns.lua new file mode 100644 index 0000000..16b2f80 --- /dev/null +++ b/config/neovim/plugin/gitsigns.lua @@ -0,0 +1,30 @@ +local gitsigns = require "gitsigns" + +gitsigns.setup { + linehl = false, + numhl = true, +} + +local set = vim.keymap.set + +set("n", "[h", "Gitsigns prev_hunk") +set("n", "]h", "Gitsigns next_hunk") + +set("n", "hR", gitsigns.reset_buffer) +set("n", "hS", gitsigns.stage_buffer) +set("n", "hb", gitsigns.blame_line) +set("n", "hp", gitsigns.preview_hunk) +set("n", "hr", gitsigns.reset_hunk) +set("n", "hs", gitsigns.stage_hunk) +set("n", "hu", gitsigns.undo_stage_hunk) + +set("v", "hr", function() + gitsigns.reset_hunk { vim.fn.line ".", vim.fn.line "v" } +end) + +set("v", "hs", function() + gitsigns.stage_hunk { vim.fn.line ".", vim.fn.line "v" } +end) + +-- Text object. +set({ "o", "x" }, "ih", ":Gitsigns select_hunk") diff --git a/config/neovim/plugin/harpoon.lua b/config/neovim/plugin/harpoon.lua new file mode 100644 index 0000000..03769f0 --- /dev/null +++ b/config/neovim/plugin/harpoon.lua @@ -0,0 +1,13 @@ +require("harpoon").setup() + +local mark = require "harpoon.mark" +local ui = require "harpoon.ui" + +vim.keymap.set("n", "", ui.toggle_quick_menu) +vim.keymap.set("n", "", mark.add_file) + +for i = 1, 5 do + vim.keymap.set("n", string.format("%s", i), function() + ui.nav_file(i) + end) +end diff --git a/config/neovim/plugin/lint.lua b/config/neovim/plugin/lint.lua new file mode 100644 index 0000000..f862107 --- /dev/null +++ b/config/neovim/plugin/lint.lua @@ -0,0 +1,19 @@ +local lint = require "lint" + +lint.linters_by_ft = { + dockerfile = { "hadolint" }, + javascript = { "eslint_d" }, + json = { "jsonlint" }, + lua = { "luacheck" }, + markdown = { "markdownlint" }, + nix = { "nix" }, + php = { "php", "phpcs", "phpstan" }, +} +local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true }) + +vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave" }, { + group = lint_augroup, + callback = function() + lint.try_lint() + end, +}) diff --git a/config/neovim/plugin/mini.lua b/config/neovim/plugin/mini.lua new file mode 100644 index 0000000..6c21746 --- /dev/null +++ b/config/neovim/plugin/mini.lua @@ -0,0 +1,23 @@ +require("mini.ai").setup { n_lines = 500 } + +require("mini.align").setup {} + +require("mini.bracketed").setup {} + +require("mini.hipatterns").setup { + highlighters = { + note = { pattern = "%f[%w]()NOTE()%f[%W]", group = "MiniHipatternsNote" }, + todo = { pattern = "%f[%w]()TODO()%f[%W]", group = "MiniHipatternsTodo" }, + }, +} + +require("mini.move").setup {} + +require("mini.operators").setup {} + +require("mini.statusline").setup { + set_vim_settings = false, + use_icons = false, +} + +require("mini.surround").setup {} diff --git a/config/neovim/plugin/netrw.lua b/config/neovim/plugin/netrw.lua new file mode 100644 index 0000000..1feb376 --- /dev/null +++ b/config/neovim/plugin/netrw.lua @@ -0,0 +1,6 @@ +vim.keymap.set("n", "pv", vim.cmd.Ex) + +vim.g.netrw_banner = 0 +vim.g.netrw_browse_split = 0 +vim.g.netrw_liststyle = 3 +vim.g.netrw_winsize = 20 diff --git a/config/neovim/plugin/oil.lua b/config/neovim/plugin/oil.lua new file mode 100644 index 0000000..5799eb2 --- /dev/null +++ b/config/neovim/plugin/oil.lua @@ -0,0 +1,16 @@ +require("oil").setup { + columns = { "icon" }, + + keymaps = { + [""] = false, + [""] = "actions.select_split", + }, + + skip_confirm_for_simple_edits = true, + + view_options = { + show_hidden = true, + }, +} + +vim.keymap.set("n", "-", "Oil", { desc = "Open parent directory" }) diff --git a/config/neovim/plugin/refactoring.lua b/config/neovim/plugin/refactoring.lua new file mode 100644 index 0000000..a23a0bb --- /dev/null +++ b/config/neovim/plugin/refactoring.lua @@ -0,0 +1,12 @@ +local refactoring = require "refactoring" + +-- TODO: add keymaps - https://github.com/ThePrimeagen/refactoring.nvim#configuration-for-refactoring-operations +refactoring.setup {} + +local opts = { silent = true } + +vim.keymap.set("n", "ri", "lua require 'refactoring'.refactor 'Inline Variable'", opts) + +vim.keymap.set("v", "re", "lua require 'refactoring'.refactor 'Extract Function'", opts) +vim.keymap.set("v", "ri", "lua require 'refactoring'.refactor 'Inline Variable'", opts) +vim.keymap.set("v", "rv", "lua require 'refactoring'.refactor 'Extract Variable'", opts) diff --git a/config/neovim/plugin/sort.lua b/config/neovim/plugin/sort.lua new file mode 100644 index 0000000..bdf96d7 --- /dev/null +++ b/config/neovim/plugin/sort.lua @@ -0,0 +1,10 @@ +require("sort").setup() + +vim.cmd([[ + nnoremap go" vi"Sort + nnoremap go' vi'Sort + nnoremap go( vi(Sort + nnoremap go[ vi[Sort + nnoremap gop vipSort + nnoremap go{ vi{Sort +]]) diff --git a/config/neovim/plugin/spectre.lua b/config/neovim/plugin/spectre.lua new file mode 100644 index 0000000..b67854e --- /dev/null +++ b/config/neovim/plugin/spectre.lua @@ -0,0 +1 @@ +require("spectre").setup() diff --git a/config/neovim/plugin/telescope.lua b/config/neovim/plugin/telescope.lua new file mode 100644 index 0000000..79fafd6 --- /dev/null +++ b/config/neovim/plugin/telescope.lua @@ -0,0 +1,67 @@ +local telescope = require "telescope" + +telescope.setup { + defaults = { + layout_config = { prompt_position = "top" }, + path_display = { truncate = 1 }, + prompt_prefix = "$ ", + sorting_strategy = "ascending", + }, + + pickers = { + lsp_references = { + previewer = false, + }, + }, + + extensions = { + ["ui-select"] = { + require("telescope.themes").get_dropdown {}, + }, + }, +} + +telescope.load_extension "fzf" +telescope.load_extension "refactoring" +telescope.load_extension "ui-select" + +local builtin = require "telescope.builtin" + +local M = {} + +M.diagnostics = function() + builtin.diagnostics { bufnr = 0 } +end + +M.grep_bluecheese = function() + builtin.live_grep { cwd = "web/sites/default/themes/bluecheese" } +end + +M.grep_drupalorg_theme = function() + builtin.live_grep { cwd = "web/themes/contrib/drupalorg_theme" } +end + +M.search_all_files = function() + builtin.find_files { + find_command = { "rg", "--no-ignore", "--files" }, + } +end + +vim.keymap.set("n", "/", builtin.current_buffer_fuzzy_find) +vim.keymap.set("n", "fb", builtin.buffers) +vim.keymap.set("n", "fd", builtin.find_files) +vim.keymap.set("n", "fg", builtin.live_grep) +vim.keymap.set("n", "fh", builtin.help_tags) +vim.keymap.set("n", "fi", M.search_all_files) +vim.keymap.set("n", "fk", builtin.keymaps) +vim.keymap.set("n", "ft", builtin.git_files) + +vim.keymap.set("n", "dl", M.diagnostics) +vim.keymap.set("n", "ds", builtin.lsp_document_symbols) + +vim.keymap.set("n", "gw", builtin.grep_string) + +vim.keymap.set("n", "dgb", M.grep_bluecheese) +vim.keymap.set("n", "dgd", M.grep_drupalorg_theme) + +vim.keymap.set({ "n", "v" }, "gw", builtin.grep_string) diff --git a/config/neovim/plugin/treesitter.lua b/config/neovim/plugin/treesitter.lua new file mode 100644 index 0000000..df5bd1a --- /dev/null +++ b/config/neovim/plugin/treesitter.lua @@ -0,0 +1,136 @@ +local configs = require "nvim-treesitter.configs" +local context = require "treesitter-context" +local ts_repeat_move = require "nvim-treesitter.textobjects.repeatable_move" + +configs.setup { + autotag = { + enable = true, + }, + + context_commenting = { + enable = true, + }, + + highlight = { + enable = true, + }, + + indent = { + disable = { "yaml" }, + enable = true, + }, + + matchup = { + enable = true, + }, + + textobjects = { + select = { + enable = true, + lookahead = true, + + keymaps = { + ["a="] = { query = "@assignment.outer", desc = "Select outer part of an assignment" }, + ["i="] = { query = "@assignment.inner", desc = "Select inner part of an assignment" }, + ["l="] = { query = "@assignment.lhs", desc = "Select left hand side of an assignment" }, + ["r="] = { query = "@assignment.rhs", desc = "Select right hand side of an assignment" }, + + ["a:"] = { query = "@property.outer", desc = "Select outer part of an object property" }, + ["i:"] = { query = "@property.inner", desc = "Select inner part of an object property" }, + ["l:"] = { query = "@property.lhs", desc = "Select left part of an object property" }, + ["r:"] = { query = "@property.rhs", desc = "Select right part of an object property" }, + + ["aa"] = { query = "@parameter.outer", desc = "Select outer part of a parameter/argument" }, + ["ia"] = { query = "@parameter.inner", desc = "Select inner part of a parameter/argument" }, + + ["ac"] = { query = "@class.outer", desc = "Select outer part of a class" }, + ["ic"] = { query = "@class.inner", desc = "Select inner part of a class" }, + + ["af"] = { query = "@call.outer", desc = "Select outer part of a function call" }, + ["if"] = { query = "@call.inner", desc = "Select inner part of a function call" }, + + ["ai"] = { query = "@conditional.outer", desc = "Select outer part of a conditional" }, + ["ii"] = { query = "@conditional.inner", desc = "Select inner part of a conditional" }, + + ["al"] = { query = "@loop.outer", desc = "Select outer part of a loop" }, + ["il"] = { query = "@loop.inner", desc = "Select inner part of a loop" }, + + ["am"] = { query = "@function.outer", desc = "Select outer part of a method/function definition" }, + ["im"] = { query = "@function.inner", desc = "Select inner part of a method/function definition" }, + }, + }, + }, + + swap = { + enable = true, + + swap_next = { + ["na"] = "@parameter.inner", -- swap parameters/argument with next + ["n:"] = "@property.outer", -- swap object property with next + ["nm"] = "@function.outer", -- swap function with next + }, + + swap_previous = { + ["pa"] = "@parameter.inner", -- swap parameters/argument with prev + ["p:"] = "@property.outer", -- swap object property with prev + ["pm"] = "@function.outer", -- swap function with previous + }, + }, + + move = { + enable = true, + set_jumps = true, -- whether to set jumps in the jumplist + + goto_next_start = { + ["]f"] = { query = "@call.outer", desc = "Next function call start" }, + ["]m"] = { query = "@function.outer", desc = "Next method/function def start" }, + ["]c"] = { query = "@class.outer", desc = "Next class start" }, + ["]i"] = { query = "@conditional.outer", desc = "Next conditional start" }, + ["]l"] = { query = "@loop.outer", desc = "Next loop start" }, + + ["]s"] = { query = "@scope", query_group = "locals", desc = "Next scope" }, + ["]z"] = { query = "@fold", query_group = "folds", desc = "Next fold" }, + }, + + goto_next_end = { + ["]F"] = { query = "@call.outer", desc = "Next function call end" }, + ["]M"] = { query = "@function.outer", desc = "Next method/function def end" }, + ["]C"] = { query = "@class.outer", desc = "Next class end" }, + ["]I"] = { query = "@conditional.outer", desc = "Next conditional end" }, + ["]L"] = { query = "@loop.outer", desc = "Next loop end" }, + }, + + goto_previous_start = { + ["[f"] = { query = "@call.outer", desc = "Prev function call start" }, + ["[m"] = { query = "@function.outer", desc = "Prev method/function def start" }, + ["[c"] = { query = "@class.outer", desc = "Prev class start" }, + ["[i"] = { query = "@conditional.outer", desc = "Prev conditional start" }, + ["[l"] = { query = "@loop.outer", desc = "Prev loop start" }, + }, + + goto_previous_end = { + ["[F"] = { query = "@call.outer", desc = "Prev function call end" }, + ["[M"] = { query = "@function.outer", desc = "Prev method/function def end" }, + ["[C"] = { query = "@class.outer", desc = "Prev class end" }, + ["[I"] = { query = "@conditional.outer", desc = "Prev conditional end" }, + ["[L"] = { query = "@loop.outer", desc = "Prev loop end" }, + }, + }, +} + +local set = vim.keymap.set + +set("n", "th", "TSHighlightCapturesUnderCursor") +set("n", "tp", "TSPlaygroundToggle") + +-- vim way: ; goes to the direction you were moving. +set({ "n", "o", "x" }, ";", ts_repeat_move.repeat_last_move) +set({ "n", "o", "x" }, ",", ts_repeat_move.repeat_last_move_opposite) + +-- Optionally, make builtin f, F, t, T also repeatable with ; and , +set({ "n", "o", "x" }, "f", ts_repeat_move.builtin_f) +set({ "n", "o", "x" }, "F", ts_repeat_move.builtin_F) +set({ "n", "o", "x" }, "t", ts_repeat_move.builtin_t) +set({ "n", "o", "x" }, "T", ts_repeat_move.builtin_T) + +context.setup { enable = true } diff --git a/config/neovim/plugin/treesj.lua b/config/neovim/plugin/treesj.lua new file mode 100644 index 0000000..a23b3ce --- /dev/null +++ b/config/neovim/plugin/treesj.lua @@ -0,0 +1,8 @@ +local tsj = require "treesj" + +tsj.setup { + use_default_keymaps = false, +} + +vim.keymap.set("n", "gJ", tsj.join) +vim.keymap.set("n", "gS", tsj.split) diff --git a/config/neovim/plugin/undotree.lua b/config/neovim/plugin/undotree.lua new file mode 100644 index 0000000..b6b9276 --- /dev/null +++ b/config/neovim/plugin/undotree.lua @@ -0,0 +1 @@ +vim.keymap.set("n", "u", vim.cmd.UndotreeToggle) diff --git a/config/neovim/plugin/vim-test.lua b/config/neovim/plugin/vim-test.lua new file mode 100644 index 0000000..b82c466 --- /dev/null +++ b/config/neovim/plugin/vim-test.lua @@ -0,0 +1,23 @@ +local map = vim.api.nvim_set_keymap + +local options = { + silent = true, +} + +map("n", "tf", ":TestFile", options) +map("n", "tg", ":TestVisit", options) +map("n", "tl", ":TestLast", options) +map("n", "tn", ":TestNearest", options) +map("n", "ts", ":TestSuite", options) + +vim.cmd [[ + let test#echo_command = 0 + let test#strategy = "neovim_sticky" + + let g:test#neovim_sticky#kill_previous = 1 + let g:test#neovim_sticky#reopen_window = 1 + let g:test#preserve_screen = 0 + + let test#php#phpunit#executable = './run test' + let test#php#phpunit#options = '--colors=always --testdox' +]] diff --git a/flake.lock b/flake.lock index cade14b..8974dd1 100644 --- a/flake.lock +++ b/flake.lock @@ -1,23 +1,5 @@ { "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1727826117, - "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, "home-manager": { "inputs": { "nixpkgs": [ @@ -71,34 +53,6 @@ "type": "github" } }, - "nixpkgs-2305": { - "locked": { - "lastModified": 1704290814, - "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1727825735, - "narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" - } - }, "nixpkgs-unstable": { "locked": { "lastModified": 1728018373, @@ -115,52 +69,12 @@ "type": "github" } }, - "nixpkgs-unstable_2": { - "locked": { - "lastModified": 1727802920, - "narHash": "sha256-HP89HZOT0ReIbI7IJZJQoJgxvB2Tn28V6XS3MNKnfLs=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "27e30d177e57d912d614c88c622dcfdb2e6e6515", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "opdavies-nvim": { - "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-2305": "nixpkgs-2305", - "nixpkgs-unstable": "nixpkgs-unstable_2" - }, - "locked": { - "lastModified": 1728128218, - "narHash": "sha256-FD+TxbOgH0CBX+7hWJrKvW75KUSIQVsERQMBQGKwNeA=", - "owner": "opdavies", - "repo": "opdavies.nvim", - "rev": "ddb9c3199aac6733aa5eadcede729dc28c335911", - "type": "github" - }, - "original": { - "owner": "opdavies", - "repo": "opdavies.nvim", - "type": "github" - } - }, "root": { "inputs": { "home-manager": "home-manager", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable", - "opdavies-nvim": "opdavies-nvim" + "nixpkgs-unstable": "nixpkgs-unstable" } } }, diff --git a/flake.nix b/flake.nix index 13a3fe8..df8aec0 100644 --- a/flake.nix +++ b/flake.nix @@ -8,10 +8,6 @@ nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; - - opdavies-nvim.inputs.nixpkgs.follows = "nixpkgs"; - opdavies-nvim.url = "github:opdavies/opdavies.nvim"; - # opdavies-nvim.url = "path:/home/opdavies/Code/opdavies.nvim"; }; outputs = @@ -40,9 +36,17 @@ mkWsl = import ./lib/wsl2 { inherit inputs self username; }; inherit (pkgs) mkShell; + inherit (pkgs.vimUtils) buildVimPlugin; in { - packages.${system}.default = mkShell { buildInputs = with pkgs; [ bashInteractive ]; }; + packages.${system} = { + default = mkShell { buildInputs = with pkgs; [ bashInteractive ]; }; + + opdavies-nvim = buildVimPlugin { + name = "opdavies-nvim"; + src = ./config/neovim; + }; + }; formatter.${system} = pkgs.nixfmt-rfc-style; diff --git a/lib/shared/home-manager.nix b/lib/shared/home-manager.nix index 4172369..4ecaa9a 100644 --- a/lib/shared/home-manager.nix +++ b/lib/shared/home-manager.nix @@ -20,7 +20,7 @@ in imports = [ (import ./modules/git.nix { inherit inputs pkgs pkgsUnstable; }) - (import ./modules/neovim.nix { inherit inputs; }) + (import ./modules/neovim.nix { inherit inputs pkgs; }) ./modules/bat.nix ./modules/bin.nix ./modules/direnv.nix diff --git a/lib/shared/modules/neovim.nix b/lib/shared/modules/neovim.nix index 2bd540e..03f7e5b 100644 --- a/lib/shared/modules/neovim.nix +++ b/lib/shared/modules/neovim.nix @@ -1,10 +1,313 @@ -{ inputs }: -{ pkgs, ... }: +{ inputs, pkgs, ... }: + let - system = pkgs.system; + inherit (pkgs) fetchFromGitHub; + inherit (pkgs.vimUtils) buildVimPlugin; + + # TODO: move these back into an overlay. + customVim = { + conf-vim = buildVimPlugin { + name = "conf-vim"; + src = fetchFromGitHub { + owner = "tjdevries"; + repo = "conf.vim"; + rev = "master"; + sha256 = "AjiTJsoim0BAnyfqk1IQzNsa6jhFM2+A66E7q9sJqz0="; + }; + }; + + edit-alternate-vim = buildVimPlugin { + name = "edit-alternate-vim"; + src = fetchFromGitHub { + owner = "tjdevries"; + repo = "edit_alternate.vim"; + rev = "master"; + sha256 = "mEKnqYAhgrdxPRoKf4S4yYecdFIHGg8bDxpqPuC1+S4="; + }; + }; + + standard-vim = buildVimPlugin { + name = "standard-vim"; + src = fetchFromGitHub { + owner = "tjdevries"; + repo = "standard.vim"; + rev = "master"; + sha256 = "9VwkvV1Dv6cE4uDkPp36DozjWJOclDR883yDMYw000E="; + }; + }; + + vim-autoread = buildVimPlugin { + name = "vim-autoread"; + src = fetchFromGitHub { + owner = "djoshea"; + repo = "vim-autoread"; + rev = "24061f84652d768bfb85d222c88580b3af138dab"; + sha256 = "fSADjNt1V9jgAPjxggbh7Nogcxyisi18KaVve8j+c3w="; + }; + }; + + vim-textobj-indent = buildVimPlugin { + name = "vim-textobj-indent"; + src = fetchFromGitHub { + owner = "kana"; + repo = "vim-textobj-indent"; + rev = "deb76867c302f933c8f21753806cbf2d8461b548"; + sha256 = "oFzUPG+IOkbKZ2gU/kduQ3G/LsLDlEjFhRP0BHBE+1Q="; + }; + }; + + toggle-checkbox-nvim = buildVimPlugin { + name = "toggle-checkbox-nvim"; + src = fetchFromGitHub { + owner = "opdavies"; + repo = "toggle-checkbox.nvim"; + rev = "main"; + sha256 = "4YSEagQnLK5MBl2z53e6sOBlCDm220GYVlc6A+HNywg="; + }; + }; + + vim-heritage = buildVimPlugin { + name = "vim-heritage"; + src = fetchFromGitHub { + owner = "jessarcher"; + repo = "vim-heritage"; + rev = "cffa05c78c0991c998adc4504d761b3068547db6"; + sha256 = "Lebe5V1XFxn4kSZ+ImZ69Vst9Nbc0N7eA9IzOCijFS0="; + }; + }; + + vim-textobj-xmlattr = buildVimPlugin { + name = "vim-textobj-xmlattr"; + src = fetchFromGitHub { + owner = "whatyouhide"; + repo = "vim-textobj-xmlattr"; + rev = "694a297f1d75fd527e87da9769f3c6519a87ebb1"; + sha256 = "+91FVP95oh00flINdltqx6qJuijYo56tHIh3J098G2Q="; + }; + }; + + tabline-vim = buildVimPlugin { + name = "tabline-vim"; + src = fetchFromGitHub { + owner = "mkitt"; + repo = "tabline.vim"; + rev = "69c9698a3240860adaba93615f44778a9ab724b4"; + sha256 = "51b8PxyKqBdeIvmmZyF2hpMBjkyrlZDdTB1opr5JZ7Y="; + }; + }; + + vim-caser = buildVimPlugin { + name = "vim-caser"; + src = fetchFromGitHub { + owner = "arthurxavierx"; + repo = "vim-caser"; + rev = "6bc9f41d170711c58e0157d882a5fe8c30f34bf6"; + sha256 = "PXAY01O/cHvAdWx3V/pyWFeiV5qJGvLcAKhl5DQc0Ps="; + }; + }; + + vim-zoom = buildVimPlugin { + name = "vim-zoom"; + src = fetchFromGitHub { + owner = "dhruvasagar"; + repo = "vim-zoom"; + rev = "01c737005312c09e0449d6518decf8cedfee32c7"; + sha256 = "/ADzScsG0u6RJbEtfO23Gup2NYdhPkExqqOPVcQa7aQ="; + }; + }; + }; in { - programs.neovim = inputs.opdavies-nvim.lib.mkHomeManager { inherit system; }; + programs.neovim = { + enable = true; + + plugins = with pkgs.vimPlugins; [ + comment-nvim + dial-nvim + fidget-nvim + gitsigns-nvim + harpoon + impatient-nvim + mini-nvim + neodev-nvim + nvim-spectre + nvim-web-devicons + oil-nvim + refactoring-nvim + sort-nvim + treesj + undotree + vim-abolish + vim-eunuch + vim-highlightedyank + vim-just + vim-nix + vim-obsession + vim-pasta + vim-repeat + vim-sleuth + vim-sort-motion + vim-terraform + vim-textobj-user + vim-unimpaired + + customVim.conf-vim + customVim.edit-alternate-vim + customVim.standard-vim + customVim.tabline-vim + customVim.vim-autoread + customVim.vim-textobj-indent + customVim.vim-textobj-xmlattr + customVim.vim-zoom + + # Testing + vim-test + + # Git + committia-vim + diffview-nvim + vim-fugitive + + # Debugging + nvim-dap + nvim-dap-ui + nvim-dap-virtual-text + + # Treesitter + (pkgs.vimPlugins.nvim-treesitter.withPlugins ( + plugins: with plugins; [ + bash + comment + css + csv + dockerfile + gitattributes + gitignore + go + html + javascript + json + kdl + lua + luadoc + make + markdown + markdown_inline + nix + php + phpdoc + query + rst + scss + sql + terraform + twig + typescript + vim + vimdoc + vue + xml + yaml + ] + )) + nvim-treesitter-context + nvim-treesitter-textobjects + + # LSP, linting and formatting + conform-nvim + lsp-status-nvim + nvim-lint + nvim-lspconfig + + # Completion + cmp-buffer + cmp-calc + cmp-cmdline + cmp-nvim-lsp + cmp-path + cmp-treesitter + cmp_luasnip + lspkind-nvim + nvim-cmp + + # Snippets + friendly-snippets + luasnip + + # Telescope + plenary-nvim + popup-nvim + telescope-frecency-nvim + telescope-fzf-native-nvim + telescope-live-grep-args-nvim + telescope-nvim + telescope-ui-select-nvim + + # Databases + vim-dadbod + vim-dadbod-ui + vim-dadbod-completion + + # Themes + catppuccin-nvim + + # Configuration. + inputs.self.packages.${pkgs.system}.opdavies-nvim + ]; + + extraLuaConfig = '' + if vim.loader then + vim.loader.enable() + end + + require "opdavies" + ''; + + extraPackages = with pkgs; [ + # Languages + nodePackages.typescript + nodejs-slim + php81 + + # Language servers + gopls + lua-language-server + lua54Packages.luacheck + marksman + nil + nodePackages."@tailwindcss/language-server" + nodePackages.bash-language-server + nodePackages.dockerfile-language-server-nodejs + nodePackages.intelephense + nodePackages.typescript-language-server + nodePackages.vls + nodePackages.volar + nodePackages.vscode-langservers-extracted + nodePackages.vue-language-server + nodePackages.yaml-language-server + phpactor + terraform-ls + + # Formatters + black + eslint_d + nixfmt-rfc-style + nodePackages.prettier + stylua + yamlfmt + + # Tools + hadolint + html-tidy + nodePackages.jsonlint + nodePackages.markdownlint-cli + php82Packages.php-codesniffer + php82Packages.phpstan + proselint + shellcheck + yamllint + ]; + }; home.file.".markdownlint.yaml".text = '' default: true