Simon Bruder
10b8d432d5
This applies the REUSE specification to the repository, so the licensing information can be tracked for every file individually.
497 lines
15 KiB
Lua
497 lines
15 KiB
Lua
-- SPDX-FileCopyrightText: 2018-2023 Simon Bruder <simon@sbruder.de>
|
|
--
|
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-- 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', '<CR>', ':nohlsearch<CR>', { silent = true }) -- hide highlights on enter
|
|
noremap('v', '<Leader>s', ':sort<CR>') -- sort in visual mode
|
|
noremap('t', '<Esc><Esc>', [[<C-\><C-n>]])
|
|
|
|
-- Filetypes
|
|
associate_filetype('*.vpy', 'python') -- vapoursynth scripts
|
|
associate_filetype('*.zone', 'bindzone') -- bind-style DNS zone file
|
|
|
|
setup_filetype('bib', 1)
|
|
setup_filetype('c', 4)
|
|
setup_filetype('cpp', 2)
|
|
setup_filetype('dockerfile', 4)
|
|
setup_filetype('gitcommit', nil, 'colorcolumn=72')
|
|
setup_filetype('go', 4, 'noexpandtab')
|
|
setup_filetype('haskell', 4)
|
|
setup_filetype('java', 4)
|
|
setup_filetype('lua', 4)
|
|
setup_filetype('nftables', 4, 'noexpandtab')
|
|
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', '<F8>', ':TagbarToggle<CR>')
|
|
|
|
-- 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 '<Ignore>'
|
|
end, {expr=true})
|
|
|
|
map('n', '[c', function()
|
|
if vim.wo.diff then return '[c' end
|
|
vim.schedule(function() gs.prev_hunk() end)
|
|
return '<Ignore>'
|
|
end, {expr=true})
|
|
|
|
-- Actions
|
|
map({'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>')
|
|
map({'n', 'v'}, '<leader>hr', ':Gitsigns reset_hunk<CR>')
|
|
map('n', '<leader>hS', gs.stage_buffer)
|
|
map('n', '<leader>hu', gs.undo_stage_hunk)
|
|
map('n', '<leader>hR', gs.reset_buffer)
|
|
map('n', '<leader>hp', gs.preview_hunk)
|
|
map('n', '<leader>hb', function() gs.blame_line{full=true} end)
|
|
map('n', '<leader>tb', gs.toggle_current_line_blame)
|
|
map('n', '<leader>hd', gs.diffthis)
|
|
map('n', '<leader>hD', function() gs.diffthis('~') end)
|
|
map('n', '<leader>td', gs.toggle_deleted)
|
|
|
|
-- Text object
|
|
map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')
|
|
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({
|
|
['<C-d>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), { 'i', 'c' }),
|
|
['<C-f>'] = cmp.mapping(cmp.mapping.scroll_docs(4), { 'i', 'c' }),
|
|
['<C-e>'] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close() }),
|
|
['<CR>'] = cmp.mapping.confirm({ select = false }),
|
|
|
|
['<Tab>'] = 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' }),
|
|
|
|
['<S-Tab>'] = 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', '<C-k>', vim.lsp.buf.signature_help, opts)
|
|
vim.keymap.set('n', '<Leader>wa', vim.lsp.buf.add_workspace_folder, opts)
|
|
vim.keymap.set('n', '<Leader>wr', vim.lsp.buf.remove_workspace_folder, opts)
|
|
vim.keymap.set('n', '<Leader>wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts)
|
|
vim.keymap.set('n', '<Leader>D', require("telescope.builtin").lsp_type_definitions, opts)
|
|
vim.keymap.set('n', '<Leader>rn', vim.lsp.buf.rename, opts)
|
|
vim.keymap.set('n', '<Leader>ca', vim.lsp.buf.code_action, opts)
|
|
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
|
|
vim.keymap.set('n', '<Leader>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', '<Leader>q', vim.diagnostic.setloclist, opts)
|
|
vim.keymap.set('n', '<Leader>f', function() vim.lsp.buf.format{ async = true } end, opts)
|
|
|
|
require('illuminate').on_attach(client)
|
|
vim.keymap.set('n', '<a-n>', function() require("illuminate").next_reference{wrap=true} end, opts)
|
|
vim.keymap.set('n', '<a-p>', function() require("illuminate").next_reference{reverse=true,wrap=true} end, opts)
|
|
end
|
|
|
|
lsp.ccls.setup {
|
|
on_attach = on_attach,
|
|
init_options = {
|
|
compilationDatabaseDirectory = 'build';
|
|
index = {
|
|
threads = 0;
|
|
};
|
|
clang = {
|
|
excludeArgs = {'-frounding-math'};
|
|
};
|
|
}
|
|
}
|
|
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.ltex.setup {
|
|
on_attach = on_attach,
|
|
settings = {
|
|
ltex = {
|
|
language = 'en-GB',
|
|
languageToolHttpServerUri = 'https://languagetool.sbruder.de/',
|
|
},
|
|
},
|
|
-- both taken from upstream, extended
|
|
filetypes = {
|
|
'bib',
|
|
'gitcommit',
|
|
'mail',
|
|
'markdown',
|
|
'org',
|
|
'plaintex',
|
|
'rst',
|
|
'rnoweb',
|
|
'tex',
|
|
'pandoc'
|
|
},
|
|
get_language_id = function(_, filetype)
|
|
local language_id_mapping = {
|
|
bib = 'bibtex',
|
|
gitcommit = 'git-commit',
|
|
mail = 'markdown', -- quite wrong, but allows magic commands
|
|
pandoc = 'markdown',
|
|
plaintex = 'tex',
|
|
rnoweb = 'sweave',
|
|
rst = 'restructuredtext',
|
|
tex = 'latex',
|
|
xhtml = 'xhtml',
|
|
}
|
|
local language_id = language_id_mapping[filetype]
|
|
if language_id then
|
|
return language_id
|
|
else
|
|
return filetype
|
|
end
|
|
end,
|
|
}
|
|
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'] = {
|
|
cargo = {
|
|
buildScripts = {
|
|
enable = true,
|
|
},
|
|
},
|
|
checkOnSave = {
|
|
command = 'clippy',
|
|
},
|
|
imports = {
|
|
granularity = {
|
|
group = "module",
|
|
},
|
|
prefix = "self",
|
|
},
|
|
procMacro = {
|
|
enable = true
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
vim.api.nvim_create_autocmd({"FileType"}, {
|
|
pattern = "java",
|
|
callback = function()
|
|
local jdtls = require('jdtls')
|
|
|
|
local config = {
|
|
cmd = {
|
|
'jdt-language-server',
|
|
'-configuration', vim.fn.expand('~') .. '/.cache/jdtls/config',
|
|
'-data', vim.fn.expand('~') .. '/.local/share/jdtls/workspace/' .. vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t'),
|
|
},
|
|
|
|
-- https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
|
|
settings = {
|
|
java = {
|
|
signatureHelp = { enabled = true };
|
|
completion = {
|
|
favoriteStaticMembers = {
|
|
},
|
|
filteredTypes = {
|
|
"com.sun.*",
|
|
"java.awt.*",
|
|
"jdk.*",
|
|
"sun.*",
|
|
},
|
|
};
|
|
maven = {
|
|
downloadSources = true,
|
|
},
|
|
|
|
implementationsCodeLens = {
|
|
enabled = true,
|
|
},
|
|
referencesCodeLens = {
|
|
enabled = true,
|
|
},
|
|
},
|
|
},
|
|
|
|
on_attach = function(client, bufnr)
|
|
on_attach(client, bufnr) -- global lsp configuration
|
|
|
|
jdtls.setup.add_commands()
|
|
|
|
local opts = { noremap = true, silent = true }
|
|
|
|
vim.keymap.set('n', '<Leader>jo', jdtls.organize_imports, opts)
|
|
vim.keymap.set('n', '<Leader>jtc', jdtls.test_class, opts)
|
|
vim.keymap.set('n', '<Leader>jtm', jdtls.test_nearest_method, opts)
|
|
vim.keymap.set('n', '<Leader>jev', jdtls.extract_variable, opts)
|
|
vim.keymap.set('v', '<Leader>jem', [[<ESC><CMD>lua require('jdtls').extract_method(true)<CR>]], opts)
|
|
vim.keymap.set('n', '<Leader>jec', jdtls.extract_constanconstant, opts)
|
|
end,
|
|
}
|
|
|
|
jdtls.start_or_attach(config)
|
|
end
|
|
})
|
|
|
|
require('trouble').setup {}
|
|
|
|
-- Tree Sitter
|
|
require('nvim-treesitter.configs').setup {
|
|
highlight = {
|
|
enable = true,
|
|
additional_vim_regex_highlighting = false,
|
|
disable = {
|
|
'gitcommit', -- only for “conventional commits”
|
|
'latex', -- incompatible with VimTeX
|
|
},
|
|
},
|
|
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', '<Leader>rt', ':RustTest<CR>')
|
|
|
|
-- Markdown
|
|
g.vim_markdown_folding_disabled = 1
|