Files
neofig/init.lua
ZockerKatze 289b80b2b5 initial
2025-06-11 10:24:57 +02:00

1260 lines
38 KiB
Lua

-- Bootstrap Lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
local result = vim.fn.system({
"git", "clone", "--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath
})
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ result, "WarningMsg" },
{ "\nPress any key to exit...", "MoreMsg" },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
-- Leader key must be set before lazy setup
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"
-- Core vim settings
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.wrap = false
vim.opt.expandtab = true
vim.opt.shiftwidth = 2
vim.opt.tabstop = 2
vim.opt.softtabstop = 2
vim.opt.smartindent = true
vim.opt.cursorline = true
vim.opt.termguicolors = true
vim.opt.signcolumn = "yes"
vim.opt.updatetime = 250
vim.opt.timeoutlen = 300
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.opt.undofile = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.mouse = "a"
vim.opt.clipboard = "unnamedplus"
vim.o.shortmess = vim.o.shortmess .. "I"
-- Plugin specifications
require("lazy").setup({
-- LSP and completion setup
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
"hrsh7th/cmp-nvim-lsp",
"folke/neodev.nvim",
},
config = function()
-- Setup neodev before lspconfig
require("neodev").setup()
-- Diagnostic configuration
vim.diagnostic.config({
virtual_text = {
prefix = "",
},
signs = true,
underline = true,
update_in_insert = false,
severity_sort = true,
})
-- Diagnostic signs
local signs = { Error = " ", Warn = " ", Hint = "󰠠 ", Info = " " }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" })
end
-- LSP keymaps
local on_attach = function(client, bufnr)
local opts = { buffer = bufnr, silent = true }
local keymap = vim.keymap.set
keymap("n", "gD", vim.lsp.buf.declaration, opts)
keymap("n", "gd", vim.lsp.buf.definition, opts)
keymap("n", "K", vim.lsp.buf.hover, opts)
keymap("n", "gi", vim.lsp.buf.implementation, opts)
keymap("n", "<C-k>", vim.lsp.buf.signature_help, opts)
keymap("n", "<leader>wa", vim.lsp.buf.add_workspace_folder, opts)
keymap("n", "<leader>wr", vim.lsp.buf.remove_workspace_folder, opts)
keymap("n", "<leader>wl", function()
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end, opts)
keymap("n", "<leader>D", vim.lsp.buf.type_definition, opts)
keymap("n", "<leader>rn", vim.lsp.buf.rename, opts)
keymap("n", "<leader>ca", vim.lsp.buf.code_action, opts)
keymap("n", "gr", vim.lsp.buf.references, opts)
keymap("n", "<leader>f", function()
vim.lsp.buf.format({ async = true })
end, opts)
end
local capabilities = require("cmp_nvim_lsp").default_capabilities()
local lspconfig = require("lspconfig")
-- Setup Mason
require("mason").setup({
ui = {
icons = {
package_installed = "",
package_pending = "",
package_uninstalled = "",
},
},
})
require("mason-lspconfig").setup({
ensure_installed = {
"lua_ls",
"pyright",
"clangd",
"html",
"cssls",
"ts_ls",
},
automatic_installation = true,
})
-- LSP server configurations
local servers = {
lua_ls = {
settings = {
Lua = {
workspace = {
checkThirdParty = false,
},
completion = {
callSnippet = "Replace",
},
},
},
},
pyright = {},
clangd = {
cmd = { "clangd", "--background-index", "--clang-tidy" },
},
html = {
filetypes = { "html", "htmldjango" },
},
cssls = {},
ts_ls = {},
}
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 {})
server.on_attach = on_attach
lspconfig[server_name].setup(server)
end,
})
end,
},
-- Mason for managing external tools
{
"williamboman/mason.nvim",
cmd = "Mason",
keys = { { "<leader>cm", "<cmd>Mason<cr>", desc = "Mason" } },
},
-- Autocompletion
{
"hrsh7th/nvim-cmp",
version = false,
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"saadparwaiz1/cmp_luasnip",
},
opts = function()
vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true })
local cmp = require("cmp")
local luasnip = require("luasnip")
return {
completion = {
completeopt = "menu,menuone,noinsert",
},
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<C-n>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-p>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = 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" }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "path" },
}, {
{ name = "buffer" },
}),
formatting = {
format = function(entry, item)
local icons = {
nvim_lsp = "",
luasnip = "",
buffer = "",
path = "",
}
item.menu = icons[entry.source.name]
return item
end,
},
experimental = {
ghost_text = {
hl_group = "CmpGhostText",
},
},
}
end,
},
-- Snippets
{
"L3MON4D3/LuaSnip",
build = (function()
if vim.fn.has("win32") == 1 or vim.fn.executable("make") == 0 then
return
end
return "make install_jsregexp"
end)(),
dependencies = {
"rafamadriz/friendly-snippets",
config = function()
require("luasnip.loaders.from_vscode").lazy_load()
end,
},
opts = {
history = true,
delete_check_events = "TextChanged",
},
},
-- Neodev for better lua development
{ "folke/neodev.nvim", opts = {} },
-- Treesitter
{
"nvim-treesitter/nvim-treesitter",
version = false,
build = ":TSUpdate",
event = { "BufReadPost", "BufNewFile" },
cmd = { "TSUpdateSync" },
opts = {
ensure_installed = {
"bash",
"c",
"cpp",
"css",
"html",
"javascript",
"json",
"lua",
"markdown",
"markdown_inline",
"python",
"query",
"regex",
"tsx",
"typescript",
"vim",
"yaml",
},
highlight = { enable = true },
indent = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>",
node_incremental = "<C-space>",
scope_incremental = false,
node_decremental = "<bs>",
},
},
},
config = function(_, opts)
require("nvim-treesitter.configs").setup(opts)
end,
},
-- Auto tag
{
"windwp/nvim-ts-autotag",
event = "InsertEnter",
opts = {},
},
-- Emmet
{
"mattn/emmet-vim",
ft = { "html", "css", "javascript", "typescript", "javascriptreact", "typescriptreact" },
init = function()
vim.g.user_emmet_leader_key = "<C-y>"
end,
},
-- Colorscheme
{
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
opts = {
style = "night",
transparent = false,
terminal_colors = true,
styles = {
comments = { italic = true },
keywords = { italic = true },
functions = {},
variables = {},
sidebars = "dark",
floats = "dark",
},
},
config = function(_, opts)
require("tokyonight").setup(opts)
vim.cmd.colorscheme("tokyonight")
end,
},
-- Statusline
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function()
return {
options = {
theme = "tokyonight",
globalstatus = true,
disabled_filetypes = { statusline = { "dashboard", "alpha" } },
},
sections = {
lualine_a = { "mode" },
lualine_b = { "branch" },
lualine_c = {
{
"diagnostics",
symbols = {
error = " ",
warn = " ",
info = " ",
hint = "󰠠 ",
},
},
{ "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } },
{ "filename", path = 1, symbols = { modified = " ", readonly = "", unnamed = "" } },
},
lualine_x = {
{
function() return require("noice").api.status.command.get() end,
cond = function() return package.loaded["noice"] and require("noice").api.status.command.has() end,
color = { fg = "#ff9e64" },
},
{
function() return require("noice").api.status.mode.get() end,
cond = function() return package.loaded["noice"] and require("noice").api.status.mode.has() end,
color = { fg = "#ff9e64" },
},
{ "encoding" },
{ "fileformat" },
},
lualine_y = { "progress" },
lualine_z = { "location" },
},
extensions = { "neo-tree", "lazy" },
}
end,
},
-- File explorer
{
"nvim-neo-tree/neo-tree.nvim",
branch = "v3.x",
cmd = "Neotree",
keys = {
{ "<leader>fe", function() require("neo-tree.command").execute({ toggle = true, dir = vim.loop.cwd() }) end, desc = "Explorer NeoTree (cwd)" },
{ "<leader>fE", function() require("neo-tree.command").execute({ toggle = true, dir = vim.fn.expand("%:p:h") }) end, desc = "Explorer NeoTree (current file)" },
{ "<leader>e", "<leader>fe", desc = "Explorer NeoTree (cwd)", remap = true },
{ "<leader>E", "<leader>fE", desc = "Explorer NeoTree (current file)", remap = true },
},
deactivate = function()
vim.cmd([[Neotree close]])
end,
init = function()
if vim.fn.argc(-1) == 1 then
local stat = vim.loop.fs_stat(vim.fn.argv(0))
if stat and stat.type == "directory" then
require("neo-tree")
end
end
end,
opts = {
sources = { "filesystem", "buffers", "git_status", "document_symbols" },
open_files_do_not_replace_types = { "terminal", "Trouble", "qf", "Outline" },
filesystem = {
bind_to_cwd = false,
follow_current_file = { enabled = true },
use_libuv_file_watcher = true,
filtered_items = {
visible = true,
hide_dotfiles = false,
hide_gitignored = false,
},
},
window = {
position = "left",
width = 30,
mappings = {
["<space>"] = "none",
},
},
default_component_configs = {
indent = {
with_expanders = true,
expander_collapsed = "",
expander_expanded = "",
expander_highlight = "NeoTreeExpander",
},
},
},
},
-- Fuzzy finder
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
version = false,
dependencies = {
"nvim-lua/plenary.nvim",
{
"nvim-telescope/telescope-fzf-native.nvim",
build = "make",
enabled = vim.fn.executable("make") == 1,
config = function()
require("telescope").load_extension("fzf")
end,
},
},
keys = {
{ "<leader>,", "<cmd>Telescope buffers show_all_buffers=true<cr>", desc = "Switch Buffer" },
{ "<leader>/", "<cmd>Telescope live_grep<cr>", desc = "Grep (root dir)" },
{ "<leader>:", "<cmd>Telescope command_history<cr>", desc = "Command History" },
{ "<leader><space>", "<cmd>Telescope find_files<cr>", desc = "Find Files (root dir)" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find Files (root dir)" },
{ "<leader>fF", "<cmd>Telescope find_files hidden=true no_ignore=true<cr>", desc = "Find Files (all)" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Grep (root dir)" },
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help Pages" },
{ "<leader>fH", "<cmd>Telescope highlights<cr>", desc = "Search Highlight Groups" },
{ "<leader>fk", "<cmd>Telescope keymaps<cr>", desc = "Key Maps" },
{ "<leader>fl", "<cmd>Telescope loclist<cr>", desc = "Location List" },
{ "<leader>fM", "<cmd>Telescope man_pages<cr>", desc = "Man Pages" },
{ "<leader>fm", "<cmd>Telescope marks<cr>", desc = "Jump to Mark" },
{ "<leader>fo", "<cmd>Telescope vim_options<cr>", desc = "Options" },
{ "<leader>fR", "<cmd>Telescope resume<cr>", desc = "Resume" },
{ "<leader>fq", "<cmd>Telescope quickfix<cr>", desc = "Quickfix List" },
{ "<leader>fw", "<cmd>Telescope grep_string word_match=-w<cr>", desc = "Word (root dir)" },
{ "<leader>fW", "<cmd>Telescope grep_string<cr>", desc = "Selection (root dir)", mode = "v" },
{ "<leader>uC", "<cmd>Telescope colorscheme enable_preview=true<cr>", desc = "Colorscheme with preview" },
},
opts = function()
return {
defaults = {
file_ignore_patterns = { "node_modules", ".git/" },
vimgrep_arguments = {
"rg",
"-L",
"--color=never",
"--no-heading",
"--with-filename",
"--line-number",
"--column",
"--smart-case",
},
prompt_prefix = " ",
selection_caret = " ",
entry_prefix = " ",
initial_mode = "insert",
selection_strategy = "reset",
sorting_strategy = "ascending",
layout_strategy = "horizontal",
layout_config = {
horizontal = {
prompt_position = "top",
preview_width = 0.55,
results_width = 0.8,
},
vertical = {
mirror = false,
},
width = 0.87,
height = 0.80,
preview_cutoff = 120,
},
path_display = { "truncate" },
winblend = 0,
border = {},
borderchars = { "", "", "", "", "", "", "", "" },
color_devicons = true,
set_env = { ["COLORTERM"] = "truecolor" },
},
pickers = {
live_grep = {
theme = "dropdown",
},
grep_string = {
theme = "dropdown",
},
find_files = {
theme = "dropdown",
previewer = false,
},
buffers = {
theme = "dropdown",
previewer = false,
initial_mode = "normal",
mappings = {
i = {
["<C-d>"] = function(...)
return require("telescope.actions").delete_buffer(...)
end,
},
n = {
["dd"] = function(...)
return require("telescope.actions").delete_buffer(...)
end,
},
},
},
planets = {
show_pluto = true,
show_moon = true,
},
colorscheme = {
enable_preview = true,
},
},
}
end,
},
-- Better vim.ui
{
"stevearc/dressing.nvim",
lazy = true,
init = function()
vim.ui.select = function(...)
require("lazy").load({ plugins = { "dressing.nvim" } })
return vim.ui.select(...)
end
vim.ui.input = function(...)
require("lazy").load({ plugins = { "dressing.nvim" } })
return vim.ui.input(...)
end
end,
},
-- Auto pairs
{
"windwp/nvim-autopairs",
event = "InsertEnter",
opts = {},
},
-- Comments
{
"numToStr/Comment.nvim",
opts = {},
lazy = false,
},
-- Which key
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {
plugins = { spelling = true },
defaults = {
mode = { "n", "v" },
["g"] = { name = "+goto" },
["gz"] = { name = "+surround" },
["]"] = { name = "+next" },
["["] = { name = "+prev" },
["<leader><tab>"] = { name = "+tabs" },
["<leader>b"] = { name = "+buffer" },
["<leader>c"] = { name = "+code" },
["<leader>f"] = { name = "+file/find" },
["<leader>g"] = { name = "+git" },
["<leader>gh"] = { name = "+hunks" },
["<leader>q"] = { name = "+quit/session" },
["<leader>s"] = { name = "+search" },
["<leader>u"] = { name = "+ui" },
["<leader>w"] = { name = "+windows" },
["<leader>x"] = { name = "+diagnostics/quickfix" },
},
},
config = function(_, opts)
local wk = require("which-key")
wk.setup(opts)
wk.register(opts.defaults)
end,
},
-- Git signs
{
"lewis6991/gitsigns.nvim",
event = { "BufReadPre", "BufNewFile" },
opts = {
signs = {
add = { text = "" },
change = { text = "" },
delete = { text = "" },
topdelete = { text = "" },
changedelete = { text = "" },
untracked = { text = "" },
},
on_attach = function(buffer)
local gs = package.loaded.gitsigns
local function map(mode, l, r, desc)
vim.keymap.set(mode, l, r, { buffer = buffer, desc = desc })
end
map("n", "]h", gs.next_hunk, "Next Hunk")
map("n", "[h", gs.prev_hunk, "Prev Hunk")
map({ "n", "v" }, "<leader>ghs", ":Gitsigns stage_hunk<CR>", "Stage Hunk")
map({ "n", "v" }, "<leader>ghr", ":Gitsigns reset_hunk<CR>", "Reset Hunk")
map("n", "<leader>ghS", gs.stage_buffer, "Stage Buffer")
map("n", "<leader>ghu", gs.undo_stage_hunk, "Undo Stage Hunk")
map("n", "<leader>ghR", gs.reset_buffer, "Reset Buffer")
map("n", "<leader>ghp", gs.preview_hunk, "Preview Hunk")
map("n", "<leader>ghb", function() gs.blame_line({ full = true }) end, "Blame Line")
map("n", "<leader>ghd", gs.diffthis, "Diff This")
map("n", "<leader>ghD", function() gs.diffthis("~") end, "Diff This ~")
map({ "o", "x" }, "ih", ":<C-U>Gitsigns select_hunk<CR>", "GitSigns Select Hunk")
end,
},
},
-- Dashboard
{
"nvimdev/dashboard-nvim",
event = "VimEnter",
opts = function()
local logo = [[
███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗
████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║
██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║
██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║
██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║
╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝
]]
logo = string.rep("\n", 8) .. logo .. "\n\n"
local opts = {
theme = "doom",
hide = {
statusline = false,
},
config = {
header = vim.split(logo, "\n"),
center = {
{ action = "Telescope find_files", desc = " Find file", icon = " ", key = "f" },
{ action = "ene | startinsert", desc = " New file", icon = " ", key = "n" },
{ action = "Telescope oldfiles", desc = " Recent files", icon = " ", key = "r" },
{ action = "Telescope live_grep", desc = " Find text", icon = " ", key = "g" },
{ action = "e $MYVIMRC", desc = " Config", icon = " ", key = "c" },
{ action = "Lazy", desc = " Lazy", icon = "󰒲 ", key = "l" },
{ action = "qa", desc = " Quit", icon = " ", key = "q" },
},
footer = function()
local stats = require("lazy").stats()
local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100)
return { "⚡ Neovim loaded " .. stats.loaded .. "/" .. stats.count .. " plugins in " .. ms .. "ms" }
end,
},
}
for _, button in ipairs(opts.config.center) do
button.desc = button.desc .. string.rep(" ", 43 - #button.desc)
button.key_format = " %s"
end
if vim.o.filetype == "lazy" then
vim.cmd.close()
vim.api.nvim_create_autocmd("User", {
pattern = "DashboardLoaded",
callback = function()
require("lazy").show()
end,
})
end
return opts
end,
},
-- Formatting
{
"stevearc/conform.nvim",
dependencies = { "mason.nvim" },
lazy = true,
cmd = "ConformInfo",
keys = {
{
"<leader>cF",
function()
require("conform").format({ formatters = { "injected" } })
end,
mode = { "n", "v" },
desc = "Format Injected Langs",
},
},
init = function()
vim.o.formatexpr = "v:lua.require'conform'.formatexpr()"
end,
opts = {
formatters_by_ft = {
lua = { "stylua" },
python = { "isort", "black" },
javascript = { { "prettierd", "prettier" } },
typescript = { { "prettierd", "prettier" } },
html = { { "prettierd", "prettier" } },
css = { { "prettierd", "prettier" } },
json = { { "prettierd", "prettier" } },
yaml = { { "prettierd", "prettier" } },
markdown = { { "prettierd", "prettier" } },
cpp = { "clang-format" },
c = { "clang-format" },
},
format_on_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
return
end
local filetype = vim.bo[bufnr].filetype
if filetype == "c" or filetype == "cpp" then
return { timeout_ms = 500 }
end
return { timeout_ms = 500, lsp_fallback = true }
end,
formatters = {
injected = { options = { ignore_errors = true } },
},
},
},
-- Linting
{
"mfussenegger/nvim-lint",
event = { "BufReadPre", "BufNewFile" },
opts = {
events = { "BufWritePost", "BufReadPost", "InsertLeave" },
linters_by_ft = {
python = { "flake8" },
cpp = { "cpplint" },
c = { "cpplint" },
javascript = { "eslint_d" },
typescript = { "eslint_d" },
},
},
config = function(_, opts)
local M = {}
local lint = require("lint")
for name, linter in pairs(opts.linters or {}) do
if type(linter) == "table" and type(lint.linters[name]) == "table" then
lint.linters[name] = vim.tbl_deep_extend("force", lint.linters[name], linter)
else
lint.linters[name] = linter
end
end
lint.linters_by_ft = opts.linters_by_ft
function M.debounce(ms, fn)
local timer = vim.loop.new_timer()
return function(...)
local argv = { ... }
timer:start(ms, 0, function()
timer:stop()
vim.schedule_wrap(fn)(unpack(argv))
end)
end
end
function M.lint()
local names = lint._resolve_linter_by_ft(vim.bo.filetype)
names = vim.list_extend({}, names)
if #names == 0 then
vim.list_extend(names, lint.linters_by_ft["_"] or {})
end
vim.list_extend(names, lint.linters_by_ft["*"] or {})
local ctx = { filename = vim.api.nvim_buf_get_name(0) }
ctx.dirname = vim.fn.fnamemodify(ctx.filename, ":h")
names = vim.tbl_filter(function(name)
local linter = lint.linters[name]
if not linter then
return false
end
return not (type(linter) == "table" and linter.condition and not linter.condition(ctx))
end, names)
if #names > 0 then
lint.try_lint(names)
end
end
vim.api.nvim_create_autocmd(opts.events, {
group = vim.api.nvim_create_augroup("nvim-lint", { clear = true }),
callback = M.debounce(100, M.lint),
})
end,
},
-- Live server for web development
{
"barrett-ruth/live-server.nvim",
build = "npm install -g live-server",
cmd = { "LiveServerStart", "LiveServerStop" },
config = true,
},
-- Debugging
{
"mfussenegger/nvim-dap",
dependencies = {
"rcarriga/nvim-dap-ui",
"nvim-neotest/nvim-nio",
"williamboman/mason.nvim",
"jay-babu/mason-nvim-dap.nvim",
},
keys = function()
local dap = require("dap")
local dapui = require("dapui")
return {
{ "<leader>dB", function() dap.set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, desc = "Breakpoint Condition" },
{ "<leader>db", function() dap.toggle_breakpoint() end, desc = "Toggle Breakpoint" },
{ "<leader>dc", function() dap.continue() end, desc = "Continue" },
{ "<leader>dC", function() dap.run_to_cursor() end, desc = "Run to Cursor" },
{ "<leader>dg", function() dap.goto_() end, desc = "Go to line (no execute)" },
{ "<leader>di", function() dap.step_into() end, desc = "Step Into" },
{ "<leader>dj", function() dap.down() end, desc = "Down" },
{ "<leader>dk", function() dap.up() end, desc = "Up" },
{ "<leader>dl", function() dap.run_last() end, desc = "Run Last" },
{ "<leader>do", function() dap.step_out() end, desc = "Step Out" },
{ "<leader>dO", function() dap.step_over() end, desc = "Step Over" },
{ "<leader>dp", function() dap.pause() end, desc = "Pause" },
{ "<leader>dr", function() dap.repl.toggle() end, desc = "Toggle REPL" },
{ "<leader>ds", function() dap.session() end, desc = "Session" },
{ "<leader>dt", function() dap.terminate() end, desc = "Terminate" },
{ "<leader>dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" },
{ "<F5>", function() dap.continue() end, desc = "Debug: Start/Continue" },
{ "<F10>", function() dap.step_over() end, desc = "Debug: Step Over" },
{ "<F11>", function() dap.step_into() end, desc = "Debug: Step Into" },
{ "<F12>", function() dap.step_out() end, desc = "Debug: Step Out" },
}
end,
config = function()
local dap = require("dap")
local dapui = require("dapui")
require("mason-nvim-dap").setup({
automatic_setup = true,
handlers = {},
ensure_installed = {
"python",
"codelldb",
},
})
dapui.setup({
icons = { expanded = "", collapsed = "", current_frame = "*" },
controls = {
icons = {
pause = "",
play = "",
step_into = "",
step_over = "",
step_out = "",
step_back = "b",
run_last = "▶▶",
terminate = "",
disconnect = "",
},
},
})
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open({})
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close({})
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close({})
end
-- Install language specific config
require("dap-python").setup("~/.virtualenvs/debugpy/bin/python")
end,
},
-- Python debugging
{
"mfussenegger/nvim-dap-python",
ft = "python",
dependencies = {
"mfussenegger/nvim-dap",
"rcarriga/nvim-dap-ui",
},
config = function(_, opts)
local path = require("mason-registry").get_package("debugpy"):get_install_path()
require("dap-python").setup(path .. "/venv/bin/python")
end,
},
-- Better notifications
{
"rcarriga/nvim-notify",
keys = {
{
"<leader>un",
function()
require("notify").dismiss({ silent = true, pending = true })
end,
desc = "Dismiss all Notifications",
},
},
opts = {
timeout = 3000,
max_height = function()
return math.floor(vim.o.lines * 0.75)
end,
max_width = function()
return math.floor(vim.o.columns * 0.75)
end,
},
init = function()
vim.notify = require("notify")
end,
},
-- Better vim.ui
{
"folke/noice.nvim",
event = "VeryLazy",
opts = {
lsp = {
override = {
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
["vim.lsp.util.stylize_markdown"] = true,
["cmp.entry.get_documentation"] = true,
},
},
routes = {
{
filter = {
event = "msg_show",
any = {
{ find = "%d+L, %d+B" },
{ find = "; after #%d+" },
{ find = "; before #%d+" },
},
},
view = "mini",
},
},
presets = {
bottom_search = true,
command_palette = true,
long_message_to_split = true,
inc_rename = true,
lsp_doc_border = false,
},
},
dependencies = {
"MunifTanjim/nui.nvim",
"rcarriga/nvim-notify",
},
},
-- Buffer remove
{
"echasnovski/mini.bufremove",
keys = {
{ "<leader>bd", function() require("mini.bufremove").delete(0, false) end, desc = "Delete Buffer" },
{ "<leader>bD", function() require("mini.bufremove").delete(0, true) end, desc = "Delete Buffer (Force)" },
},
},
-- Session management
{
"folke/persistence.nvim",
event = "BufReadPre",
opts = { options = vim.opt.sessionoptions:get() },
keys = {
{ "<leader>qs", function() require("persistence").load() end, desc = "Restore Session" },
{ "<leader>ql", function() require("persistence").load({ last = true }) end, desc = "Restore Last Session" },
{ "<leader>qd", function() require("persistence").stop() end, desc = "Don't Save Current Session" },
},
},
}, {
defaults = {
lazy = false,
version = false,
},
install = { colorscheme = { "tokyonight", "habamax" } },
checker = { enabled = true },
performance = {
rtp = {
disabled_plugins = {
"gzip",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
-- Additional keymaps
local keymap = vim.keymap.set
local opts = { silent = true }
-- Better up/down
keymap({ "n", "x" }, "j", "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true })
keymap({ "n", "x" }, "k", "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true })
-- Move to window using the <ctrl> hjkl keys
keymap("n", "<C-h>", "<C-w>h", { desc = "Go to left window", remap = true })
keymap("n", "<C-j>", "<C-w>j", { desc = "Go to lower window", remap = true })
keymap("n", "<C-k>", "<C-w>k", { desc = "Go to upper window", remap = true })
keymap("n", "<C-l>", "<C-w>l", { desc = "Go to right window", remap = true })
-- Resize window using <ctrl> arrow keys
keymap("n", "<C-Up>", "<cmd>resize +2<cr>", { desc = "Increase window height" })
keymap("n", "<C-Down>", "<cmd>resize -2<cr>", { desc = "Decrease window height" })
keymap("n", "<C-Left>", "<cmd>vertical resize -2<cr>", { desc = "Decrease window width" })
keymap("n", "<C-Right>", "<cmd>vertical resize +2<cr>", { desc = "Increase window width" })
-- Move Lines
keymap("n", "<A-j>", "<cmd>m .+1<cr>==", { desc = "Move down" })
keymap("n", "<A-k>", "<cmd>m .-2<cr>==", { desc = "Move up" })
keymap("i", "<A-j>", "<esc><cmd>m .+1<cr>==gi", { desc = "Move down" })
keymap("i", "<A-k>", "<esc><cmd>m .-2<cr>==gi", { desc = "Move up" })
keymap("v", "<A-j>", ":m '>+1<cr>gv=gv", { desc = "Move down" })
keymap("v", "<A-k>", ":m '<-2<cr>gv=gv", { desc = "Move up" })
-- Buffers
keymap("n", "<S-h>", "<cmd>bprevious<cr>", { desc = "Prev buffer" })
keymap("n", "<S-l>", "<cmd>bnext<cr>", { desc = "Next buffer" })
keymap("n", "[b", "<cmd>bprevious<cr>", { desc = "Prev buffer" })
keymap("n", "]b", "<cmd>bnext<cr>", { desc = "Next buffer" })
-- Clear search with <esc>
keymap({ "i", "n" }, "<esc>", "<cmd>noh<cr><esc>", { desc = "Escape and clear hlsearch" })
-- Save file
keymap({ "i", "x", "n", "s" }, "<C-s>", "<cmd>w<cr><esc>", { desc = "Save file" })
-- Better indenting
keymap("v", "<", "<gv")
keymap("v", ">", ">gv")
-- Lazy
keymap("n", "<leader>l", "<cmd>Lazy<cr>", { desc = "Lazy" })
-- New file
keymap("n", "<leader>fn", "<cmd>enew<cr>", { desc = "New File" })
-- Quit
keymap("n", "<leader>qq", "<cmd>qa<cr>", { desc = "Quit all" })
-- Highlights under cursor
keymap("n", "<leader>ui", vim.show_pos, { desc = "Inspect Pos" })
-- Python run
keymap("n", "<leader>r", function()
if vim.bo.filetype == "python" then
vim.cmd("w")
vim.cmd("!python3 %")
else
print("Not a Python file")
end
end, { desc = "Run Python file" })
-- Toggle options
keymap("n", "<leader>uf", function()
vim.g.disable_autoformat = not vim.g.disable_autoformat
if vim.g.disable_autoformat then
print("Disabled format on save")
else
print("Enabled format on save")
end
end, { desc = "Toggle auto format (global)" })
keymap("n", "<leader>uF", function()
vim.b.disable_autoformat = not vim.b.disable_autoformat
if vim.b.disable_autoformat then
print("Disabled format on save (buffer)")
else
print("Enabled format on save (buffer)")
end
end, { desc = "Toggle auto format (buffer)" })
-- Auto commands
local function augroup(name)
return vim.api.nvim_create_augroup("lazyvim_" .. name, { clear = true })
end
-- Check if we need to reload the file when it changed
vim.api.nvim_create_autocmd({ "FocusGained", "TermClose", "TermLeave" }, {
group = augroup("checktime"),
command = "checktime",
})
-- Highlight on yank
vim.api.nvim_create_autocmd("TextYankPost", {
group = augroup("highlight_yank"),
callback = function()
vim.highlight.on_yank()
end,
})
-- Resize splits if window got resized
vim.api.nvim_create_autocmd({ "VimResized" }, {
group = augroup("resize_splits"),
callback = function()
local current_tab = vim.fn.tabpagenr()
vim.cmd("tabdo wincmd =")
vim.cmd("tabnext " .. current_tab)
end,
})
-- Go to last loc when opening a buffer
vim.api.nvim_create_autocmd("BufReadPost", {
group = augroup("last_loc"),
callback = function(event)
local exclude = { "gitcommit" }
local buf = event.buf
if vim.tbl_contains(exclude, vim.bo[buf].filetype) or vim.b[buf].lazyvim_last_loc then
return
end
vim.b[buf].lazyvim_last_loc = true
local mark = vim.api.nvim_buf_get_mark(buf, '"')
local lcount = vim.api.nvim_buf_line_count(buf)
if mark[1] > 0 and mark[1] <= lcount then
pcall(vim.api.nvim_win_set_cursor, 0, mark)
end
end,
})
-- Close some filetypes with <q>
vim.api.nvim_create_autocmd("FileType", {
group = augroup("close_with_q"),
pattern = {
"PlenaryTestPopup",
"help",
"lspinfo",
"man",
"notify",
"qf",
"query",
"spectre_panel",
"startuptime",
"tsplayground",
"neotest-output",
"checkhealth",
"neotest-summary",
"neotest-output-panel",
},
callback = function(event)
vim.bo[event.buf].buflisted = false
vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = event.buf, silent = true })
end,
})
-- Wrap and check for spell in text filetypes
vim.api.nvim_create_autocmd("FileType", {
group = augroup("wrap_spell"),
pattern = { "gitcommit", "markdown" },
callback = function()
vim.opt_local.wrap = true
vim.opt_local.spell = true
end,
})
-- Auto create dir when saving a file, in case some intermediate directory does not exist
vim.api.nvim_create_autocmd({ "BufWritePre" }, {
group = augroup("auto_create_dir"),
callback = function(event)
if event.match:match("^%w%w+://") then
return
end
local file = vim.loop.fs_realpath(event.match) or event.match
vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p")
end,
})
-- Format on save for web files
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup("format_web_files"),
pattern = { "*.html", "*.css", "*.js", "*.ts", "*.json" },
callback = function()
if vim.g.disable_autoformat or vim.b.disable_autoformat then
return
end
local conform = require("conform")
conform.format({ async = false, lsp_fallback = true })
end,
})