From e2697f61fd340202bf7f38d1e50fd47143b5baf8 Mon Sep 17 00:00:00 2001 From: Alex Selimov Date: Wed, 3 Sep 2025 09:05:52 -0400 Subject: [PATCH] Finally modularize neovim configuration --- init.lua | 1016 +----------------------------------- lua/config/autocmds.lua | 35 ++ lua/config/formatting.lua | 34 ++ lua/config/keymaps.lua | 19 + lua/config/lazy.lua | 16 + lua/config/options.lua | 55 ++ lua/plugins/autopairs.lua | 59 +++ lua/plugins/completion.lua | 136 +++++ lua/plugins/dev-tools.lua | 30 ++ lua/plugins/editor.lua | 22 + lua/plugins/formatting.lua | 31 ++ lua/plugins/iron.lua | 51 ++ lua/plugins/lsp.lua | 193 +++++++ lua/plugins/telescope.lua | 29 + lua/plugins/ui.lua | 135 +++++ lua/utils/statusline.lua | 15 + 16 files changed, 872 insertions(+), 1004 deletions(-) create mode 100644 lua/config/autocmds.lua create mode 100644 lua/config/formatting.lua create mode 100644 lua/config/keymaps.lua create mode 100644 lua/config/lazy.lua create mode 100644 lua/config/options.lua create mode 100644 lua/plugins/autopairs.lua create mode 100644 lua/plugins/completion.lua create mode 100644 lua/plugins/dev-tools.lua create mode 100644 lua/plugins/editor.lua create mode 100644 lua/plugins/formatting.lua create mode 100644 lua/plugins/iron.lua create mode 100644 lua/plugins/lsp.lua create mode 100644 lua/plugins/telescope.lua create mode 100644 lua/plugins/ui.lua create mode 100644 lua/utils/statusline.lua diff --git a/init.lua b/init.lua index cba2549..b0115a2 100644 --- a/init.lua +++ b/init.lua @@ -1,1010 +1,18 @@ --- Specify the leader --- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used) -vim.g.mapleader = ";" -vim.g.maplocalleader = ";" +-- Neovim configuration +-- Modularized structure for better organization --- [[ Setting options ]] --- Make line numbers default -vim.opt.number = true -vim.opt.relativenumber = true +-- Load core configuration +require("config.options") +require("config.keymaps") +require("config.autocmds") +require("config.lazy") --- Disable mouse mode -vim.opt.mouse = "" - --- Don't show the mode, since it's already in status line -vim.opt.showmode = false - --- Case-insensitive searching UNLESS \C or capital in search -vim.opt.ignorecase = true -vim.opt.smartcase = true - --- Keep signcolumn on by default -vim.opt.signcolumn = "yes" - --- Configure how new splits should be opened -vim.opt.splitright = true -vim.opt.splitbelow = true - --- Sets how neovim will display certain whitespace in the editor. --- See :help 'list' --- and :help 'listchars' -vim.opt.list = true -vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" } - --- Preview substitutions live, as you type! -vim.opt.inccommand = "split" - --- Show which line your cursor is on -vim.opt.cursorline = true - --- Minimal number of screen lines to keep above and below the cursor. --- vim.opt.scrolloff = 10 - --- General universal settings -vim.opt.sw = 4 -vim.opt.ts = 4 -vim.opt.expandtab = true -vim.opt.tw = 100 -vim.opt.colorcolumn = "+1" -vim.opt.termguicolors = true -vim.opt.pumheight = 5 - --- Disable semantic tokens -vim.api.nvim_create_autocmd("LspAttach", { - callback = function(args) - local client = vim.lsp.get_client_by_id(args.data.client_id) - client.server_capabilities.semanticTokensProvider = nil - end, -}) - --- Function to get work count in status line -local function getWords() - -- the third string here is the string for visual-block mode (^V) - if vim.fn.mode() == "v" or vim.fn.mode() == "V" or vim.fn.mode() == "" then - return vim.fn.wordcount().visual_words .. "" - else - return vim.fn.wordcount().words .. "" - end -end --- [[ Basic Keymaps ]] --- See `:help vim.keymap.set()` - --- Set highlight on search, but clear on pressing in normal mode -vim.opt.hlsearch = true -vim.keymap.set("n", "", "nohlsearch") - --- Diagnostic keymaps -vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, { desc = "Go to previous [D]iagnostic message" }) -vim.keymap.set("n", "]d", vim.diagnostic.goto_next, { desc = "Go to next [D]iagnostic message" }) -vim.keymap.set("n", "e", vim.diagnostic.open_float, { desc = "Show diagnostic [E]rror messages" }) -vim.keymap.set("n", "q", vim.diagnostic.setloclist, { desc = "Open diagnostic [Q]uickfix list" }) - --- Keybinds to make split navigation easier. --- Use CTRL+ to switch between windows --- --- See `:help wincmd` for a list of all window commands -vim.keymap.set("n", "", "", { desc = "Move focus to the left window" }) -vim.keymap.set("n", "", "", { desc = "Move focus to the right window" }) -vim.keymap.set("n", "", "", { desc = "Move focus to the lower window" }) -vim.keymap.set("n", "", "", { desc = "Move focus to the upper window" }) --- Jump to the definition of the word under your cursor. --- This is where a variable was first declared, or where a function is defined, etc. --- To jump back, press . -vim.keymap.set("n", "gd", vim.lsp.buf.definition, { desc = "" }) --- [[ Basic Autocommands ]] --- Go back to last edited line when reopening file -vim.api.nvim_create_autocmd("BufRead", { - callback = function(opts) - vim.api.nvim_create_autocmd("BufWinEnter", { - once = true, - buffer = opts.buf, - callback = function() - local ft = vim.bo[opts.buf].filetype - local last_known_line = vim.api.nvim_buf_get_mark(opts.buf, '"')[1] - if - not (ft:match("gitcommit") and ft:match("gitrebase")) - and last_known_line > 1 - and last_known_line <= vim.api.nvim_buf_line_count(opts.buf) - then - vim.api.nvim_feedkeys([[g`"]], "nx", false) - end - end, - }) - end, -}) - --- [[ Install `lazy.nvim` plugin manager ]] --- See `:help lazy.nvim.txt` or https://github.com/folke/lazy.nvim for more info -local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" -if not vim.loop.fs_stat(lazypath) then - local lazyrepo = "https://github.com/folke/lazy.nvim.git" - vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) -end ---@diagnostic disable-next-line: undefined-field -vim.opt.rtp:prepend(lazypath) - --- Get user dictionary -local path = vim.fn.stdpath("config") .. "/spell/en.utf-8.add" -local words = {} - -for word in io.open(path, "r"):lines() do - table.insert(words, word) -end --- [[ Configure and install plugins ]] --- --- To check the current status of your plugins, run --- :Lazy --- --- You can press `?` in this menu for help. Use `:q` to close the window --- --- To update plugins, you can run --- :Lazy update --- --- - --- NOTE: Here is where you install your plugins. +-- Setup lazy.nvim and load plugins require("lazy").setup({ - - -- [[ Plugin Specs list ]] - -- NOTE: Plugins can be added with a link (or for a github repo: 'owner/repo' link). - { - "windwp/nvim-autopairs", - config = function() - local remap = vim.api.nvim_set_keymap - local npairs = require("nvim-autopairs") - - npairs.setup({ map_bs = false, map_cr = false }) - - _G.MUtils = {} - - MUtils.CR = function() - if vim.fn.pumvisible() ~= 0 then - if vim.fn.complete_info({ "selected" }).selected ~= -1 then - return npairs.esc("") - else - return npairs.esc("") .. npairs.autopairs_cr() - end - else - return npairs.autopairs_cr() - end - end - remap("i", "", "v:lua.MUtils.CR()", { expr = true, noremap = true }) - - MUtils.BS = function() - if vim.fn.pumvisible() ~= 0 and vim.fn.complete_info({ "mode" }).mode == "eval" then - return npairs.esc("") .. npairs.autopairs_bs() - else - return npairs.autopairs_bs() - end - end - remap("i", "", "v:lua.MUtils.BS()", { expr = true, noremap = true }) - - -- put this to setup function and press to use fast_wrap - npairs.setup({ - fast_wrap = {}, - }) - - -- change default fast_wrap - npairs.setup({ - fast_wrap = { - map = "", - chars = { "{", "[", "(", '"', "'" }, - pattern = [=[[%'%"%>%]%)%}%,]]=], - end_key = "$", - before_key = "h", - after_key = "l", - cursor_pos_before = true, - keys = "qwertyuiopzxcvbnmasdfghjkl", - manual_position = true, - highlight = "Search", - highlight_grey = "Comment", - }, - }) - - npairs.remove_rule("`") - end, - }, - "christoomey/vim-tmux-navigator", - "kana/vim-textobj-user", - "mechatroner/rainbow_csv", - { - "GCBallesteros/vim-textobj-hydrogen", - dependencies = { - "kana/vim-textobj-user", - }, - }, - "godlygeek/tabular", - "tpope/vim-sleuth", - { - "codersauce/runst.nvim", - lazy = false, - opts = {}, - config = function() - require("runst").setup() - end, - }, - { - "Vigemus/iron.nvim", - ft = { "python" }, - config = function() - local iron = require("iron.core") - local view = require("iron.view") - - iron.setup({ - config = { - -- Whether a repl should be discarded or not - scratch_repl = true, - -- Your repl definitions come here - repl_definition = { - python = { - -- Can be a table or a function that - -- returns a table (see below) - command = "ipython --no-autoindent", - }, - }, - -- How the repl window will be displayed - -- See below for more information - repl_open_cmd = require("iron.view").bottom(20), - }, - -- Iron doesn't set keymaps by default anymore. - -- You can set them here or manually add keymaps to the functions in iron.core - keymaps = { - visual_send = "sc", - send_file = "sf", - send_line = "sl", - cr = "s", - interrupt = "s", - exit = "sq", - clear = "cl", - }, - -- If the highlight is on, you can change how it looks - -- For the available options, check nvim_set_hl - highlight = { - italic = true, - }, - ignore_blank_lines = false, -- ignore blank lines when sending visual select lines - }) - vim.keymap.set("n", ";rs", "IronRepl") - vim.keymap.set("n", ";rr", "IronRestart") - vim.keymap.set("n", ";rf", "IronFocus") - vim.keymap.set("n", ";rh", "IronHide") - - repl_open_cmd = "horizontal bot 20 split" - end, - }, - "norcalli/nvim-colorizer.lua", - "ixru/nvim-markdown", - "KeitaNakamura/tex-conceal.vim", - "christoomey/vim-tmux-navigator", - "skywind3000/asyncrun.vim", - -- NOTE: Plugins can specify dependencies. - -- - -- The dependencies are proper plugin specifications as well - anything - -- you do for a plugin at the top level, you can do for a dependency. - -- - -- Use the `dependencies` key to specify the dependencies of a particular plugin - { -- Fuzzy Finder (files, lsp, etc) - "nvim-telescope/telescope.nvim", - event = "VeryLazy", - branch = "0.1.x", - dependencies = { - "nvim-lua/plenary.nvim", - -- Useful for getting pretty icons, but requires special font. - -- If you already have a Nerd Font, or terminal set up with fallback fonts - -- you can enable this - { "nvim-tree/nvim-web-devicons" }, - { "nvim-telescope/telescope-live-grep-args.nvim" }, - }, - config = function() - -- Telescope is a fuzzy finder that comes with a lot of different things that - -- it can fuzzy find! It's more than just a "file finder", it can search - -- many different aspects of Neovim, your workspace, LSP, and more! - -- - -- The easiest way to use telescope, is to start by doing something like: - -- :Telescope help_tags - -- - -- After running this command, a window will open up and you're able to - -- type in the prompt window. You'll see a list of help_tags options and - -- a corresponding preview of the help. - -- - -- Two important keymaps to use while in telescope are: - -- - Insert mode: - -- - Normal mode: ? - -- - -- This opens a window that shows you all of the keymaps for the current - -- telescope picker. This is really useful to discover what Telescope can - -- do as well as how to actually do it! - - -- [[ Configure Telescope ]] - -- See `:help telescope` and `:help telescope.setup()` - require("telescope").setup({ - -- You can put your default mappings / updates / etc. in here - -- All the info you're looking for is in `:help telescope.setup()` - -- - defaults = vim.tbl_extend("force", require("telescope.themes").get_ivy(), { - path_display = { "smart" }, - }), - }) - - pcall(require("telescope").load_extension("live_grep_args")) - local builtin = require("telescope.builtin") - vim.keymap.set("n", "", require("telescope").extensions.live_grep_args.live_grep_args) - vim.keymap.set( - "x", - "", - "\"zy:lua require('telescope').extensions.live_grep_args.live_grep_args(require('telescope.themes').get_ivy({}))z" - ) - vim.keymap.set("n", "", builtin.find_files) - end, - }, - { - "mfussenegger/nvim-jdtls", - config = function() - local jdtls = require("jdtls") - - -- Find project root - local root_dir = require("jdtls.setup").find_root({ ".git", "mvnw", "gradlew" }) - - -- Path to your exported Eclipse/IntelliJ style xml - local style_path = vim.fn.expand("~/.config/nvim/GoogleStyle.xml") - - local config = { - cmd = { "jdtls" }, -- or path to your startup script - root_dir = root_dir, - settings = { - java = { - format = { - settings = { - url = "file://" .. style_path, - profile = "GoogleStyle", -- must match the profile inside the xml - }, - }, - }, - }, - } - - jdtls.start_or_attach(config) - end, - }, - - { -- LSP Configuration & Plugins - "neovim/nvim-lspconfig", - opts = { - autoformat = false, - }, - dependencies = { - -- Automatically install LSPs and related tools to stdpath for neovim - "williamboman/mason.nvim", - "williamboman/mason-lspconfig.nvim", - "WhoIsSethDaniel/mason-tool-installer.nvim", - - -- Useful status updates for LSP. - -- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})` - { "j-hui/fidget.nvim", opts = {} }, - }, - config = function() - vim.api.nvim_create_autocmd("LspAttach", { - group = vim.api.nvim_create_augroup("kickstart-lsp-attach", { clear = true }), - callback = function(event) - -- NOTE: Remember that lua is a real programming language, and as such it is possible - -- to define small helper and utility functions so you don't have to repeat yourself - -- many times. - -- - -- In this case, we create a function that lets us more easily define mappings specific - -- for LSP related items. It sets the mode, buffer and description for us each time. - local map = function(keys, func, desc) - vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) - end - - -- Find references for the word under your cursor. - map("gr", require("telescope.builtin").lsp_references, "[G]oto [R]eferences") - - -- Jump to the implementation of the word under your cursor. - -- Useful when your language has ways of declaring types without an actual implementation. - map("I", require("telescope.builtin").lsp_implementations, "[G]oto [I]mplementation") - - -- Jump to the type of the word under your cursor. - -- Useful when you're not sure what type a variable is and you want to see - -- the definition of its *type*, not where it was *defined*. - map("D", require("telescope.builtin").lsp_type_definitions, "Type [D]efinition") - - -- Fuzzy find all the symbols in your current document. - -- Symbols are things like variables, functions, types, etc. - map("ds", require("telescope.builtin").lsp_document_symbols, "[D]ocument [S]ymbols") - - -- Fuzzy find all the symbols in your current workspace - -- Similar to document symbols, except searches over your whole project. - map( - "ws", - require("telescope.builtin").lsp_dynamic_workspace_symbols, - "[W]orkspace [S]ymbols" - ) - - -- Rename the variable under your cursor - -- Most Language Servers support renaming across files, etc. - map("rn", vim.lsp.buf.rename, "[R]e[n]ame") - - -- Execute a code action, usually your cursor needs to be on top of an error - -- or a suggestion from your LSP for this to activate. - map("ca", vim.lsp.buf.code_action, "[C]ode [A]ction") - - -- Opens a popup that displays documentation about the word under your cursor - -- See `:help K` for why this keymap - map("K", vim.lsp.buf.hover, "Hover Documentation") - - -- WARN: This is not Goto Definition, this is Goto Declaration. - -- For example, in C this would take you to the header - map("gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration") - - -- The following two autocommands are used to highlight references of the - -- word under your cursor when your cursor rests there for a little while. - -- See `:help CursorHold` for information about when this is executed - -- - -- When you move your cursor, the highlights will be cleared (the second autocommand). - local client = vim.lsp.get_client_by_id(event.data.client_id) - if client and client.server_capabilities.documentHighlightProvider then - vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { - buffer = event.buf, - callback = vim.lsp.buf.document_highlight, - }) - - vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, { - buffer = event.buf, - callback = vim.lsp.buf.clear_references, - }) - end - end, - }) - - -- LSP servers and clients are able to communicate to each other what features they support. - -- By default, Neovim doesn't support everything that is in the LSP Specification. - -- When you add nvim-cmp, luasnip, etc. Neovim now has *more* capabilities. - -- So, we create new capabilities with nvim cmp, and then broadcast that to the servers. - local capabilities = vim.lsp.protocol.make_client_capabilities() - capabilities = vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities()) - - -- Enable the following language servers - -- Feel free to add/remove any LSPs that you want here. They will automatically be installed. - -- - -- Add any additional override configuration in the following tables. Available keys are: - -- - cmd (table): Override the default command used to start the server - -- - filetypes (table): Override the default list of associated filetypes for the server - -- - capabilities (table): Override fields in capabilities. Can be used to disable certain LSP features. - -- - settings (table): Override the default settings passed when initializing the server. - -- For example, to see the options for `lua_ls`, you could go to: https://luals.github.io/wiki/settings/ - local servers = { - clangd = { - filetypes = { - "c", - "cpp", - }, - }, - taplo = {}, - yamlls = { settings = { yaml = { format = { enable = false } } } }, - -- gopls = {}, - pyright = {}, - fortls = {}, - jsonls = {}, - bashls = { dependencies = "shellcheck" }, - kotlin_language_server = {}, - ts_ls = {}, - rust_analyzer = { - settings = { - ["rust-analyzer"] = { - check = { - command = "clippy", - }, - rustfmt = { - extraArgs = { "+nightly" }, - }, - }, - }, - }, - arduino_language_server = {}, - ltex = { - settings = { - ltex = { - enabled = { "latex", "tex", "bib", "markdown" }, - language = "auto", - diagnosticSeverity = "information", - sentenceCacheSize = 2000, - latex = { - commands = { - ["\\hypertarget"] = "dummy", - }, - }, - dictionary = (function() - -- For dictionary, search for files in the runtime to have - -- and include them as externals the format for them is - -- dict/{LANG}.txt - -- - -- Also add dict/default.txt to all of them - local files = {} - for _, file in ipairs(vim.api.nvim_get_runtime_file("dict/*", true)) do - local lang = vim.fn.fnamemodify(file, ":t:r") - local fullpath = vim.fs.normalize(file, ":p") - files[lang] = { ":" .. fullpath } - end - - if files.default then - for lang, _ in pairs(files) do - if lang ~= "default" then - vim.list_extend(files[lang], files.default) - end - end - files.default = nil - end - return files - end)(), - }, - }, - }, - lua_ls = { - -- cmd = {...}, - -- filetypes { ...}, - -- capabilities = {}, - settings = { - Lua = { - runtime = { version = "LuaJIT" }, - workspace = { - checkThirdParty = false, - -- Tells lua_ls where to find all the Lua files that you have loaded - -- for your neovim configuration. - library = { - "${3rd}/luv/library", - unpack(vim.api.nvim_get_runtime_file("", true)), - }, - -- If lua_ls is really slow on your computer, you can try this instead: - -- library = { vim.env.VIMRUNTIME }, - }, - -- You can toggle below to ignore Lua_LS's noisy `missing-fields` warnings - -- diagnostics = { disable = { 'missing-fields' } }, - }, - }, - }, - } - - -- Ensure the servers and tools above are installed - -- To check the current status of installed tools and/or manually install - -- other tools, you can run - -- :Mason - -- - -- You can press `g?` for help in this menu - require("mason").setup() - - -- You can add other tools here that you want Mason to install - -- for you, so that they are available from within Neovim. - local ensure_installed = vim.tbl_keys(servers or {}) - vim.list_extend(ensure_installed, { - "stylua", -- Used to format lua code - "black", - "clang-format", - "beautysh", - "latexindent", - "prettier", - }) - require("mason-tool-installer").setup({ ensure_installed = ensure_installed }) - - require("mason-lspconfig").setup({ - handlers = { - function(server_name) - local server = servers[server_name] or {} - require("lspconfig")[server_name].setup({ - cmd = server.cmd, - on_init = function(client) - client.offset_encoding = "utf-8" - end, - settings = server.settings, - filetypes = server.filetypes, - -- This handles overriding only values explicitly passed - -- by the server configuration above. Useful when disabling - -- certain features of an LSP (for example, turning off formatting for tsserver) - capabilities = vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {}), - }) - end, - }, - }) - end, - }, - - { -- Autoformat - "stevearc/conform.nvim", - setup = { - formatters = { - black = { - command = "black", - prepend_args = { "--line-length", "100" }, - }, - }, - }, - opts = { - notify_on_error = false, - format_on_save = { - timeout_ms = 500, - lsp_fallback = false, - }, - formatters_by_ft = { - lua = { "stylua" }, - -- Conform can also run multiple formatters sequentially - python = { "black" }, - cpp = { "clang-format" }, - c = { "clang-format" }, - sh = { "beautysh" }, - tex = { "latexindent" }, - yaml = { "prettier" }, - }, - }, - }, - - { -- Autocompletion - "hrsh7th/nvim-cmp", - event = "InsertEnter", - dependencies = { - -- Snippet Engine & its associated nvim-cmp source - { - "L3MON4D3/LuaSnip", - build = (function() - -- Build Step is needed for regex support in snippets - -- This step is not supported in many windows environments - -- Remove the below condition to re-enable on windows - if vim.fn.has("win32") == 1 or vim.fn.executable("make") == 0 then - return - end - return "make install_jsregexp" - end)(), - config = function() - require("luasnip.loaders.from_snipmate").lazy_load({ paths = "./snippets" }) - local ls = require("luasnip") - -- some shorthands... - local snip = ls.snippet - local node = ls.snippet_node - local text = ls.text_node - local insert = ls.insert_node - local func = ls.function_node - local choice = ls.choice_node - local dynamicn = ls.dynamic_node - - ls.add_snippets(nil, { - python = { - snip({ - trig = "imp", - namr = "Imports", - dscr = "Comments for imports", - }, { - text({ "# Core modules", "" }), - insert(1), - text({ "", "# Non-core modules", "" }), - insert(2), - text({ "", "# SEI modules", "" }), - insert(3), - }), - }, - tex = { - snip({ - trig = "input", - namr = "Input Cell", - dscr = "Cell for SEIInputTable", - }, { - text({ "\\hypertarget{" }), - insert(1), - text({ "}{" }), - insert(2), - text({ "} & \\SEICell{", "\t" }), - insert(3), - text({ "", "}\\\\", "" }), - }), - }, - markdown = { - snip({ - trig = "img", - namr = "image", - dscr = "Markdown img", - }, { - text({ "![" }), - insert(1), - text("]("), - insert(2), - text(")"), - }), - snip({ - trig = "header", - namr = "header", - dscr = "Yaml header for markdown notes", - }, { - text({ "---", "" }), - text("title: "), - insert(1), - text({ "", "author: Alex Selimov", "" }), - text("tags: ["), - insert(2), - text({ "]", "", "" }), - text({ "---", "" }), - }), - }, - }) - end, - }, - "saadparwaiz1/cmp_luasnip", - "hrsh7th/cmp-nvim-lsp", - "hrsh7th/cmp-path", - }, - config = function() - -- See `:help cmp` - local cmp = require("cmp") - local luasnip = require("luasnip") - luasnip.config.setup({}) - cmp.setup({ - snippet = { - expand = function(args) - luasnip.lsp_expand(args.body) - end, - }, - completion = { completeopt = "menu,menuone,noinsert" }, - - -- For an understanding of why these mappings were - -- chosen, you will need to read `:help ins-completion` - -- - -- No, but seriously. Please read `:help ins-completion`, it is really good! - mapping = cmp.mapping.preset.insert({ - -- Select the [n]ext item - [""] = cmp.mapping.select_next_item(), - -- Select the [p]revious item - [""] = cmp.mapping.select_prev_item(), - - -- Accept ([y]es) the completion. - -- This will auto-import if your LSP supports it. - -- This will expand snippets if the LSP sent a snippet. - [""] = cmp.mapping.confirm({ select = true }), - - ["j"] = cmp.mapping(function() - if luasnip.expand_or_locally_jumpable() then - luasnip.expand_or_jump() - end - end, { "i", "s" }), - ["k"] = cmp.mapping(function() - if luasnip.locally_jumpable(-1) then - luasnip.jump(-1) - end - end, { "i", "s" }), - }), - sources = { - { name = "nvim_lsp" }, - { name = "luasnip" }, - { name = "path" }, - }, - }) - end, - snippet = { - expand = function(args) - local luasnip = require("luasnip") - if not luasnip then - return - end - luasnip.lsp_expand(args.body) - end, - }, - }, - { -- You can easily change to a different colorscheme. - -- Change the name of the colorscheme plugin below, and then - -- change the command in the config to whatever the name of that colorscheme is - -- - -- If you want to see what colorschemes are already installed, you can use `:Telescope colorscheme` - lazy = false, -- make sure we load this during startup if it is your main colorscheme - priority = 1000, -- make sure to load this before all the other start plugins - "zenbones-theme/zenbones.nvim", - dependencies = "rktjmp/lush.nvim", - config = function() - -- Load the colorscheme here - vim.cmd.colorscheme("zenwritten") - end, - }, - - -- Highlight todo, notes, etc in comments - { "folke/todo-comments.nvim", dependencies = { "nvim-lua/plenary.nvim" }, opts = { signs = false } }, - - { -- Collection of various small independent plugins/modules - -- Simple and easy statusline. - -- You could remove this setup call if you don't like it, - -- and try some other statusline plugin - "echasnovski/mini.nvim", - keys = { - { - "m", - function() - require("mini.files").open(vim.api.nvim_buf_get_name(0), true) - end, - desc = "Open mini.files (Directory of Current File)", - }, - }, - config = function() - require("mini.files").setup() - require("mini.statusline").setup({ - content = { - active = function() - local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 }) - local git = MiniStatusline.section_git({ trunc_width = 75 }) - local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 }) - local filename = MiniStatusline.section_filename({ trunc_width = 140 }) - local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 }) - local location = MiniStatusline.section_location({ trunc_width = 75 }) - local search = MiniStatusline.section_searchcount({ trunc_width = 75 }) - local words = getWords() - return MiniStatusline.combine_groups({ - - { hl = mode_hl, strings = { mode } }, - { hl = "MiniStatuslineDevinfo", strings = { git, diagnostics } }, - "%<", -- Mark general truncate point - { hl = "MiniStatuslineFilename", strings = { filename } }, - "%=", -- End left alignment - { hl = "MiniStatuslineFileinfo", strings = { fileinfo } }, - { hl = "MiniStatuslineFileinfo", strings = { words } }, - { hl = mode_hl, strings = { search, location } }, - }) - end, - }, - }) - end, - }, - - { -- Highlight, edit, and navigate code - "nvim-treesitter/nvim-treesitter", - build = ":TSUpdate", - config = function() - -- [[ Configure Treesitter ]] See `:help nvim-treesitter` - - ---@diagnostic disable-next-line: missing-fields - require("nvim-treesitter.configs").setup({ - ensure_installed = { "bash", "c", "html", "lua", "markdown", "vim", "vimdoc" }, - -- Autoinstall languages that are not installed - auto_install = true, - highlight = { enable = false }, - indent = { enable = false }, - }) - - -- There are additional nvim-treesitter modules that you can use to interact - -- with nvim-treesitter. You should go explore a few and see what interests you: - -- - -- - Incremental selection: Included, see :help nvim-treesitter-incremental-selection-mod - -- - Show your current context: https://github.com/nvim-treesitter/nvim-treesitter-context - -- - Treesitter + textobjects: https://github.com/nvim-treesitter/nvim-treesitter-textobjects - end, - }, - { - "folke/snacks.nvim", - priority = 1000, - lazy = false, - opts = { - -- your configuration comes here - -- or leave it empty to use the default settings - -- refer to the configuration section below - dashboard = { enabled = true }, - input = { enabled = true }, - terminal = { enabled = true }, - notify = { enabled = true }, - }, - keys = { - { - "T", - function() - Snacks.terminal() - end, - desc = "Toggle Terminal", - }, - }, - }, - "airblade/vim-gitgutter", - "tpope/vim-abolish", - { - "danymat/neogen", - setup = { - snippet_engine = "luasnip", - }, - config = function() - require("neogen").setup({}) - local opts = { noremap = true, silent = true } - vim.api.nvim_set_keymap("n", "dg", ":lua require('neogen').generate()", opts) - end, - version = "*", - }, - "dhruvasagar/vim-table-mode", - { - "MeanderingProgrammer/render-markdown.nvim", - dependencies = { "nvim-treesitter/nvim-treesitter", "echasnovski/mini.nvim" }, -- if you use the mini.nvim suite - -- dependencies = { 'nvim-treesitter/nvim-treesitter', 'echasnovski/mini.icons' }, -- if you use standalone mini plugins - -- dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-tree/nvim-web-devicons' }, -- if you prefer nvim-web-devicons - ---@module 'render-markdown' - ---@type render.md.UserConfig - opts = {}, - }, - { - "folke/trouble.nvim", - opts = {}, -- for default options, refer to the configuration section for custom setup. - cmd = "Trouble", - keys = { - { - "xx", - "Trouble diagnostics toggle", - desc = "Diagnostics (Trouble)", - }, - { - "xX", - "Trouble diagnostics toggle filter.buf=0", - desc = "Buffer Diagnostics (Trouble)", - }, - { - "cs", - "Trouble symbols toggle focus=false", - desc = "Symbols (Trouble)", - }, - { - "cl", - "Trouble lsp toggle focus=false win.position=right", - desc = "LSP Definitions / references / ... (Trouble)", - }, - { - "xL", - "Trouble loclist toggle", - desc = "Location List (Trouble)", - }, - { - "xQ", - "Trouble qflist toggle", - desc = "Quickfix List (Trouble)", - }, - }, - }, + -- Import all plugin configurations + { import = "plugins" }, }) +-- Load additional configuration require("colorizer").setup() ---Set rustfmt command -vim.g["rustfmt_command"] = "rustfmt +nightly" ---Disable semantic highlights -for _, group in ipairs(vim.fn.getcompletion("@lsp", "highlight")) do - vim.api.nvim_set_hl(0, group, {}) -end - --- Commands to disable formatting -require("conform").setup({ - format_on_save = function(bufnr) - -- Disable with a global or buffer-local variable - if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then - return - end - return { timeout_ms = 500, lsp_fallback = true } - end, -}) - -vim.api.nvim_create_user_command("FormatDisable", function(args) - if args.bang then - -- FormatDisable! will disable formatting just for this buffer - vim.b.disable_autoformat = true - else - vim.g.disable_autoformat = true - end -end, { - desc = "Disable autoformat-on-save", - bang = true, -}) -vim.api.nvim_create_user_command("FormatEnable", function() - vim.b.disable_autoformat = false - vim.g.disable_autoformat = false -end, { - desc = "Re-enable autoformat-on-save", -}) - -function file_exists(name) - local f = io.open(name, "r") - return f ~= nil and io.close(f) -end - -local home = os.getenv("HOME") -if file_exists(home .. "/.config/nvim/light_mode") then - vim.opt.background = "light" -end - --- Autocommand for java to organize imports on save ---vim.api.nvim_create_autocmd("BufWritePost", { --- pattern = "*.java", --- callback = function() --- require("jdtls").organize_imports() --- end, ---}) +require("config.formatting") \ No newline at end of file diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua new file mode 100644 index 0000000..d1c5449 --- /dev/null +++ b/lua/config/autocmds.lua @@ -0,0 +1,35 @@ +-- Autocmds configuration + +-- Disable semantic tokens +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + client.server_capabilities.semanticTokensProvider = nil + end, +}) + +-- Go back to last edited line when reopening file +vim.api.nvim_create_autocmd("BufRead", { + callback = function(opts) + vim.api.nvim_create_autocmd("BufWinEnter", { + once = true, + buffer = opts.buf, + callback = function() + local ft = vim.bo[opts.buf].filetype + local last_known_line = vim.api.nvim_buf_get_mark(opts.buf, '"')[1] + if + not (ft:match("gitcommit") and ft:match("gitrebase")) + and last_known_line > 1 + and last_known_line <= vim.api.nvim_buf_line_count(opts.buf) + then + vim.api.nvim_feedkeys([[g`"]], "nx", false) + end + end, + }) + end, +}) + +-- Disable semantic highlights +for _, group in ipairs(vim.fn.getcompletion("@lsp", "highlight")) do + vim.api.nvim_set_hl(0, group, {}) +end \ No newline at end of file diff --git a/lua/config/formatting.lua b/lua/config/formatting.lua new file mode 100644 index 0000000..19efff5 --- /dev/null +++ b/lua/config/formatting.lua @@ -0,0 +1,34 @@ +-- Formatting configuration + +-- Set rustfmt command +vim.g["rustfmt_command"] = "rustfmt +nightly" + +-- Commands to disable/enable formatting +require("conform").setup({ + format_on_save = function(bufnr) + -- Disable with a global or buffer-local variable + if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then + return + end + return { timeout_ms = 500, lsp_fallback = true } + end, +}) + +vim.api.nvim_create_user_command("FormatDisable", function(args) + if args.bang then + -- FormatDisable! will disable formatting just for this buffer + vim.b.disable_autoformat = true + else + vim.g.disable_autoformat = true + end +end, { + desc = "Disable autoformat-on-save", + bang = true, +}) + +vim.api.nvim_create_user_command("FormatEnable", function() + vim.b.disable_autoformat = false + vim.g.disable_autoformat = false +end, { + desc = "Re-enable autoformat-on-save", +}) \ No newline at end of file diff --git a/lua/config/keymaps.lua b/lua/config/keymaps.lua new file mode 100644 index 0000000..ddec785 --- /dev/null +++ b/lua/config/keymaps.lua @@ -0,0 +1,19 @@ +-- Keymaps configuration + +-- Clear search highlight +vim.keymap.set("n", "", "nohlsearch") + +-- Diagnostic keymaps +vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, { desc = "Go to previous [D]iagnostic message" }) +vim.keymap.set("n", "]d", vim.diagnostic.goto_next, { desc = "Go to next [D]iagnostic message" }) +vim.keymap.set("n", "e", vim.diagnostic.open_float, { desc = "Show diagnostic [E]rror messages" }) +vim.keymap.set("n", "q", vim.diagnostic.setloclist, { desc = "Open diagnostic [Q]uickfix list" }) + +-- Window navigation +vim.keymap.set("n", "", "", { desc = "Move focus to the left window" }) +vim.keymap.set("n", "", "", { desc = "Move focus to the right window" }) +vim.keymap.set("n", "", "", { desc = "Move focus to the lower window" }) +vim.keymap.set("n", "", "", { desc = "Move focus to the upper window" }) + +-- LSP keymaps +vim.keymap.set("n", "gd", vim.lsp.buf.definition, { desc = "Goto Definition" }) \ No newline at end of file diff --git a/lua/config/lazy.lua b/lua/config/lazy.lua new file mode 100644 index 0000000..0f79ac9 --- /dev/null +++ b/lua/config/lazy.lua @@ -0,0 +1,16 @@ +-- Lazy.nvim plugin manager setup + +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not vim.loop.fs_stat(lazypath) then + local lazyrepo = "https://github.com/folke/lazy.nvim.git" + vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) +end +vim.opt.rtp:prepend(lazypath) + +-- User dictionary for spell checking +local path = vim.fn.stdpath("config") .. "/spell/en.utf-8.add" +local words = {} + +for word in io.open(path, "r"):lines() do + table.insert(words, word) +end \ No newline at end of file diff --git a/lua/config/options.lua b/lua/config/options.lua new file mode 100644 index 0000000..4a749cb --- /dev/null +++ b/lua/config/options.lua @@ -0,0 +1,55 @@ +-- Options configuration +vim.g.mapleader = ";" +vim.g.maplocalleader = ";" + +-- Line numbers +vim.opt.number = true +vim.opt.relativenumber = true + +-- Mouse +vim.opt.mouse = "" + +-- Mode display +vim.opt.showmode = false + +-- Search +vim.opt.ignorecase = true +vim.opt.smartcase = true +vim.opt.hlsearch = true + +-- Sign column +vim.opt.signcolumn = "yes" + +-- Splits +vim.opt.splitright = true +vim.opt.splitbelow = true + +-- Whitespace display +vim.opt.list = true +vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" } + +-- Live substitution preview +vim.opt.inccommand = "split" + +-- Cursor line +vim.opt.cursorline = true + +-- General settings +vim.opt.shiftwidth = 4 +vim.opt.tabstop = 4 +vim.opt.expandtab = true +vim.opt.tw = 100 +vim.opt.colorcolumn = "+1" +vim.opt.termguicolors = true +vim.opt.pumheight = 5 + +-- Light mode check +local function file_exists(name) + local f = io.open(name, "r") + return f ~= nil and io.close(f) +end + +local home = os.getenv("HOME") +if file_exists(home .. "/.config/nvim/light_mode") then + vim.opt.background = "light" +end \ No newline at end of file diff --git a/lua/plugins/autopairs.lua b/lua/plugins/autopairs.lua new file mode 100644 index 0000000..afc64f8 --- /dev/null +++ b/lua/plugins/autopairs.lua @@ -0,0 +1,59 @@ +-- Autopairs plugin configuration + +return { + "windwp/nvim-autopairs", + config = function() + local remap = vim.api.nvim_set_keymap + local npairs = require("nvim-autopairs") + + npairs.setup({ map_bs = false, map_cr = false }) + + _G.MUtils = {} + + MUtils.CR = function() + if vim.fn.pumvisible() ~= 0 then + if vim.fn.complete_info({ "selected" }).selected ~= -1 then + return npairs.esc("") + else + return npairs.esc("") .. npairs.autopairs_cr() + end + else + return npairs.autopairs_cr() + end + end + remap("i", "", "v:lua.MUtils.CR()", { expr = true, noremap = true }) + + MUtils.BS = function() + if vim.fn.pumvisible() ~= 0 and vim.fn.complete_info({ "mode" }).mode == "eval" then + return npairs.esc("") .. npairs.autopairs_bs() + else + return npairs.autopairs_bs() + end + end + remap("i", "", "v:lua.MUtils.BS()", { expr = true, noremap = true }) + + -- put this to setup function and press to use fast_wrap + npairs.setup({ + fast_wrap = {}, + }) + + -- change default fast_wrap + npairs.setup({ + fast_wrap = { + map = "", + chars = { "{", "[", "(", '"', "'" }, + pattern = [=[[%'%"%>%]%)%}%,]]=], + end_key = "$", + before_key = "h", + after_key = "l", + cursor_pos_before = true, + keys = "qwertyuiopzxcvbnmasdfghjkl", + manual_position = true, + highlight = "Search", + highlight_grey = "Comment", + }, + }) + + npairs.remove_rule("`") + end, +} \ No newline at end of file diff --git a/lua/plugins/completion.lua b/lua/plugins/completion.lua new file mode 100644 index 0000000..475bc04 --- /dev/null +++ b/lua/plugins/completion.lua @@ -0,0 +1,136 @@ +-- Completion and snippets configuration + +return { + { + "hrsh7th/nvim-cmp", + event = "InsertEnter", + dependencies = { + { + "L3MON4D3/LuaSnip", + build = (function() + if vim.fn.has("win32") == 1 or vim.fn.executable("make") == 0 then + return + end + return "make install_jsregexp" + end)(), + config = function() + require("luasnip.loaders.from_snipmate").lazy_load({ paths = "./snippets" }) + local ls = require("luasnip") + local snip = ls.snippet + local node = ls.snippet_node + local text = ls.text_node + local insert = ls.insert_node + local func = ls.function_node + local choice = ls.choice_node + local dynamicn = ls.dynamic_node + + ls.add_snippets(nil, { + python = { + snip({ + trig = "imp", + namr = "Imports", + dscr = "Comments for imports", + }, { + text({ "# Core modules", "" }), + insert(1), + text({ "", "# Non-core modules", "" }), + insert(2), + text({ "", "# SEI modules", "" }), + insert(3), + }), + }, + tex = { + snip({ + trig = "input", + namr = "Input Cell", + dscr = "Cell for SEIInputTable", + }, { + text({ "\\hypertarget{" }), + insert(1), + text({ "}{" }), + insert(2), + text({ "} & \\SEICell{", "\t" }), + insert(3), + text({ "", "}\\\\", "" }), + }), + }, + markdown = { + snip({ + trig = "img", + namr = "image", + dscr = "Markdown img", + }, { + text({ "![" }), + insert(1), + text("]("), + insert(2), + text(")"), + }), + snip({ + trig = "header", + namr = "header", + dscr = "Yaml header for markdown notes", + }, { + text({ "---", "" }), + text("title: "), + insert(1), + text({ "", "author: Alex Selimov", "" }), + text("tags: ["), + insert(2), + text({ "]", "", "" }), + text({ "---", "" }), + }), + }, + }) + end, + }, + "saadparwaiz1/cmp_luasnip", + "hrsh7th/cmp-nvim-lsp", + "hrsh7th/cmp-path", + }, + config = function() + local cmp = require("cmp") + local luasnip = require("luasnip") + luasnip.config.setup({}) + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + completion = { completeopt = "menu,menuone,noinsert" }, + + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.select_next_item(), + [""] = cmp.mapping.select_prev_item(), + [""] = cmp.mapping.confirm({ select = true }), + + ["j"] = cmp.mapping(function() + if luasnip.expand_or_locally_jumpable() then + luasnip.expand_or_jump() + end + end, { "i", "s" }), + ["k"] = cmp.mapping(function() + if luasnip.locally_jumpable(-1) then + luasnip.jump(-1) + end + end, { "i", "s" }), + }), + sources = { + { name = "nvim_lsp" }, + { name = "luasnip" }, + { name = "path" }, + }, + }) + end, + snippet = { + expand = function(args) + local luasnip = require("luasnip") + if not luasnip then + return + end + luasnip.lsp_expand(args.body) + end, + }, + }, +} \ No newline at end of file diff --git a/lua/plugins/dev-tools.lua b/lua/plugins/dev-tools.lua new file mode 100644 index 0000000..e8b0bdb --- /dev/null +++ b/lua/plugins/dev-tools.lua @@ -0,0 +1,30 @@ +-- Development tools and utilities + +return { + { + "codersauce/runst.nvim", + lazy = false, + opts = {}, + config = function() + require("runst").setup() + end, + }, + + { + "Exafunction/windsurf.vim", + event = "BufEnter", + }, + + { + "danymat/neogen", + setup = { + snippet_engine = "luasnip", + }, + config = function() + require("neogen").setup({}) + local opts = { noremap = true, silent = true } + vim.api.nvim_set_keymap("n", "dg", ":lua require('neogen').generate()", opts) + end, + version = "*", + }, +} \ No newline at end of file diff --git a/lua/plugins/editor.lua b/lua/plugins/editor.lua new file mode 100644 index 0000000..0460c4e --- /dev/null +++ b/lua/plugins/editor.lua @@ -0,0 +1,22 @@ +-- Editor enhancement plugins + +return { + "christoomey/vim-tmux-navigator", + "kana/vim-textobj-user", + "mechatroner/rainbow_csv", + { + "GCBallesteros/vim-textobj-hydrogen", + dependencies = { + "kana/vim-textobj-user", + }, + }, + "godlygeek/tabular", + "tpope/vim-sleuth", + "norcalli/nvim-colorizer.lua", + "ixru/nvim-markdown", + "KeitaNakamura/tex-conceal.vim", + "skywind3000/asyncrun.vim", + "airblade/vim-gitgutter", + "tpope/vim-abolish", + "dhruvasagar/vim-table-mode", +} \ No newline at end of file diff --git a/lua/plugins/formatting.lua b/lua/plugins/formatting.lua new file mode 100644 index 0000000..2ac164d --- /dev/null +++ b/lua/plugins/formatting.lua @@ -0,0 +1,31 @@ +-- Formatting plugins + +return { + { + "stevearc/conform.nvim", + setup = { + formatters = { + black = { + command = "black", + prepend_args = { "--line-length", "100" }, + }, + }, + }, + opts = { + notify_on_error = false, + format_on_save = { + timeout_ms = 500, + lsp_fallback = false, + }, + formatters_by_ft = { + lua = { "stylua" }, + python = { "black" }, + cpp = { "clang-format" }, + c = { "clang-format" }, + sh = { "beautysh" }, + tex = { "latexindent" }, + java = { "java" }, + }, + }, + }, +} \ No newline at end of file diff --git a/lua/plugins/iron.lua b/lua/plugins/iron.lua new file mode 100644 index 0000000..7defb3d --- /dev/null +++ b/lua/plugins/iron.lua @@ -0,0 +1,51 @@ +-- Iron REPL plugin configuration + +return { + "Vigemus/iron.nvim", + ft = { "python" }, + config = function() + local iron = require("iron.core") + local view = require("iron.view") + + iron.setup({ + config = { + -- Whether a repl should be discarded or not + scratch_repl = true, + -- Your repl definitions come here + repl_definition = { + python = { + -- Can be a table or a function that + -- returns a table (see below) + command = "ipython --no-autoindent", + }, + }, + -- How the repl window will be displayed + -- See below for more information + repl_open_cmd = require("iron.view").bottom(20), + }, + -- Iron doesn't set keymaps by default anymore. + -- You can set them here or manually add keymaps to the functions in iron.core + keymaps = { + visual_send = "sc", + send_file = "sf", + send_line = "sl", + cr = "s", + interrupt = "s", + exit = "sq", + clear = "cl", + }, + -- If the highlight is on, you can change how it looks + -- For the available options, check nvim_set_hl + highlight = { + italic = true, + }, + ignore_blank_lines = false, -- ignore blank lines when sending visual select lines + }) + vim.keymap.set("n", ";rs", "IronRepl") + vim.keymap.set("n", ";rr", "IronRestart") + vim.keymap.set("n", ";rf", "IronFocus") + vim.keymap.set("n", ";rh", "IronHide") + + repl_open_cmd = "horizontal bot 20 split" + end, +} \ No newline at end of file diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua new file mode 100644 index 0000000..4cd33e8 --- /dev/null +++ b/lua/plugins/lsp.lua @@ -0,0 +1,193 @@ +-- LSP configuration + +return { + { + "mfussenegger/nvim-jdtls", + config = function() + local jdtls = require("jdtls") + + -- Find project root + local root_dir = require("jdtls.setup").find_root({ ".git", "mvnw", "gradlew" }) + + -- Path to your exported Eclipse/IntelliJ style xml + local style_path = vim.fn.expand("~/.config/nvim/GoogleStyle.xml") + + local config = { + cmd = { "jdtls" }, -- or path to your startup script + root_dir = root_dir, + settings = { + java = { + format = { + settings = { + url = "file://" .. style_path, + profile = "GoogleStyle", -- must match the profile inside the xml + }, + }, + }, + }, + } + + jdtls.start_or_attach(config) + end, + }, + + { + "neovim/nvim-lspconfig", + opts = { + autoformat = false, + }, + dependencies = { + "williamboman/mason.nvim", + "williamboman/mason-lspconfig.nvim", + "WhoIsSethDaniel/mason-tool-installer.nvim", + { "j-hui/fidget.nvim", opts = {} }, + }, + config = function() + vim.api.nvim_create_autocmd("LspAttach", { + group = vim.api.nvim_create_augroup("kickstart-lsp-attach", { clear = true }), + callback = function(event) + local map = function(keys, func, desc) + vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) + end + + map("gr", require("telescope.builtin").lsp_references, "[G]oto [R]eferences") + map("I", require("telescope.builtin").lsp_implementations, "[G]oto [I]mplementation") + map("D", require("telescope.builtin").lsp_type_definitions, "Type [D]efinition") + map("ds", require("telescope.builtin").lsp_document_symbols, "[D]ocument [S]ymbols") + map( + "ws", + require("telescope.builtin").lsp_dynamic_workspace_symbols, + "[W]orkspace [S]ymbols" + ) + map("rn", vim.lsp.buf.rename, "[R]e[n]ame") + map("ca", vim.lsp.buf.code_action, "[C]ode [A]ction") + map("K", vim.lsp.buf.hover, "Hover Documentation") + map("gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration") + + local client = vim.lsp.get_client_by_id(event.data.client_id) + if client and client.server_capabilities.documentHighlightProvider then + vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { + buffer = event.buf, + callback = vim.lsp.buf.document_highlight, + }) + + vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, { + buffer = event.buf, + callback = vim.lsp.buf.clear_references, + }) + end + end, + }) + + local capabilities = vim.lsp.protocol.make_client_capabilities() + capabilities = vim.tbl_deep_extend("force", capabilities, require("cmp_nvim_lsp").default_capabilities()) + + local servers = { + clangd = { + filetypes = { + "c", + "cpp", + }, + }, + taplo = {}, + yamlls = { settings = { yaml = { format = { enable = false } } } }, + pyright = {}, + fortls = {}, + jsonls = {}, + bashls = { dependencies = "shellcheck" }, + kotlin_language_server = {}, + ts_ls = {}, + rust_analyzer = { + settings = { + ["rust-analyzer"] = { + check = { + command = "clippy", + }, + rustfmt = { + extraArgs = { "+nightly" }, + }, + }, + }, + }, + arduino_language_server = {}, + ltex = { + settings = { + ltex = { + enabled = { "latex", "tex", "bib", "markdown" }, + language = "auto", + diagnosticSeverity = "information", + sentenceCacheSize = 2000, + latex = { + commands = { + ["\\hypertarget"] = "dummy", + }, + }, + dictionary = (function() + local files = {} + for _, file in ipairs(vim.api.nvim_get_runtime_file("dict/*", true)) do + local lang = vim.fn.fnamemodify(file, ":t:r") + local fullpath = vim.fs.normalize(file, ":p") + files[lang] = { ":" .. fullpath } + end + + if files.default then + for lang, _ in pairs(files) do + if lang ~= "default" then + vim.list_extend(files[lang], files.default) + end + end + files.default = nil + end + return files + end)(), + }, + }, + }, + lua_ls = { + settings = { + Lua = { + runtime = { version = "LuaJIT" }, + workspace = { + checkThirdParty = false, + library = { + "${3rd}/luv/library", + unpack(vim.api.nvim_get_runtime_file("", true)), + }, + }, + }, + }, + }, + } + + require("mason").setup() + + local ensure_installed = vim.tbl_keys(servers or {}) + vim.list_extend(ensure_installed, { + "stylua", + "black", + "clang-format", + "beautysh", + "latexindent", + "prettier", + }) + require("mason-tool-installer").setup({ ensure_installed = ensure_installed }) + + require("mason-lspconfig").setup({ + handlers = { + function(server_name) + local server = servers[server_name] or {} + require("lspconfig")[server_name].setup({ + cmd = server.cmd, + on_init = function(client) + client.offset_encoding = "utf-8" + end, + settings = server.settings, + filetypes = server.filetypes, + capabilities = vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {}), + }) + end, + }, + }) + end, + }, +} \ No newline at end of file diff --git a/lua/plugins/telescope.lua b/lua/plugins/telescope.lua new file mode 100644 index 0000000..bf75963 --- /dev/null +++ b/lua/plugins/telescope.lua @@ -0,0 +1,29 @@ +-- Telescope fuzzy finder configuration + +return { + "nvim-telescope/telescope.nvim", + event = "VeryLazy", + branch = "0.1.x", + dependencies = { + "nvim-lua/plenary.nvim", + { "nvim-tree/nvim-web-devicons" }, + { "nvim-telescope/telescope-live-grep-args.nvim" }, + }, + config = function() + require("telescope").setup({ + defaults = vim.tbl_extend("force", require("telescope.themes").get_ivy(), { + path_display = { "smart" }, + }), + }) + + pcall(require("telescope").load_extension("live_grep_args")) + local builtin = require("telescope.builtin") + vim.keymap.set("n", "", require("telescope").extensions.live_grep_args.live_grep_args) + vim.keymap.set( + "x", + "", + "\"zy:lua require('telescope').extensions.live_grep_args.live_grep_args(require('telescope.themes').get_ivy({}))z" + ) + vim.keymap.set("n", "", builtin.find_files) + end, +} \ No newline at end of file diff --git a/lua/plugins/ui.lua b/lua/plugins/ui.lua new file mode 100644 index 0000000..45c3bcc --- /dev/null +++ b/lua/plugins/ui.lua @@ -0,0 +1,135 @@ +-- UI and appearance plugins + +return { + { + "zenbones-theme/zenbones.nvim", + lazy = false, + priority = 1000, + dependencies = "rktjmp/lush.nvim", + config = function() + vim.cmd.colorscheme("zenwritten") + end, + }, + + { "folke/todo-comments.nvim", dependencies = { "nvim-lua/plenary.nvim" }, opts = { signs = false } }, + + { + "echasnovski/mini.nvim", + keys = { + { + "m", + function() + require("mini.files").open(vim.api.nvim_buf_get_name(0), true) + end, + desc = "Open mini.files (Directory of Current File)", + }, + }, + config = function() + require("mini.files").setup() + local getWords = require("utils.statusline").getWords + require("mini.statusline").setup({ + content = { + active = function() + local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 }) + local git = MiniStatusline.section_git({ trunc_width = 75 }) + local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 }) + local filename = MiniStatusline.section_filename({ trunc_width = 140 }) + local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 }) + local location = MiniStatusline.section_location({ trunc_width = 75 }) + local search = MiniStatusline.section_searchcount({ trunc_width = 75 }) + local words = getWords() + return MiniStatusline.combine_groups({ + + { hl = mode_hl, strings = { mode } }, + { hl = "MiniStatuslineDevinfo", strings = { git, diagnostics } }, + "%<", + { hl = "MiniStatuslineFilename", strings = { filename } }, + "%=", + { hl = "MiniStatuslineFileinfo", strings = { fileinfo } }, + { hl = "MiniStatuslineFileinfo", strings = { words } }, + { hl = mode_hl, strings = { search, location } }, + }) + end, + }, + }) + end, + }, + + { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function() + require("nvim-treesitter.configs").setup({ + ensure_installed = { "bash", "c", "html", "lua", "markdown", "vim", "vimdoc" }, + auto_install = true, + highlight = { enable = false }, + indent = { enable = false }, + }) + end, + }, + + { + "folke/snacks.nvim", + priority = 1000, + lazy = false, + opts = { + dashboard = { enabled = true }, + input = { enabled = true }, + terminal = { enabled = true }, + notify = { enabled = true }, + }, + keys = { + { + "T", + function() + Snacks.terminal() + end, + desc = "Toggle Terminal", + }, + }, + }, + + { + "MeanderingProgrammer/render-markdown.nvim", + dependencies = { "nvim-treesitter/nvim-treesitter", "echasnovski/mini.nvim" }, + opts = {}, + }, + + { + "folke/trouble.nvim", + opts = {}, + cmd = "Trouble", + keys = { + { + "xx", + "Trouble diagnostics toggle", + desc = "Diagnostics (Trouble)", + }, + { + "xX", + "Trouble diagnostics toggle filter.buf=0", + desc = "Buffer Diagnostics (Trouble)", + }, + { + "cs", + "Trouble symbols toggle focus=false", + desc = "Symbols (Trouble)", + }, + { + "cl", + "Trouble lsp toggle focus=false win.position=right", + desc = "LSP Definitions / references / ... (Trouble)", + }, + { + "xL", + "Trouble loclist toggle", + desc = "Location List (Trouble)", + }, + { + "xQ", + "Trouble qflist toggle", + desc = "Quickfix List (Trouble)", + }, + }, + }, +} \ No newline at end of file diff --git a/lua/utils/statusline.lua b/lua/utils/statusline.lua new file mode 100644 index 0000000..1c29c1a --- /dev/null +++ b/lua/utils/statusline.lua @@ -0,0 +1,15 @@ +-- Statusline utilities + +local M = {} + +-- Function to get word count in status line +function M.getWords() + -- the third string here is the string for visual-block mode (^V) + if vim.fn.mode() == "v" or vim.fn.mode() == "V" or vim.fn.mode() == "" then + return vim.fn.wordcount().visual_words .. "" + else + return vim.fn.wordcount().words .. "" + end +end + +return M \ No newline at end of file