local M = {} M.spec = { { 'tpope/vim-sleuth', -- Automatically set the 'shiftwidth' and 'expandtab' options based on the current file }, { 'lewis6991/gitsigns.nvim', -- Git signs in the sign column opts = { signs = { add = { text = '+' }, change = { text = '' }, delete = { text = '_' }, topdelete = { text = '‾' }, changedelete = { text = '' }, }, }, }, { 'folke/which-key.nvim', event = 'VimEnter', opts = { delay = 0, icons = { mappings = vim.g.have_nerd_font, keys = vim.g.have_nerd_font and {} or { Up = ' ', Down = ' ', Left = ' ', Right = ' ', C = ' ', M = ' ', D = ' ', S = ' ', CR = ' ', Esc = ' ', ScrollWheelDown = ' ', ScrollWheelUp = ' ', NL = ' ', BS = ' ', Space = ' ', Tab = ' ', F1 = '', F2 = '', F3 = '', F4 = '', F5 = '', F6 = '', F7 = '', F8 = '', F9 = '', F10 = '', F11 = '', F12 = '', }, }, spec = { { 'c', group = '[C]ode', mode = { 'n', 'x' } }, { 'd', group = '[D]ocument' }, { 'r', group = '[R]ename' }, { 's', group = '[S]earch' }, { 'w', group = '[W]orkspace' }, { 't', group = '[T]oggle' }, { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, }, }, }, { 'nvim-tree/nvim-tree.lua', event = 'VimEnter', config = function() require('nvim-tree').setup { renderer = { icons = { glyphs = { default = '', symlink = '', folder = { arrow_open = '', arrow_closed = '', default = '', open = '', empty = '', empty_open = '', symlink = '', }, git = { unstaged = '✗', staged = '✓', unmerged = '', renamed = '➜', untracked = '★', deleted = '', ignored = '◌', }, }, }, }, } vim.keymap.set('n', 'e', 'NvimTreeToggle', { desc = 'Toggle File Explorer' }) end, }, { 'wakatime/vim-wakatime', }, { 'github/copilot.vim', }, { 'jose-elias-alvarez/null-ls.nvim', dependencies = { 'nvim-lua/plenary.nvim' }, config = function() local null_ls = require 'null-ls' null_ls.setup { sources = { null_ls.builtins.formatting.prettier, null_ls.builtins.diagnostics.eslint, null_ls.builtins.code_actions.eslint, }, } end, }, { 'nvim-lualine/lualine.nvim', dependencies = { 'nvim-tree/nvim-web-devicons' }, config = function() require('lualine').setup { options = { theme = 'auto', section_separators = '', component_separators = '', }, sections = { lualine_a = { 'mode' }, lualine_b = { 'branch', 'diff', 'diagnostics' }, lualine_c = { 'filename' }, lualine_x = { 'encoding', 'fileformat', 'filetype' }, lualine_y = { 'progress' }, lualine_z = { 'location' }, }, } end, }, { 'lewis6991/gitsigns.nvim', config = function() require('gitsigns').setup { current_line_blame = true, } end, }, { 'mfussenegger/nvim-dap', config = function() local dap = require 'dap' dap.adapters.node2 = { type = 'executable', command = 'node', args = { os.getenv 'HOME' .. '/.local/share/nvim/mason/packages/node-debug2-adapter/out/src/nodeDebug.js' }, } dap.configurations.javascript = { { name = 'Launch Node.js', type = 'node2', request = 'launch', program = '${file}', cwd = vim.fn.getcwd(), sourceMaps = true, protocol = 'inspector', console = 'integratedTerminal', }, } end, }, { 'numToStr/Comment.nvim', config = function() require('Comment').setup() end, }, { 'windwp/nvim-autopairs', event = 'InsertEnter', config = function() require('nvim-autopairs').setup {} end, }, { 'nvim-pack/nvim-spectre', dependencies = { 'nvim-lua/plenary.nvim' }, config = function() require('spectre').setup() end, }, { 'nvim-telescope/telescope.nvim', event = 'VimEnter', branch = '0.1.x', dependencies = { 'nvim-lua/plenary.nvim', { -- If encountering errors, see telescope-fzf-native README for installation instructions 'nvim-telescope/telescope-fzf-native.nvim', build = 'make', cond = function() return vim.fn.executable 'make' == 1 end, }, { 'nvim-telescope/telescope-ui-select.nvim' }, { 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font }, }, config = function() require('telescope').setup { extensions = { ['ui-select'] = { require('telescope.themes').get_dropdown(), }, }, } pcall(require('telescope').load_extension, 'fzf') pcall(require('telescope').load_extension, 'ui-select') local builtin = require 'telescope.builtin' vim.keymap.set('n', 'sh', builtin.help_tags, { desc = '[S]earch [H]elp' }) vim.keymap.set('n', 'sk', builtin.keymaps, { desc = '[S]earch [K]eymaps' }) vim.keymap.set('n', 'sf', builtin.find_files, { desc = '[S]earch [F]iles' }) vim.keymap.set('n', 'ss', builtin.builtin, { desc = '[S]earch [S]elect Telescope' }) vim.keymap.set('n', 'sw', builtin.grep_string, { desc = '[S]earch current [W]ord' }) vim.keymap.set('n', 'sg', builtin.live_grep, { desc = '[S]earch by [G]rep' }) vim.keymap.set('n', 'sd', builtin.diagnostics, { desc = '[S]earch [D]iagnostics' }) vim.keymap.set('n', 'sr', builtin.resume, { desc = '[S]earch [R]esume' }) vim.keymap.set('n', 's.', builtin.oldfiles, { desc = '[S]earch Recent Files ("." for repeat)' }) vim.keymap.set('n', '', builtin.buffers, { desc = '[ ] Find existing buffers' }) vim.keymap.set('n', '/', function() builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { winblend = 10, previewer = false, }) end, { desc = '[/] Fuzzily search in current buffer' }) vim.keymap.set('n', 's/', function() builtin.live_grep { grep_open_files = true, prompt_title = 'Live Grep in Open Files', } end, { desc = '[S]earch [/] in Open Files' }) vim.keymap.set('n', 'sn', function() builtin.find_files { cwd = vim.fn.stdpath 'config' } end, { desc = '[S]earch [N]eovim files' }) end, }, { 'folke/lazydev.nvim', ft = 'lua', opts = { library = { { path = '${3rd}/luv/library', words = { 'vim%.uv' } }, }, }, }, { 'neovim/nvim-lspconfig', dependencies = { { 'williamboman/mason.nvim', opts = {} }, 'williamboman/mason-lspconfig.nvim', 'WhoIsSethDaniel/mason-tool-installer.nvim', { 'j-hui/fidget.nvim', opts = {} }, 'hrsh7th/cmp-nvim-lsp', }, config = function() require('mason').setup() require('mason-lspconfig').setup { ensure_installed = { 'ts_ls', 'tailwindcss', 'cssls', 'lua_ls' }, automatic_installation = true, } local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities()) if vim.g.have_nerd_font then local signs = { ERROR = '', WARN = '', INFO = '', HINT = '' } for type, icon in pairs(signs) do local hl = 'DiagnosticSign' .. type vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = '' }) end end local servers = { ts_ls = { on_attach = function(client, bufnr) client.server_capabilities.documentFormattingProvider = false local map = function(keys, func, desc) vim.keymap.set('n', keys, func, { buffer = bufnr, desc = 'LSP: ' .. desc }) end vim.api.nvim_create_autocmd('BufWritePre', { buffer = bufnr, callback = function() vim.lsp.buf.code_action { context = { only = { 'source.fixAll' }, diagnostics = {} }, apply = true, } end, }) map('oi', function() vim.lsp.buf.code_action { context = { only = { 'source.organizeImports' }, diagnostics = {} }, apply = true, } end, '[O]rganize [I]mports') end, capabilities = capabilities, settings = { typescript = { inlayHints = { includeInlayParameterNameHints = 'all' }, preferences = { importModuleSpecifierPreference = 'non-relative', quotePreference = 'single' }, }, javascript = { preferences = { importModuleSpecifierPreference = 'non-relative', quotePreference = 'single' }, }, }, }, tailwindcss = { filetypes = { 'html', 'css', 'scss', 'javascript', 'typescript', 'javascriptreact', 'typescriptreact' }, }, cssls = {}, lua_ls = { settings = { Lua = { completion = { callSnippet = 'Replace' }, }, }, }, } local ensure_installed = vim.tbl_keys(servers) require('mason-tool-installer').setup { ensure_installed = ensure_installed } require('mason-lspconfig').setup { ensure_installed = ensure_installed, automatic_installation = true, handlers = { function(server_name) local server = servers[server_name] or {} server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {}) require('lspconfig')[server_name].setup(server) end, }, } end, }, { 'stevearc/conform.nvim', event = { 'BufWritePre' }, cmd = { 'ConformInfo' }, keys = { { 'f', function() require('conform').format { async = true, lsp_format = 'fallback' } end, mode = '', desc = '[F]ormat buffer', }, }, opts = { notify_on_error = false, format_on_save = function(bufnr) local disable_filetypes = { c = true, cpp = true } return { timeout_ms = 500, lsp_format = disable_filetypes[vim.bo[bufnr].filetype] and 'never' or 'fallback', } end, formatters_by_ft = { lua = { 'stylua' }, javascript = { 'prettier' }, typescript = { 'prettier' }, css = { 'prettier' }, scss = { 'prettier' }, html = { 'prettier' }, }, }, }, { '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)(), }, 'saadparwaiz1/cmp_luasnip', 'hrsh7th/cmp-nvim-lsp', 'hrsh7th/cmp-path', 'hrsh7th/cmp-buffer', 'hrsh7th/cmp-cmdline', }, 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.scroll_docs(-4), [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.confirm { select = true }, [''] = cmp.mapping.complete {}, [''] = cmp.mapping(function() if luasnip.expand_or_locally_jumpable() then luasnip.expand_or_jump() end end, { 'i', 's' }), [''] = cmp.mapping(function() if luasnip.locally_jumpable(-1) then luasnip.jump(-1) end end, { 'i', 's' }), }, sources = cmp.config.sources { { name = 'lazydev', group_index = 0 }, { name = 'nvim_lsp' }, { name = 'luasnip' }, { name = 'path' }, { name = 'buffer' }, }, } end, }, { -- 🎨 Colorscheme 'folke/tokyonight.nvim', priority = 1000, init = function() -- Load colorscheme with specific style vim.cmd.colorscheme 'tokyonight-night' -- Customize Comment color (removes italic) vim.cmd.hi 'Comment gui=none' end, }, -- ✅ Highlight TODOs, FIXMEs, and other comments { 'folke/todo-comments.nvim', event = 'VimEnter', dependencies = { 'nvim-lua/plenary.nvim' }, opts = { signs = false }, }, { -- 🛠️ mini.nvim utility plugins (better text objects, surround, and statusline) 'echasnovski/mini.nvim', config = function() -- Setup mini.ai for better text objects require('mini.ai').setup { n_lines = 500, } -- Setup mini.surround for surrounding text objects require('mini.surround').setup() -- Setup mini.statusline for a better status line require('mini.statusline').setup { use_icons = vim.g.have_nerd_font, content = { active = function() return '%2l:%-2v' -- line:column display end, }, } end, }, { 'nvim-treesitter/nvim-treesitter', build = ':tsupdate', main = 'nvim-treesitter.configs', opts = { ensure_installed = { 'bash', 'c', 'diff', 'html', 'css', 'javascript', 'typescript', 'tsx', 'json', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc', }, auto_install = true, highlight = { enable = true, additional_vim_regex_highlighting = { 'ruby' }, }, indent = { enable = true, disable = { 'ruby' } }, }, }, ui = { icons = vim.g.have_nerd_font and {} or { cmd = '⌘', config = '🛠', event = '📅', ft = '📂', init = '⚙', keys = '🗝', plugin = '🔌', runtime = '💻', require = '🌙', source = '📄', start = '🚀', task = '📌', lazy = '💤 ', }, }, } return M