1-- utilities
2local function t(str)
3 return vim.api.nvim_replace_termcodes(str, true, true, true)
4end
5
6local function hi(group, str)
7 return str ~= '' and string.format('%%#%s#%s', group, str) or ''
8end
9
10vim.loader.enable()
11
12-- terminal options
13vim.api.nvim_exec([[
14 set t_Co=256
15 set t_ZH=^\[\[3m
16 set t_ZR=^\[\[23m
17 let &t_Cs = "\e\[4:3m"
18 let &t_Ce = "\e\[4:0m"
19 let &t_ut=''
20]], false)
21
22-- default save options
23vim.o.hidden = true
24vim.o.encoding = 'utf8'
25
26-- scroll and mouse options
27vim.o.mouse = 'a'
28vim.o.scrolloff = 2
29
30-- indentation options
31vim.o.autoindent = true
32vim.o.smartindent = false
33vim.o.breakindent = true
34
35-- default tab options
36vim.o.expandtab = true
37vim.o.tabstop = 2
38vim.o.softtabstop = 2
39vim.o.shiftwidth = 2
40
41-- tweak redraw timings
42vim.o.lazyredraw = true
43vim.o.updatetime = 500
44vim.o.timeoutlen = 500
45
46-- fold options
47vim.o.foldenable = true
48vim.o.foldmethod = 'indent'
49vim.o.foldnestmax = 9
50vim.o.foldlevelstart = 3
51
52-- search options
53vim.o.ignorecase = true
54vim.o.smartcase = true
55vim.o.infercase = true
56vim.o.hlsearch = false
57vim.o.incsearch = true
58vim.o.inccommand = 'nosplit'
59
60-- status options
61vim.o.laststatus = 2
62vim.o.statusline = '%F%m%r%h%w [%l,%c] [%L,%p%%]'
63
64-- disable backups and swaps
65vim.o.backup = false
66vim.o.writebackup = false
67vim.o.swapfile = false
68
69-- disable matchparen
70vim.o.showmatch = false
71vim.g.loaded_matchparen = 1
72
73-- disable netrw
74vim.g.loaded_netrw = 1
75vim.g.loaded_netrwPlugin = 1
76
77-- no completion or startup messages
78vim.o.shortmess = vim.o.shortmess .. 'WcCIAa'
79
80-- line numbers
81vim.wo.number = true
82
83-- char options
84vim.opt.fillchars = {
85 vert = '│',
86 diff = '╱',
87 horiz = '─',
88 horizup = '┴',
89 horizdown = '┬',
90 vertleft = '┤',
91 vertright = '├',
92 verthoriz = '┼',
93 eob = ' ',
94}
95vim.opt.listchars = {
96 nbsp = "␣",
97 extends = "»",
98 precedes = "«",
99 tab = " ",
100}
101vim.o.list = true
102
103-- splitting options
104vim.go.diffopt = 'filler,vertical,foldcolumn:0,closeoff,indent-heuristic,iwhite,algorithm:patience'
105vim.go.splitbelow = true
106vim.go.splitright = true
107vim.o.splitkeep = 'screen'
108vim.o.switchbuf = 'uselast'
109vim.o.previewheight = 5
110
111-- undo history
112local undodir = vim.fn.expand('$HOME') .. '/.cache/nvim/undo'
113if vim.fn.isdirectory(undodir) == 0 then
114 vim.fn.mkdir(undodir, 'p')
115end
116
117vim.o.undodir = undodir
118vim.o.undofile = true
119vim.o.undolevels = 1000
120vim.o.undoreload = 10000
121
122-- display options
123vim.o.wrap = false
124vim.o.showmode = false
125vim.o.ruler = false
126vim.o.termguicolors = true
127vim.o.cmdheight = 0
128vim.o.background = 'dark'
129vim.wo.signcolumn = 'number'
130vim.wo.cursorline = true
131vim.cmd('colorscheme theme')
132
133-- misc. options
134vim.o.completeopt = 'menuone,noinsert,noselect,popup'
135vim.o.pumheight = 10
136vim.o.winblend = 5
137vim.o.backspace = 'indent,eol,start'
138vim.o.virtualedit = 'block' -- Allow going past the end of line in visual block mode
139vim.o.formatoptions = 'qjl1' -- Don't autoformat comments
140vim.o.synmaxcol = 300 -- Don't highlight long lines
141vim.o.path = '**' -- Use a recursive path (for :find)
142vim.o.gdefault = true -- Use //g for replacements by default
143
144-- wildmenu
145vim.opt.wildignore:append(
146 '*.png,*.jpg,*.jpeg,*.gif,*.wav,*.aiff,*.dll,*.pdb,*.mdb,*.so,*.swp,*.zip,*.gz,*.bz2,*.meta,*.svg,*.cache,*/.git/*'
147)
148vim.o.wildmenu = true
149vim.o.wildmode = 'longest,list,full'
150
151-- built-in ftplugins should not change my keybindings
152vim.g.no_plugin_maps = true
153vim.cmd.filetype({ args = { 'plugin', 'on' } })
154vim.cmd.filetype({ args = { 'plugin', 'indent', 'on' } })
155
156--- ripgrep
157vim.o.grepprg = nix_bins.ripgrep .. ' --vimgrep --no-heading --smart-case'
158vim.o.grepformat = "%f:%l:%c:%m,%f:%l:%m"
159
160-- unmap special keys
161local key_opt = { noremap = true, silent = true }
162
163vim.keymap.set('', '<Space>', '<nop>', { noremap = true, silent = true })
164vim.keymap.set('', '<F1>', '<nop>', { noremap = true, silent = true })
165
166-- period command in visual mode
167vim.keymap.set('x', '.', '<cmd>norm .<cr>', { noremap = true, silent = true })
168
169-- match ctrl-c to escape
170vim.keymap.set('', '<c-c>', '<esc>', { noremap = true, silent = true })
171vim.keymap.set('!', '<c-c>', '<esc>', { noremap = true, silent = true })
172
173-- window controls
174vim.keymap.set('', '<c-w>,', ':vsp<cr>', { noremap = true, silent = true })
175vim.keymap.set('', '<c-w>.', ':sp<cr>', { noremap = true, silent = true })
176
177-- remap semicolon to colon
178vim.keymap.set('n', ';', ':', { noremap = true, silent = true })
179
180-- destructive x-commands
181vim.keymap.set('', 'X', '"_d', { noremap = true, silent = true })
182vim.keymap.set('n', 'XX', '"_dd', { noremap = true, silent = true })
183vim.keymap.set('v', 'x', '"_d', { noremap = true, silent = true })
184vim.keymap.set('n', 'x', 'v"_d', { noremap = true, silent = true })
185
186-- clipboard controls
187vim.keymap.set('x', 'Y', '"+y', { noremap = true, silent = true })
188vim.keymap.set('x', '<m-c>', '"+y', { noremap = true, silent = true })
189vim.keymap.set('x', '<m-v>', '"+p', { noremap = true, silent = true })
190vim.keymap.set('n', '<m-v>', '"+P', { noremap = true, silent = true })
191
192-- indentation in visual mode
193vim.keymap.set('v', '<', '<gv', { noremap = true, silent = true })
194vim.keymap.set('v', '>', '>gv', { noremap = true, silent = true })
195
196-- swap visual gj, gk, with jk
197vim.keymap.set({'n', 'x'}, 'j', [[v:count == 0 ? 'gj' : 'j']], { noremap = true, silent = true, expr = true })
198vim.keymap.set({'n', 'x'}, 'k', [[v:count == 0 ? 'gk' : 'k']], { noremap = true, silent = true, expr = true})
199
200-- macros per line
201vim.keymap.set('v', '@', ':<C-u>execute ":\'<,\'>normal @".nr2char(getchar())<CR>', { noremap = true, silent = true })
202
203-- fold controls
204vim.keymap.set('n', '<bar>', '<cmd>norm zc<cr>', { noremap = true, silent = true })
205vim.keymap.set('n', '<bslash>', '<cmd>norm za<cr>', { noremap = true, silent = true })
206
207-- set space as leader
208vim.g.mapleader = ' '
209vim.g.maplocalleader = ' '
210
211-- buffer controls
212vim.keymap.set('n', '<leader>h', '<cmd>bp<cr>', { desc = 'Previous Buffer' })
213vim.keymap.set('n', '<leader>l', '<cmd>bn<cr>', { desc = 'Next Buffer' })
214vim.keymap.set('n', '<leader>j', '<cmd>enew<cr>', { desc = 'New Buffer' })
215vim.keymap.set('n', '<leader>k', '<cmd>bp <bar> bd #<cr>', { desc = 'Close Buffer' })
216
217-- golden_size
218local function ignore_trouble_window()
219 local ft = vim.api.nvim_buf_get_option(0, 'filetype')
220 if ft == 'Trouble' then
221 return 1
222 elseif ft == 'gitsigns-blame' then
223 return 1
224 end
225end
226
227require('golden_size').set_ignore_callbacks {
228 { ignore_trouble_window },
229 { require('golden_size').ignore_float_windows },
230 { require('golden_size').ignore_by_window_flag },
231}
232
233-- markdown
234vim.api.nvim_create_autocmd({ 'FileType' }, {
235 pattern = "markdown",
236 callback = function()
237 vim.opt_local.wrap = true
238 vim.o.whichwrap = 'h,l'
239 vim.opt_local.linebreak = true
240 vim.opt_local.formatoptions = vim.opt_local.formatoptions + 'tcn12'
241 end,
242})
243
244-- telescope
245require('telescope').load_extension('zf-native')
246require('telescope').setup {
247 defaults = {
248 vimgrep_arguments = {
249 nix_bins.ripgrep,
250 '--color=never', '--no-heading', '--with-filename', '--line-number', '--column', '--smart-case'
251 },
252 prompt_prefix = ' ',
253 selection_caret = '→ ',
254 mappings = {
255 i = {
256 ['<c-t>'] = require('trouble.sources.telescope').open,
257 ['<c-c>'] = require('telescope.actions').close,
258 },
259 n = {
260 ['<c-t>'] = require('trouble.sources.telescope').open,
261 ['<esc>'] = require('telescope.actions').close,
262 },
263 },
264 },
265}
266
267vim.keymap.set('n', '<leader>b', function()
268 require('telescope.builtin').buffers()
269end, { desc = 'Workspace Search' })
270
271vim.keymap.set('n', '<leader>f', function()
272 require('telescope.builtin').live_grep()
273end, { desc = 'Workspace Search' })
274
275vim.keymap.set('n', '<leader>n', function()
276 require('telescope.builtin').lsp_document_symbols(
277 require('telescope.themes').get_ivy({
278 ignore_symbols = { 'variable', 'constant', 'property' },
279 })
280 )
281end, { desc = 'Document Symbols' })
282
283vim.keymap.set('n', '<leader>N', function()
284 require('telescope.builtin').lsp_dynamic_workspace_symbols(
285 require('telescope.themes').get_ivy({
286 ignore_symbols = { 'variable', 'constant', 'property' },
287 })
288 )
289end, { desc = 'Workspace Symbols' })
290
291vim.keymap.set('n', '<leader>o', function()
292 local telescope_builtins = require('telescope.builtin')
293 vim.fn.system('git rev-parse --is-inside-work-tree')
294 if vim.v.shell_error == 0 then
295 telescope_builtins.git_files()
296 else
297 telescope_builtins.find_files()
298 end
299end, { desc = 'Workspace Files' })
300
301-- define signs
302vim.fn.sign_define("DiagnosticSignError", { text = "●", texthl = "DiagnosticSignError" })
303vim.fn.sign_define("DiagnosticSignWarn", { text = "◐", texthl = "DiagnosticSignWarn" })
304vim.fn.sign_define("DiagnosticSignHint", { text = "", texthl = "DiagnosticSignHint" })
305vim.fn.sign_define("DiagnosticSignInfo", { text = "○", texthl = "DiagnosticSignInfo" })
306
307-- configure vim diagnostics
308vim.diagnostic.config({
309 underline = true,
310 signs = true,
311 update_in_insert = false,
312 severity_sort = true,
313 virtual_text = {
314 severity = { min = vim.diagnostic.severity.W },
315 source = 'if_many',
316 },
317 float = {
318 show_header = true,
319 source = 'if_many',
320 border = 'rounded',
321 focusable = false,
322 severity_sort = true,
323 },
324})
325
326vim.keymap.set('n', 'gk', vim.diagnostic.open_float, { desc = 'Show Diagnostic' })
327vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Previous Diagnostic' })
328vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Next Diagnostic' })
329
330-- customise hover window size
331vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
332 max_width = math.max(math.floor(vim.o.columns * 0.7), 100),
333 max_height = math.max(math.floor(vim.o.lines * 0.3), 30),
334})
335
336-- register custom filetypes
337vim.filetype.add({
338 extension = {
339 mdx = 'markdown',
340 astro = 'astro',
341 envrc = 'bash',
342 },
343 pattern = {
344 ['.*/%.vscode/.*%.json'] = 'json5',
345 },
346})
347
348-- lspconfig
349local lsp = require('lspconfig')
350local lsp_util = require('lspconfig.util')
351
352local function lsp_on_attach(client, buf)
353 if client.config.flags and not client.config.flags.allow_incremental_sync ~= nil then
354 client.config.flags.allow_incremental_sync = true
355 end
356
357 vim.api.nvim_buf_set_option(buf, 'formatexpr', 'v:lua.vim.lsp.formatexpr()')
358 vim.api.nvim_buf_set_option(buf, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
359 vim.api.nvim_buf_set_option(buf, 'tagfunc', 'v:lua.vim.lsp.tagfunc')
360
361 if pcall(function()
362 return vim.api.nvim_buf_get_var(buf, 'lsp:keys_attached')
363 end) ~= true then
364 vim.api.nvim_buf_set_var(buf, 'lsp:keys_attached', true)
365
366 vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = 'Go to definition', buffer = buf })
367 vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, { desc = 'Go to declaration', buffer = buf })
368 vim.keymap.set('n', 'gy', vim.lsp.buf.type_definition, { desc = 'Go to type definition', buffer = buf })
369 vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, { desc = 'Go to implementation', buffer = buf })
370 vim.keymap.set('n', 'gn', vim.lsp.buf.rename, { desc = 'Rename', buffer = buf })
371 vim.keymap.set('n', 'gf', vim.lsp.buf.code_action, { desc = 'Code Actions', buffer = buf })
372 vim.keymap.set('n', 'gr', '<cmd>Trouble lsp open<cr>', { desc = 'Show references', buffer = buf })
373 vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = buf })
374 vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, { buffer = buf })
375 vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, { buffer = buf })
376 end
377end
378
379local function lsp_capabilities(extends)
380 local capabilities = vim.tbl_deep_extend(
381 "force",
382 {},
383 vim.lsp.protocol.make_client_capabilities(),
384 require('cmp_nvim_lsp').default_capabilities() or {},
385 extends or {}
386 )
387 capabilities.textDocument.completion.completionItem.documentationFormat = { "markdown" }
388 capabilities.textDocument.completion.completionItem.snippetSupport = true
389 capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true
390 capabilities.workspace.workspaceFolders = true
391 return capabilities
392end
393
394local function lsp_setup(server, opts)
395 local config = lsp[server]
396 opts.autostart = false
397 opts.capabilities = lsp_capabilities(opts.capabilities)
398 opts.on_attach = lsp_on_attach
399 config.setup(opts)
400
401 local event
402 local pattern
403 if config.filetypes then
404 event = 'FileType'
405 pattern = table.concat(config.filetypes, ',')
406 else
407 event = 'BufReadPost'
408 pattern = '*'
409 end
410
411 vim.api.nvim_create_autocmd(event, {
412 pattern = pattern,
413 callback = function(opt)
414 if not vim.b[opt.buf or vim.api.nvim_get_current_buf()].big then
415 config.manager:try_add(opt.buf)
416 end
417 end,
418 group = lsp_group,
419 })
420end
421
422lsp_setup('ts_ls', {
423 cmd = { nix_bins.tsserver, "--stdio" },
424 flags = { debounce_text_changes = 200 },
425 single_file_support = false,
426 init_options = {
427 hostInfo = 'neovim',
428 disableAutomaticTypingAcquisition = true,
429 preferences = {
430 importModuleSpecifierPreference = 'project-relative',
431 },
432 },
433 root_dir = function(fname)
434 if lsp_util.root_pattern('.flowconfig')(fname) ~= nil then
435 return nil
436 else
437 return lsp_util.root_pattern('tsconfig.json', 'package.json', 'jsconfig.json', '.git')(fname)
438 end
439 end,
440})
441
442lsp_setup('flow', {
443 cmd = { nix_bins.bunx, "--no-install", "flow", "lsp" },
444 flags = { debounce_text_changes = 200 },
445 single_file_support = false,
446})
447
448lsp_setup('eslint', {
449 cmd = { nix_bins.eslintls, "--stdio" },
450 flags = { debounce_text_changes = 200 },
451 settings = {
452 rulesCustomizations = {
453 { rule = 'prettier/prettier', severity = 'off' },
454 { rule = 'sort-keys', severity = 'off' },
455 { rule = 'quotes', severity = 'off' },
456 { rule = 'max-len', severity = 'off' },
457 { rule = 'no-tabs', severity = 'off' },
458 },
459 },
460})
461
462lsp_setup('cssls', {
463 cmd = { nix_bins.cssls, "--stdio" },
464 flags = { debounce_text_changes = 200 },
465})
466
467lsp_setup('html', {
468 cmd = { nix_bins.htmlls, "--stdio" },
469 flags = { debounce_text_changes = 200 },
470})
471
472lsp_setup('jsonls', {
473 cmd = { nix_bins.jsonls, "--stdio" },
474 flags = { debounce_text_changes = 200 },
475})
476
477lsp_setup('rust_analyzer', {
478 cmd = { nix_bins.rustanalyzer },
479 flags = { debounce_text_changes = 200 },
480 settings = {
481 ["rust-analyzer"] = {
482 assist = {
483 importGranularity = "module",
484 importPrefix = "self",
485 },
486 cargo = {
487 loadOutDirsFromCheck = true,
488 },
489 procMacro = {
490 enable = true,
491 },
492 },
493 },
494})
495
496lsp_setup('terraformls', {
497 cmd = { nix_bins.terraformls },
498 flags = { debounce_text_changes = 200 },
499})
500
501lsp_setup('zls', {
502 cmd = { nix_bins.zls },
503 flags = { debounce_text_changes = 200 },
504 settings = {
505 zls = {
506 zig_exe_path = nix_bins.zig,
507 },
508 }
509})
510
511-- treesitter
512vim.opt.runtimepath:append("~/.local/share/nvim/site/parser")
513
514require('nvim-treesitter.install').compilers = { nix_bins.clang }
515
516require('nvim-treesitter.configs').setup {
517 auto_install = false,
518 highlight = {
519 enable = true,
520 disable = function(lang, buf)
521 return vim.b[buf].big
522 end,
523 },
524 incremental_selection = {
525 enable = true,
526 disable = function(lang, buf)
527 return vim.b[buf].big
528 end,
529 keymaps = {
530 node_incremental = "]",
531 scope_incremental = "=",
532 node_decremental = "[",
533 },
534 },
535 refactor = {
536 highlight_definitions = {
537 enable = true,
538 disable = function(lang, buf)
539 return vim.b[buf].big
540 end,
541 },
542 smart_rename = {
543 enable = true,
544 disable = function(lang, buf)
545 return vim.b[buf].big
546 end,
547 keymaps = { smart_rename = "gn" }
548 },
549 },
550 textobjects = {
551 select = {
552 enable = true,
553 disable = function(lang, buf)
554 return vim.b[buf].big
555 end,
556 keymaps = {
557 ["af"] = "@function.outer",
558 ["if"] = "@function.inner",
559 ["ac"] = "@call.outer",
560 ["ic"] = "@call.inner",
561 },
562 },
563 },
564}
565
566vim.keymap.set('n', '<c-e', function()
567 print(vim.inspect(vim.treesitter.get_captures_at_cursor(0)))
568end, { desc = 'Output Treesitter Token' })
569
570-- lspkind
571require('lspkind').init {
572 mode = 'symbol_text',
573 preset = 'codicons',
574}
575
576-- file browser
577require("oil").setup({
578 default_file_explorer = true,
579 delete_to_trash = false,
580 skip_confirm_for_simple_edits = true,
581 prompt_save_on_select_new_entry = true,
582 cleanup_delay_ms = 2000,
583 lsp_file_methods = {
584 timeout_ms = 1000,
585 autosave_changes = false,
586 },
587 constrain_cursor = 'editable',
588 watch_for_changes = false,
589 use_default_keymaps = true,
590 keymaps = {
591 ["<C-c>"] = false,
592 },
593 view_options = {
594 show_hidden = false,
595 is_hidden_file = function(name, bufnr)
596 return vim.startswith(name, '.')
597 end,
598 is_always_hidden = function(name, bufnr)
599 return name == '.DS_Store'
600 end,
601 natural_order = true,
602 case_insensitive = true,
603 sort = {
604 { 'type', 'asc' },
605 { 'name', 'asc' },
606 },
607 },
608 float = {
609 preview_split = 'auto',
610 border = 'none',
611 win_options = {
612 winblend = 5,
613 },
614 },
615 preview = {
616 border = 'none',
617 win_options = { winblend = 5 },
618 update_on_cursor_moved = true,
619 },
620 progress = {
621 border = 'none',
622 minimized_border = 'none',
623 win_options = { winblend = 5 },
624 },
625 ssh = { border = 'none', },
626 keymaps_help = { border = 'none' },
627})
628
629vim.keymap.set('n', '-', require('oil').open, { noremap = true, silent = true })
630
631-- hide sticky commands
632vim.api.nvim_create_autocmd({ 'CursorHold' }, {
633 pattern = "*",
634 callback = function()
635 vim.defer_fn(function()
636 if vim.api.nvim_get_mode().mode == 'n' then
637 vim.cmd('echon ""')
638 end
639 end, 3000)
640 end,
641})
642
643-- mark big buffers
644vim.api.nvim_create_autocmd({ 'BufReadPre' }, {
645 pattern = "*",
646 callback = function()
647 local buf = vim.api.nvim_get_current_buf()
648 local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
649 vim.b[buf].big = ok and stats and (stats.size > 100 * 1024)
650 if vim.b[buf].big then
651 vim.opt_local.spell = false
652 vim.opt_local.showmatch = false
653 vim.opt_local.undofile = false
654 vim.opt_local.foldmethod = 'manual'
655 end
656 end,
657})
658
659-- customise fold text
660vim.api.nvim_exec([[
661 function! FoldText()
662 let nl = v:foldend - v:foldstart + 1
663 let start = substitute(getline(v:foldstart), "^ *", "", 1)
664 let end = substitute(getline(v:foldend),"^ *", "", 1)
665 let txt = ' ' . start . ' … ' . end . ' (' . nl . ' lines) '
666 return txt
667 endfunction
668 set foldtext=FoldText()
669]], false)
670
671-- autocompletion
672local cmp = require('cmp')
673
674local has_words_before = function()
675 local line, col = unpack(vim.api.nvim_win_get_cursor(0))
676 return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
677end
678
679cmp.setup {
680 enabled = function()
681 local context = require('cmp.config.context')
682 if vim.b[vim.api.nvim_get_current_buf()].big then
683 return false
684 elseif vim.api.nvim_get_mode().mode == 'c' then
685 return true
686 else
687 return not context.in_treesitter_capture('comment')
688 and not context.in_syntax_group('Comment')
689 end
690 end,
691 preselect = cmp.PreselectMode.None,
692 mapping = cmp.mapping.preset.insert({
693 ['<C-d>'] = cmp.mapping.scroll_docs(-4),
694 ['<C-f>'] = cmp.mapping.scroll_docs(4),
695 ['<C-Space>'] = cmp.mapping.complete(),
696 ['<C-c>'] = cmp.mapping(function(fallback)
697 if cmp.visible() then
698 cmp.close()
699 end
700 fallback()
701 end, {"i", "c"}),
702 ['<CR>'] = cmp.mapping({
703 i = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false }),
704 c = function(fallback)
705 if cmp.visible() then
706 cmp.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false })
707 else
708 fallback()
709 end
710 end
711 }),
712 ['<Tab>'] = cmp.mapping(function(fallback)
713 if cmp.visible() then
714 cmp.select_next_item()
715 elseif has_words_before() then
716 cmp.complete()
717 else
718 fallback()
719 end
720 end, {"i", "s"}),
721 ['<S-Tab>'] = cmp.mapping(function(fallback)
722 if cmp.visible() then
723 cmp.select_prev_item()
724 else
725 fallback()
726 end
727 end, {"i", "s"}),
728 }),
729 snippet = {
730 expand = function(args)
731 vim.snippet.expand(args.body)
732 end,
733 },
734 completion = {
735 keyword_length = 3
736 },
737 view = {
738 entries = "native",
739 },
740 experimental = {
741 ghost_text = {
742 hl_group = 'GhostText',
743 },
744 },
745 sources = cmp.config.sources({
746 { name = 'nvim_lsp_signature_help' },
747 { name = 'nvim_lsp' },
748 { name = 'treesitter' },
749 }),
750 formatting = {
751 format = require('lspkind').cmp_format(),
752 },
753}
754
755cmp.setup.filetype('gitcommit', {
756 enabled = false,
757})
758
759-- gitsigns
760require('gitsigns').setup {
761 signcolumn = true,
762 numhl = false,
763 linehl = false,
764 word_diff = false,
765 current_line_blame = false,
766 signs = {
767 add = { text = '│' },
768 change = { text = '│' },
769 delete = { text = '' },
770 topdelete = { text = '' },
771 changedelete = { text = '' },
772 },
773 diff_opts = {
774 algorithm = 'histogram',
775 internal = true,
776 linematch = true,
777 indent_heuristic = true,
778 ignore_blank_lines = true,
779 ignore_whitespace_change = true,
780 ignore_whitespace_change_at_eol = true,
781 },
782 preview_config = {
783 border = 'none',
784 style = 'minimal',
785 relative = 'cursor',
786 row = 1,
787 col = 0
788 },
789 on_attach = function(buf)
790 local actions = require('gitsigns.actions')
791
792 vim.keymap.set('n', ']c', function()
793 require('gitsigns.actions').next_hunk()
794 end, { desc = 'Next Git Hunk', buffer = buf })
795 vim.keymap.set('n', '[c', function()
796 require('gitsigns.actions').next_hunk()
797 end, { desc = 'Previous Git Hunk', buffer = buf })
798 vim.keymap.set('n', 'gb', function()
799 require('gitsigns.actions').blame_line({ full = true })
800 end, { desc = 'Blame Line', buffer = buf })
801 vim.keymap.set('n', 'gB', function()
802 require('gitsigns.actions').blame()
803 end, { desc = 'Blame Buffer', buffer = buf })
804 vim.keymap.set('n', 'gh', function()
805 require('gitsigns.actions').preview_hunk(true)
806 end, { desc = 'Show Git Hunk', buffer = buf })
807 vim.keymap.set('n', 'gs', function()
808 require('gitsigns.actions').stage_hunk()
809 end, { desc = 'Stage Git Hunk', buffer = buf })
810 vim.keymap.set('n', 'gS', function()
811 require('gitsigns.actions').stage_hunk()
812 end, { desc = 'Unstage Git Hunk', buffer = buf })
813 vim.keymap.set('n', 'gt', function()
814 require('gitsigns.actions').diffthis()
815 end, { desc = 'Diff against HEAD', buffer = buf })
816 vim.keymap.set('n', 'gT', function()
817 require('gitsigns.actions').diffthis('~')
818 end, { desc = 'Diff against HEAD~1', buffer = buf })
819 end,
820}
821
822-- hardline
823local function status_diagnostic_handler(prefix, severity)
824 return function()
825 local count = #vim.diagnostic.get(0, { severity = severity })
826 if count > 0 then
827 return string.format('%s %d', prefix, count)
828 else
829 return ''
830 end
831 end
832end
833
834local function status_git()
835 local b = vim.b
836 if b.gitsigns_status_dict then
837 return table.concat({
838 hi('GitSignsAdd', string.format(' %d', b.gitsigns_status_dict.added or 0)),
839 hi('GitSignsChange', string.format(' %d', b.gitsigns_status_dict.changed or 0)),
840 hi('GitSignsDelete', string.format(' %d', b.gitsigns_status_dict.removed or 0)),
841 hi('DiagnosticSignInfo', b.gitsigns_head ~= nil and string.format('( %s)', b.gitsigns_head) or ''),
842 }, ' ')
843 else
844 return ''
845 end
846end
847
848local function status_filetype()
849 return (vim.bo.filetype ~= '' and string.format(' %s', vim.bo.filetype) or '')
850end
851
852local function status_progress()
853 local blocks = {'█', '▇', '▆', '▅', '▄', '▃', '▂', '▁', ' '}
854 local nbline = vim.fn.line('$')
855 local line = vim.fn.line('.')
856 local progress = blocks[math.ceil(line / (nbline / 9))]
857 return nbline ~= 1 and table.concat({
858 string.format('%s%d/%d', string.rep('·', #tostring(nbline) - #tostring(line)), line, nbline),
859 progress,
860 }, ' ') or ''
861end
862
863require('hardline').setup {
864 bufferline = false,
865 theme = vim.tbl_deep_extend(
866 "force",
867 require('hardline.themes.custom_colors').set(hardline_colors),
868 {
869 mode = {
870 inactive = {
871 guibg = hardline_colors.inactive_menu.gui,
872 ctermbg = hardline_colors.inactive_menu.cterm,
873 },
874 },
875 }
876 ),
877 sections = {
878 {class = 'mode', item = require('hardline.parts.mode').get_item},
879 {class = 'high', item = function() return vim.fn.expand('%:~:.') end },
880 {class = 'med', item = status_git, hide = 100},
881 '%<',
882 {class = 'med', item = '%='},
883 {class = 'error', item = status_diagnostic_handler('', vim.diagnostic.severity.ERROR) },
884 {class = 'warning', item = status_diagnostic_handler('', vim.diagnostic.severity.WARN) },
885 {class = 'high', item = status_filetype, hide = 80},
886 {class = 'mode', item = status_progress },
887 },
888}
889
890-- trouble
891require('trouble').setup {
892 height = 13,
893 fold_open = "",
894 fold_closed = "",
895 padding = false,
896 indent_lines = false,
897 icons = {
898 ---@type trouble.Indent.symbols
899 indent = {
900 top = "│ ",
901 middle = "├╴",
902 last = "└╴",
903 fold_open = " ",
904 fold_closed = " ",
905 ws = " ",
906 },
907 folder_closed = " ",
908 folder_open = " ",
909 kinds = {
910 Array = " ",
911 Boolean = " ",
912 Class = " ",
913 Constant = " ",
914 Constructor = " ",
915 Enum = " ",
916 EnumMember = " ",
917 Event = " ",
918 Field = " ",
919 File = " ",
920 Function = " ",
921 Interface = " ",
922 Key = " ",
923 Method = " ",
924 Module = " ",
925 Namespace = " ",
926 Null = " ",
927 Number = " ",
928 Object = " ",
929 Operator = " ",
930 Package = " ",
931 Property = " ",
932 String = " ",
933 Struct = " ",
934 TypeParameter = " ",
935 Variable = " ",
936 },
937 },
938}
939
940vim.api.nvim_create_autocmd({ 'FileType' }, {
941 pattern = "Trouble",
942 callback = function()
943 vim.opt_local.wrap = true
944 vim.opt_local.linebreak = true
945 end,
946})
947
948vim.keymap.set('n', '<leader>q', '<cmd>Trouble qflist open<cr>', { desc = 'Quickfix list' })
949vim.keymap.set('n', '<leader>p', '<cmd>Trouble loclist open<cr>', { desc = 'Location list' })
950vim.keymap.set('n', '<leader>d', '<cmd>Trouble diagnostics open filter.buf=0<cr>', { desc = 'Document Diagnostics' })
951vim.keymap.set('n', '<leader>D', '<cmd>Trouble diagnostics open<cr>', { desc = 'Workspace Diagnostics' })
952
953-- dressing
954require('dressing').setup {
955 select = {
956 backend = { "telescope", "builtin", "nui" },
957 telescope = require('telescope.themes').get_cursor(),
958 },
959}