" Prefer $HOME/.vim to $HOME/.config/nvim (for now): set runtimepath^=~/.vim runtimepath+=~/.vim/after let &packpath = &runtimepath set background=dark syntax on filetype plugin indent on let mapleader=',' " netrw let g:netrw_banner = 0 let g:netrw_liststyle = 1 let g:netrw_list_hide = '^\.' let g:netrw_winsize=25 map n :Lexplore " fix helptags for opt/ plugins " https://vi.stackexchange.com/questions/17210/generating-help-tags-for-packages-that-are-loaded-by-vim-8s-package-management command! -nargs=0 -bar Helptags \ for p in glob('~/.vim/pack/git-plugins/opt/*', 1, 1) \| exe 'packadd ' . fnamemodify(p, ':t') \| endfor \| helptags ALL set shiftwidth=2 set shiftround set tabstop=2 set expandtab set smarttab set splitbelow set splitright set encoding=utf-8 set hidden set nowrap set smartindent set copyindent set autoindent set hlsearch set showmatch set wildmenu set wildmode=list:full,full set number set norelativenumber set ruler set backspace=indent,eol,start set ignorecase set smartcase set incsearch set history=1000 set undolevels=1000 set title set novisualbell set noerrorbells set nobackup set noswapfile set mouse=a set mousemodel=popup_setpos " https://github.com/tpope/vim-sensible/issues/78: set lazyredraw set timeoutlen=1000 set scrolloff=15 set dictionary=/usr/share/dict/words " See :help thesaurus set thesaurus=$HOME/Documents/thesaurus/thesaurus_pkg/thesaurus.txt set shortmess+=I set updatetime=250 set pastetoggle= set pumheight=200 " Required for nvim-compe: set complete-=i set nrformats-=octal set ttimeout set ttimeoutlen=100 set formatoptions+=j set autoread " https://github.com/tpope/vim-sensible/issues/142: set synmaxcol=500 " Enable 24-bit colours. " https://github.com/alacritty/alacritty/issues/109#issuecomment-440353106 if exists('+termguicolors') let &t_8f="\[38;2;%lu;%lu;%lum" let &t_8b="\[48;2;%lu;%lu;%lum" set termguicolors endif " Colour scheme: packadd! nord-vim " https://github.com/arcticicestudio/nord-vim.git colorscheme nord augroup vimrc autocmd! " Automatically load/save views on files. " https://vi.stackexchange.com/posts/13874/revisions autocmd BufWinLeave,BufLeave,BufWritePost,BufHidden,QuitPre ?* nested silent! mkview! autocmd BufWinEnter ?* silent! loadview " set cursor line highlight in insert mode. autocmd InsertEnter * set cul autocmd InsertLeavePre * set nocul " use tabs in .gitconfig autocmd BufEnter .gitconfig setlocal shiftwidth=2 tabstop=2 noexpandtab " Quickfix window is always full-width. " https://github.com/fatih/vim-go/issues/1757#issuecomment-565130503 autocmd FileType qf if (getwininfo(win_getid())[0].loclist != 1) | wincmd J | endif " Remove trailing whitespace pre-save: autocmd FileType go,ruby,markdown,rust,python,markdown,lua autocmd BufWritePre silent! %s/\s\+$//e augroup end " Don't remember the current directory for a given file: set viewoptions-=curdir set viewoptions-=options set sessionoptions-=options " Key mappings: nnoremap j gj nnoremap k gk map map map map imap imap imap imap nmap ]q :cn nmap [q :cp nnoremap w :up nnoremap / :nohlsearch nnoremap :nohlsearch " Select just-pasted text: nnoremap 0 `[v`] " Select just-pasted text and re-indent: nnoremap ) `[v`]= nnoremap 1 :set relativenumber! nnoremap ! :windo set norelativenumber nnoremap m :marks nnoremap v :vsplit nnoremap s :split nnoremap rp "_ddP nnoremap dq ggVG"_d:wq " abbrevs iabbrev esdebug debugger; // eslint-disable-line no-debugger iabbrev bashstrict set -euo pipefailIFS=$'\n\t' lua <pu", [[:lua _G.insert_uuid()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("i", "", [[:lua _G.insert_uuid()a]], {noremap = true, silent = true}) function _G.insert_jira_url() local url = vim.call("system", "jira") local errcode = vim.v.shell_error if errcode ~= 0 then vim.api.nvim_err_writeln("jira returned error code: " .. errcode) return end vim.call("setreg", "u", url) vim.api.nvim_command([[normal! "up]]) end vim.api.nvim_set_keymap("n", "pj", [[:lua _G.insert_jira_url()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("i", "", [[:lua _G.insert_jira_url()a]], {noremap = true, silent = true}) function _G.insert_iso8601_timestamp() local ts = vim.call("system", "echo -n $(date --iso-8601=seconds)") local errcode = vim.v.shell_error if errcode ~= 0 then vim.api.nvim_err_writeln("date returned error code: " .. errcode) return end vim.call("setreg", "u", ts) vim.api.nvim_command([[normal! "up]]) end vim.api.nvim_set_keymap("n", "pt", [[:lua _G.insert_iso8601_timestamp()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("i", "", [[:lua _G.insert_iso8601_timestamp()a]], {noremap = true, silent = true}) _G._test_cmd_to_wins = {} _G._build_test_cmd = function() local bufnr = vim.call("bufnr", "%") local filetype = vim.call("getbufvar", bufnr, "&filetype") if filetype == "" then vim.api.nvim_err_writeln("cannot build test command for filetype:" .. filetype) return nil end local path = vim.call("expand", "%:p") -- TODO: allow line number to be passed -- TODO: check file exists before running command if filetype == "ruby" then if not path:find("_spec.rb") then path = path:gsub("/app/", "/spec/"):gsub([[.rb$]], "_spec.rb") end return "bundle exec rspec --format=progress --no-profile " .. path elseif filetype == "go" then return "go test -v " .. path:gsub("^(.*)/(.*go)$", "%1/...") else vim.api.nvim_err_writeln("filetype not supported: " .. filetype) return nil end end _G.run_tests = function() local cmd = _G._build_test_cmd() if cmd == nil then return end vim.api.nvim_command([[silent up]]) local winid = _G._test_cmd_to_wins[cmd] local current_winid = vim.call("win_getid") if winid == nil or not vim.api.nvim_win_is_valid(winid) or winid == current_winid or not vim.call("win_gotoid", winid) == -1 then vim.api.nvim_command([[10split]]) winid = vim.call("win_getid") _G._test_cmd_to_wins[cmd] = winid end local buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_option(buf, "bufhidden", "delete") vim.api.nvim_win_set_buf(winid, buf) vim.call("termopen", cmd) vim.api.nvim_command([[normal! G]]) vim.api.nvim_command([[wincmd p]]) end _G.focus_tests = function() local cmd = _G._build_test_cmd() if cmd == nil then return end local winid = _G._test_cmd_to_wins[cmd] if winid == nil or not vim.api.nvim_win_is_valid(winid) then return end vim.call("win_gotoid", winid) vim.api.nvim_command([[wincmd =]]) end _G.close_tests = function() local current_winid = vim.api.nvim_get_current_win() for _, winid in pairs(_G._test_cmd_to_wins) do if current_winid == winid and vim.api.nvim_win_is_valid(winid) then vim.api.nvim_win_close(winid, false) return end end local cmd = _G._build_test_cmd() if cmd == nil then return end local winid = _G._test_cmd_to_wins[cmd] if winid == nil or not vim.api.nvim_win_is_valid(winid) then return end vim.api.nvim_win_close(winid, false) end _G.copy_test_cmd = function() local cmd = _G._build_test_cmd() if cmd == nil then return end vim.call("setreg", "+", cmd) print("Copied: " .. cmd) end vim.api.nvim_set_keymap("n", "cr", [[:lua _G.run_tests()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "cf", [[:lua _G.focus_tests()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "cq", [[:lua _G.close_tests()]], {noremap = true, silent = true}) vim.api.nvim_set_keymap("n", "cc", [[:lua _G.copy_test_cmd()]], {noremap = true, silent = true}) EOF " Function to eat trailing character when applying iabbrevs. " https://stackoverflow.com/questions/11858927/preventing-trailing-whitespace-when-using-vim-abbreviations func! Eatchar(pat) let c = nr2char(getchar(0)) return (c =~ a:pat) ? '' : c endfunc " Git mappings " " Copy Git permalink to clipboard for either current line or range function! CopyGitURLToLineOrRange() range if a:firstline == a:lastline execute ".GBrowse!" else execute a:firstline . "," . a:lastline . "GBrowse!" endif endfunction function! OpenGitURLToLineOrRange() range if a:firstline == a:lastline execute ".GBrowse" else execute a:firstline . "," . a:lastline . "GBrowse" endif endfunction nnoremap as :Git nnoremap ab :Git blame nnoremap ac :Commits nnoremap ah :GitGutterLineHighlightsToggle nnoremap aY :call CopyGitURLToLineOrRange() vnoremap aY :call CopyGitURLToLineOrRange() nnoremap ao :call OpenGitURLToLineOrRange() nnoremap ap :execute 'silent !ghpr ' \| redraw! " echo filename of current buffer: nmap fe :echom expand("%:p") " yank filename of current buffer: function! CopyToDefaultRegister() let @" = expand("%:p") echom expand("%:p") endfunction nmap fy :call CopyToDefaultRegister() " and into + register: function! CopyToSystemClipboard() let @* = expand("%:p") let @+ = expand("%:p") echom expand("%:p") endfunction nmap fY :call CopyToSystemClipboard() " disable Ex mode nnoremap Q function! ToggleQuickFix() if empty(filter(getwininfo(), 'v:val.quickfix')) copen else cclose endif endfunction nmap q :call ToggleQuickFix() nmap l :lclose " vim-markdown configuration: let g:vim_markdown_conceal = 1 " fzf configuration: let g:fzf_action = { \ 'ctrl-h': 'leftabove vsplit', \ 'ctrl-j': 'rightbelow split', \ 'ctrl-k': 'leftabove split', \ 'ctrl-l': 'rightbelow vsplit', \ 'ctrl-o': 'only | e', \ 'ctrl-s': 'split', \ 'ctrl-t': 'tab split', \ 'ctrl-v': 'vsplit', \ } nmap t :GFiles nmap T :Files nmap b :Buffers nmap rg :Rg " Lightline configuration: packadd! lightline.vim " https://github.com/itchyny/lightline.vim.git set laststatus=2 function! LightlineLSPErrorText() let l:count = luaeval("vim.diagnostic.get(0, [[Error]])") if l:count == 0 return '' endif return l:count . 'E' endfunction function! LightlineLSPWarningText() let l:count = luaeval("vim.diagnostic.get(0, [[Warning]])") if l:count == 0 return '' endif return l:count . 'W' endfunction function! LightlineLSPInformationText() let l:count = luaeval("vim.diagnostic.get(0, [[Information]])") if l:count == 0 return '' endif return l:count . 'I' endfunction let g:lightline = { \ 'colorscheme': 'seoul256', \ 'active': { \ 'left': [ [ 'mode', 'paste' ], \ [ 'readonly', 'filename', 'modified' ], \ [ 'gitbranch' ], ['lsperror', 'lspwarn', 'lspinfo' ] ], \ 'right': [ ['lineinfo'], ['percent'], ['filetype'], ['gobuild'] ], \ }, \ 'component_function': { \ 'gobuild': 'go#statusline#Show', \ 'gitbranch': 'FugitiveStatusline', \ }, \ 'component_expand': { \ 'lsperror': 'LightlineLSPErrorText', \ 'lspwarn': 'LightlineLSPWarningText', \ 'lspinfo': 'LightlineLSPInformationText', \ }, \ 'component_type': { \ 'lsperror': 'error', \ 'lspwarn': 'warning', \ }, \ 'mode_map': { \ 'n' : 'N', \ 'i' : 'I', \ 'R' : 'R', \ 'v' : 'V', \ 'V' : 'V-LINE', \ "\": 'V-BLOCK', \ 'c' : 'C', \ 's' : 'S', \ 'S' : 'S-LINE', \ "\": 'S-BLOCK', \ 't': 'TERM', \ }, \ } " vim-gitgutter configuration packadd! vim-gitgutter " https://github.com/airblade/vim-gitgutter.git set signcolumn=yes nmap ]h (GitGutterNextHunk) nmap [h (GitGutterPrevHunk) omap ih (GitGutterTextObjectInnerPending) omap ah (GitGutterTextObjectOuterPending) xmap ih (GitGutterTextObjectInnerVisual) xmap ah (GitGutterTextObjectOuterVisual) " vim-fugitive packadd! vim-fugitive " https://github.com/tpope/vim-fugitive.git " vim-go " Doesn't work in after/ftplugin/go.vim: let g:go_def_mapping_enabled = 0 packadd! vim-go " https://github.com/fatih/vim-go.git " load internal plugins: runtime macros/matchit.vim " load other plugins: packadd! rust.vim " https://github.com/rust-lang/rust.vim.git packadd! tmux-complete.vim " https://github.com/wellle/tmux-complete.vim.git packadd! vim-commentary " https://github.com/tpope/vim-commentary.git packadd! vim-rails " https://github.com/tpope/vim-rails.git packadd! vim-jsx-pretty " https://github.com/MaxMEllon/vim-jsx-pretty.git " Requires both fzf.vim plugin to be manually installed: " https://github.com/junegunn/fzf/blob/master/plugin/fzf.vim " and also this separate plugin, which is loaded here: packadd! fzf.vim " https://github.com/junegunn/fzf.vim.git packadd! vim-surround " https://github.com/tpope/vim-surround.git packadd! vim-rhubarb " https://github.com/tpope/vim-rhubarb.git packadd! editorconfig-vim " https://github.com/editorconfig/editorconfig-vim.git packadd! vim-wordmotion " https://github.com/chaoren/vim-wordmotion.git " formatter.nvim packadd! formatter.nvim " https://github.com/mhartington/formatter.nvim.git lua <F', 'silent Format', { noremap=true, silent=true }) EOF " enable inline vim highlighting: let g:vimsyn_embed= 'lPr' " Treesitter packadd! nvim-treesitter " https://github.com/nvim-treesitter/nvim-treesitter.git packadd! nvim-treesitter-textobjects " https://github.com/nvim-treesitter/nvim-treesitter-textobjects.git packadd! nvim-treesitter-refactor " https://github.com/nvim-treesitter/nvim-treesitter-refactor.git packadd! nvim-treesitter-context " https://github.com/nvim-treesitter/nvim-treesitter-context.git packadd! playground " https://github.com/nvim-treesitter/playground.git lua <d"] = "@class.outer", ["d"] = "@function.outer", }, }, }, refactor = { highlight_definitions = { enable = true }, highlight_current_scope = { enable = false }, smart_rename = { enable = true, keymaps = { smart_rename = "grr", }, }, navigation = { enable = true, keymaps = { -- goto_definition_lsp_fallback = "gd", goto_next_usage = "]r", goto_previous_usage = "[r", }, }, }, playground = { enable = true, disable = {}, updatetime = 25, persist_queries = false, keybindings = { toggle_query_editor = 'o', toggle_hl_groups = 'i', toggle_injected_languages = 't', toggle_anonymous_nodes = 'a', toggle_language_display = 'I', focus_language = 'f', unfocus_language = 'F', update = 'R', goto_node = '', show_help = '?', }, } } EOF lua <'] = cmp.mapping.scroll_docs(-4), [''] = cmp.mapping.scroll_docs(4), [''] = cmp.mapping.complete(), [''] = cmp.mapping.close(), [''] = cmp.mapping.confirm({ select = true, behaviour = cmp.ConfirmBehavior.Replace }), }, sources = cmp.config.sources({ { name = 'nvim_lsp' }, { name = 'buffer', option = { get_bufnrs = function() return vim.api.nvim_list_bufs() end }, }, { name = 'path' }, { name = 'calc' }, }), sorting = { comparators = { function(...) return cmp_buffer:compare_locality(...) end, } }, view = { entries = 'native', }, experimental = { ghost_text = true, }, }) EOF " LSP packadd! nvim-lspconfig " https://github.com/neovim/nvim-lspconfig.git packadd! lsp_signature.nvim " https://github.com/ray-x/lsp_signature.nvim.git packadd! fzf-lsp.nvim " https://github.com/gfanto/fzf-lsp.nvim.git packadd! lsp-colors.nvim " https://github.com/folke/lsp-colors.nvim.git lua <lua vim.lsp.buf.definition()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', 'lua vim.lsp.buf.type_definition()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gd', 'lua vim.lsp.buf.definition()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', 'lua vim.lsp.buf.type_definition()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'e', 'lua vim.lsp.buf.rename()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'r', 'lua vim.lsp.buf.references()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'i', 'lua vim.lsp.buf.implementation()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'ca', 'lua vim.lsp.buf.code_action()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'ci', 'lua vim.lsp.buf.incoming_calls()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'co', 'lua vim.lsp.buf.outgoing_calls()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'K', 'lua vim.lsp.buf.hover()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', ']e', 'lua vim.diagnostic.goto_next({severity="Error"})', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', '[e', 'lua vim.diagnostic.goto_prev({severity="Error"})', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', ']d', 'lua vim.diagnostic.goto_next()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', '[d', 'lua vim.diagnostic.goto_prev()', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'cd', 'lua vim.diagnostic.hide(nil, 0)', opts) -- fzf triggers: vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fr', 'References', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fi', 'Implementations', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fs', 'DocumentSymbols', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fw', 'WorkspaceSymbols', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fc', 'CodeActions', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fci', 'IncomingCalls', opts) vim.api.nvim_buf_set_keymap(bufnr, 'n', 'fco', 'OutgoingCalls', opts) require 'lsp_signature'.on_attach({ hint_enable = false, }) end -- Go local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true capabilities.textDocument.completion.completionItem.resolveSupport = { properties = { 'documentation', 'detail', 'additionalTextEdits', } } require('cmp_nvim_lsp').update_capabilities(capabilities) nvim_lsp.gopls.setup{ settings = { gopls = { staticcheck = true, analyses = { unusedparams = true, shadow = true, unusedwrite = true, unusedresult = true, nilness = true, }, }, }, capabilities = capabilities, on_attach = on_attach, } -- Rust nvim_lsp.rust_analyzer.setup{ on_attach = on_attach, settings = { ["rust-analyzer"] = { imports = { granularity = { group = "module", }, prefix = "self", }, cargo = { buildScripts = { enable = true, }, }, procMacro = { enable = true }, } } } -- Ruby nvim_lsp.solargraph.setup{ on_attach = on_attach, } -- Typescript -- https://jose-elias-alvarez.medium.com/configuring-neovims-lsp-client-for-typescript-development-5789d58ea9c nvim_lsp.tsserver.setup{ on_attach = on_attach, } local filetypes = { typescript = "eslint", typescriptreact = "eslint", } -- diagnosticls local linters = { eslint = { sourceName = "eslint", -- fallback to global eslint if the local is not found? -- https://github.com/creativenull/diagnosticls-configs-nvim/blob/e7d6f7e99f6b416d2aeee89314bc46fc36df7b22/lua/diagnosticls-configs/fs.lua#L20 command = "./node_modules/.bin/eslint", rootPatterns = {".eslintrc", ".eslintrc.js"}, debounce = 100, args = {"--stdin", "--stdin-filename", "%filepath", "--format", "json"}, parseJson = { errorsRoot = "[0].messages", line = "line", column = "column", endLine = "endLine", endColumn = "endColumn", message = "${message} [${ruleId}]", security = "severity" }, securities = {[1] = "error", [2] = "warning"} } } nvim_lsp.diagnosticls.setup{ on_attach = on_attach, filetypes = vim.tbl_keys(filetypes), init_options = { linters = linters, filetypes = filetypes, } } -- Lua local system_name if vim.fn.has("mac") == 1 then system_name = "macOS" elseif vim.fn.has("unix") == 1 then system_name = "Linux" else print("Unsupported system for sumneko") end local sumneko_root_path = os.getenv("HOME").."/dev/lua-language-server" local sumneko_binary = sumneko_root_path.."/bin/"..system_name.."/lua-language-server" local runtime_path = vim.split(package.path, ';') table.insert(runtime_path, "lua/?.lua") table.insert(runtime_path, "lua/?/init.lua") nvim_lsp.sumneko_lua.setup { cmd = {sumneko_binary, "-E", sumneko_root_path .. "/main.lua"}; settings = { Lua = { runtime = { version = 'LuaJIT', path = runtime_path, }, diagnostics = { globals = {'vim'}, }, workspace = { library = vim.api.nvim_get_runtime_file("", true), }, telemetry = { enable = false, }, }, }, on_attach = on_attach, } EOF