1260 lines
38 KiB
Lua
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,
|
|
})
|