Add neovim plugins (+configuration)

master
Hektor Misplon 2025-05-05 22:50:28 +02:00
parent 958883c21c
commit e660a92443
17 changed files with 810 additions and 0 deletions

View File

@ -0,0 +1,28 @@
require("conform").setup({
format_on_save = {
lsp_fallback = true,
async = false,
timeout_ms = 500,
},
formatters_by_ft = {
awk = { "awk" },
bash = { "shellcheck" },
c = { "clang-format" },
cpp = { "clang-format" },
css = { "prettierd", "prettier" },
gdscript = { "gdformat" },
haskell = { "ormolu" },
html = { "prettierd", "prettier" },
lua = { "stylua" }, -- configured in stylua.toml
markdown = { "prettierd", "prettier" },
javascript = { "prettierd", "prettier" },
javascriptreact = { "prettierd", "prettier" },
json = { "prettierd", "prettier" },
jsonc = { "prettierd", "prettier" },
python = { "isort", "black" },
svelte = { "prettierd", "prettier" },
typescript = { "prettierd", "prettier" },
typescriptreact = { "prettierd", "prettier" },
yaml = { "prettierd", "prettier" },
},
})

View File

@ -0,0 +1,11 @@
local gitblame = require("gitblame")
gitblame.setup({
enabled = false,
date_format = "%r",
message_template = " [<date> (<author>) <sha> <summary>]",
message_when_not_committed = " [Uncommitted changes]",
delay = 250,
})
-- vim.g.gitblame_virtual_text_column = 80

View File

@ -0,0 +1,8 @@
require("image").setup({
processor = "magick_cli",
integrations = {
markdown = {
filetypes = { "markdown", "pandoc" },
},
},
})

View File

@ -0,0 +1,7 @@
require("lsp_lines").setup()
vim.diagnostic.config({
virtual_text = false,
})
vim.keymap.set("", "<leader>dd", require("lsp_lines").toggle, { desc = "Toggle lsp_lines" })

View File

@ -0,0 +1,135 @@
require("neodev").setup() -- should setup before lspconfig
local lspconfig = require("lspconfig")
-- vim.g.coq_settings = { auto_start = 'shut-up' }
-- local capabilities = coq.lsp_ensure_capabilities()
local cmp_nvim_lsp = require("cmp_nvim_lsp")
local capabilities = cmp_nvim_lsp.default_capabilities()
local servers = {
bashls = {},
eslint = {},
emmet_language_server = {},
gdscript = {},
hls = { filetypes = { "haskell", "lhaskell", "cabal" } },
html = {},
jsonls = {
settings = {
json = {
schemas = require("schemastore").json.schemas(),
validate = { enable = true },
},
},
},
lua_ls = {},
-- marksman = {},
pyright = {},
-- tsserver = {},
svelte = {
plugin = {
svelte = {
defaultScriptLanguage = "ts",
},
},
},
tailwindcss = {},
-- vtsls = {},
ts_ls = {},
-- vtsls = {
-- maxTsServerMemory = 16384,
-- filetypes = {
-- "javascript",
-- "javascriptreact",
-- "javascript.jsx",
-- "typescript",
-- "typescriptreact",
-- "typescript.tsx",
-- },
-- settings = {
-- complete_function_calls = true,
-- vtsls = {
-- enableMoveToFileCodeAction = true,
-- autoUseWorkspaceTsdk = true,
-- experimental = {
-- completion = {
-- enableServerSideFuzzyMatch = true,
-- },
-- },
-- },
-- typescript = {
-- updateImportsOnFileMove = { enabled = "always" },
-- suggest = {
-- completeFunctionCalls = true,
-- },
-- inlayHints = {
-- enumMemberValues = { enabled = true },
-- functionLikeReturnTypes = { enabled = true },
-- parameterNames = { enabled = "literals" },
-- parameterTypes = { enabled = true },
-- propertyDeclarationTypes = { enabled = true },
-- variableTypes = { enabled = false },
-- },
-- },
-- },
-- },
yamlls = {
settings = {
yaml = {
schemaStore = {
-- You must disable built-in schemaStore support if you want to use
-- this plugin and its advanced options like `ignore`.
enable = false,
-- Avoid TypeError: Cannot read properties of undefined (reading 'length')
url = "",
},
schemas = require("schemastore").yaml.schemas(),
},
},
},
}
for server, config in pairs(servers) do
config.capabilities = capabilities
lspconfig[server].setup(config)
end
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(e)
local opts = { buffer = e.bufnr }
local set = vim.keymap.set
local lsp_buf = vim.lsp.buf
set("n", "gD", lsp_buf.declaration, opts)
set("n", "gd", lsp_buf.definition, opts)
set("n", "K", lsp_buf.hover, opts)
set("n", "gi", lsp_buf.implementation, opts)
set("n", "<C-k>", lsp_buf.signature_help, opts)
set("n", "<space>wa", lsp_buf.add_workspace_folder, opts)
set("n", "<space>wr", lsp_buf.remove_workspace_folder, opts)
set("n", "<space>wl", function()
print(vim.inspect(lsp_buf.list_workspace_folders()))
end, opts)
set("n", "<space>D", lsp_buf.type_definition, opts)
set("n", "<space>rn", lsp_buf.rename, opts)
set({ "n", "v" }, "<space>ca", lsp_buf.code_action, opts)
set("n", "gr", lsp_buf.references, opts)
-- Taken from https://blog.viktomas.com/graph/neovim-lsp-rename-normal-mode-keymaps/
set("n", "<leader>r", vim.lsp.buf.rename)
-- function()
-- -- Automatically switch to `cmdwin` for normal mode renaming
-- -- (normally you would have to press <C-f> to open the `cmdwin`)
-- vim.api.nvim_create_autocmd({ "CmdlineEnter" }, {
-- callback = function()
-- local key = vim.api.nvim_replace_termcodes("<C-f>", true, false, true)
-- vim.api.nvim_feedkeys(key, "c", false)
-- vim.api.nvim_feedkeys("0", "n", false)
-- return true
-- end,
-- })
-- vim.lsp.buf.rename()
-- end, bufoptsWithDesc("Rename symbol")
-- )
end,
})

