kickstart.nvim/init.lua

1699 lines
55 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

vim.g.maplocalleader = ' '
vim.g.mapleader = ' '
vim.loader.enable()
local version = vim.version()
if version.major ~= 0 or version.minor ~= 12 then
error(('This config supports Neovim 0.12.x (found %s)'):format(tostring(version)))
end
-- =========================
-- Lazy.nvim bootstrap
-- =========================
local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
if not vim.uv.fs_stat(lazypath) then
local result = vim.system({
'git',
'clone',
'--filter=blob:none',
'https://github.com/folke/lazy.nvim.git',
'--branch=stable',
lazypath,
}, { text = true }):wait()
if result.code ~= 0 then
error(('Unable to install lazy.nvim:\n%s'):format(result.stderr or 'unknown error'))
end
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 = {} },
{
'karb94/neoscroll.nvim',
opts = {},
config = function()
local neoscroll = require 'neoscroll'
neoscroll.setup {
-- remove <C-u>/<C-d> from defaults so your custom maps win
mappings = {
'<C-b>',
'<C-f>',
'<C-y>',
'<C-e>',
'zt',
'zz',
'zb',
},
hide_cursor = true, -- Hide cursor while scrolling
stop_eof = true, -- Stop at <EOF> when scrolling downwards
respect_scrolloff = true, -- Stop scrolling when the cursor reaches the scrolloff margin of the file
cursor_scrolls_alone = true, -- The cursor will keep on scrolling even if the window cannot scroll further
duration_multiplier = 0.3, -- Global duration multiplier
easing = 'quadratic', -- Default easing function
pre_hook = function(info)
if info == 'center' then
vim.cmd 'normal! M'
end
end,
post_hook = nil,
}
local function near_file_edge()
local line = vim.api.nvim_win_get_cursor(0)[1]
local last = vim.api.nvim_buf_line_count(0)
local so = vim.wo.scrolloff
return line <= so + 1 or line >= last - so
end
local modes = { 'n', 'v', 'x' }
vim.keymap.set(modes, '<C-d>', function()
neoscroll.ctrl_d { duration = 300 }
if not near_file_edge() then
vim.cmd 'normal! M'
end
end)
vim.keymap.set(modes, '<C-u>', function()
neoscroll.ctrl_u { duration = 300 }
if not near_file_edge() then
vim.cmd 'normal! M'
end
end)
end,
},
{
'nvim-treesitter/nvim-treesitter',
branch = 'main',
lazy = false,
build = ':TSUpdate',
},
{
'MeanderingProgrammer/treesitter-modules.nvim',
dependencies = { 'nvim-treesitter/nvim-treesitter' },
config = function()
local parsers = {
'bash',
'c',
'c_sharp',
'cpp',
'gdscript',
'javascript',
'json',
'lua',
'markdown',
'python',
'rust',
'toml',
'typescript',
'yaml',
}
require('treesitter-modules').setup {
ensure_installed = vim.env.NVIM_SKIP_TOOL_INSTALL == '1' and {} or parsers,
highlight = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = '<A-Space>',
node_incremental = '<A-Space>',
node_decremental = '<A-BS>',
scope_incremental = '<A-S-Space>',
},
},
}
end,
},
'rhysd/git-messenger.vim',
{
'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,
},
{
'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 = {
enable_preview = true,
filesystem = {
-- open images (and anything else) externally
commands = {
system_open = function(state)
local node = state.tree:get_node()
local path = node:get_id() -- absolute path
vim.fn.jobstart({ 'xdg-open', path }, { detach = true })
end,
},
window = {
mappings = {
['P'] = 'toggle_preview',
['O'] = 'system_open', -- press O to open with external viewer
},
},
filtered_items = {
visible = false,
hide_dotfiles = true,
hide_gitignored = true,
hide_by_name = { '.git', '.elc' },
hide_by_pattern = { '*.uid', 'node_modules' },
},
},
},
},
-- LAZY PLUGIN SPEC (replace bufferline with barbar)
{
'romgrk/barbar.nvim',
dependencies = {
'nvim-tree/nvim-web-devicons',
'lewis6991/gitsigns.nvim', -- optional, for git status icons
},
init = function()
vim.g.barbar_auto_setup = false
end,
opts = {
animation = true,
auto_hide = false,
tabpages = false, -- show buffers, not Vim tabpages
clickable = true,
icons = {
preset = 'slanted',
button = '󰅖',
},
-- Treat neo-tree as a sidebar so it doesn't become a "tab" and layout stays sane
sidebar_filetypes = {
['neo-tree'] = { event = 'BufWipeout' },
},
},
},
'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 = {} },
{
'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,
},
-- Terminal UX
{
'akinsho/toggleterm.nvim',
opts = {},
config = function()
require('toggleterm').setup {
size = 20,
open_mapping = [[<c-\>]],
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.enable(false, { bufnr = 0 })
vim.api.nvim_buf_set_keymap(0, 't', '<esc>', [[<C-\><C-n>]], 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.enable(false, { bufnr = 0 })
vim.api.nvim_buf_set_keymap(0, 't', '<esc>', '<cmd>close<CR>', { silent = false, noremap = true })
if vim.fn.mapcheck('<esc>', 't') ~= '' then
vim.api.nvim_buf_del_keymap(term.bufnr, 't', '<esc>')
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', '<leader>lg', '<cmd>lua _lazygit_toggle()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<leader>ip', '<cmd>lua _python_toggle()<CR>', { 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 = {
['<C-t>'] = actions.select_tab,
['<C-v>'] = actions.select_vertical,
['<C-x>'] = actions.select_horizontal,
},
n = {
['<C-t>'] = actions.select_tab,
['<C-v>'] = actions.select_vertical,
['<C-x>'] = 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 = {
{ 'mason-org/mason.nvim', config = true },
'mason-org/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('<leader>D', require('telescope.builtin').lsp_type_definitions, 'Type [D]efinition')
map('<leader>ds', require('telescope.builtin').lsp_document_symbols, '[D]ocument [S]ymbols')
map('<leader>ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, '[W]orkspace [S]ymbols')
map('<leader>rn', vim.lsp.buf.rename, '[R]e[n]ame')
map('<leader>ca', vim.lsp.buf.code_action, '[C]ode [A]ction')
map('T', vim.lsp.buf.hover, 'Hover Documentation')
map('<C-k>', 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 = {
settings = {
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' } }
for server_name, server in pairs(servers) do
server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {})
vim.lsp.config(server_name, server)
end
require('mason-lspconfig').setup {
ensure_installed = vim.env.NVIM_SKIP_TOOL_INSTALL == '1' and {} or vim.tbl_keys(servers),
automatic_enable = true,
}
require('mason-tool-installer').setup {
ensure_installed = {
'clangd',
'clang-format',
'lua_ls',
'pyright',
'ruff',
'codelldb',
'cpptools',
'cpplint',
'csharpier',
'csharp-language-server',
'netcoredbg',
'omnisharp',
'omnisharp-mono',
'rust-analyzer',
'sonarlint-language-server',
'stylua',
'prettier',
},
auto_update = false,
run_on_start = vim.env.NVIM_SKIP_TOOL_INSTALL ~= '1',
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 {
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = true },
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { 'i', 's' }),
['<S-Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { 'i', 's' }),
},
sources = {
{ 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', '!' } } },
}),
})
vim.diagnostic.config {
virtual_text = { prefix = '' },
severity_sort = true,
float = { source = 'always' },
signs = {
text = {
[vim.diagnostic.severity.ERROR] = '',
[vim.diagnostic.severity.WARN] = '',
[vim.diagnostic.severity.HINT] = '',
[vim.diagnostic.severity.INFO] = '',
},
},
}
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' },
rust = { 'rustfmt' },
['*'] = { '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_format = 'fallback', range = range }
end, { range = true })
vim.keymap.set('', '<leader>fa', function()
require('conform').format { async = true, lsp_format = 'fallback' }
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,
},
{
'Kurama622/llm.nvim',
dependencies = { 'nvim-lua/plenary.nvim', 'MunifTanjim/nui.nvim' },
cmd = { 'LLMSessionToggle', 'LLMSelectedTextHandler', 'LLMAppHandler' },
config = function()
require('llm').setup {
-- GitHub Models (Azure inference)
url = 'https://api.openai.com/v1/chat/completions',
api_type = 'openai',
-- Set this to what you want to use (must support /chat/completions)
model = _G.chatgpt_model or 'gpt-5-nano',
-- Sensible coding defaults
--max_tokens = 4095,
--temperature = 0.2,
--top_p = 0.1,
-- Keep it code-focused
prompt = 'You are a helpful programming assistant. Be concise, show code when needed, and prefer practical fixes.',
-- Visual selection -> context prompt
selected_text_handler = {
prompt = function(selection)
return string.format(
'You are a helpful programming assistant.\n\n' .. 'Context (selected text):\n' .. '```text\n%s\n```\n\n' .. 'Task:\n',
selection
)
end,
},
-- Optional cosmetics (minimal)
spinner = {
text = { '', '', '', '', '', '', '', '', '', '' },
hl = 'Title',
},
prefix = {
user = { text = 'You: ', hl = 'Title' },
assistant = { text = 'AI: ', hl = 'Added' },
},
-- Persist chat history
save_session = true,
max_history = 30,
max_history_name_length = 40,
-- Keymaps inside the session UI
keys = {
-- Input window
['Input:Submit'] = { mode = 'i', key = '<cr>' }, -- Enter sends (your issue)
['Input:Cancel'] = { mode = { 'n', 'i' }, key = '<C-c>' },
['Input:Resend'] = { mode = { 'n', 'i' }, key = '<C-r>' },
-- History (only if save_session=true)
['Input:HistoryNext'] = { mode = { 'n', 'i' }, key = '<C-j>' },
['Input:HistoryPrev'] = { mode = { 'n', 'i' }, key = '<C-k>' },
-- Output window ("split" style)
['Output:Ask'] = { mode = 'n', key = 'i' },
['Output:Cancel'] = { mode = 'n', key = '<C-c>' },
['Output:Resend'] = { mode = 'n', key = '<C-r>' },
-- Session window ("float" style)
['Session:Toggle'] = { mode = 'n', key = '<leader>ai' }, -- your chosen toggle
['Session:Close'] = { mode = 'n', key = { '<esc>', 'Q' } },
},
-- Optional diff display used by some tools/handlers
display = {
diff = {
layout = 'vertical',
opts = {
'internal',
'filler',
'closeoff',
'algorithm:patience',
'followwrap',
'linematch:120',
},
provider = 'mini_diff',
disable_diagnostic = true,
},
},
callbacks = {
on_response = function(resp)
local usage = resp and resp.usage
if usage then
vim.notify(
string.format(
'LLM tokens — prompt: %d, completion: %d, total: %d',
usage.prompt_tokens or 0,
usage.completion_tokens or 0,
usage.total_tokens or 0
),
vim.log.levels.INFO
)
end
end,
},
-- If you later add tools:
app_handler = {
-- Visual-select text -> run this -> output in a floating result window.
AskWithContext = {
handler = 'attach_to_chat_handler',
prompt = [[
Answer the users question (likely code related) using the context very briefly and concisely:
]],
opts = {
-- this is the key part: append the visual selection after the prompt
apply_visual_selection = true, -- described in docs for flexi_handler :contentReference[oaicite:2]{index=2}
is_codeblock = true,
enter_input = true,
enter_flexible_window = false,
--exit_on_move = true,
},
},
CodeExplain = {
handler = 'flexi_handler',
prompt = 'Explain the following code very briefly, please only return the explanation',
opts = {
enter_flexible_window = true,
},
},
},
}
end,
keys = {
{ '<leader>ai', '<cmd>LLMSessionToggle<cr>', mode = 'n', silent = true, desc = 'LLM: toggle session' },
{ '<leader>as', '<cmd>LLMSelectedTextHandler<cr>', mode = 'x', silent = true, desc = 'LLM: send selection' },
-- If you add tools:
{ '<leader>ax', '<cmd>LLMAppHandler CodeExplain<cr>', mode = 'x', silent = true, desc = 'LLM tool: Explain' },
{ '<leader>ai', '<cmd>LLMAppHandler AskWithContext<cr>', mode = 'x', silent = true, desc = 'LLM tool: Ask with context' },
},
},
{
'MeanderingProgrammer/render-markdown.nvim',
dependencies = {
'nvim-treesitter/nvim-treesitter',
'nvim-mini/mini.icons',
}, -- if you use standalone mini plugins
ft = { 'markdown', 'llm' },
config = function()
require('render-markdown').setup {
restart_highlighter = true,
heading = {
enabled = true,
sign = false,
position = 'overlay', -- inline | overlay
icons = { '󰎤 ', '󰎧 ', '󰎪 ', '󰎭 ', '󰎱 ', '󰎳 ' },
signs = { '󰫎 ' },
width = 'block',
left_margin = 0,
left_pad = 0,
right_pad = 0,
min_width = 0,
border = false,
border_virtual = false,
border_prefix = false,
above = '',
below = '',
backgrounds = {},
foregrounds = {
'RenderMarkdownH1',
'RenderMarkdownH2',
'RenderMarkdownH3',
'RenderMarkdownH4',
'RenderMarkdownH5',
'RenderMarkdownH6',
},
},
dash = {
enabled = true,
icon = '',
width = 0.5,
left_margin = 0.5,
highlight = 'RenderMarkdownDash',
},
code = { style = 'normal' },
}
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,
},
}, {
lockfile = vim.fn.stdpath 'config' .. '/lazy-lock.json',
checker = { enabled = false },
change_detection = { enabled = false, notify = false },
install = { colorscheme = { 'gruvbox-material', 'habamax' } },
})
-- =========================================
-- ============ 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)
cursor_color = '#ffe6b2',
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,
},
}
-- smear settings
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,
})
-- Apply a default on startup (pick one)
apply_smear_profile 'silver_blade'
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', '<leader>pn', '<cmd>SmearProfileNext<CR>', { desc = 'Smear profile: next' })
vim.keymap.set('n', '<leader>pp', '<cmd>SmearProfilePrev<CR>', { desc = 'Smear profile: prev' })
-- =========================================
-- ============ 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' },
callback = function()
set_indent(2)
end,
})
vim.api.nvim_create_autocmd('FileType', {
pattern = { 'python', 'sh', 'bash', 'zsh', 'go', 'rust', 'cpp', 'c' },
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, disabled for neoscroll
-- vim.keymap.set('n', '<C-d>', '<C-d>zz', { desc = 'Scroll down and center cursor' })
-- vim.keymap.set('n', '<C-u>', '<C-u>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 '>+1<CR>gv=gv", { desc = 'Move selection down' })
vim.keymap.set('v', 'K', ":m '<-2<CR>gv=gv", { desc = 'Move selection up' })
-- window navigation
vim.keymap.set('n', '<C-h>', '<C-W>h', { desc = 'Go to left window' })
vim.keymap.set('n', '<C-j>', '<C-W>j', { desc = 'Go to lower window' })
vim.keymap.set('n', '<C-k>', '<C-W>k', { desc = 'Go to upper window' })
vim.keymap.set('n', '<C-l>', '<C-W>l', { desc = 'Go to right window' })
-- clipboard / yank
vim.keymap.set('x', '<leader>p', [["_dP]], { desc = 'Paste without overwriting register' })
vim.keymap.set('n', '<leader>ya', ':%y+<CR>', { desc = 'Yank entire buffer to clipboard' })
-- diagnostics
vim.keymap.set('n', '[d', function()
vim.diagnostic.jump { count = -1, float = true }
end, { desc = 'Previous diagnostic' })
vim.keymap.set('n', ']d', function()
vim.diagnostic.jump { count = 1, float = true }
end, { desc = 'Next diagnostic' })
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float, { desc = 'Show diagnostic under cursor' })
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Diagnostics to loclist' })
vim.keymap.set('n', '<leader>dd', function()
vim.diagnostic.enable(false)
end, { desc = 'Disable diagnostics' })
vim.keymap.set('n', '<leader>de', function()
vim.diagnostic.enable(true)
end, { desc = 'Enable diagnostics' })
-- misc
vim.keymap.set('n', '<leader>cw', ':cd %:p:h<CR>:pwd<CR>', { desc = 'cd to current file directory' })
vim.keymap.set('n', '<C-I>', '<C-I>', { noremap = true, desc = 'Jump forward in jumplist' })
vim.keymap.set('n', '<C-s>', ':write<CR>', { desc = 'Save file' })
vim.keymap.set('i', '<C-s>', ':write<CR>', { desc = 'Save file' })
vim.keymap.set('n', '<leader>cd', ':ToggleAutoComplete<CR>', { desc = 'Toggle autocomplete' })
vim.keymap.set('n', '<leader>ce', ':ToggleAutoComplete<CR>', { desc = 'Toggle autocomplete (alias)' })
-- Telescope
vim.keymap.set('n', '<leader>?', require('telescope.builtin').oldfiles, { desc = 'Find recently opened files' })
vim.keymap.set('n', '<leader><space>', require('telescope.builtin').buffers, { desc = 'List open buffers' })
vim.keymap.set('n', '<leader>/', 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', '<leader>sf', require('telescope.builtin').find_files, { desc = 'Find files' })
vim.keymap.set('n', '<leader>sh', require('telescope.builtin').help_tags, { desc = 'Search help tags' })
vim.keymap.set('n', '<leader>sw', require('telescope.builtin').grep_string, { desc = 'Search word under cursor' })
vim.keymap.set('n', '<leader>sg', require('telescope.builtin').live_grep, { desc = 'Live grep' })
vim.keymap.set('n', '<leader>sd', require('telescope.builtin').diagnostics, { desc = 'Search diagnostics' })
vim.keymap.set('n', '<leader>sp', require('telescope.builtin').spell_suggest, { desc = 'Spell suggestions' })
vim.keymap.set('n', '<leader>sk', require('telescope.builtin').keymaps, { desc = 'Search keymaps' })
vim.keymap.set('n', '<leader>gf', require('telescope.builtin').git_files, { desc = 'Find git files' })
vim.keymap.set('n', '<leader>sc', require('telescope.builtin').git_commits, { desc = 'Search git commits' })
vim.keymap.set('n', '<leader>sr', require('telescope.builtin').resume, { desc = 'Resume last Telescope picker' })
vim.keymap.set('n', '<leader>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', '<leader>sn', function()
require('telescope.builtin').find_files { cwd = vim.fn.stdpath 'config' }
end, { desc = 'Search Neovim config files' })
-- run / permissions
vim.keymap.set('n', '<leader>ru', ':w<CR>:!%:p', { desc = 'Save and run current file' })
vim.keymap.set('n', '<leader>me', ':!chmod +x %:p<CR>', { desc = 'Make file executable' })
vim.keymap.set('n', '<leader>P', require('spectre').open, { desc = 'Open Spectre search/replace' })
-- Trouble
vim.keymap.set('n', '<leader>xx', '<cmd>Trouble diagnostics toggle<cr>', { silent = true, desc = 'Toggle Trouble diagnostics' })
vim.keymap.set('n', '<leader>xw', '<cmd>Trouble diagnostics toggle<cr>', { silent = true, desc = 'Workspace diagnostics (Trouble)' })
vim.keymap.set('n', '<leader>xd', '<cmd>Trouble diagnostics toggle filter.buf=0<cr>', { silent = true, desc = 'Document diagnostics (Trouble)' })
-- DAP
vim.keymap.set('n', '<leader>dap', ":lua require('dapui').toggle()<CR>", { desc = 'Toggle DAP UI' })
vim.keymap.set('n', '<leader>dc', ":lua require('dap').continue()<CR>", { desc = 'DAP continue' })
vim.keymap.set('n', '<leader>do', ":lua require('dap').step_over()<CR>", { desc = 'DAP step over' })
vim.keymap.set('n', '<leader>di', ":lua require('dap').step_into()<CR>", { desc = 'DAP step into' })
vim.keymap.set('n', '<leader>dk', function()
require('dap.ui.widgets').hover()
end, { desc = 'DAP hover value' })
vim.keymap.set('n', '<leader>d?', function()
local widgets = require 'dap.ui.widgets'
widgets.centered_float(widgets.scopes)
end, { desc = 'DAP scopes' })
vim.keymap.set('n', '<leader>du', ":lua require('dap').step_out()<CR>", { desc = 'DAP step out' })
vim.keymap.set('n', '<leader>dl', ":lua require('dapui').float_element()<CR>", { silent = true, noremap = true, desc = 'DAP floating window' })
vim.keymap.set('n', '<leader>dt', ":lua require('dap').toggle_breakpoint()<CR>", { silent = true, noremap = true, desc = 'Toggle breakpoint' })
vim.keymap.set('n', '<leader>dm', ":lua require('dap-python').test_method()<CR>", { silent = true, noremap = true, desc = 'DAP test method' })
vim.keymap.set('n', '<leader>df', ":lua require('dap-python').test_class()<CR>", { silent = true, noremap = true, desc = 'DAP test class' })
-- bufferline
local opts = { noremap = true, silent = true }
-- KEYMAPS (Barbar)
local opts = { noremap = true, silent = true }
vim.keymap.set('n', '<A-1>', '<Cmd>BufferGoto 1<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 1' }))
vim.keymap.set('n', '<A-2>', '<Cmd>BufferGoto 2<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 2' }))
vim.keymap.set('n', '<A-3>', '<Cmd>BufferGoto 3<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 3' }))
vim.keymap.set('n', '<A-4>', '<Cmd>BufferGoto 4<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 4' }))
vim.keymap.set('n', '<A-5>', '<Cmd>BufferGoto 5<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 5' }))
vim.keymap.set('n', '<A-6>', '<Cmd>BufferGoto 6<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 6' }))
vim.keymap.set('n', '<A-7>', '<Cmd>BufferGoto 7<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 7' }))
vim.keymap.set('n', '<A-8>', '<Cmd>BufferGoto 8<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 8' }))
vim.keymap.set('n', '<A-9>', '<Cmd>BufferGoto 9<CR>', vim.tbl_extend('force', opts, { desc = 'Go to buffer 9' }))
vim.keymap.set('n', '<A-0>', '<Cmd>BufferLast<CR>', vim.tbl_extend('force', opts, { desc = 'Go to last buffer' }))
-- Close current buffer (Barbar)
vim.keymap.set('n', '<A-w>', '<Cmd>BufferClose<CR>', vim.tbl_extend('force', opts, { desc = 'Close buffer' }))
-- Close current window/split
vim.keymap.set('n', '<A-q>', '<Cmd>close<CR>', vim.tbl_extend('force', opts, { desc = 'Close window' }))
vim.keymap.set('n', '<Tab>', '<Cmd>BufferNext<CR>', { desc = 'Go to next buffer' })
vim.keymap.set('n', '<S-Tab>', '<Cmd>BufferPrevious<CR>', { desc = 'Go to previous buffer' })
-- barbar colors
-- quickfix
vim.keymap.set('n', '<leader>cn', ':cnext<CR>', { desc = 'Next quickfix item' })
vim.keymap.set('n', '<leader>cp', ':cprevious<CR>', { desc = 'Previous quickfix item' })
-- markdown
vim.keymap.set('n', '<leader>mp', ':MarkdownPreview<CR>', { desc = 'Markdown preview' })
-- git
vim.keymap.set('n', '<leader>ga', ':Telescope coauthors<CR>', { desc = 'Select git co-authors' })
-- delete backwards
vim.keymap.set({ 'i', 'c' }, '<C-BS>', '<C-w>', { noremap = true, desc = 'Delete previous word' })
vim.keymap.set('n', '<leader>tc', ':tabclose<CR>', { desc = 'Close current tab' })
vim.keymap.set('n', '<leader>fk', ':FloatermKill!<CR>', { desc = 'Kill all floaterm terminals' })
vim.keymap.set('x', 'S', '<Plug>(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', '<C-w>', '<cmd>wqa<cr>')
-- split views
vim.keymap.set('n', '<leader>sv', '<cmd>vsplit<CR>')
vim.keymap.set('n', '<leader>sh', '<cmd>split<CR>')
-- 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', '<C-n>', ':lua toggle_neotree()<CR>', { noremap = true, silent = true })
-- Floaterm
vim.api.nvim_set_keymap('n', '<C-`>', ':FloatermToggle<CR>', { noremap = true })
vim.api.nvim_set_keymap('i', '<C-`>', ':FloatermToggle<CR>', { noremap = true })
vim.api.nvim_set_keymap('t', '<C-`>', '<C-\\><C-n>:FloatermToggle<CR>', { noremap = true, silent = true })
-- Comment.nvim keymaps
vim.api.nvim_set_keymap('n', '<C-/>', '<cmd>lua require("Comment.api").toggle.linewise.current()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<C-S-/>', '<cmd>lua require("Comment.api").toggle.blockwise.current()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('v', '<C-/>', '<ESC><cmd>lua require("Comment.api").toggle.linewise(vim.fn.visualmode())<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('v', '<C-S-/>', '<ESC><cmd>lua require("Comment.api").toggle.blockwise(vim.fn.visualmode())<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('i', '<C-H>', '<C-W>', { noremap = true })
-- git coauthors
vim.keymap.set('n', '<leader>ga', ':Telescope coauthors<CR>')
-- ======== LSP settings ===========
-- signature help
vim.api.nvim_set_keymap('i', '<C-S-Space>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<C-S-Space>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('i', '<C-S-Space>', '<cmd>lua vim.lsp.buf.hover()<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<C-S-Space>', '<cmd>lua vim.lsp.buf.hover()<CR>', { noremap = true, silent = true })
-- code actions (normal + insert)
vim.keymap.set({ 'n', 'i' }, '<C-.>', vim.lsp.buf.code_action, { silent = true, desc = 'Code Action' })
-- rename bindings
vim.keymap.set('n', '<F2>', vim.lsp.buf.rename, { silent = true, desc = 'LSP: Rename' })
vim.keymap.set('n', '<F12>', vim.lsp.buf.definition, { silent = true, desc = 'LSP: Go To Defintion' })
-- put after colorscheme load
vim.api.nvim_set_hl(0, '@lsp.type.typeAlias', { link = 'Type' })
vim.api.nvim_set_hl(0, '@lsp.type.type', { link = 'Type' })
vim.api.nvim_set_hl(0, '@lsp.type.struct', { link = 'Type' })