-- Aliases local cmd = vim.cmd local fn = vim.fn local g = vim.g local opt = vim.opt local map = vim.api.nvim_set_keymap -- Helpers local function noremap(mode, lhs, rhs, opts) local options = { noremap = true } if opts then options = vim.tbl_extend('force', options, opts) end vim.keymap.set(mode, lhs, rhs, options) end local function associate_filetype(glob, ft) cmd('autocmd BufRead,BufNewFile ' .. glob .. ' set filetype=' .. ft) end local function setup_filetype(ft, tab, opts) local cmdline = 'autocmd Filetype ' .. ft .. ' setlocal' if tab then cmdline = cmdline .. ' ts=' .. tab .. ' sw=' .. tab .. ' sts=' .. tab end if opts then cmdline = cmdline .. ' ' .. opts end -- FIXME: opts should be a table cmd(cmdline) end local has_words_before = function() local line, col = unpack(vim.api.nvim_win_get_cursor(0)) return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match('%s') == nil end -- Display opt.number = true -- line numbers opt.colorcolumn = { '80' } -- line at 80 characters opt.scrolloff = 5 -- context at top/bottom opt.sidescrolloff = 8 -- context on the left/right side opt.conceallevel = 1 -- conceal inactive lines opt.termguicolors = true cmd('colorscheme solarized') -- Undo opt.undofile = true opt.undolevels = 4096 opt.undoreload = 16384 cmd('autocmd BufWritePre /dev/shm/* setlocal noundofile') -- exclude pass cmd('autocmd BufWritePre /tmp/* setlocal noundofile') -- exclude sops -- Behaviour opt.hidden = true -- background buffers opt.expandtab = true -- spaces instead of tabs by default opt.smartindent = true -- automatic indentation opt.tabstop = 2 -- tabs have width of 2 by default opt.shiftwidth = 0 -- shift width = tab width opt.joinspaces = false -- no double spaces when joining opt.clipboard = 'unnamedplus' -- use system clipboard opt.modeline = true opt.mouse = ''; -- I want the mouse to only interact with the terminal, not vim cmd([[ autocmd BufReadPost * if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit' | exe "normal! g`\"" | endif ]]) -- see :h last-position-jump -- Search opt.ignorecase = true -- ignore case opt.inccommand = 'nosplit' -- live preview of substitution opt.smartcase = true -- but only with all-lowercase searches -- Highlight trailing spaces cmd([[autocmd BufWinEnter * match errorMsg /\s\+$\| \+\ze\t/]]) -- Mappings g.mapleader = ',' noremap('n', '', ':nohlsearch', { silent = true }) -- hide highlights on enter noremap('v', 's', ':sort') -- sort in visual mode noremap('t', '', [[]]) -- Filetypes associate_filetype('*.vpy', 'python') -- vapoursynth scripts setup_filetype('bib', 1) setup_filetype('c', 4) setup_filetype('cpp', 4) setup_filetype('dockerfile', 4) setup_filetype('gitcommit', nil, 'colorcolumn=72') setup_filetype('go', 4, 'noexpandtab') setup_filetype('haskell', 4) setup_filetype('lua', 4) setup_filetype('python', 4) setup_filetype('sh', 4, 'noexpandtab') setup_filetype('tex', 1) setup_filetype('zsh', 4) -- Plugins -- Status line local custom_solarized_lualine = require('lualine.themes.solarized') -- default colours are very hard to read custom_solarized_lualine.normal.b = { fg = '#eee8d5', bg = '#586e75' } require('lualine').setup { options = { theme = custom_solarized_lualine, }, sections = { lualine_c = { 'filename', 'lsp_progress', }, }, } -- Tagbar noremap('n', '', ':TagbarToggle') -- Keymap popup require('which-key').setup {} -- Icons require('nvim-web-devicons').setup { default = true } -- Git require('plenary') -- otherwise neogit SIGABRTs require('neogit').setup { disable_commit_confirmation = true, integrations = { diffview = true, }, } cmd([[ hi NeogitNotificationInfo guifg=#268bd2 hi NeogitNotificationWarning guifg=#cb4b16 hi NeogitNotificationError guifg=#dc322f ]]) require('gitsigns').setup { -- copied from upstream readme on_attach = function(bufnr) local gs = package.loaded.gitsigns local function map(mode, l, r, opts) opts = opts or {} opts.buffer = bufnr vim.keymap.set(mode, l, r, opts) end -- Navigation map('n', ']c', function() if vim.wo.diff then return ']c' end vim.schedule(function() gs.next_hunk() end) return '' end, {expr=true}) map('n', '[c', function() if vim.wo.diff then return '[c' end vim.schedule(function() gs.prev_hunk() end) return '' end, {expr=true}) -- Actions map({'n', 'v'}, 'hs', ':Gitsigns stage_hunk') map({'n', 'v'}, 'hr', ':Gitsigns reset_hunk') map('n', 'hS', gs.stage_buffer) map('n', 'hu', gs.undo_stage_hunk) map('n', 'hR', gs.reset_buffer) map('n', 'hp', gs.preview_hunk) map('n', 'hb', function() gs.blame_line{full=true} end) map('n', 'tb', gs.toggle_current_line_blame) map('n', 'hd', gs.diffthis) map('n', 'hD', function() gs.diffthis('~') end) map('n', 'td', gs.toggle_deleted) -- Text object map({'o', 'x'}, 'ih', ':Gitsigns select_hunk') end } require('diffview').setup {} -- Fuzzy finder/option picker require('telescope').setup {} require("telescope").load_extension("ui-select") -- Completion/Snippets local cmp = require('cmp') local lspkind = require('lspkind') local luasnip = require('luasnip') luasnip.config.set_config { enable_autosnippets = true, } require('snippets') cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = cmp.mapping.preset.insert({ [''] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }), [''] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }), [''] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close() }), [''] = cmp.mapping.confirm({ select = false }), [''] = cmp.mapping(function(fallback) if luasnip.expand_or_jumpable() then luasnip.expand_or_jump() elseif cmp.visible() then cmp.select_next_item() elseif has_words_before() then cmp.complete() else fallback() end end, { 'i', 's' }), [''] = cmp.mapping(function(fallback) if luasnip.jumpable(-1) then luasnip.jump(-1) elseif cmp.visible() then cmp.select_prev_item() else fallback() end end, { 'i', 's' }), }), sources = cmp.config.sources({ { name = 'nvim_lsp' }, { name = 'luasnip' }, { name = 'path' } }, { { name = 'buffer', option = { keyword_pattern = [[\k\+]], -- allow unicode multibyte characters }, } }), formatting = { format = lspkind.cmp_format({ with_text = false, maxwidth = 50, }) }, } -- LSP local lsp = require('lspconfig') vim.lsp.handlers['textDocument/publishDiagnostics'] = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { update_in_insert = true, -- update suggestions in insert mode }) for type, icon in pairs({ Error = ' ', Warn = ' ', Hint = ' ', Info = ' ' }) do fn.sign_define("DiagnosticSign" .. type, { text = icon, texthl = "Diagnostic" .. type }) end local on_attach = function(client, bufnr) vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') local opts = { noremap = true, silent = true } vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) vim.keymap.set('n', 'gd', require("telescope.builtin").lsp_definitions, opts) vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) vim.keymap.set('n', 'gi', require("telescope.builtin").lsp_implementations, opts) vim.keymap.set('n', '', vim.lsp.buf.signature_help, opts) vim.keymap.set('n', 'wa', vim.lsp.buf.add_workspace_folder, opts) vim.keymap.set('n', 'wr', vim.lsp.buf.remove_workspace_folder, opts) vim.keymap.set('n', 'wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts) vim.keymap.set('n', 'D', require("telescope.builtin").lsp_type_definitions, opts) vim.keymap.set('n', 'rn', vim.lsp.buf.rename, opts) vim.keymap.set('n', 'ca', vim.lsp.buf.code_action, opts) vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts) vim.keymap.set('n', 'd', vim.diagnostic.open_float, opts) vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts) vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts) vim.keymap.set('n', 'q', vim.diagnostic.setloclist, opts) vim.keymap.set('n', 'f', function() vim.lsp.buf.format{ async = true } end, opts) require('illuminate').on_attach(client) vim.keymap.set('n', '', function() require("illuminate").next_reference{wrap=true} end, opts) vim.keymap.set('n', '', function() require("illuminate").next_reference{reverse=true,wrap=true} end, opts) end lsp.gopls.setup { on_attach = on_attach, } lsp.hls.setup { on_attach = on_attach, root_dir = lsp.util.root_pattern('*.cabal', 'stack.yaml', 'cabal.project', 'package.yaml', 'hie.yaml', '.git'), } lsp.pylsp.setup { on_attach = on_attach, } lsp.rnix.setup { on_attach = on_attach, } lsp.rust_analyzer.setup { on_attach = on_attach, settings = { ['rust-analyzer'] = { checkOnSave = { command = 'clippy', }, }, }, } require('trouble').setup {} -- Tree Sitter require('nvim-treesitter.configs').setup { highlight = { enable = true, additional_vim_regex_highlighting = false, }, indent = { enable = true, }, } -- VimTeX g.tex_flavor = 'latex' g.vimtex_view_method = 'zathura' g.tex_conceal = 'abdmg' -- this disables some helful warnings that often have a reason why I ignore them g.vimtex_quickfix_ignore_filters = { [[Underfull \\hbox (badness [0-9]*) in ]], [[Overfull \\hbox ([0-9]*.[0-9]*pt too wide) in ]], [[Overfull \\vbox ([0-9]*.[0-9]*pt too high) detected ]], [[Package hyperref Warning: Token not allowed in a PDF string]], [[Package typearea Warning: Bad type area settings!]], } g.vimtex_syntax_custom_cmds = { { name = 'llbracket', mathmode = true, concealchar = '⟦', }, { name = 'rrbracket', mathmode = true, concealchar = '⟧', }, } -- When using math environments vim does not know if if it currently is in one or outside of one -- unless it parses the file from the start. -- Parsing the file from the start each time fixes this -- but leads to a performance drop (depending on the number of lines). -- Also, somehow using FileType tex does not work, so this will enable slow syntax highlighting -- everywhere once a *.tex file is opened. cmd([[autocmd BufEnter *.tex syntax sync fromstart]]) -- rust.vim g.rustfmt_autosave_if_config_present = 1 g.rust_fold = 1 noremap('n', 'rt', ':RustTest') -- Markdown g.vim_markdown_folding_disabled = 1