View File

@ -0,0 +1,313 @@
local ls = require("luasnip")
local s = ls.snippet
local sn = ls.snippet_node
local t = ls.text_node
local i = ls.insert_node
local f = ls.function_node
local c = ls.choice_node
local d = ls.dynamic_node
local r = ls.restore_node
local l = require("luasnip.extras").lambda
local rep = require("luasnip.extras").rep
local p = require("luasnip.extras").partial
local m = require("luasnip.extras").match
local n = require("luasnip.extras").nonempty
local dl = require("luasnip.extras").dynamic_lambda
local fmt = require("luasnip.extras.fmt").fmt
local fmta = require("luasnip.extras.fmt").fmta
local conds = require("luasnip.extras.expand_conditions")
ls.config.set_config({
history = true,
update_events = "TextChanged,TextChangedI",
delete_check_events = "TextChanged",
ext_prio_increase = 1,
enable_autosnippets = true,
store_selection_keys = "<Tab>",
})
local function copy(args)
return args[1]
end
local function bash(_, _, command)
local file = io.popen(command, "r")
local res = {}
if file then
for line in file:lines() do
table.insert(res, line)
end
end
return res
end
local date_input = function(args, snip, old_state, date_format)
print(args, snip, old_state)
return sn(nil, i(1, os.date(date_format or "%Y-%m-%d")))
end
-- -- FIXME: EXAMPLE
-- ls.add_snippets("all", {
-- -- arg1: trigger `fn`,
-- -- arg2: nodes to insert into buffer on expansion.
-- s("fn", {
-- t("//Parameters: "), -- Text.
-- f(copy, 2), -- 1: function, 2: placeholders to copy text from
-- t({ "", "function " }), -- placeholder/insert.
-- i(1),
-- t("("), -- placeholder with initial text.
-- i(2, "int foo"), -- linebreak
-- t({ ") {", "\t" }), -- last placeholder, snippet exit point
-- i(0),
-- t({ "", "}" }),
-- }),
-- s("class", {
-- -- Choice: Switch between two different Nodes, first parameter is its position, second a list of nodes.
-- c(1, {
-- t("public "),
-- t("private "),
-- }),
-- t("class "),
-- i(2),
-- t(" "),
-- c(3, {
-- t("{"),
-- -- sn: Nested Snippet. Instead of a trigger, it has a position, just like insert-nodes. !!! These don't expect a 0-node!!!!
-- -- Inside Choices, Nodes don't need a position as the choice node is the one being jumped to.
-- sn(nil, {
-- t("extends "),
-- -- restoreNode: stores and restores nodes.
-- -- pass position, store-key and nodes.
-- r(1, "other_class", i(1)),
-- t(" {"),
-- }),
-- sn(nil, {
-- t("implements "),
-- -- no need to define the nodes for a given key a second time.
-- r(1, "other_class"),
-- t(" {"),
-- }),
-- }),
-- t({ "", "\t" }),
-- i(0),
-- t({ "", "}" }),
-- }),
-- -- Alternative printf-like notation for defining snippets. It uses format
-- -- string with placeholders similar to the ones used with Python's .format().
-- s(
-- "fmt1",
-- fmt("To {title} {} {}.", {
-- i(2, "Name"),
-- i(3, "Surname"),
-- title = c(1, { t("Mr."), t("Ms.") }),
-- })
-- ),
-- -- To escape delimiters use double them, e.g. `{}` -> `{{}}`.
-- -- Multi-line format strings by default have empty first/last line removed.
-- -- Indent common to all lines is also removed. Use the third `opts` argument
-- -- to control this behaviour.
-- s(
-- "fmt2",
-- fmt(
-- [[
-- foo({1}, {3}) {{
-- return {2} * {4}
-- }}
-- ]],
-- {
-- i(1, "x"),
-- rep(1),
-- i(2, "y"),
-- rep(2),
-- }
-- )
-- ),
-- -- Empty placeholders are numbered automatically starting from 1 or the last
-- -- value of a numbered placeholder. Named placeholders do not affect numbering.
-- s(
-- "fmt3",
-- fmt("{} {a} {} {1} {}", {
-- t("1"),
-- t("2"),
-- a = t("A"),
-- })
-- ),
-- -- The delimiters can be changed from the default `{}` to something else.
-- s("fmt4", fmt("foo() { return []; }", i(1, "x"), { delimiters = "[]" })),
-- -- `fmta` is a convenient wrapper that uses `<>` instead of `{}`.
-- s("fmt5", fmta("foo() { return <>; }", i(1, "x"))),
-- -- By default all args must be used. Use strict=false to disable the check
-- s(
-- "fmt6",
-- fmt("use {} only", { t("this"), t("not this") }, { strict = false })
-- ),
-- -- Use a dynamic_node to interpolate the output of a
-- -- function (see date_input above) into the initial
-- -- value of an insert_node.
-- s("novel", {
-- t("It was a dark and stormy night on "),
-- d(1, date_input, {}, { user_args = { "%A, %B %d of %Y" } }),
-- t(" and the clocks were striking thirteen."),
-- }),
-- -- Parsing snippets: First parameter: Snippet-Trigger, Second: Snippet body.
-- -- Placeholders are parsed into choices with 1. the placeholder text(as a snippet) and 2. an empty string.
-- -- This means they are not SELECTed like in other editors/Snippet engines.
-- ls.parser.parse_snippet(
-- "lspsyn",
-- "Wow! This ${1:Stuff} really ${2:works. ${3:Well, a bit.}}"
-- ),
-- -- When wordTrig is set to false, snippets may also expand inside other words.
-- ls.parser.parse_snippet(
-- { trig = "te", wordTrig = false },
-- "${1:cond} ? ${2:true} : ${3:false}"
-- ),
-- -- When regTrig is set, trig is treated like a pattern, this snippet will expand after any number.
-- ls.parser.parse_snippet({ trig = "%d", regTrig = true }, "A Number!!"),
-- -- Using the condition, it's possible to allow expansion only in specific cases.
-- s("cond", {
-- t("will only expand in c-style comments"),
-- }, {
-- condition = function(line_to_cursor, matched_trigger, captures)
-- -- optional whitespace followed by //
-- return line_to_cursor:match("%s*//")
-- end,
-- }),
-- -- there's some built-in conditions in "luasnip.extras.expand_conditions".
-- s("cond2", {
-- t("will only expand at the beginning of the line"),
-- }, {
-- condition = conds.line_begin,
-- }),
-- -- The last entry of args passed to the user-function is the surrounding snippet.
-- s(
-- { trig = "a%d", regTrig = true },
-- f(function(_, snip)
-- return "Triggered with " .. snip.trigger .. "."
-- end, {})
-- ),
-- -- It's possible to use capture-groups inside regex-triggers.
-- s(
-- { trig = "b(%d)", regTrig = true },
-- f(function(_, snip)
-- return "Captured Text: " .. snip.captures[1] .. "."
-- end, {})
-- ),
-- s({ trig = "c(%d+)", regTrig = true }, {
-- t("will only expand for even numbers"),
-- }, {
-- condition = function(line_to_cursor, matched_trigger, captures)
-- return tonumber(captures[1]) % 2 == 0
-- end,
-- }),
-- -- Use a function to execute any shell command and print its text.
-- s("bash", f(bash, {}, "ls")),
-- -- Short version for applying String transformations using function nodes.
-- s("transform", {
-- i(1, "initial text"),
-- t({ "", "" }),
-- -- lambda nodes accept an l._1,2,3,4,5, which in turn accept any string transformations.
-- -- This list will be applied in order to the first node given in the second argument.
-- l(l._1:match("[^i]*$"):gsub("i", "o"):gsub(" ", "_"):upper(), 1),
-- }),
-- s("transform2", {
-- i(1, "initial text"),
-- t("::"),
-- i(2, "replacement for e"),
-- t({ "", "" }),
-- -- Lambdas can also apply transforms USING the text of other nodes:
-- l(l._1:gsub("e", l._2), { 1, 2 }),
-- }),
-- s({ trig = "trafo(%d+)", regTrig = true }, {
-- -- env-variables and captures can also be used:
-- l(l.CAPTURE1:gsub("1", l.TM_FILENAME), {}),
-- }),
-- -- Set store_selection_keys = "<Tab>" (for example) in your
-- -- luasnip.config.setup() call to populate
-- -- TM_SELECTED_TEXT/SELECT_RAW/SELECT_DEDENT.
-- -- In this case: select a URL, hit Tab, then expand this snippet.
-- s("link_url", {
-- t('<a href="'),
-- f(function(_, snip)
-- -- TM_SELECTED_TEXT is a table to account for multiline-selections.
-- -- In this case only the first line is inserted.
-- return snip.env.TM_SELECTED_TEXT[1] or {}
-- end, {}),
-- t('">'),
-- i(1),
-- t("</a>"),
-- i(0),
-- }),
-- -- Shorthand for repeating the text in a given node.
-- s("repeat", { i(1, "text"), t({ "", "" }), rep(1) }),
-- -- Directly insert the ouput from a function evaluated at runtime.
-- s("part", p(os.date, "%Y")),
-- -- use matchNodes (`m(argnode, condition, then, else)`) to insert text
-- -- based on a pattern/function/lambda-evaluation.
-- -- It's basically a shortcut for simple functionNodes:
-- s("mat", {
-- i(1, { "sample_text" }),
-- t(": "),
-- m(1, "%d", "contains a number", "no number :("),
-- }),
-- -- The `then`-text defaults to the first capture group/the entire
-- -- match if there are none.
-- s("mat2", {
-- i(1, { "sample_text" }),
-- t(": "),
-- m(1, "[abc][abc][abc]"),
-- }),
-- -- It is even possible to apply gsubs' or other transformations
-- -- before matching.
-- s("mat3", {
-- i(1, { "sample_text" }),
-- t(": "),
-- m(
-- 1,
-- l._1:gsub("[123]", ""):match("%d"),
-- "contains a number that isn't 1, 2 or 3!"
-- ),
-- }),
-- -- `match` also accepts a function in place of the condition, which in
-- -- turn accepts the usual functionNode-args.
-- -- The condition is considered true if the function returns any
-- -- non-nil/false-value.
-- -- If that value is a string, it is used as the `if`-text if no if is explicitly given.
-- s("mat4", {
-- i(1, { "sample_text" }),
-- t(": "),
-- m(1, function(args)
-- -- args is a table of multiline-strings (as usual).
-- return (#args[1][1] % 2 == 0 and args[1]) or nil
-- end),
-- }),
-- -- The nonempty-node inserts text depending on whether the arg-node is
-- -- empty.
-- s("nempty", {
-- i(1, "sample_text"),
-- n(1, "i(1) is not empty!"),
-- }),
-- -- dynamic lambdas work exactly like regular lambdas, except that they
-- -- don't return a textNode, but a dynamicNode containing one insertNode.
-- -- This makes it easier to dynamically set preset-text for insertNodes.
-- s("dl1", {
-- i(1, "sample_text"),
-- t({ ":", "" }),
-- dl(2, l._1, 1),
-- }),
-- -- Obviously, it's also possible to apply transformations, just like lambdas.
-- s("dl2", {
-- i(1, "sample_text"),
-- i(2, "sample_text_2"),
-- t({ "", "" }),
-- dl(3, l._1:gsub("\n", " linebreak ") .. l._2, { 1, 2 }),
-- }),
-- }, {
-- key = "all",
-- })
require("luasnip.loaders.from_lua").lazy_load({ paths = { "~/.config/nvim/snips" } })
require("luasnip.loaders.from_vscode").lazy_load({ paths = { "~/.config/Code - Insiders/User/snippets" } })

View File

@ -0,0 +1,93 @@
local cmp = require("cmp")
local luasnip = require("luasnip")
-- TODO: Fix command mode completion (should behave similar to insert mode)
local c_j = cmp.mapping(function()
if cmp.visible() then
cmp.select_next_item()
else
cmp.complete()
cmp.select_next_item()
end
end, { "i", "s" })
local c_k = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
-- NOTE: Keep <C-k> fallback for digraphs
-- ```lua
-- cmp.complete()
-- cmp.select_prev_item()
-- ```
fallback()
end
end, { "i", "s" })
local c_h = cmp.mapping(function(fallback)
if luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" })
local c_l = cmp.mapping(function(fallback)
if cmp.visible() and cmp.get_active_entry() then
cmp.confirm()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { "i", "s" })
cmp.setup({
completion = {
autocomplete = false,
},
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
-- See `:h luasnip` for the commands
-- Note: have not added choice note mappings yet
mapping = cmp.mapping.preset.insert({
["<C-j>"] = c_j,
["<C-k>"] = c_k,
["<C-h>"] = c_h,
["<C-l>"] = c_l,
["<CR>"] = c_l,
}),
sources = {
{ name = "nvim_lsp", keyword_length = 1 },
{ name = "luasnip", max_item_count = 16 },
{ name = "path" },
{ name = "buffer", max_item_count = 8 },
},
window = {
completion = cmp.config.window.bordered({ border = { "", "", "", "", "", "", "", "" } }),
documentation = cmp.config.window.bordered({ border = { "", "", "", "", "", "", "", "" } }),
},
formatting = {
fields = {
"abbr",
"menu",
"kind",
},
format = function(entry, item)
-- Rename kind to shorthand
item.menu = ({
nvim_lsp = "[lsp]",
luasnip = "[snip]",
path = "[path]",
buffer = "[buf]",
})[entry.source.name]
return item
end,
expandable_indicator = true,
},
})

View File

@ -0,0 +1 @@
require("dbee").setup()

View File

@ -0,0 +1,3 @@
vim.opt.termguicolors = true
require("nvim-highlight-colors").setup({})

View File

@ -0,0 +1,36 @@
local eslint_linter = "eslint_d"
require("lint").linters_by_ft = {
bash = { "shellcheck" },
c = { "clangtidy", "flawfinder" },
cmake = { "cmakelint" },
cpp = { "clangtidy", "flawfinder" }, -- "cpplint", "cppcheck", "flawfinder"
css = { "stylelint" },
dockerfile = { "hadolint" },
editorconfig = { "editorconfig-checker" },
haskell = { "hlint" },
-- html = { "htmlhint" },
-- javascript = { eslint_linter },
-- javascriptreact = { eslint_linter },
gdscript = { "gdlint" },
latex = { "chktex" },
-- lua = { "luacheck", "selene" },
make = { "checkmake" },
-- pandoc = { "proselint", "woke" },
-- python = { "pylint" },
sh = { "shellcheck" },
svelte = { eslint_linter },
systemd = { "systemdlint" },
-- typescript = { eslint_linter },
-- typescriptreact = { eslint_linter },
yaml = { "yamllint" },
}
-- TODO: Wouldn't it be possible / nice to only try to load the linters when they are
-- actually needed?
vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave" }, {
callback = function()
require("lint").try_lint()
end,
})

