From eb7f522795c3a2b597acb576c80b23214ff9eedb Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Fri, 3 May 2024 18:56:57 -0500 Subject: [PATCH] all the languages! --- README.md | 4 +- flake.nix | 32 ++- init.lua | 14 +- lua/autocommands.lua | 74 +++---- lua/keymaps.lua | 5 +- lua/options.lua | 2 - lua/plugins/cmp_autocompletion.lua | 218 +++++++++++--------- lua/plugins/editor_lua_line.lua | 234 +++++++++++----------- lua/plugins/editor_noice.lua | 7 +- lua/plugins/formatter.lua | 51 +++-- lua/plugins/lsp.lua | 309 ++++++++++++++++------------- lua/plugins/text_case.lua | 1 - lua/plugins/whichkey.lua | 14 +- lua/util.lua | 213 ++++++++++---------- 14 files changed, 630 insertions(+), 548 deletions(-) diff --git a/README.md b/README.md index cca1199..a4b206a 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,7 @@ rm -rf ~/.local/state/nvim ## NOTES/TODOS -- h/l movement broken in insert mode, probably due to cmp hotkeys -- h/l in telescope -- arrows still work in insert mode and telescope, need to remove trying to break arrow key habit +- See what linters/formaters to add or are the LSP's enough? FUTURE diff --git a/flake.nix b/flake.nix index dea41c6..2c0b808 100644 --- a/flake.nix +++ b/flake.nix @@ -87,7 +87,14 @@ "nvim_plugin-saadparwaiz1/cmp_luasnip" = cmp_luasnip; "nvim_plugin-hrsh7th/cmp-nvim-lsp" = cmp-nvim-lsp; "nvim_plugin-hrsh7th/cmp-path" = cmp-path; + "nvim_plugin-hrsh7th/cmp-buffer" = cmp-buffer; + "nvim_plugin-zbirenbaum/copilot-cmp" = copilot-cmp; + "nvim_plugin-zbirenbaum/copilot.lua" = copilot-lua; "nvim_plugin-folke/neodev.nvim" = neodev-nvim; + "nvim_plugin-mrcjkb/rustaceanvim" = rustaceanvim; + "nvim_plugin-Saecki/crates.nvim" = crates-nvim; + "nvim_plugin-lvimuser/lsp-inlayhints.nvim" = lsp-inlayhints-nvim; + "nvim_plugin-rafamadriz/friendly-snippets" = friendly-snippets; }; # This will be how we put any nix related stuff into our lua config luaNixGlobal = @@ -113,6 +120,11 @@ fzf # search fuzzy tree-sitter glow # markdown renderer + # curl # http requests TODO + # nodePackages.cspell TODO + ]; + + defaultRuntimeDependencies = with pkgs; [ # linters markdownlint-cli luajitPackages.luacheck @@ -121,14 +133,23 @@ stylua nixfmt-rfc-style nodePackages.prettier + rustywind markdownlint-cli2 # LSPs + nil # nix lua-language-server + vscode-langservers-extracted # HTML/CSS/JSON/ESLint nodePackages.typescript-language-server + tailwindcss-language-server nodePackages.pyright - - # curl # http requests TODO - # nodePackages.cspell TODO + rust-analyzer + marksman # markdown + taplo #toml + yaml-language-server + lemminx # xml + # Other + # typescript + nodejs_20 ]; in { @@ -152,6 +173,11 @@ "PATH" ":" "${lib.makeBinPath runtimeDependencies}" + # Some we will suffix so we pick up the local dev shell intead and default to these otherwise + "--suffix" + "PATH" + ":" + "${lib.makeBinPath defaultRuntimeDependencies}" # Set the LAZY env path to the nix store, see init.lua for how it is used "--set" "LAZY" diff --git a/init.lua b/init.lua index ad190dc..1ce9a03 100644 --- a/init.lua +++ b/init.lua @@ -45,6 +45,13 @@ local function getSpec() -- Convert plugins to use nix store, this auto sets the `dir` property for us on all plugins. local function convertPluginToNixStore(plugin) local p = ensure_table(plugin) + if U.isArray(p) and #p > 1 then + local plugins = {} + table.foreachi(p, function(i, inner) + table.insert(plugins, convertPluginToNixStore(inner)) + end) + return plugins + end if p.enabled == false then return plugin end @@ -69,14 +76,11 @@ local function getSpec() local plugins_path = debug.getinfo(2, "S").source:sub(2):match("(.*/)") .. "lua/plugins" for _, file in ipairs(vim.fn.readdir(plugins_path, [[v:val =~ '\.lua$']])) do local plugin = string.sub(file, 0, -5) - table.insert(plugins, convertPluginToNixStore(require("plugins." .. plugin))) + local converted = convertPluginToNixStore(require("plugins." .. plugin)) + table.insert(plugins, converted) end return plugins else - -- TODO I want this to work in the nixos versionhttps://github.com/RingOfStorms/nvim/blob/nix-flake/init.lua#L39-L55 - -- but it is not resolving properly to the nix store. - -- Will revisit at some point, instead we manually pull them - -- in above with a directory scan. return { { import = "plugins" } } end end diff --git a/lua/autocommands.lua b/lua/autocommands.lua index 41d0465..1b10486 100644 --- a/lua/autocommands.lua +++ b/lua/autocommands.lua @@ -1,58 +1,60 @@ -local group = vim.api.nvim_create_augroup("myconfig-autocommands-group", { clear = true }); +local group = vim.api.nvim_create_augroup("myconfig-autocommands-group", { clear = true }) -- Highlight when yanking (copying) text -- Try it with `yap` in normal mode -- See `:help vim.highlight.on_yank()` vim.api.nvim_create_autocmd("TextYankPost", { - group = group, - desc = "Highlight when yanking (copying) text", - callback = function() - vim.highlight.on_yank({ timeout = 300 }) - end, + group = group, + desc = "Highlight when yanking (copying) text", + callback = function() + vim.highlight.on_yank({ timeout = 300 }) + end, }) + -- TODO is there a better way for these? +-- https://www.youtube.com/watch?v=NecszftvMFI vim.filetype.add vim.api.nvim_create_autocmd("BufRead", { - group = group, - pattern = ".env*", - command = "set filetype=sh", + group = group, + pattern = ".env*", + command = "set filetype=sh", }) vim.api.nvim_create_autocmd("BufRead", { - group = group, - pattern = ".*rc", - command = "set filetype=sh", + group = group, + pattern = ".*rc", + command = "set filetype=sh", }) vim.api.nvim_create_autocmd("BufRead", { - group = group, - pattern = "Dockerfile.*", - command = "set filetype=dockerfile", + group = group, + pattern = "Dockerfile.*", + command = "set filetype=dockerfile", }) vim.api.nvim_create_autocmd("BufRead", { - group = group, - pattern = "*.http", - command = "set filetype=http", + group = group, + pattern = "*.http", + command = "set filetype=http", }) -- Auto exit insert mode whenever we switch screens vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, { - group = group, - callback = function() - if vim.bo.filetype ~= "TelescopePrompt" and vim.bo.filetype ~= nil and vim.bo.filetype ~= "" then - vim.api.nvim_command("stopinsert") - end - end, + group = group, + callback = function() + if vim.bo.filetype ~= "TelescopePrompt" and vim.bo.filetype ~= nil and vim.bo.filetype ~= "" then + vim.api.nvim_command("stopinsert") + end + end, }) vim.api.nvim_create_autocmd("VimLeavePre", { - group = group, - callback = function() - vim.cmd("NvimTreeClose") - -- Close all buffers with the 'httpResult' type - local close_types = { "httpResult", "noice", "help" } - local buffers = vim.api.nvim_list_bufs() - for _, bufnr in ipairs(buffers) do - if U.table_contains(close_types, vim.bo[bufnr].filetype) then - vim.api.nvim_buf_delete(bufnr, { force = true }) - end - end - end, + group = group, + callback = function() + vim.cmd("NvimTreeClose") + -- Close all buffers with the 'httpResult' type + local close_types = { "httpResult", "noice", "help" } + local buffers = vim.api.nvim_list_bufs() + for _, bufnr in ipairs(buffers) do + if U.table_contains(close_types, vim.bo[bufnr].filetype) then + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + end + end, }) diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 3ac393c..59f77e1 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -84,7 +84,7 @@ U.keymaps({ -- Editor { "J", "mzJ`z", desc = "Move line below onto this line" }, - { -- TODO stay here, are these already mapped? + { "]d", vim.diagnostic.goto_next, desc = "Go to next diagnostic message", @@ -107,13 +107,12 @@ U.keymaps({ { "", "", mode = { "i", "c" }, desc = "Movements in insert/command mode" }, -- Tabs - -- TODO revisit, do I even need these tab things? { "tn", "tabnew", desc = "Create new tab", mode = nvx }, { "tq", "tabclose", desc = "Close current tab", mode = nvx }, { "H", "tabprevious", desc = "Move to previous tab" }, { "L", "tabnext", desc = "Move to next tab" }, - -- LSP/IDE/etc TODO move to lsp config file + -- LSP/IDE/ { "ld", vim.diagnostic.open_float, diff --git a/lua/options.lua b/lua/options.lua index 5c7f2bb..d30d2ed 100644 --- a/lua/options.lua +++ b/lua/options.lua @@ -53,14 +53,12 @@ vim.opt.undofile = true -- and `:help 'listchars'` vim.opt.list = true vim.opt.listchars = { tab = "│ ", trail = "·", nbsp = "␣", eol = "↴" } --- TODO REVISIT IF I WANT THESE -- Search settings vim.opt.hlsearch = true vim.opt.incsearch = true -- Preview substitutions live, as you type --- TODO revisit, what does this actually do vim.opt.inccommand = "split" -- Show which line your cursor is on diff --git a/lua/plugins/cmp_autocompletion.lua b/lua/plugins/cmp_autocompletion.lua index e9dd17c..4e1b7d4 100644 --- a/lua/plugins/cmp_autocompletion.lua +++ b/lua/plugins/cmp_autocompletion.lua @@ -1,103 +1,127 @@ return { - "hrsh7th/nvim-cmp", - event = "InsertEnter", - dependencies = { - -- Snippet Engine & its associated nvim-cmp source - { - "L3MON4D3/LuaSnip", - dependencies = { - -- TODO use or remove this? - -- `friendly-snippets` contains a variety of premade snippets. - -- See the README about individual language/framework/plugin snippets: - -- https://github.com/rafamadriz/friendly-snippets - -- { - -- 'rafamadriz/friendly-snippets', - -- config = function() - -- require('luasnip.loaders.from_vscode').lazy_load() - -- end, - -- }, - }, - }, - "saadparwaiz1/cmp_luasnip", + "hrsh7th/nvim-cmp", + event = "InsertEnter", + dependencies = { + -- Snippet Engine & its associated nvim-cmp source + { + "L3MON4D3/LuaSnip", + dependencies = { + { + "rafamadriz/friendly-snippets", + config = function() + require("luasnip.loaders.from_vscode").lazy_load() + end, + }, + }, + }, + "saadparwaiz1/cmp_luasnip", - -- Adds other completion capabilities. - -- nvim-cmp does not ship with all sources by default. They are split - -- into multiple repos for maintenance purposes. - "hrsh7th/cmp-nvim-lsp", - "hrsh7th/cmp-path", - }, - config = function() - -- See `:help cmp` - local cmp = require("cmp") - local luasnip = require("luasnip") - luasnip.config.setup({}) + -- Adds other completion capabilities. + -- nvim-cmp does not ship with all sources by default. They are split + -- into multiple repos for maintenance purposes. + "hrsh7th/cmp-nvim-lsp", + "hrsh7th/cmp-buffer", + "hrsh7th/cmp-path", + { + "zbirenbaum/copilot.lua", + cmd = "Copilot", + event = "InsertEnter", + opts = { + -- suggestion = { enabled = false, auto_trigger = false }, + -- panel = { enabled = false, auto_trigger = false }, + }, + main = "copilot", + }, + { "zbirenbaum/copilot-cmp", opts = {}, main = "copilot_cmp" }, + }, + config = function() + -- See `:help cmp` + local cmp = require("cmp") + local luasnip = require("luasnip") + luasnip.config.setup({}) - cmp.setup({ - snippet = { - expand = function(args) - luasnip.lsp_expand(args.body) - end, - }, - completion = { completeopt = "menu,menuone,noinsert" }, + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + completion = { completeopt = "menu,menuone,noinsert" }, - -- For an understanding of why these mappings were - -- chosen, you will need to read `:help ins-completion` - -- - -- No, but seriously. Please read `:help ins-completion`, it is really good! - mapping = cmp.mapping.preset.insert({ - -- Select the [n]ext item - [""] = cmp.mapping.select_next_item(), - -- Select the [p]revious item - [""] = cmp.mapping.select_prev_item(), + mapping = cmp.mapping.preset.insert({ + -- Scroll the documentation window [b]ack / [f]orward + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.abort() + fallback() + else + fallback() + end + end), - -- Scroll the documentation window [b]ack / [f]orward - [""] = cmp.mapping.scroll_docs(-4), - [""] = cmp.mapping.scroll_docs(4), - - -- Accept ([y]es) the completion. - -- This will auto-import if your LSP supports it. - -- This will expand snippets if the LSP sent a snippet. - [""] = cmp.mapping.confirm({ select = true }), - - -- If you prefer more traditional completion keymaps, - -- you can uncomment the following lines - --[''] = cmp.mapping.confirm { select = true }, - --[''] = cmp.mapping.select_next_item(), - --[''] = cmp.mapping.select_prev_item(), - - -- Manually trigger a completion from nvim-cmp. - -- Generally you don't need this, because nvim-cmp will display - -- completions whenever it has completion options available. - [""] = cmp.mapping.complete({}), - - -- TODO remove these or make them soemthing else, this collided with my normal movements in insert mode - -- Think of as moving to the right of your snippet expansion. - -- So if you have a snippet that's like: - -- function $name($args) - -- $body - -- end - -- - -- will move you to the right of each of the expansion locations. - -- is similar, except moving you backwards. - -- [""] = cmp.mapping(function() - -- if luasnip.expand_or_locally_jumpable() then - -- luasnip.expand_or_jump() - -- end - -- end, { "i", "s" }), - -- [""] = cmp.mapping(function() - -- if luasnip.locally_jumpable(-1) then - -- luasnip.jump(-1) - -- end - -- end, { "i", "s" }), - - -- For more advanced Luasnip keymaps (e.g. selecting choice nodes, expansion) see: - -- https://github.com/L3MON4D3/LuaSnip?tab=readme-ov-file#keymaps - }), - sources = { - { name = "nvim_lsp" }, - { name = "luasnip" }, - { name = "path" }, - }, - }) - end, + -- Select the [n]ext item + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + -- elseif luasnip.expand_or_jumpable() then + elseif luasnip.expand_or_locally_jumpable() then + luasnip.expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + -- Select the [p]revious item + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping.confirm({ select = true }), + [""] = cmp.mapping.complete({}), + }), + sources = { + { + name = "copilot", + priority = 9, + keyword_length = 1, + filter = function(keyword) + -- Check if keyword length is some number and not just whitespace + if #keyword < 2 or keyword:match("^%s*$") then + return false + end + return true + end, + }, + { name = "nvim_lsp", priority = 8, max_item_count = 100 }, + { name = "luasnip", priority = 7 }, + -- This source provides file path completions, helping you to complete file paths in your code + { name = "path", priority = 7 }, + -- This source provides completion items from the current buffer, meaning it suggests words that have already been typed in the same file. + { name = "buffer", priority = 6 }, + -- Rust crates.io integration + { name = "crates" }, + }, + -- TODO revisit if I want these or not + -- sorting = { + -- priority_weight = 1, + -- comparators = { + -- cmp.config.compare.locality, + -- cmp.config.compare.recently_used, + -- cmp.config.compare.score, + -- cmp.config.compare.offset, + -- cmp.config.compare.order, + -- }, + -- }, + -- window = { -- also? https://github.com/RingOfStorms/nvim/blob/master/lua/plugins/lsp.lua#L330-L347 + -- completion = cmp.config.window.bordered(), + -- documentation = cmp.config.window.bordered(), + -- }, + }) + end, } diff --git a/lua/plugins/editor_lua_line.lua b/lua/plugins/editor_lua_line.lua index 3af51f2..551d6d0 100644 --- a/lua/plugins/editor_lua_line.lua +++ b/lua/plugins/editor_lua_line.lua @@ -1,137 +1,133 @@ --- TODO checkout https://github.com/nvim-lua/lsp-status.nvim --- https://www.reddit.com/r/neovim/comments/o4bguk/comment/h2kcjxa/ local function lsp_clients() - local clients = {} - for _, client in pairs(vim.lsp.buf_get_clients(0)) do - local name = client.name - -- TODO revisit this doesn't work - if not client.initialized then - name = name .. " (loading)" - end - clients[#clients + 1] = name - end - - table.sort(clients) - return table.concat(clients, " • "), " " + local clients = {} + for _, client in pairs(vim.lsp.buf_get_clients(0)) do + clients[#clients + 1] = client.name + end + table.sort(clients) + return table.concat(clients, " • "), " " end local function langs() - local l = {} - for _, client in pairs(vim.lsp.buf_get_clients(0)) do - local out = nil - if client.name == "pyright" then - out = vim.fn.system({ "python", "-V" }) - elseif client.name == "tsserver" then - out = "node " .. vim.fn.system({ "node", "--version" }) - end - if out ~= nil and out ~= "" then - l[#l + 1] = vim.trim(out) - end - end + local l = {} + for _, client in pairs(vim.lsp.buf_get_clients(0)) do + local out = nil + if client.name == "pyright" then + out = vim.fn.system({ "python", "-V" }) + elseif client.name == "tsserver" then + out = "node " .. vim.fn.system({ "node", "--version" }) + end + if out ~= nil and out ~= "" then + l[#l + 1] = vim.trim(out) + end + end - table.sort(l) - return table.concat(l, " • "), " " + table.sort(l) + return table.concat(l, " • "), " " end local last_blame = nil local last_blame_time = vim.loop.now() local function gitblame() - local d = vim.b.gitsigns_blame_line_dict + local d = vim.b.gitsigns_blame_line_dict - if d then - last_blame = d - last_blame_time = vim.loop.now() - elseif vim.loop.now() - last_blame_time <= 2000 then - d = last_blame - end + if d then + last_blame = d + last_blame_time = vim.loop.now() + elseif vim.loop.now() - last_blame_time <= 2000 then + d = last_blame + end - if d then - local ok, res = pcall(os.date, "%d %b %y", d.committer_time) - return d.committer:sub(1, 12) .. " - " .. (ok and res or d.committer_time) - end - return "" + if d then + local ok, res = pcall(os.date, "%d %b %y", d.committer_time) + return d.committer:sub(1, 12) .. " - " .. (ok and res or d.committer_time) + end + return "" end return { - "nvim-lualine/lualine.nvim", - dependencies = { { "folke/noice.nvim", optional = true } }, - lazy = false, - opts = function() - return { - options = { - theme = "codedark", - section_separators = { left = "", right = "" }, - component_separators = "|", - }, - sections = { - lualine_a = { "mode" }, - lualine_b = { "branch", "diff", "diagnostics" }, - lualine_c = { - { "filename", separator = { right = "" } }, - { "reg_recording", icon = { "󰻃" }, color = { fg = "#D37676" } }, - { gitblame, color = { fg = "#696969" } }, - }, - lualine_x = { - lsp_clients, - langs, - "encoding", - "filetype", - "filesize", - }, - lualine_y = { "searchcount", "selectioncount" }, - lualine_z = { "location" }, - }, - winbar = { - lualine_a = { - { - "filename", - symbols = { - modified = "", -- Text to show when the file is modified. - readonly = "[-]", -- Text to show when the file is non-modifiable or readonly. - unnamed = "[No Name]", -- Text to show for unnamed buffers. - newfile = "[New]", -- Text to show for newly created file before first write - }, - }, - }, - lualine_b = { - "mode", - }, - }, - inactive_winbar = { - lualine_a = { - { - "filename", - symbols = { - modified = "", -- Text to show when the file is modified. - readonly = "[-]", -- Text to show when the file is non-modifiable or readonly. - unnamed = "[No Name]", -- Text to show for unnamed buffers. - newfile = "[New]", -- Text to show for newly created file before first write - }, - }, - }, - }, - } - end, - config = function(_, opts) - require("lualine").setup(opts) + "nvim-lualine/lualine.nvim", + dependencies = { { "folke/noice.nvim", optional = true } }, + lazy = false, + opts = function() + return { + options = { + theme = "codedark", + section_separators = { left = "", right = "" }, + component_separators = "|", + }, + sections = { + lualine_a = { "mode" }, + lualine_b = { + "branch", + "filename", + }, + lualine_c = { + "diff", + "diagnostics", + { "reg_recording", icon = { "󰻃" }, color = { fg = "#D37676" } }, + { gitblame, color = { fg = "#696969" } }, + }, + lualine_x = { + lsp_clients, + langs, + "encoding", + "filetype", + "filesize", + }, + lualine_y = { "searchcount", "selectioncount" }, + lualine_z = { "location" }, + }, + winbar = { + lualine_a = { + { + "filename", + symbols = { + modified = "", -- Text to show when the file is modified. + readonly = "[-]", -- Text to show when the file is non-modifiable or readonly. + unnamed = "[No Name]", -- Text to show for unnamed buffers. + newfile = "[New]", -- Text to show for newly created file before first write + }, + }, + }, + lualine_b = { + "mode", + }, + }, + inactive_winbar = { + lualine_a = { + { + "filename", + symbols = { + modified = "", -- Text to show when the file is modified. + readonly = "[-]", -- Text to show when the file is non-modifiable or readonly. + unnamed = "[No Name]", -- Text to show for unnamed buffers. + newfile = "[New]", -- Text to show for newly created file before first write + }, + }, + }, + }, + } + end, + config = function(_, opts) + require("lualine").setup(opts) - local ref = function() - require("lualine").refresh({ - place = { "statusline" }, - }) - end + local ref = function() + require("lualine").refresh({ + place = { "statusline" }, + }) + end - local group = vim.api.nvim_create_augroup("myconfig-lua-line-group", { clear = true }) - vim.api.nvim_create_autocmd("RecordingEnter", { - group = group, - callback = ref, - }) - vim.api.nvim_create_autocmd("RecordingLeave", { - group = group, - callback = function() - local timer = vim.loop.new_timer() - timer:start(50, 0, vim.schedule_wrap(ref)) - end, - }) - end, + local group = vim.api.nvim_create_augroup("myconfig-lua-line-group", { clear = true }) + vim.api.nvim_create_autocmd("RecordingEnter", { + group = group, + callback = ref, + }) + vim.api.nvim_create_autocmd("RecordingLeave", { + group = group, + callback = function() + local timer = vim.loop.new_timer() + timer:start(50, 0, vim.schedule_wrap(ref)) + end, + }) + end, } diff --git a/lua/plugins/editor_noice.lua b/lua/plugins/editor_noice.lua index c0870d1..90096e8 100644 --- a/lua/plugins/editor_noice.lua +++ b/lua/plugins/editor_noice.lua @@ -7,11 +7,6 @@ return { }, event = "VeryLazy", opts = { - routes = { - -- I want telescope-ui-select to trigger here not noice - { filter = { event = "lsp", kind = "search_count" }, opts = { skip = true } }, - { filter = { event = "lsp" }, opts = { skip = true } }, -- TODO come back to this, im having weird issues with insert mode getting broken. - }, messages = { view = "mini", -- default view for messages view_error = "notify", -- view for errors @@ -26,6 +21,8 @@ return { ["vim.lsp.util.stylize_markdown"] = true, ["cmp.entry.get_documentation"] = true, -- requires hrsh7th/nvim-cmp }, + -- I had an issue with auto_open kicking me out of insert mode when entering insert mode + signature = { auto_open = { trigger = false } }, }, }, config = function(_, opts) diff --git a/lua/plugins/formatter.lua b/lua/plugins/formatter.lua index 4384f9a..e17359b 100644 --- a/lua/plugins/formatter.lua +++ b/lua/plugins/formatter.lua @@ -1,36 +1,45 @@ +local function formatCurrent(retry) + require("conform").format({ async = true, lsp_fallback = true }, function(err, edited) + if edited then + print("Formatted!") + elseif err then + -- Sometimes I am too fast and vim is saving from my InsertExit and this fails so + -- I give it one retry + if not retry and string.find(err, "concurrent modification") then + return formatCurrent(true) + end + print(err) + else + print("Nothing to format!") + end + end) +end + return { "stevearc/conform.nvim", opts = { -- https://github.com/stevearc/conform.nvim?tab=readme-ov-file#setup notify_on_error = true, - -- Note that all these need to be available at runtime, add them to flake.nix#runtimeDependencies + -- Note that all these need to be available at runtime, add them to flake.nix#runtimeDependencies formatters_by_ft = { lua = { "stylua" }, - nix = { "nixfmt" }, - typescript = { { "prettierd", "prettier" } }, - typescriptreact = { { "prettierd", "prettier" } }, - javascript = { { "prettierd", "prettier" } }, - javascriptreact = { { "prettierd", "prettier" } }, - -- TODO revisit these I'd like to use them but they are not in nixpkgs yet - -- https://nixos.org/guides/nix-pills/ - -- markdown = { "mdslw", "mdsf"}, - markdown = { "markdownlint-cli2" }, + nix = { "nixfmt" }, + typescript = { { "prettierd", "prettier" }, "rustywind" }, + typescriptreact = { { "prettierd", "prettier" }, "rustywind" }, + javascript = { { "prettierd", "prettier" }, "rustywind" }, + javascriptreact = { { "prettierd", "prettier" }, "rustywind" }, + + -- TODO revisit these I'd like to use them but they are not in nixpkgs yet + -- https://nixos.org/guides/nix-pills/ + -- markdown = { "mdslw", "mdsf"}, + markdown = { "markdownlint-cli2" }, + -- rust = { "rustfmt" }, }, }, keys = { { "l", - function() - require("conform").format({ async = true, lsp_fallback = true }, function(err, edited) - if edited then - print("Formatted!") - elseif err then - print(err) - else - print("Nothing to format!") - end - end) - end, + formatCurrent, mode = { "n", "v", "x" }, desc = "Format buffer", }, diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua index adebcd9..954274b 100644 --- a/lua/plugins/lsp.lua +++ b/lua/plugins/lsp.lua @@ -1,145 +1,176 @@ return { - "neovim/nvim-lspconfig", - event = "BufEnter", - dependencies = { - -- Automatically install LSPs and related tools to stdpath for Neovim - { "williamboman/mason.nvim", enabled = not NIX, config = true }, -- NOTE: Must be loaded before dependants - { "williamboman/mason-lspconfig.nvim", enabled = not NIX }, - { "WhoIsSethDaniel/mason-tool-installer.nvim", enabled = not NIX }, + -- LSP helper plugins for various languages + { "folke/neodev.nvim", event = { "BufRead *.lua", "BufRead *.vim" }, opts = {}, main = "neodev" }, + { "mrcjkb/rustaceanvim", lazy = false }, -- uses ftplugins to enable itself lazily already + -- TODO add some hotkeys for opening the popup menus on crates + { "Saecki/crates.nvim", event = "BufRead Cargo.toml", tag = "stable", opts = {}, main = "crates" }, + { + "neovim/nvim-lspconfig", + event = "BufEnter", + dependencies = { + { + "lvimuser/lsp-inlayhints.nvim", + init = function() + vim.api.nvim_create_augroup("LspAttach_inlayhints", { clear = true }) + vim.api.nvim_create_autocmd("LspAttach", { + group = "LspAttach_inlayhints", + callback = function(args) + if not (args.data and args.data.client_id) then + return + end - -- TODO revisit if I want this or not, is this already solved? - -- Useful status updates for LSP. - -- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})` - -- { "j-hui/fidget.nvim", opts = {} }, + local bufnr = args.buf + local client = vim.lsp.get_client_by_id(args.data.client_id) + require("lsp-inlayhints").on_attach(client, bufnr) + end, + }) + end, + opts = { + type_hints = { prefix = " ::" }, + }, + main = "lsp-inlayhints", + }, + -- Automatically install LSPs and related tools to stdpath for Neovim + { "williamboman/mason.nvim", enabled = not NIX, config = true }, -- NOTE: Must be loaded before dependants + { "williamboman/mason-lspconfig.nvim", enabled = not NIX }, + { "WhoIsSethDaniel/mason-tool-installer.nvim", enabled = not NIX }, + }, + config = function() + vim.api.nvim_create_autocmd("LspAttach", { + group = vim.api.nvim_create_augroup("myconfig-lsp-attach", { clear = true }), + callback = function(event) + local map = function(keys, func, desc) + vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) + end + map("gd", require("telescope.builtin").lsp_definitions, "Goto Definition") + map("gr", require("telescope.builtin").lsp_references, "Goto References") + map("gI", require("telescope.builtin").lsp_implementations, "Goto Implementation") + map("lr", vim.lsp.buf.rename, "Rename") + map("la", vim.lsp.buf.code_action, "Code Action") + map("K", vim.lsp.buf.hover, "Hover Documentation") + map("gD", vim.lsp.buf.declaration, "Goto Declaration") + end, + }) - -- `neodev` configures Lua LSP for your Neovim config, runtime and plugins - -- used for completion, annotations and signatures of Neovim apis - { "folke/neodev.nvim", opts = {}, main = "neodev" }, - }, - config = function() - vim.api.nvim_create_autocmd("LspAttach", { - group = vim.api.nvim_create_augroup("myconfig-lsp-attach", { clear = true }), - callback = function(event) - local map = function(keys, func, desc) - vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) - end - map("gd", require("telescope.builtin").lsp_definitions, "Goto Definition") - map("gr", require("telescope.builtin").lsp_references, "Goto References") - map("gI", require("telescope.builtin").lsp_implementations, "Goto Implementation") - -- TODO do I want these? - -- map("D", require("telescope.builtin").lsp_type_definitions, "Type Definition") - -- map("ds", require("telescope.builtin").lsp_document_symbols, "Document Symbols") - -- map("ws", require("telescope.builtin").lsp_dynamic_workspace_symbols, "Workspace Symbols") - map("lr", vim.lsp.buf.rename, "Rename") - map("la", vim.lsp.buf.code_action, "Code Action") - map("K", vim.lsp.buf.hover, "Hover Documentation") - map("gD", vim.lsp.buf.declaration, "Goto Declaration") + vim.api.nvim_create_autocmd("LspDetach", { + group = vim.api.nvim_create_augroup("myconfig-lsp-detach", { clear = true }), + callback = function(event) + vim.lsp.buf.clear_references() + vim.api.nvim_clear_autocmds({ group = "myconfig-lsp-highlight", buffer = event.buf }) + end, + }) - local client = vim.lsp.get_client_by_id(event.data.client_id) - if client and client.server_capabilities.inlayHintProvider and vim.lsp.inlay_hint then - map("lth", function() - vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) - end, "Toggle Inlay Hints") - end - end, - }) + local capabilities = vim.lsp.protocol.make_client_capabilities() + U.safeRequire("cmp_nvim_lsp", function(c) + capabilities = vim.tbl_deep_extend("force", capabilities, c.default_capabilities()) + end) - vim.api.nvim_create_autocmd("LspDetach", { - group = vim.api.nvim_create_augroup("myconfig-lsp-detach", { clear = true }), - callback = function(event) - vim.lsp.buf.clear_references() - vim.api.nvim_clear_autocmds({ group = "myconfig-lsp-highlight", buffer = event.buf }) - end, - }) - - local capabilities = vim.lsp.protocol.make_client_capabilities() - U.safeRequire("cmp_nvim_lsp", function(c) - capabilities = vim.tbl_deep_extend("force", capabilities, c.default_capabilities()) - end) - - -- TODO finish porting over lsp configs: https://github.com/RingOfStorms/nvim/blob/master/lua/plugins/lsp.lua - local servers = { - -- clangd = {}, - -- gopls = {}, - -- pyright = {}, - -- rust_analyzer = {}, - -- ... etc. See `:help lspconfig-all` for a list of all the pre-configured LSPs - -- - -- Some languages (like typescript) have entire language plugins that can be useful: - -- https://github.com/pmizio/typescript-tools.nvim - -- - -- But for many setups, the LSP (`tsserver`) will work just fine - tsserver = { - -- typescript/javascript - implicitProjectConfiguration = { - checkJs = true, - }, - }, - pyright = { - -- python - }, - lua_ls = { - -- cmd = { ... }, - -- filetypes = { ...}, - -- capabilities = {}, - settings = { - Lua = { - runtime = { - -- Tell the language server which version of Lua you're using - -- (most likely LuaJIT in the case of Neovim) - version = "LuaJIT", - }, - completion = { - callSnippet = "Replace", - }, - workspace = { - checkThirdParty = false, - library = { - vim.env.VIMRUNTIME, - vim.api.nvim_get_runtime_file("", true), - vim.fn.expand("$VIMRUNTIME/lua"), - vim.fn.expand("$VIMRUNTIME/lua/vim/lsp"), - }, - telemetry = { enable = false }, - diagnostics = { - globals = { - "vim", - "require", - "NIX", - "U", - -- Hammerspoon - "hs", - }, - }, - }, - -- You can toggle below to ignore Lua_LS's noisy `missing-fields` warnings - -- diagnostics = { disable = { 'missing-fields' } }, - }, - }, - }, - } - if NIX then - local servers = vim.tbl_keys(servers or {}) - for _, server_name in ipairs(servers) do - local server_opts = servers[server_name] or {} - require("lspconfig")[server_name].setup(server_opts) - end - else - require("mason").setup() - local ensure_installed = vim.tbl_keys(servers or {}) - vim.list_extend(ensure_installed, { - "stylua", -- Used to format Lua code TODO come back to this, more about linter/formatter configs - }) - require("mason-tool-installer").setup({ ensure_installed = ensure_installed }) - require("mason-lspconfig").setup({ - handlers = { - function(server_name) - local server = servers[server_name] or {} - server.capabilities = vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {}) - require("lspconfig")[server_name].setup(server) - end, - }, - }) - end - end, + -- TODO finish porting over lsp configs: https://github.com/RingOfStorms/nvim/blob/master/lua/plugins/lsp.lua + local servers = { + -- Some languages (like typescript) have entire language plugins that can be useful: + -- https://github.com/pmizio/typescript-tools.nvim + -- + -- But for many setups, the LSP (`tsserver`) will work just fine + -- Note that `rust-analyzer` is done via mrcjkb/rustaceanvim plugin above, do not register it here. + lua_ls = { + settings = { + Lua = { + runtime = { + -- Tell the language server which version of Lua you're using + -- (most likely LuaJIT in the case of Neovim) + version = "LuaJIT", + }, + completion = { + callSnippet = "Replace", + }, + workspace = { + checkThirdParty = false, + library = { + vim.env.VIMRUNTIME, + vim.api.nvim_get_runtime_file("", true), + vim.fn.expand("$VIMRUNTIME/lua"), + vim.fn.expand("$VIMRUNTIME/lua/vim/lsp"), + }, + telemetry = { enable = false }, + diagnostics = { + globals = { + "vim", + "require", + "NIX", + "U", + -- Hammerspoon for macos + "hs", + }, + }, + }, + }, + }, + }, + nil_ls = {}, + tsserver = { + -- typescript/javascript + implicitProjectConfiguration = { + checkJs = true, + }, + }, + tailwindcss = { + -- tailwind css + -- https://www.tailwind-variants.org/docs/getting-started#intellisense-setup-optional + tailwindCSS = { + experimental = { + classRegex = { + { "tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" }, + }, + }, + }, + }, + cssls = { + -- css + }, + jsonls = { + -- json + }, + pyright = { + -- python + }, + marksman = { + -- markdown + }, + taplo = { + -- toml + }, + yamlls = { + -- yaml + }, + lemminx = { + -- xml + }, + } + if NIX then + local lsp_servers = vim.tbl_keys(servers or {}) + for _, server_name in ipairs(lsp_servers) do + local server_opts = servers[server_name] or {} + require("lspconfig")[server_name].setup(server_opts) + end + else + -- TODO test this out on a non nix setup... + require("mason").setup() + local ensure_installed = vim.tbl_keys(servers or {}) + vim.list_extend(ensure_installed, { + "stylua", -- Used to format Lua code TODO come back to this, more about linter/formatter configs + }) + require("mason-tool-installer").setup({ ensure_installed = ensure_installed }) + require("mason-lspconfig").setup({ + handlers = { + function(server_name) + local server = servers[server_name] or {} + server.capabilities = + vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {}) + require("lspconfig")[server_name].setup(server) + end, + }, + }) + end + end, + }, } diff --git a/lua/plugins/text_case.lua b/lua/plugins/text_case.lua index 1d580c8..fdbefc6 100644 --- a/lua/plugins/text_case.lua +++ b/lua/plugins/text_case.lua @@ -11,7 +11,6 @@ return { ",c", function() if next(vim.lsp.get_active_clients()) ~= nil then - -- TODO test that this works vim.cmd("TextCaseOpenTelescopeLSPChange") else vim.cmd("TextCaseOpenTelescope") diff --git a/lua/plugins/whichkey.lua b/lua/plugins/whichkey.lua index f7d186e..4277601 100644 --- a/lua/plugins/whichkey.lua +++ b/lua/plugins/whichkey.lua @@ -15,15 +15,15 @@ return { local wk = require("which-key") wk.setup(opts) wk.register({ - ["b"] = { name = "Buffers", mode = { "n", "x", "v", "c" } }, - ["t"] = { name = "Tabs", mode = { "n", "x", "v", "c" } }, - [","] = { name = "Miscellaneous Tools", mode = { "n", "x", "v", "c" } }, + ["b"] = { name = "Buffers", mode = { "n", "x", "v" } }, + ["t"] = { name = "Tabs", mode = { "n", "x", "v" } }, + [","] = { name = "Miscellaneous Tools", mode = { "n", "x", "v" } }, -- ["c"] = { name = "Copilot" }, - ["f"] = { name = "Find [Telescope]", mode = { "n", "x", "v", "c" } }, + ["f"] = { name = "Find [Telescope]", mode = { "n", "x", "v" } }, -- ["fs"] = { name = "Find in Scratches [Telescope]" }, - ["g"] = { name = "Git", mode = { "n", "x", "v", "c" } }, - ["l"] = { name = "LSP", mode = { "n", "x", "v", "c" } }, - ["lf"] = { name = "LSP Find", mode = { "n", "x", "v", "c" } }, + ["g"] = { name = "Git", mode = { "n", "x", "v" } }, + ["l"] = { name = "LSP", mode = { "n", "x", "v" } }, + ["lf"] = { name = "LSP Find", mode = { "n", "x", "v" } }, -- ["Q"] = { name = "+Q Quit and remove session" }, -- ["s"] = { name = "Scratch Files" }, -- ["x"] = { name = "Generative AI, Ollama" }, diff --git a/lua/util.lua b/lua/util.lua index 8149e60..afe5eff 100644 --- a/lua/util.lua +++ b/lua/util.lua @@ -1,30 +1,41 @@ local M = {} +function M.isArray(table) + local i = 0 + for _ in pairs(table) do + i = i + 1 + if table[i] == nil then + return false + end + end + return true +end + function M.cmd_executable(cmd, callback) - local executable = vim.fn.executable(cmd) == 1 - -- Check if a callback is provided and it is a function - if executable and callback and type(callback) == "function" then - callback() - end + local executable = vim.fn.executable(cmd) == 1 + -- Check if a callback is provided and it is a function + if executable and callback and type(callback) == "function" then + callback() + end - -- Check if a callback is provided and it is a table - if type(callback) == "table" then - if executable and (callback[1] or callback[true]) then - -- Call the function associated with key 1 or true if the command is executable - local func = callback[1] or callback[true] - if type(func) == "function" then - func() - end - elseif not executable and (callback[2] or callback[false]) then - -- Call the function associated with key 2 or false if the command is not executable - local func = callback[2] or callback[false] - if type(func) == "function" then - func() - end - end - end + -- Check if a callback is provided and it is a table + if type(callback) == "table" then + if executable and (callback[1] or callback[true]) then + -- Call the function associated with key 1 or true if the command is executable + local func = callback[1] or callback[true] + if type(func) == "function" then + func() + end + elseif not executable and (callback[2] or callback[false]) then + -- Call the function associated with key 2 or false if the command is not executable + local func = callback[2] or callback[false] + if type(func) == "function" then + func() + end + end + end - return executable + return executable end -- [1]: (string) lhs (required) @@ -33,127 +44,115 @@ end -- ft: (string|string[]) filetype for buffer-local keymaps (optional) -- any other option valid for vim.keymap.set function M.keymaps(keymaps) - -- is not an array, will pass directly to keymaps - if type(keymaps[1]) == "string" then - M.keymap(keymaps) - else - -- is array will iterate over - for _, keymap in pairs(keymaps) do - M.keymap(keymap) - end - end + -- is not an array, will pass directly to keymaps + if type(keymaps[1]) == "string" then + M.keymap(keymaps) + else + -- is array will iterate over + for _, keymap in pairs(keymaps) do + M.keymap(keymap) + end + end end function M.keymap(keymap) - local lhs = keymap[1] - local rhs = keymap[2] - local mode = keymap["mode"] or "n" - local opts = { silent = true } - for key, value in pairs(keymap) do - if type(key) ~= "number" and key ~= "mode" then - opts[key] = value - end - end + local lhs = keymap[1] + local rhs = keymap[2] + local mode = keymap["mode"] or "n" + local opts = { silent = true } + for key, value in pairs(keymap) do + if type(key) ~= "number" and key ~= "mode" then + opts[key] = value + end + end - local status, err = pcall(function() - vim.keymap.set(mode, lhs, rhs, opts) - end) - if not status then - vim.notify("Failed to create keymap: " .. err, 3) - end + local status, err = pcall(function() + vim.keymap.set(mode, lhs, rhs, opts) + end) + if not status then + vim.notify("Failed to create keymap: " .. err, 3) + end end -- spread({})({}) function M.spread(template) - local result = {} - for key, value in pairs(template) do - result[key] = value - end + local result = {} + for key, value in pairs(template) do + result[key] = value + end - return function(table) - for key, value in pairs(table) do - result[key] = value - end - return result - end + return function(table) + for key, value in pairs(table) do + result[key] = value + end + return result + end end -- assign({}, {}) function M.assign(obj, assign) - for key, value in pairs(assign) do - obj[key] = value - end - return obj + for key, value in pairs(assign) do + obj[key] = value + end + return obj end function M.table_contains(table, element) - for _, value in pairs(table) do - if value == element then - return true - end - end - return false + for _, value in pairs(table) do + if value == element then + return true + end + end + return false end -- From https://github.com/lukas-reineke/onedark.nvim/blob/master/lua/onedark.lua function M.highlight(group, options) - local guifg = options.fg or "NONE" - local guibg = options.bg or "NONE" - local guisp = options.sp or "NONE" - local gui = options.gui or "NONE" - local blend = options.blend or 0 - local ctermfg = options.ctermfg or "NONE" + local guifg = options.fg or "NONE" + local guibg = options.bg or "NONE" + local guisp = options.sp or "NONE" + local gui = options.gui or "NONE" + local blend = options.blend or 0 + local ctermfg = options.ctermfg or "NONE" - vim.cmd( - string.format( - "highlight %s guifg=%s ctermfg=%s guibg=%s guisp=%s gui=%s blend=%d", - group, - guifg, - ctermfg, - guibg, - guisp, - gui, - blend - ) - ) + vim.cmd( + string.format( + "highlight %s guifg=%s ctermfg=%s guibg=%s guisp=%s gui=%s blend=%d", + group, + guifg, + ctermfg, + guibg, + guisp, + gui, + blend + ) + ) end function M.safeRequire(module, func, errorFunc) - local ok, result = pcall(require, module) - if ok then - return func(result) - elseif errorFunc then - return errorFunc(result) - end - return nil + local ok, result = pcall(require, module) + if ok then + return func(result) + elseif errorFunc then + return errorFunc(result) + end + return nil end --- TODO remove if not needed --- local startup_time = vim.loop.now() --- function M.delayFromStartup(delay, func) --- local current_time = vim.loop.now() --- local diff = current_time - startup_time --- if diff >= delay then --- func() --- else --- vim.defer_fn(func, (delay - diff)) --- end --- end - function M.fnFalse() - return false + return false end function M.fnNil() - return nil + return nil end function M.fnEmptyStr() - return "" + return "" end function M.fnZero() - return 0 + return 0 end return M