-- theming -- Default options vim.opt.termguicolors = true vim.cmd.colorscheme 'duskfox' -- turn on relative line numbers vim.opt.relativenumber = true vim.opt.number = true vim.opt.expandtab = true vim.opt.paste = false vim.opt.autoindent = true vim.opt.linebreak = true vim.opt.mouse = "" -- neovim tabstop and shift behavior got more complicated. -- This should cover all the cases though vim.opt.tabstop = 4 vim.opt.shiftwidth = 4 --vim.opt.smarttab = false vim.opt.fileformats = "unix,dos" -- Recommended by Avante docs -- views can only be fully collapsed with the global statusline vim.opt.laststatus = 3 vim.g.BASH_AuthorName = 'Jeremy Wall' vim.g.BASH_AuthorRef = 'jw' vim.g.BASH_Email = 'jeremy@marzhillstudios.com' -- We want to use a different leader key vim.g.mapleader = ',' vim.cmd("noswapfile") vim.cmd("syntax on") vim.cmd("filetype plugin on") vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, { pattern = { "*.qnt" }, callback = function(args) vim.lsp.start({ name = 'quint', cmd = { 'quint-language-server', '--stdio' }, root_dir = vim.fs.dirname(vim.uri_from_bufnr(args.buf)) }) end, }) vim.cmd([[ au BufNewFile,BufRead *Makefile,*.mk set noexpandtab ]]) vim.cmd([[ au BufNewFile,BufRead *.py,*.java set tabstop=2 ]]) vim.cmd([[ au BufNewFile,BufRead *.app set filetype=erlang ]]) vim.cmd([[ au BufNewFile,BufRead *.tf set filetype=terraform ]]) vim.cmd([[ au BufNewFile,BufRead *.hcl,*.hcl2 set filetype=hcl ]]) vim.cmd([[ au BufNewFile,BufRead .bash_* set filetype=sh ]]) vim.cmd([[ au BufNewFile,BufRead *.erl filetype indent off ]]) vim.cmd([[ au BufNewFile,BufRead *.hrl filetype indent off ]]) vim.cmd([[ au BufNewFile,BufRead *.nix set tabstop=2 nosmarttab ]]) vim.cmd([[ au BufNewFile,BufRead *.ebnf set filetype=ebnf ]]) -- Telelscope Imports local telescope = require('telescope') local telescope_builtins = require('telescope.builtin') local telescope_actions = require('telescope.actions') -- --https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md -- TODO(jwall): See about proper snippet support (ie. license comments?) local cmp = require('cmp') cmp.setup({ -- Enable LSP snippets snippet = { expand = function(args) vim.fn["vsnip#anonymous"](args.body) end, }, mapping = cmp.mapping.preset.insert { [''] = cmp.mapping.select_prev_item(), [''] = cmp.mapping.select_next_item(), [''] = cmp.mapping.scroll_docs(-4), [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = true, }, }, -- Installed sources: sources = cmp.config.sources( { { name = 'nvim_lsp', keyword_length = 3 }, -- from language server { name = 'nvim_lsp_signature_help' }, -- display function signatures with current parameter emphasized }, { { name = 'path' }, -- file paths }, { { name = 'nvim_lua', keyword_length = 2 }, -- complete neovim's Lua runtime API such vim.lsp.* { name = 'buffer', keyword_length = 2 }, -- source current buffer { name = 'vsnip', keyword_length = 2 }, -- nvim-cmp source for vim-vsnip }), window = { completion = cmp.config.window.bordered(), documentation = cmp.config.window.bordered(), }, }) -- logging --vim.lsp.set_log_level('trace') --vim.lsp.log.set_format_func(vim.inspect) local caps = vim.tbl_deep_extend( 'force', vim.lsp.protocol.make_client_capabilities(), require('cmp_nvim_lsp').default_capabilities(), -- File watching is disabled by default for neovim. -- See: https://github.com/neovim/neovim/pull/22405 { workspace = { didChangeWatchedFiles = { dynamicRegistration = true } } }, { window = { progress = false } } ); local lspconfig = require("lspconfig") -- Typst lspconfig.tinymist.setup { capabilities = caps, settings = { exportPdf = "onSave", }, } -- Terraform lsp setup lspconfig.terraformls.setup {} -- Nix language server support lspconfig.nil_ls.setup { capabilities = caps, } lspconfig.jedi_language_server.setup {} require('roslyn').setup({ -- client, bufnr on_attach = function(_, _) --vim.notify(vim.inspect(client)) end, sdk_framework = "net8.0", capabilities = caps, log_level = "Trace", }); --ocaml lspconfig.ocamllsp.setup { capabilities = caps } -- Java language server support lspconfig.java_language_server.setup { capabilities = caps } -- Typescript language server support lspconfig.tsserver.setup { capabilities = caps } -- Rust language server support lspconfig.rust_analyzer.setup { settings = { -- https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/generated_config.adoc ['rust-analyzer'] = { cargo = { features = "all" } } }, capabilities = caps } -- lua language server setup. lspconfig.lua_ls.setup { settings = { Lua = { runtime = { version = 'LuaJIT', }, diagnostics = { -- Get the language server to recognize the `vim` global globals = { 'vim' }, }, workspace = { -- Make the server aware of Neovim runtime files library = vim.api.nvim_get_runtime_file("", true), -- Disable the checkThirdParty prompts. checkThirdParty = false, }, telemetry = { enable = false, }, }, }, capabilities = caps } -- lsp configuration vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) local opts = { buffer = args.buf } vim.keymap.set("n", '', function() vim.lsp.buf.hover() end, opts) vim.keymap.set({ "n", "v" }, "a", vim.lsp.buf.code_action, opts) vim.keymap.set("n", "f", vim.lsp.buf.format, opts) local client = vim.lsp.get_client_by_id(args.data.client_id) ---@diagnostic disable-next-line: undefined-field if client and client.server_capabilities.codelens then vim.lsp.codelens.refresh() end end, }) vim.api.nvim_create_autocmd({ 'BufEnter', 'InsertLeave', 'CursorHold' }, { callback = function(args) local clients = vim.lsp.get_clients({ bufnr = args.buf }) for cid = 1, #clients do ---@diagnostic disable-next-line: undefined-field if clients[cid].server_capabilities.codelens then vim.lsp.codelens.refresh() break end end end, }) -- LSP Diagnostics Options Setup local sign = function(opts) vim.fn.sign_define(opts.name, { texthl = opts.name, text = opts.text, numhl = '' }) end sign({ name = 'DiagnosticSignError', text = '🔥' }) sign({ name = 'DiagnosticSignWarn', text = '⚠️' }) sign({ name = 'DiagnosticSignHint', text = '➡️' }) sign({ name = 'DiagnosticSignInfo', text = '🗒️' }) vim.diagnostic.config({ virtual_text = false, signs = true, update_in_insert = true, underline = true, severity_sort = false, float = { border = 'rounded', source = true, header = '', prefix = '', }, }) vim.cmd([[ set signcolumn=yes autocmd CursorHold * lua vim.diagnostic.open_float(nil, { focusable = false }) ]]) --Set completeopt to have a better completion experience -- :help completeopt -- menuone: popup even when there's only one match -- noinsert: Do not insert text until a selection is made -- noselect: Do not select, force to select one from the menu -- shortness: avoid showing extra messages when using completion -- updatetime: set updatetime for CursorHold vim.opt.completeopt = { 'menuone', 'noselect', 'noinsert' } vim.opt.shortmess = vim.opt.shortmess + { c = true } vim.api.nvim_set_option_value('updatetime', 300, { scope = "global" }) vim.opt.sessionoptions = { 'buffers', 'curdir', 'skiprtp', 'localoptions', 'terminal', 'tabpages' } -- Fixed column for diagnostics to appear -- Show autodiagnostic popup on cursor hover_range -- Goto previous / next diagnostic warning / error -- Show inlay_hints more frequently vim.cmd([[ set signcolumn=yes autocmd CursorHold * lua vim.diagnostic.open_float(nil, { focusable = false }) ]]) -- Treesitter Plugin Setup require('nvim-treesitter.configs').setup { highlight = { enable = true, additional_vim_regex_highlighting = false, }, indent = { enable = true }, rainbow = { enable = true, extended_mode = true, max_file_lines = nil, }, --textobjects = { -- enable = true, -- select = { -- enable = true, -- lookahead = true, -- Automatically jump forward to textobj, similar to targets.vim -- keymaps = { -- -- You can use the capture groups defined in textobjects.scm -- ['aa'] = '@parameter.outer', -- ['ia'] = '@parameter.inner', -- ['af'] = '@function.outer', -- ['if'] = '@function.inner', -- ['ac'] = '@class.outer', -- ['ic'] = '@class.inner', -- }, -- }, --}, --incremental_selection = { -- enable = true, -- keymaps = { -- init_selection = 'c', -- node_incremental = 'c', -- scope_incremental = 'ci', -- node_decremental = 'cx', -- }, --}, } require 'treesitter-context'.setup { enable = true, -- Enable this plugin (Can be enabled/disabled later via commands) max_lines = 5, -- How many lines the window should span. Values <= 0 mean no limit. min_window_height = 45, -- Minimum editor window height to enable context. Values <= 0 mean no limit. line_numbers = true, multiline_threshold = 20, -- Maximum number of lines to collapse for a single context line trim_scope = 'outer', -- Which context lines to discard if `max_lines` is exceeded. Choices: 'inner', 'outer' mode = 'cursor', -- Line used to calculate context. Choices: 'cursor', 'topline' -- Separator between context and content. Should be a single character string, like '-'. -- When separator is set, the context will only show up when there are at least 2 lines above cursorline. separator = nil, zindex = 20, -- The Z-index of the context window on_attach = nil, -- (fun(buf: integer): boolean) return false to disable attaching } vim.g.loaded_netrw = 1 vim.g.loaded_netrwPlugin = 1 -- set termguicolors to enable highlight groups vim.opt.termguicolors = true -- setup nvim-tree require("nvim-tree").setup { hijack_unnamed_buffer_when_opening = true, update_focused_file = { enable = true, }, renderer = { icons = { show = { file = false, }, glyphs = { default = "-", symlink = "S", bookmark = "🎗", modified = "●", folder = { arrow_closed = "→", arrow_open = "↓", default = "📁", open = "📂", empty = "📁", empty_open = "📂", symlink = "S", symlink_open = "S", }, git = { unstaged = "✗", staged = "✓", unmerged = "∦", renamed = "➜", untracked = "★", deleted = "X", ignored = "◌", }, }, }, }, diagnostics = { enable = true, show_on_dirs = true, icons = { hint = "➡️", info = "🗒️", warning = "⚠️", error = "🔥", }, }, } require('trouble').setup { icons = false, signs = { hint = "➡️", information = "🗒️", warning = "⚠️", error = "🔥", other = "?", }, } vim.keymap.set("n", "", function() require("nvim-tree.api").tree.toggle() end) vim.keymap.set("n", "tc", function() vim.cmd("tabclose") end) vim.keymap.set("n", "tn", function() vim.cmd("tabnew") end) vim.keymap.set("n", "tk", function() vim.cmd("tabnext") end) vim.keymap.set("n", "tj", function() vim.cmd("tabprev") end) vim.keymap.set("n", "ts", function() vim.cmd("tabs") end) -- Neogit integration -- See https://github.com/NeogitOrg/neogit for configuration information. local neogit = require('neogit') neogit.setup {} vim.keymap.set("n", "mg", function() neogit.open() end) -- Add a file to git vim.keymap.set("n", "ga", function() vim.cmd("!git add %") end) require('possession').setup { commands = { save = 'SSave', load = 'SLoad', delete = 'SDelete', list = 'SList', }, autosave = { current = true, on_load = true, on_quit = true, }, telescope = { list = { default_action = 'load', mappings = { save = { n = '', i = '' }, load = { n = '', i = '' }, delete = { n = '', i = '' }, rename = { n = '', i = '' }, }, }, }, } telescope.load_extension('possession') -- https://github.com/nvim-telescope/telescope.nvim telescope.setup({ defaults = { initial_mode = "normal", mappings = { n = { ["ql"] = telescope_actions.send_selected_to_qflist + telescope_actions.open_qflist, }, }, }, pickers = { buffers = { mappings = { n = { [""] = telescope_actions.delete_buffer }, }, }, }, }) local harpoon = require('harpoon') harpoon:setup() local lean = require 'lean' lean.setup { lsp = { -- client, bufnr on_attach = function(_, bufnr) local opts = { buffer = bufnr } vim.keymap.set({ "n", "v" }, "ti", function() vim.cmd("LeanInfoviewToggle") end, opts) vim.keymap.set({ "n", "v" }, "sg", function() vim.cmd("LeanGoal") end, opts) vim.keymap.set({ "n", "v" }, "stg", function() vim.cmd("LeanTermGoal") end, opts) vim.api.nvim_set_option_value('omnifunc', 'v:lua.vim.lsp.omnifunc', { scope = "local", buf = bufnr }) end }, mappings = true, } -- telescope keymaps vim.keymap.set("n", "pl", telescope.extensions.possession.list) -- TODO(zaphar): Remove this once my muscle memory has set in. vim.keymap.set("n", "nff", telescope_builtins.fd) vim.keymap.set("n", "ff", telescope_builtins.fd) vim.keymap.set("n", "rl", telescope_builtins.lsp_references) vim.keymap.set("n", "rn", vim.lsp.buf.rename) vim.keymap.set("n", "sl", telescope_builtins.lsp_workspace_symbols) vim.keymap.set("n", "dl", telescope_builtins.diagnostics) vim.keymap.set("n", "rg", telescope_builtins.live_grep) vim.keymap.set("n", "bl", function() telescope_builtins.buffers({ }) end) vim.keymap.set("n", "lds", telescope_builtins.lsp_document_symbols, { desc = "[D]ocument [S]ymbols" }) vim.keymap.set("n", "lws", telescope_builtins.lsp_dynamic_workspace_symbols, { desc = "[W]orkspace [S]ymbols" }) -- harpoon keymaps vim.keymap.set("n", "ha", function() harpoon:list():append() end) vim.keymap.set("n", "he", function() harpoon.ui:toggle_quick_menu(harpoon:list()) end) vim.keymap.set("n", "hj", function() harpoon:list():prev() end) vim.keymap.set("n", "hk", function() harpoon:list():next() end) -- codelens keymaps vim.keymap.set("n", "rr", vim.lsp.codelens.run) -- debugging DAP keymaps vim.keymap.set("n", "tdb", function() vim.cmd("DBUIToggle") end) require('lualine').setup { icons_enabled = false, disabled_filetypes = { statusline = {}, winbar = {}, }, sections = { -- left side lualine_a = { 'mode' }, lualine_b = { 'filename' }, lualine_c = { 'encoding', 'fileformat', 'filetype' }, -- right side lualine_x = { 'diagnostics' }, lualine_y = { 'progress', 'lsp_progress' }, lualine_z = { 'location' } } } -- Hunk diff tree viewer and editor. Replacement for Meld and company local hunk = require("hunk") hunk.setup({ keys = { global = { quit = { "q" }, accept = { "" }, focus_tree = { "e" }, }, tree = { expand_node = { "l", "" }, collapse_node = { "h", "" }, open_file = { "" }, toggle_file = { "a" }, }, diff = { toggle_line = { "a" }, toggle_hunk = { "A" }, }, }, ui = { tree = { -- Mode can either be `nested` or `flat` mode = "nested", width = 35, }, --- Can be either `vertical` or `horizontal` layout = "vertical", }, icons = { selected = "󰡖", deselected = "", partially_selected = "󰛲", folder_open = "", folder_closed = "", }, -- Called right after each window and buffer are created. --hooks = { -- ---@param _context { buf: number, tree: NuiTree, opts: table } -- on_tree_mount = function(_context) end, -- ---@param _context { buf: number, win: number } -- on_diff_mount = function(_context) end, --}, }) local dap = require('dap') dap.adapters.lldb = { type = "executable", command = "/run/current-system/sw/bin/lldb", name = "lldb", } dap.adapters.coreclr = { type = 'executable', command = '/run/current-system/sw/bin/netcoredbg', args = { '--interpreter=vscode' } } dap.configurations.rust = { { name = 'Launch Rust', type = 'lldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, } dap.configurations.cs = { { name = "Launch - netcoredbg", type = "coreclr", request = "launch", program = function() return vim.fn.input('Path to dll', vim.fn.getcwd(), 'file') end, }, } local mcphub = require("mcphub") mcphub.setup({ -- This sets vim.g.mcphub_auto_approve to false by default (can also be toggled from the HUB UI with `ga`) config = vim.fn.expand("~/.config/mcphub/servers.json"), auto_approve = true, auto_toggle_mcp_servers = true, -- Let LLMs start and stop MCP servers automatically extensions = { avante = { make_slash_commands = true, -- make /slash commands from MCP server prompts }, }, cmd = "mcp-hub", }) function get_server_list_prompt(hub_instance) -- returns a list of mcp-servers with a `name` and a list of tools with `name` local mcp_tool_prompt = "# MCP SERVERS\n\nThe Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities.\n\n# Connected MCP Servers\n\nWhen a server is connected, you can use the server's tools via the `use_mcp_tool` tool, and access the server's resources via the `access_mcp_resource` tool.\nNote: Server names are case sensitive and you should always use the exact full name like `Firecrawl MCP` or `src/user/main/time-mcp` etc\n\n" if not hub_instance then return "" end local servers = hub_instance:get_servers() if not servers or #servers == 0 then return "" end for _, server in ipairs(servers) do mcp_tool_prompt = mcp_tool_prompt .. "## server name: `" .. server.name .. "`\n\n" if server.capabilities.tools and #server.capabilities.tools > 0 then mcp_tool_prompt = mcp_tool_prompt .. "Available tools:\n\n" for _, tool in ipairs(server.capabilities.tools) do mcp_tool_prompt = mcp_tool_prompt .. "- tool name: `" .. tool.name .. "`\n" if tool.description then mcp_tool_prompt = mcp_tool_prompt .. " - Description: " .. tool.description .. "\n" end end mcp_tool_prompt = mcp_tool_prompt .. "\n" end end return mcp_tool_prompt end function make_avante_system_prompt(hub_instance) return hub_instance and get_server_list_prompt(hub_instance) or "" end function update_avante_system_prompt() local hub_instance = mcphub.get_hub_instance(); local system_prompt = make_avante_system_prompt(hub_instance) if system_prompt then require("avante.config").override({system_prompt = system_prompt}) end end require('copilot').setup(); require('avante').setup({ provider = "claude", mode = "planning", cursor_applying_provider = nil, -- default to whatever provider is configured claude = { endpoint = "https://api.anthropic.com", model = "claude-3-7-sonnet-20250219", timeout = 30000, -- Timeout in milliseconds temperature = 0, max_tokens = 20480, }, copilot = { model = "claude-3.7-sonnet", }, behavior = { enable_cursor_planning_mode = true, }, system_prompt = make_avante_system_prompt(mcphub.get_hub_instance()), custom_tools = { require("mcphub.extensions.avante").mcp_tool() }, -- Disable these because we'll use the mcphub versions instead --disabled_tools = { -- "list_files", -- Built-in file operations -- "search_files", -- "read_file", -- "create_file", -- "rename_file", -- "delete_file", -- "create_dir", -- "rename_dir", -- "delete_dir", -- "bash", -- Built-in terminal access --}, })