View File

@ -0,0 +1,4 @@
require("sniprun").setup({
selected_interpreters = { "JS_TS_deno" },
repl_enable = { "JS_TS_deno" },
})

View File

@ -0,0 +1 @@
require("tailwind-fold").setup({ ft = { "html", "svelte", "tsx" } })

View File

@ -0,0 +1,131 @@
local ts = require("treesj")
local vim = vim
local keymap = vim.keymap
local opt = vim.opt
local treesitter_configs = require("nvim-treesitter.configs")
treesitter_configs.setup({
-- Basically added what I might need from the docs
-- <https://github.com/nvim-treesitter/nvim-treesitter?tab=readme-ov-file#supported-languages>
ensure_installed = {
"awk",
"bash",
"bibtex",
"c",
"cmake",
"comment",
"cpp",
"css",
"csv",
"diff",
"dockerfile",
"dot",
"gdscript",
"gdshader",
"git_config",
"git_rebase",
"gitattributes",
"gitcommit",
"gitignore",
"glsl",
"gnuplot",
"go",
"godot_resource",
"gpg",
"graphql",
"haskell",
"html",
"java",
"javascript",
"jq",
"jsdoc",
"json",
"jsonc",
"latex",
"lua",
"luadoc",
"make",
"python",
"query",
"r",
"racket",
"readline",
"regex",
"requirements",
"scheme",
"scss",
"sql",
"ssh_config",
"supercollider",
"svelte",
"tmux",
"toml",
"tsv",
"tsx",
"typescript",
"udev",
"vim",
"vimdoc",
"xml",
"yaml",
"zathurarc",
},
highlight = {
enable = true,
},
incremental_selection = {
enable = true,
keymaps = {
init_selection = "vv",
node_incremental = "vv",
scope_incremental = "VV",
node_decremental = "vd",
},
},
indent = {
enable = true,
},
sync_install = false,
auto_install = true,
ignore_install = {},
modules = {},
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
-- Functions
["if"] = "@function.inner",
["af"] = "@function.outer",
["ip"] = "@parameter.inner",
["ap"] = "@parameter.outer",
},
},
},
node_movement = {
enable = true,
keymaps = {
move_up = "vk",
move_down = "vj",
move_left = "vh",
move_right = "vl",
swap_left = "vH",
swap_right = "vL",
select_current_node = "vi",
},
swappable_textobjects = { "@function.outer", "@parameter.inner", "@statement.outer" },
allow_switch_parents = true,
allow_next_parent = true,
},
})
opt.foldmethod = "expr"
opt.foldexpr = "nvim_treesitter#foldexpr()"
opt.foldenable = false
-- TreeSJ
require("treesj").setup({
use_default_keymaps = false,
})
keymap.set("n", ";", ts.toggle, { desc = "Toggle join/split (TreeTSJ)" })

