vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' -- for chatgpt _G.chatgpt_model = 'gpt-5.1-codex-mini' -- ========================= -- Lazy.nvim bootstrap -- ========================= local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' if not vim.loop.fs_stat(lazypath) then vim.fn.system { 'git', 'clone', '--filter=blob:none', 'https://github.com/folke/lazy.nvim.git', '--branch=stable', lazypath, } end vim.opt.rtp:prepend(lazypath) -- ========================= -- PLUGINS -- ========================= require('lazy').setup({ 'voldikss/vim-floaterm', 'tpope/vim-fugitive', 'rhysd/conflict-marker.vim', { 'kylechui/nvim-surround', config = true }, { 'numToStr/Comment.nvim', opts = {} }, { 'nvim-treesitter/nvim-treesitter', lazy = false, build = ':TSUpdate', opts = { ensure_installed = { 'lua', 'rust', 'python', 'javascript', 'typescript', 'c', 'c_sharp', 'cpp', 'bash', 'json', 'yaml', 'toml', }, highlight = { enable = true }, }, }, { 'MeanderingProgrammer/treesitter-modules.nvim', dependencies = { 'nvim-treesitter/nvim-treesitter' }, opts = { incremental_selection = { enable = true, keymaps = { init_selection = '', -- Alt+Space node_incremental = '', -- Alt+Space (expand) node_decremental = '', -- Alt+Backspace (shrink) scope_incremental = '', -- Alt+Shift+Space (scope expand) }, }, }, }, 'rhysd/git-messenger.vim', { 'jackMort/ChatGPT.nvim', event = 'VeryLazy', dependencies = { 'nvim-lua/plenary.nvim', 'MunifTanjim/nui.nvim', 'nvim-telescope/telescope.nvim', }, config = function() require('chatgpt').setup { openai_params = { -- NOTE: model can be a function returning the model name -- this is useful if you want to change the model on the fly -- using commands -- Example: -- model = function() -- if some_condition() then -- return "gpt-5" -- else -- return "gpt-5-mini" -- end -- end, model = function() return _G.chatgpt_model end, frequency_penalty = 0, presence_penalty = 0, max_tokens = 4095, temperature = 0.2, top_p = 0.1, n = 1, api_key_cmd = 'echo $OPENAI_API_KEY', }, } end, }, { 'lewis6991/gitsigns.nvim', event = 'VeryLazy', config = function() require('gitsigns').setup { signs = { add = { text = '│' }, change = { text = '│' }, delete = { text = '_' }, topdelete = { text = '‾' }, changedelete = { text = '~' }, untracked = { text = '┆' }, }, } end, }, { 'sindrets/diffview.nvim', event = 'VeryLazy', dependencies = { 'nvim-lua/plenary.nvim' }, }, { 'ErickKramer/git-coauthors.nvim', dependencies = { 'nvim-telescope/telescope.nvim' }, }, -- Editor UX { 'windwp/nvim-autopairs', config = function() require('nvim-autopairs').setup {} end, }, { 'iamcco/markdown-preview.nvim', build = function() vim.fn['mkdp#util#install']() end, config = function() vim.g.mkdp_refresh_slow = 1 vim.g.mkdp_page_title = '「${name}」' vim.g.mkdp_theme = 'dark' vim.g.mkdp_auto_close = 0 vim.g.mkdp_combine_preview = 1 vim.g.mkdp_combine_preview_auto_refresh = 1 end, }, -- Colors { 'folke/tokyonight.nvim', lazy = false, priority = 1000, config = function() vim.cmd.colorscheme 'gruvbox-material' vim.cmd.hi 'Comment gui=none' end, }, { 'sphamba/smear-cursor.nvim', opts = { time_interval = 3, cursor_color = '#ff4000', particles_enabled = true, stiffness = 0.5, trailing_stiffness = 0.2, trailing_exponent = 5, damping = 0.6, gradient_exponent = 0, gamma = 1, never_draw_over_target = true, hide_target_hack = true, particle_spread = 1, particles_per_second = 500, particles_per_length = 50, particle_max_lifetime = 800, particle_max_initial_velocity = 20, particle_velocity_from_cursor = 0.5, particle_damping = 0.15, particle_gravity = -50, min_distance_emit_particles = 0, }, }, { 'f4z3r/gruvbox-material.nvim', name = 'gruvbox-material', lazy = false, priority = 1000, opts = {}, }, 'mg979/vim-visual-multi', -- File tree { 'nvim-neo-tree/neo-tree.nvim', branch = 'v3.x', dependencies = { 'nvim-lua/plenary.nvim', 'nvim-tree/nvim-web-devicons', 'MunifTanjim/nui.nvim', }, opts = { filesystem = { filtered_items = { visible = false, hide_dotfiles = true, hide_gitignored = true, hide_by_name = { '.git', '.elc' }, hide_by_pattern = { '*.uid', 'node_modules' }, }, }, }, }, -- Buffers/tabs { 'akinsho/bufferline.nvim', version = '*', dependencies = 'nvim-tree/nvim-web-devicons', opts = { options = { mode = 'buffers', -- tabs or buffers diagnostics = 'nvim_lsp', show_buffer_close_icons = false, show_close_icon = false, hover = { enabled = true, delay = 200, reveal = {'close'} }, }, }, }, 'nvim-tree/nvim-web-devicons', { 'folke/todo-comments.nvim', dependencies = 'nvim-lua/plenary.nvim', opts = {}, }, -- Flash (treesitter features removed) { 'folke/flash.nvim', event = 'VeryLazy', opts = {}, keys = { { 'r', mode = 'o', function() require('flash').remote() end, desc = 'Remote Flash', }, -- NOTE: removed: flash.treesitter(), flash.treesitter_search() }, }, { 'folke/trouble.nvim', dependencies = 'nvim-tree/nvim-web-devicons', config = function() require('trouble').setup { signs = { error = '', warning = '', hint = '', information = '', other = '﫠', }, } end, }, { 'folke/which-key.nvim', opts = {} }, { 'nvim-lualine/lualine.nvim', config = function() local function get_venv() local venv = vim.env.VIRTUAL_ENV if venv then local env = string.match(venv, '[^/]+$') return ' ' .. env end return '' end require('lualine').setup { options = { icons_enabled = true, theme = 'gruvbox-material', component_separators = '|', section_separators = '', ignore_focus = { 'dapui_watches', 'dapui_breakpoints', 'dapui_scopes', 'dapui_console', 'dapui_stacks', 'dap-repl', }, disabled_filetypes = { 'NvimTree' }, }, sections = { lualine_a = { 'mode' }, lualine_b = { 'branch', 'diff', 'diagnostics' }, lualine_c = { 'filename' }, lualine_x = { { get_venv }, 'fileformat', 'filetype' }, lualine_y = { 'progress' }, lualine_z = { 'location' }, }, } end, }, { 'nvim-pack/nvim-spectre', opts = {} }, { 'romgrk/barbar.nvim', event = 'BufEnter', dependencies = 'nvim-tree/nvim-web-devicons', init = function() vim.g.barbar_auto_setup = false end, lazy = true, }, { 'lukas-reineke/indent-blankline.nvim', main = 'ibl', opts = {}, config = function() local highlight = { 'RainbowRed', 'RainbowYellow', 'RainbowBlue', 'RainbowOrange', 'RainbowGreen', 'RainbowViolet', 'RainbowCyan', } local hooks = require 'ibl.hooks' hooks.register(hooks.type.HIGHLIGHT_SETUP, function() vim.api.nvim_set_hl(0, 'RainbowRed', { fg = '#E06C75' }) vim.api.nvim_set_hl(0, 'RainbowYellow', { fg = '#E5C07B' }) vim.api.nvim_set_hl(0, 'RainbowBlue', { fg = '#61AFEF' }) vim.api.nvim_set_hl(0, 'RainbowOrange', { fg = '#D19A66' }) vim.api.nvim_set_hl(0, 'RainbowGreen', { fg = '#98C379' }) vim.api.nvim_set_hl(0, 'RainbowViolet', { fg = '#C678DD' }) vim.api.nvim_set_hl(0, 'RainbowCyan', { fg = '#56B6C2' }) end) require('ibl').setup { indent = { highlight = highlight, char = '┊' }, scope = { enabled = false }, } end, }, -- Tooling installer { 'WhoIsSethDaniel/mason-tool-installer.nvim', dependencies = { 'williamboman/mason.nvim' }, opts = { ensure_installed = { 'clangd', 'clang-format', 'codelldb', 'cpplint', 'cpptools', 'csharpier', 'csharp-language-server', 'lua-language-server', 'netcoredbg', 'omnisharp', 'omnisharp-mono', 'prettier', 'ruff', 'rust-analyzer', 'sonarlint-language-server', 'stylua', }, auto_update = false, run_on_start = true, }, }, -- Terminal UX { 'akinsho/toggleterm.nvim', opts = {}, config = function() require('toggleterm').setup { size = 20, open_mapping = [[]], hide_numbers = true, shade_filetypes = {}, shade_terminals = true, shading_factor = 2, start_in_insert = true, insert_mappings = true, persist_size = true, direction = 'float', close_on_exit = true, shell = vim.o.shell, float_opts = { border = 'curved', winblend = 0, highlights = { border = 'Normal', background = 'Normal' }, }, } function _G.set_terminal_keymaps() local opts = { noremap = true } vim.diagnostic.disable(0) vim.api.nvim_buf_set_keymap(0, 't', '', [[]], opts) end vim.cmd 'autocmd! TermOpen term://* lua set_terminal_keymaps()' local Terminal = require('toggleterm.terminal').Terminal local lazygit = Terminal:new { cmd = 'lazygit', hidden = true, direction = 'float', float_opts = { width = vim.o.columns, height = vim.o.lines }, on_open = function(term) vim.cmd 'startinsert!' vim.diagnostic.disable(0) vim.api.nvim_buf_set_keymap(0, 't', '', 'close', { silent = false, noremap = true }) if vim.fn.mapcheck('', 't') ~= '' then vim.api.nvim_buf_del_keymap(term.bufnr, 't', '') end end, } function _G._lazygit_toggle() lazygit.dir = vim.fn.expand '%:p:h' lazygit:toggle() end local python = Terminal:new { cmd = 'ipython3', direction = 'horizontal', hidden = true, hidden_numbers = true, } function _G._python_toggle() python:toggle() end vim.api.nvim_set_keymap('n', 'lg', 'lua _lazygit_toggle()', { noremap = true, silent = true }) vim.api.nvim_set_keymap('n', 'ip', 'lua _python_toggle()', { noremap = true, silent = true }) end, }, { 'stevearc/dressing.nvim', opts = {}, config = function() require('dressing').setup { input = { get_config = function() return { title_pos = 'center', win_options = { sidescrolloff = 10 }, insert_only = false, } end, }, } end, }, { 'navarasu/onedark.nvim', priority = 1000 }, -- Telescope { 'nvim-telescope/telescope.nvim', branch = 'master', dependencies = { 'nvim-lua/plenary.nvim', { 'nvim-telescope/telescope-fzf-native.nvim', build = 'make', cond = function() return vim.fn.executable 'make' == 1 end, }, 'benfowler/telescope-luasnip.nvim', 'nvim-telescope/telescope-live-grep-args.nvim', }, config = function() local actions = require 'telescope.actions' require('telescope').setup { defaults = { prompt_prefix = '🔍 ', vimgrep_arguments = { 'rg', '--color=never', '--no-heading', '--with-filename', '--line-number', '--column', '--smart-case', '--follow', }, path_display = { truncate = 3 }, mappings = { i = { [''] = actions.select_tab, [''] = actions.select_vertical, [''] = actions.select_horizontal, }, n = { [''] = actions.select_tab, [''] = actions.select_vertical, [''] = actions.select_horizontal, }, }, }, pickers = { find_files = { find_command = { 'rg', '--files', '--hidden', '-g', '!.git' }, follow = true, }, lsp_document_symbols = { show_line = true }, }, extensions = { fzf = { fuzzy = true, override_generic_sorter = true, override_file_sorter = true, case_mode = 'smart_case', }, }, } require('telescope').load_extension 'fzf' require('telescope').load_extension 'luasnip' require('telescope').load_extension 'live_grep_args' end, }, -- LSP { 'neovim/nvim-lspconfig', dependencies = { { 'williamboman/mason.nvim', config = true }, 'williamboman/mason-lspconfig.nvim', 'WhoIsSethDaniel/mason-tool-installer.nvim', { 'j-hui/fidget.nvim', config = function() require('fidget').setup { notification = { window = { normal_hl = 'Comment', border = 'rounded', zindex = 45, max_width = 0, max_height = 0, x_padding = 1, y_padding = 0, align = 'bottom', relative = 'editor', }, }, } end, }, }, config = function() vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('personal-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('gd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition') map('gr', require('telescope.builtin').lsp_references, '[G]oto [R]eferences') map('gI', 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('', vim.lsp.buf.signature_help, 'Signature Documentation') 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 servers = { clangd = { cmd = { 'clangd', '--background-index', '--clang-tidy', '--completion-style=bundled', '--cross-file-rename', '--header-insertion=iwyu', }, }, pyright = { python = { analysis = { autoSearchPaths = true, diagnosticMode = 'openFilesOnly', useLibraryCodeForTypes = true, reportDuplicateImport = true, }, }, }, lua_ls = { settings = { Lua = { runtime = { version = 'LuaJIT' }, workspace = { checkThirdParty = false, library = { '${3rd}/luv/library', unpack(vim.api.nvim_get_runtime_file('', true)) }, }, completion = { callSnippet = 'Replace' }, diagnostics = { globals = { 'vim', 'require' } }, }, }, }, rust_analyzer = {}, } local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities = require('cmp_nvim_lsp').default_capabilities(capabilities) capabilities.offsetEncoding = { 'utf-16' } capabilities.textDocument.completion.completionItem.snippetSupport = true capabilities.textDocument.foldingRange = { dynamicRegistration = false, lineFoldingOnly = true } require('mason').setup { ui = { border = 'rounded' } } 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 {}) require('lspconfig')[server_name].setup(server) end, }, } require('mason-tool-installer').setup { ensure_installed = { 'clangd', 'lua_ls', 'pyright', 'ruff', 'codelldb', 'cpptools', 'cpplint', 'stylua', 'prettier', }, auto_update = false, run_on_start = true, start_delay = 3000, debounce_hours = 5, } local cmp = require 'cmp' local luasnip = require 'luasnip' cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, 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.complete(), [''] = cmp.mapping.abort(), [''] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = true }, [''] = 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' }), [''] = 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 = { { name = 'nvim_lsp', max_item_count = 10 }, { name = 'luasnip' }, { name = 'path', max_item_count = 5 }, { name = 'buffer', max_item_count = 5 }, }, window = { completion = cmp.config.window.bordered(), documentation = cmp.config.window.bordered(), }, } function _G.leave_snippet() if ((vim.v.event.old_mode == 's' and vim.v.event.new_mode == 'n') or vim.v.event.old_mode == 'i') and require('luasnip').session.current_nodes[vim.api.nvim_get_current_buf()] and not require('luasnip').session.jump_active then require('luasnip').unlink_current() end end vim.api.nvim_command [[ autocmd ModeChanged * lua leave_snippet() ]] local cmp_enabled = true vim.api.nvim_create_user_command('ToggleAutoComplete', function() if cmp_enabled then require('cmp').setup.buffer { enabled = false } cmp_enabled = false else require('cmp').setup.buffer { enabled = true } cmp_enabled = true end end, {}) cmp.setup.cmdline('/', { mapping = cmp.mapping.preset.cmdline(), sources = { { name = 'buffer' } }, }) cmp.setup.cmdline(':', { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = 'path' } }, { { name = 'cmdline', option = { ignore_cmds = { 'Man', '!' } } }, }), }) require('lspconfig.ui.windows').default_options.border = 'rounded' vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = 'rounded' }) vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = 'rounded' }) local diagnostic_signs = { { name = 'DiagnosticSignError', text = ' ' }, { name = 'DiagnosticSignWarn', text = ' ' }, { name = 'DiagnosticSignHint', text = ' ' }, { name = 'DiagnosticSignInfo', text = ' ' }, } for _, sign in ipairs(diagnostic_signs) do vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = sign.name }) end vim.diagnostic.config { virtual_text = { prefix = '●' }, severity_sort = true, float = { source = 'always' }, signs = true, } end, }, -- Formatter { 'stevearc/conform.nvim', config = function() require('conform').setup { formatters_by_ft = { json = { { 'prettierd', 'prettier' } }, lua = { 'stylua' }, markdown = { 'prettier' }, python = function(bufnr) if require('conform').get_formatter_info('ruff_format', bufnr).available then return { 'ruff_format' } end return { 'isort', 'black' } end, yaml = { 'prettier' }, ['*'] = { 'injected' }, }, ignore_errors = true, -- NOTE: removed treesitter mapping; keep only what you need lang_to_ext = { bash = 'sh', latex = 'tex', markdown = 'md', python = 'py', }, } vim.api.nvim_create_user_command('Format', function(args) local range = nil if args.count ~= -1 then local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1] range = { start = { args.line1, 0 }, ['end'] = { args.line2, end_line:len() }, } end require('conform').format { async = true, lsp_fallback = true, range = range } end, { range = true }) vim.keymap.set('', 'fa', function() require('conform').format { async = true, lsp_fallback = true } end, { desc = '[F]ormat [a]ll' }) end, }, -- Completion { 'hrsh7th/nvim-cmp', dependencies = { 'L3MON4D3/LuaSnip', 'saadparwaiz1/cmp_luasnip', 'hrsh7th/cmp-nvim-lsp', 'hrsh7th/cmp-buffer', 'hrsh7th/cmp-path', 'hrsh7th/cmp-cmdline', }, }, { 'rafamadriz/friendly-snippets', config = function() require('luasnip.loaders.from_vscode').lazy_load() require('luasnip/loaders/from_vscode').lazy_load { paths = { vim.fn.stdpath 'config' .. '/snippets' }, } end, }, -- DAP { 'mfussenegger/nvim-dap', dependencies = { 'mfussenegger/nvim-dap-python', 'rcarriga/nvim-dap-ui', 'theHamsta/nvim-dap-virtual-text', 'nvim-neotest/nvim-nio', 'folke/neodev.nvim', }, config = function() require('neodev').setup { library = { plugins = { 'nvim-dap-ui' }, types = true } } local dap = require 'dap' local sign = vim.fn.sign_define sign('DapBreakpoint', { text = ' ', texthl = 'DapBreakpoint', linehl = '', numhl = '' }) sign('DapBreakpointCondition', { text = ' ', texthl = 'DapBreakpointCondition', linehl = '', numhl = '' }) sign('DapLogPoint', { text = '◆ ', texthl = 'DapLogPoint', linehl = '', numhl = '' }) sign('DapStoppedLine', { text = '󰁕 ', texthl = 'DapLogPoint', linehl = '', numhl = '' }) sign('DapBreakpointRejected', { text = ' ', texthl = 'DapBreakpointRejected', linehl = '', numhl = '' }) dap.adapters.codelldb = { type = 'server', port = '${port}', executable = { command = vim.fn.expand '$HOME/.local/share/nvim/mason/bin/codelldb', args = { '--port', '${port}' }, }, } dap.configurations.cpp = { { name = 'C++: Run file', type = 'codelldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, }, { name = 'C++: Attach to process', type = 'codelldb', request = 'attach', pid = require('dap.utils').pick_process, args = {}, }, } require('dapui').setup { controls = { icons = { pause = '⏸ ', play = '▶ ', terminate = '⏹ ' } }, floating = { border = 'rounded' }, layouts = { { elements = { { id = 'stacks', size = 0.30 }, { id = 'breakpoints', size = 0.20 }, { id = 'scopes', size = 0.50 }, }, position = 'left', size = 40, }, { elements = { { id = 'console', size = 0.50 }, { id = 'repl', size = 0.50 } }, position = 'bottom', size = 10, }, }, } require('nvim-dap-virtual-text').setup() require('dap-python').setup() require('dap-python').test_runner = 'pytest' vim.api.nvim_create_user_command('DapUIToggle', ":lua require('dapui').toggle()", {}) vim.api.nvim_create_user_command('DapPytestMethod', ":lua require('dap-python').test_method()", {}) vim.api.nvim_create_user_command('DapResetUI', ":lua require('dapui').open({reset = true})", { desc = 'Reset DAP UI Layout' }) end, }, }, {}) -- ========================================= -- ============ START SMEAR PROFILE ======== -- ========================================= local smear = require 'smear_cursor' local smear_profiles = { silver_blade = { -- General cursor_color = '#ffe6b2', smear_between_buffers = true, smear_between_neighbor_lines = true, min_horizontal_distance_smear = 0, min_vertical_distance_smear = 0, smear_horizontally = true, smear_vertically = true, smear_diagonally = true, smear_to_cmd = true, scroll_buffer_space = true, legacy_computing_symbols_support = false, legacy_computing_symbols_support_vertical_bars = false, use_diagonal_blocks = true, vertical_bar_cursor = false, smear_insert_mode = true, vertical_bar_cursor_insert_mode = true, smear_replace_mode = false, smear_terminal_mode = false, horizontal_bar_cursor_replace_mode = true, never_draw_over_target = false, hide_target_hack = false, max_kept_windows = 50, windows_zindex = 300, filetypes_disabled = {}, -- High FPS (smooth) time_interval = 7, delay_disable = nil, delay_event_to_smear = 1, delay_after_key = 6, -- Physics: fast head, laggy tail, smooth decay stiffness = 0.95, trailing_stiffness = 0.33, anticipation = 0.06, damping = 0.90, trailing_exponent = 5.5, distance_stop_animating = 0.06, -- Insert mode: match feel stiffness_insert_mode = 0.95, trailing_stiffness_insert_mode = 0.7, damping_insert_mode = 0.92, trailing_exponent_insert_mode = 5.5, distance_stop_animating_vertical_bar = 0.25, -- Diagonal + shading tuned for “pretty” max_slope_horizontal = (1 / 3) / 1.7, min_slope_vertical = 2 * 1.7, max_angle_difference_diagonal = math.pi / 18, max_offset_diagonal = 0.18, min_shade_no_diagonal = 0.22, min_shade_no_diagonal_vertical_bar = 0.55, -- Rich blending (costly but nice) color_levels = 24, gamma = 2.2, gradient_exponent = 2.8, max_shade_no_matrix = 0.78, matrix_pixel_threshold = 0.72, matrix_pixel_threshold_vertical_bar = 0.28, matrix_pixel_min_factor = 0.55, volume_reduction_exponent = 0.22, minimum_volume_factor = 0.78, -- Longer trail max_length = 34, max_length_insert_mode = 2, -- Particles off particles_enabled = false, particle_max_num = 100, particle_spread = 0.5, particles_per_second = 200, particles_per_length = 1.0, particle_max_lifetime = 300, particle_lifetime_distribution_exponent = 5, particle_max_initial_velocity = 10, particle_velocity_from_cursor = 0.2, particle_random_velocity = 100, particle_damping = 0.2, particle_gravity = 20, min_distance_emit_particles = 1.5, particle_switch_octant_braille = 0.3, particles_over_text = false, }, -- 1) Frost Mist eco_smear = { -- General (keep core behavior) smear_between_buffers = true, smear_between_neighbor_lines = true, min_horizontal_distance_smear = 1, -- reduces tiny smears min_vertical_distance_smear = 1, smear_horizontally = true, smear_vertically = true, smear_diagonally = false, -- big CPU win smear_to_cmd = true, scroll_buffer_space = false, -- cheaper on scroll legacy_computing_symbols_support = false, legacy_computing_symbols_support_vertical_bars = false, use_diagonal_blocks = true, vertical_bar_cursor = false, smear_insert_mode = true, vertical_bar_cursor_insert_mode = true, smear_replace_mode = false, smear_terminal_mode = false, horizontal_bar_cursor_replace_mode = true, never_draw_over_target = false, hide_target_hack = false, max_kept_windows = 20, -- fewer render windows kept windows_zindex = 300, filetypes_disabled = {}, -- Lower FPS (much cheaper) time_interval = 14, delay_disable = nil, delay_event_to_smear = 2, delay_after_key = 10, -- Physics: still fast head, shorter/cheaper tail stiffness = 0.90, trailing_stiffness = 0.28, anticipation = 0.04, damping = 0.88, trailing_exponent = 3.0, distance_stop_animating = 0.12, -- Insert mode: keep it tight stiffness_insert_mode = 0.90, trailing_stiffness_insert_mode = 0.28, damping_insert_mode = 0.90, trailing_exponent_insert_mode = 3.0, distance_stop_animating_vertical_bar = 0.30, -- Simpler shading (cheaper) max_slope_horizontal = (1 / 3) / 1.6, min_slope_vertical = 2 * 1.6, max_angle_difference_diagonal = math.pi / 16, max_offset_diagonal = 0.2, min_shade_no_diagonal = 0.30, min_shade_no_diagonal_vertical_bar = 0.60, -- Reduced blending cost color_levels = 16, gamma = 2.2, gradient_exponent = 1.3, max_shade_no_matrix = 0.80, matrix_pixel_threshold = 0.80, matrix_pixel_threshold_vertical_bar = 0.35, matrix_pixel_min_factor = 0.65, volume_reduction_exponent = 0.35, minimum_volume_factor = 0.82, -- Shorter trail max_length = 18, max_length_insert_mode = 1, -- Particles off particles_enabled = false, particle_max_num = 100, particle_spread = 0.5, particles_per_second = 200, particles_per_length = 1.0, particle_max_lifetime = 300, particle_lifetime_distribution_exponent = 5, particle_max_initial_velocity = 10, particle_velocity_from_cursor = 0.2, particle_random_velocity = 100, particle_damping = 0.2, particle_gravity = 20, min_distance_emit_particles = 1.5, particle_switch_octant_braille = 0.3, particles_over_text = false, }, } local smear_profile_order = { 'silver_blade', 'eco_smear', } local current_idx = 1 local function apply_smear_profile(name) local p = smear_profiles[name] if not p then vim.notify('Unknown smear profile: ' .. tostring(name), vim.log.levels.ERROR) return end smear.setup(vim.deepcopy(p)) vim.g.smear_cursor_profile = name vim.notify('smear-cursor → ' .. name) end -- Commands vim.api.nvim_create_user_command('SmearProfile', function(opts) apply_smear_profile(opts.args) end, { nargs = 1, complete = function() return smear_profile_order end, }) vim.api.nvim_create_user_command('SmearProfileNext', function() current_idx = (current_idx % #smear_profile_order) + 1 apply_smear_profile(smear_profile_order[current_idx]) end, {}) vim.api.nvim_create_user_command('SmearProfilePrev', function() current_idx = ((current_idx - 2) % #smear_profile_order) + 1 apply_smear_profile(smear_profile_order[current_idx]) end, {}) -- Keybindings (edit if you want) vim.keymap.set('n', 'pn', 'SmearProfileNext', { desc = 'Smear profile: next' }) vim.keymap.set('n', 'pp', 'SmearProfilePrev', { desc = 'Smear profile: prev' }) -- Apply a default on startup (pick one) apply_smear_profile 'silver_blade' -- ========================================= -- ============ END SMEAR PROFILE ========== -- ========================================= -- ========================= -- Editor settings -- ========================= vim.o.hlsearch = false vim.opt.nu = true vim.opt.relativenumber = true vim.o.mouse = 'a' vim.o.breakindent = true vim.o.undofile = true vim.o.ignorecase = true vim.o.smartcase = true vim.o.updatetime = 250 vim.o.timeoutlen = 300 vim.opt.splitright = true vim.opt.splitbelow = true vim.opt.inccommand = 'split' vim.wo.signcolumn = 'yes' vim.opt.signcolumn = 'yes:1' vim.opt.cursorline = true vim.opt.colorcolumn = '100' vim.opt.tabstop = 4 vim.opt.shiftwidth = 4 vim.opt.expandtab = true vim.opt.listchars = { trail = '⇲', tab = '◦ ' } vim.opt.list = true vim.opt.clipboard = 'unnamedplus' vim.o.completeopt = 'menuone,noselect' vim.keymap.set('n', 'k', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) vim.keymap.set('n', 'j', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) vim.opt.termguicolors = true -- ============================ -- ====== TABSTOP START ======= -- ============================ local function set_indent(ts) vim.opt_local.expandtab = true vim.opt_local.tabstop = ts vim.opt_local.shiftwidth = ts vim.opt_local.softtabstop = ts end vim.api.nvim_create_autocmd('FileType', { pattern = { 'lua', 'javascript', 'typescript', 'tsx', 'json', 'yaml', 'toml', 'html', 'css', 'rust', 'c', 'cpp' }, callback = function() set_indent(2) end, }) vim.api.nvim_create_autocmd('FileType', { pattern = { 'python', 'sh', 'bash', 'zsh', 'go' }, callback = function() set_indent(4) end, }) -- Makefiles must use real tabs vim.api.nvim_create_autocmd('FileType', { pattern = { 'make' }, callback = function() vim.opt_local.expandtab = false vim.opt_local.tabstop = 8 vim.opt_local.shiftwidth = 8 vim.opt_local.softtabstop = 0 end, }) -- ============================ -- ====== TABSTOP END ========= -- ============================ local highlight_group = vim.api.nvim_create_augroup('YankHighlight', { clear = true }) vim.api.nvim_create_autocmd('TextYankPost', { callback = function() vim.highlight.on_yank() end, group = highlight_group, pattern = '*', }) local numtogGrp = vim.api.nvim_create_augroup('NumberToggle', { clear = true }) vim.api.nvim_create_autocmd({ 'BufEnter', 'InsertLeave', 'FocusGained' }, { pattern = '*', callback = function() vim.opt.relativenumber = true end, group = numtogGrp, }) vim.api.nvim_create_autocmd({ 'BufLeave', 'InsertEnter', 'FocusLost' }, { pattern = '*', callback = function() vim.opt.relativenumber = false end, group = numtogGrp, }) -- ========================= -- Keymaps (treesitter keymaps removed) -- ========================= -- scrolling / search centering vim.keymap.set('n', '', 'zz', { desc = 'Scroll down and center cursor' }) vim.keymap.set('n', '', 'zz', { desc = 'Scroll up and center cursor' }) vim.keymap.set('n', 'n', 'nzzzv', { desc = 'Next search result and center' }) vim.keymap.set('n', 'N', 'Nzzzv', { desc = 'Previous search result and center' }) -- move selected lines vim.keymap.set('v', 'J', ":m '>+1gv=gv", { desc = 'Move selection down' }) vim.keymap.set('v', 'K', ":m '<-2gv=gv", { desc = 'Move selection up' }) -- window navigation vim.keymap.set('n', '', 'h', { desc = 'Go to left window' }) vim.keymap.set('n', '', 'j', { desc = 'Go to lower window' }) vim.keymap.set('n', '', 'k', { desc = 'Go to upper window' }) vim.keymap.set('n', '', 'l', { desc = 'Go to right window' }) -- clipboard / yank vim.keymap.set('x', 'p', [["_dP]], { desc = 'Paste without overwriting register' }) vim.keymap.set('n', 'ya', ':%y+', { desc = 'Yank entire buffer to clipboard' }) -- diagnostics vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Previous diagnostic' }) vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Next diagnostic' }) vim.keymap.set('n', 'e', vim.diagnostic.open_float, { desc = 'Show diagnostic under cursor' }) vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Diagnostics to loclist' }) vim.keymap.set('n', 'dd', vim.diagnostic.disable, { desc = 'Disable diagnostics' }) vim.keymap.set('n', 'de', vim.diagnostic.enable, { desc = 'Enable diagnostics' }) -- misc vim.keymap.set('n', 'cw', ':cd %:p:h:pwd', { desc = 'cd to current file directory' }) vim.keymap.set('n', '', '', { noremap = true, desc = 'Jump forward in jumplist' }) vim.keymap.set('n', '', ':write', { desc = 'Save file' }) vim.keymap.set('n', 'cd', ':ToggleAutoComplete', { desc = 'Toggle autocomplete' }) vim.keymap.set('n', 'ce', ':ToggleAutoComplete', { desc = 'Toggle autocomplete (alias)' }) -- Telescope vim.keymap.set('n', '?', require('telescope.builtin').oldfiles, { desc = 'Find recently opened files' }) vim.keymap.set('n', '', require('telescope.builtin').buffers, { desc = 'List open buffers' }) vim.keymap.set('n', '/', function() require('telescope.builtin').current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { previewer = false, sorting_strategy = 'ascending', }) end, { desc = 'Fuzzy search in current buffer' }) vim.keymap.set('n', 'sf', require('telescope.builtin').find_files, { desc = 'Find files' }) vim.keymap.set('n', 'sh', require('telescope.builtin').help_tags, { desc = 'Search help tags' }) vim.keymap.set('n', 'sw', require('telescope.builtin').grep_string, { desc = 'Search word under cursor' }) vim.keymap.set('n', 'sg', require('telescope.builtin').live_grep, { desc = 'Live grep' }) vim.keymap.set('n', 'sd', require('telescope.builtin').diagnostics, { desc = 'Search diagnostics' }) vim.keymap.set('n', 'sp', require('telescope.builtin').spell_suggest, { desc = 'Spell suggestions' }) vim.keymap.set('n', 'sk', require('telescope.builtin').keymaps, { desc = 'Search keymaps' }) vim.keymap.set('n', 'gf', require('telescope.builtin').git_files, { desc = 'Find git files' }) vim.keymap.set('n', 'sc', require('telescope.builtin').git_commits, { desc = 'Search git commits' }) vim.keymap.set('n', 'sr', require('telescope.builtin').resume, { desc = 'Resume last Telescope picker' }) vim.keymap.set('n', 's/', function() require('telescope.builtin').live_grep { grep_open_files = true, prompt_title = 'Live Grep in Open Files', } end, { desc = 'Live grep in open files' }) vim.keymap.set('n', 'sn', function() require('telescope.builtin').find_files { cwd = vim.fn.stdpath 'config' } end, { desc = 'Search Neovim config files' }) -- run / permissions vim.keymap.set('n', 'ru', ':w:!%:p', { desc = 'Save and run current file' }) vim.keymap.set('n', 'me', ':!chmod +x %:p', { desc = 'Make file executable' }) vim.keymap.set('n', 'P', require('spectre').open, { desc = 'Open Spectre search/replace' }) -- Trouble vim.keymap.set('n', 'xx', 'TroubleToggle', { silent = true, noremap = true, desc = 'Toggle Trouble' }) vim.keymap.set('n', 'xw', 'TroubleToggle workspace_diagnostics', { silent = true, noremap = true, desc = 'Workspace diagnostics (Trouble)' }) vim.keymap.set('n', 'xd', 'TroubleToggle document_diagnostics', { silent = true, noremap = true, desc = 'Document diagnostics (Trouble)' }) -- DAP vim.keymap.set('n', '', ":lua require('dapui').toggle()", { desc = 'Toggle DAP UI' }) vim.keymap.set('n', 'dc', ":lua require('dap').continue()", { desc = 'DAP continue' }) vim.keymap.set('n', 'do', ":lua require('dap').step_over()", { desc = 'DAP step over' }) vim.keymap.set('n', 'di', ":lua require('dap').step_into()", { desc = 'DAP step into' }) vim.keymap.set('n', 'dk', function() require('dap.ui.widgets').hover() end, { desc = 'DAP hover value' }) vim.keymap.set('n', 'd?', function() local widgets = require 'dap.ui.widgets' widgets.centered_float(widgets.scopes) end, { desc = 'DAP scopes' }) vim.keymap.set('n', 'du', ":lua require('dap').step_out()", { desc = 'DAP step out' }) vim.keymap.set('n', 'dl', ":lua require('dapui').float_element()", { silent = true, noremap = true, desc = 'DAP floating window' }) vim.keymap.set('n', 'dt', ":lua require('dap').toggle_breakpoint()", { silent = true, noremap = true, desc = 'Toggle breakpoint' }) vim.keymap.set('n', 'dm', ":lua require('dap-python').test_method()", { silent = true, noremap = true, desc = 'DAP test method' }) vim.keymap.set('n', 'df', ":lua require('dap-python').test_class()", { silent = true, noremap = true, desc = 'DAP test class' }) -- barbar vim.keymap.set('n', '', 'BufferPrevious', { silent = true, noremap = true, desc = 'Previous buffer' }) vim.keymap.set('n', '', 'BufferNext', { silent = true, noremap = true, desc = 'Next buffer' }) vim.keymap.set('n', '', 'BufferClose', { silent = true, noremap = true, desc = 'Close buffer' }) local opts = { noremap = true, silent = true } vim.keymap.set('n', '', 'BufferLineGoToBuffer 1', vim.tbl_extend('force', opts, { desc = 'Go to buffer 1' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 2', vim.tbl_extend('force', opts, { desc = 'Go to buffer 2' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 3', vim.tbl_extend('force', opts, { desc = 'Go to buffer 3' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 4', vim.tbl_extend('force', opts, { desc = 'Go to buffer 4' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 5', vim.tbl_extend('force', opts, { desc = 'Go to buffer 5' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 6', vim.tbl_extend('force', opts, { desc = 'Go to buffer 6' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 7', vim.tbl_extend('force', opts, { desc = 'Go to buffer 7' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 8', vim.tbl_extend('force', opts, { desc = 'Go to buffer 8' })) vim.keymap.set('n', '', 'BufferLineGoToBuffer 9', vim.tbl_extend('force', opts, { desc = 'Go to buffer 9' })) vim.keymap.set('n', '', 'BufferLast', vim.tbl_extend('force', opts, { desc = 'Go to last buffer' })) -- quickfix vim.keymap.set('n', 'cn', ':cnext', { desc = 'Next quickfix item' }) vim.keymap.set('n', 'cp', ':cprevious', { desc = 'Previous quickfix item' }) -- markdown vim.keymap.set('n', 'mp', ':MarkdownPreview', { desc = 'Markdown preview' }) -- git vim.keymap.set('n', 'ga', ':Telescope coauthors', { desc = 'Select git co-authors' }) -- AI model switching vim.keymap.set('n', 'an', function() _G.chatgpt_model = 'gpt-5-nano' print 'ChatGPT → gpt-5-nano' end, { desc = 'ChatGPT model: gpt-5-nano' }) vim.keymap.set('n', 'ac', function() _G.chatgpt_model = 'gpt-5.1-codex-mini' print 'ChatGPT → gpt-5.1-codex-mini' end, { desc = 'ChatGPT model: gpt-5.1-codex-mini' }) vim.keymap.set('n', 'ai', 'ChatGPT', { desc = 'Open ChatGPT prompt' }) -- delete backwards vim.keymap.set({ 'i', 'c' }, '', '', { noremap = true, desc = 'Delete previous word' }) vim.keymap.set('n', '', 'BufferLineCycleNext', { desc = 'Go to next tab' }) vim.keymap.set('n', '', 'BufferLineCyclePrev', { desc = 'Go backwards a tab' }) vim.keymap.set('n', 'tc', ':tabclose', { desc = 'Close current tab' }) vim.keymap.set('n', 'fk', ':FloatermKill!', { desc = 'Kill all floaterm terminals' }) vim.keymap.set('x', 'S', '(nvim-surround-visual)', { remap = true }, { desc = 'Surround selected text' }) -- yank binding vim.api.nvim_set_keymap('n', 'Y', 'yy', { noremap = true, silent = true }) -- exit and save vim.keymap.set('n', '', 'wqa') -- split views vim.keymap.set('n', 'sv', 'vsplit') vim.keymap.set('n', 'sh', 'split') -- signature help vim.api.nvim_set_keymap('i', '', 'lua vim.lsp.buf.signature_help()', { noremap = true, silent = true }) vim.api.nvim_set_keymap('n', '', 'lua vim.lsp.buf.signature_help()', { noremap = true, silent = true }) -- toggle neotree local function toggle_neotree() local manager = require 'neo-tree.sources.manager' local renderer = require 'neo-tree.ui.renderer' local state = manager.get_state 'filesystem' local window_exists = renderer.window_exists(state) if window_exists then vim.cmd 'Neotree close' else vim.cmd 'Neotree show' end end _G.toggle_neotree = toggle_neotree vim.api.nvim_set_keymap('n', '', ':lua toggle_neotree()', { noremap = true, silent = true }) -- Floaterm vim.api.nvim_set_keymap('n', '', ':FloatermToggle', { noremap = true }) vim.api.nvim_set_keymap('i', '', ':FloatermToggle', { noremap = true }) vim.api.nvim_set_keymap('t', '', ':FloatermToggle', { noremap = true, silent = true }) -- Comment.nvim keymaps vim.api.nvim_set_keymap('n', '', 'lua require("Comment.api").toggle.linewise.current()', { noremap = true, silent = true }) vim.api.nvim_set_keymap('n', '', 'lua require("Comment.api").toggle.blockwise.current()', { noremap = true, silent = true }) vim.api.nvim_set_keymap('v', '', 'lua require("Comment.api").toggle.linewise(vim.fn.visualmode())', { noremap = true, silent = true }) vim.api.nvim_set_keymap('v', '', 'lua require("Comment.api").toggle.blockwise(vim.fn.visualmode())', { noremap = true, silent = true }) vim.api.nvim_set_keymap('i', '', '', { noremap = true }) -- git coauthors vim.keymap.set('n', 'ga', ':Telescope coauthors') -- install treesitter parsers require('nvim-treesitter').install { 'c', 'rust', 'gdscript', 'c#' }