View File

@ -0,0 +1 @@
require("trouble").setup()

View File

@ -0,0 +1 @@
vim.cmd("colorscheme zenwritten")

View File

@ -35,5 +35,42 @@ vim.api.nvim_create_autocmd("VimEnter", {
bootstrap_paq({ bootstrap_paq({
{ "savq/paq-nvim" }, { "savq/paq-nvim" },
{ "ibhagwan/fzf-lua" }, { "ibhagwan/fzf-lua" },
{ "barreiroleo/ltex_extra.nvim" },
{ "neovim/nvim-lspconfig" },
{ "https://git.sr.ht/~whynothugo/lsp_lines.nvim" },
{ "linrongbin16/lsp-progress.nvim" },
{ "folke/neodev.nvim" }, -- Nvim
{ "b0o/schemastore.nvim" }, -- JSON Schemas
{ "mfussenegger/nvim-lint" },
{ "stevearc/conform.nvim" },
{ "L3MON4D3/LuaSnip" },
{ "saadparwaiz1/cmp_luasnip" },
{ "hrsh7th/nvim-cmp" },
{ "hrsh7th/cmp-nvim-lsp" },
{ "hrsh7th/cmp-buffer" },
{ "hrsh7th/cmp-path" },
{ "stevearc/dressing.nvim" },
{ "nvim-lua/plenary.nvim" },
{ "MunifTanjim/nui.nvim" },
{ "nvim-telescope/telescope.nvim" },
{ "folke/trouble.nvim" },
{ "rktjmp/shipwright.nvim" }, -- For building themes based on lush (e.g. terminal)
{ "rktjmp/lush.nvim" },
{ "mcchrish/zenbones.nvim" }, -- Zenbones themes (contains zenwritten)
{ "theHamsta/crazy-node-movement" },
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" },
{ "nvim-treesitter/nvim-treesitter-textobjects" },
-- { "nvim-treesitter/nvim-treesitter-context" },
{ "JoosepAlviste/nvim-ts-context-commentstring" }, -- commentstring based on cursor position (e.g. for Svelte)
{ "Wansmer/treesj" },
{ "michaelb/sniprun", build = "sh install.sh" },
{ "f-person/git-blame.nvim" },
{ "brenoprata10/nvim-highlight-colors" },
{ "razak17/tailwind-fold.nvim" },
{ "rmagatti/auto-session" },
{ "kndndrj/nvim-dbee" },
{ "3rd/image.nvim", build = false },
{ "polarmutex/beancount.nvim" },
{ "jamesblckwell/nvimkit.nvim" },
}) })
-- }}} -- }}}