" @incomplete Move all leader definitions to the bottom, so that it's easier to see them. " @incomplete Add setup steps (plugins, cache setup, search tool, etc). "################################################################################################### " " The config is chopped up into sections. These are the headings, which you " can use to quickly jump to a particular section: " #0 GLOBALS " #1 PLUGINS " #2 BASE CONFIG " #3 PLUGIN CONFIGS " #4 VISUALS " #5 CUSTOM FUNCTIONS / COMMANDS " "################################################################################################### scriptencoding utf-8 " @note If the file contains a BOM then vim will automatically set `bomb` for " the buffer so that the BOM is written out again. set encoding=utf-8 fileencoding=utf-8 fileencodings=ucs-bom,utf8,prc set nocompatible filetype off " Store the current system name so that we can conditionally set configs for " different platforms let s:uname = system("echo -n \"$(uname)\"") let s:vim_dir = $HOME . "/.vim" let mapleader="," function! IsWindows() if s:uname =~ "mingw" || s:uname =~ "msys" return 1 endif return 0 endfunction if has('termguicolors') set termguicolors " Set Vim-specific sequences for RGB colors let &t_8f = "\[38;2;%lu;%lu;%lum" let &t_8b = "\[48;2;%lu;%lu;%lum" endif function! PrintError(msg) abort exec 'normal! \' echohl ErrorMsg echomsg a:msg echohl None endfunction "################################################################ "################################################################ "################################################################ "#0 GLOBALS "################################################################ "################################################################ "################################################################ " @note The following globals can be used to customize various functions in " this file. The easiest way to set them is in ~/.vimrc.private or an .lvimrc " file in the root folder that you want it applied to. " " Some variables cannot be customized in an .lvimrc because their value is used " by settings in this file when its sourced. These have been flagged with a note. " " Also take note that an .lvimrc has precedence because it's loaded after this " and the private vimrc. " -------------------------------------------------------------------------------------------------- " @note unsupported in lvimrc let g:campo_max_line_length = 100 " Display a vertical bar at x=. " Set the row height of the quickfix pane, which is used to display results " from various plugins (like ctrl-p, ripgrep, compilation errors, etc), in rows let g:quickfix_pane_height = 20 """""""""""""" " COLORS """""""""""""" let g:campo_default_bg_mode = 'dark' " Start vim with the dark theme. Set to 'light' for the light theme. let g:campo_dark_theme = 'campo-dark-simple' "'campo-dark-blue' let g:campo_light_theme = 'campo-light-simple' "'campo-light' let g:campo_theme_use_rainbow_parens = 1 """""""""""""" " FORMATTING """""""""""""" " When set to 1, all files will be stripped of trailing whitespace when the " file is saved. Set to 0 to disable. You can customize which files are " ignored or always stripped; see below. let g:campo_strip_trailing_whitespace = 1 " If g:campo_strip_trailing_whitespace is 1 then you can stop stripping in " specific files by setting this to a list of filenames. This has no effect " when g:campo_strip_trailing_whitespace is 0. " " e.g. let g:campo_files_to_ignore_when_stripping_trailing_whitespace = ['app.h', 'config.h'] let g:campo_files_to_ignore_when_stripping_trailing_whitespace = [] " If g:campo_strip_trailing_whitespace is 0 then you can force whitespace " stripping in specific files by setting this to a list of filenames. This has " no effect when g:campo_strip_trailing_whitespace is 1. " e.g. let g:campo_files_to_force_stripping_trailing_whitespace = ['app.h', 'config.h'] let g:campo_files_to_force_stripping_trailing_whitespace = [] """""""""""""" " SEARCH """""""""""""" " This is included in the ripgrep args. You can use this to do things like " ignore folders in your project or limit the search to specific file types. " For example, if you want to ignore the 3rd_party dir and only search C files " (remove the backslash from the first quote as that's just here to escape it " in this comment string) " let g:campo_custom_search_args = \"-g \"!3rd_party/*\" -tc" let g:campo_custom_search_args = "" """""""""""""" " CTAGS """""""""""""" " If != 0 then ctag generation will always happen on a file save, otherwise " it'll only be triggered if the file being saved has an extension in the " g:campo_extensions_that_run_ctags list. let g:campo_force_ctags_regardless_of_extension = 0 " If one of these file types are saved then the ctags creation function will be called. " You can append to this list in another config like so: " let g:campo_extensions_that_run_ctags = g:campo_extensions_that_run_ctags + ['foo', 'bar'] let g:campo_extensions_that_run_ctags = ['c','cpp','h','hpp','inc','cs','py','asm','ex','exs'] " Default files and directories that ctags should ignore when doing a " recursive crawl. " @note The CreateCtags function will always ignore .git and node_modules " regardless of this variable's value. let g:campo_ctags_exclude = ['*.txt', '*.config', '.cache'] " This is included in the ctags autocmd args. You can use this to customize " how ctags are built. " Examples: " * Recursive ctag generation with `let g:campo_custom_ctags_args = '-R'` " * Create tags for specific langauges: `let g:campo_custom_ctags_args = '--languages=C,C++,Elixir' " * You can see a list of languages with `ctags --list-languages` " * For C# you have to escape the ampersand like so: `--languages=C\\#` " * Exclude a directory with `let g:campo_custom_ctags_args = '--exclude=3rd_party'` let g:campo_custom_ctags_args = "" "################################################################ "################################################################ "################################################################ "#1 PLUGINS "################################################################ "################################################################ "################################################################ call plug#begin('~/.vim/plugged') "//////////////////////////////////////////////////////////////// " MISC "//////////////////////////////////////////////////////////////// Plug 'bling/vim-airline' " Enhanced status/tabline. Plug 'embear/vim-localvimrc' " Add a .lvimrc to a folder to override .vimrc config. Plug 'tpope/vim-obsession' " Continuously updated session files (tracks window positions, open folds, etc). Plug 'tpope/vim-fugitive' " Git wrapper (I particularly like :Gblame, which I've wrapped as :Blame) Plug 'sir-pinecone/vim-ripgrep' " Fast grep-like search. Requires ripgrep; install Rust package: `cargo install ripgrep`. Plug 'itchyny/vim-cursorword' " Underlines all instances of the symbol under the cursor. Plug 'airblade/vim-gitgutter' " Displays a git diff in the vim gutter and allows staging/unstaging of hunks. Plug 'ctrlpvim/ctrlp.vim' " Fuzzy file, buffer, mru, tag, etc finder. Plug 'majutsushi/tagbar' " Display ctags in a window, ordered by scope. Plug 'tommcdo/vim-lion' " For text alignment, use gl= and gL= Plug 'tpope/tpope-vim-abolish' " Easily search for, substitute, and abbreviate multiple variants of a word. Add them to `vim/after/plugin/abolish.vim` Plug 'vim-scripts/AnsiEsc.vim' " Ansi escape sequences concealed, but highlighted as specified. Plug 'mh21/errormarker.vim' " Build error highlighting (requires skywind3000/asyncrun.vim). Plug 'skywind3000/asyncrun.vim' " Async commands. Plug 'nelstrom/vim-qargs' " For the GlobalReplaceIt function (i.e. search and replace). if IsWindows() Plug 'suxpert/vimcaps' " Disable capslock (useful if the OS isn't configured to do so). endif "//////////////////////////////////////////////////////////////// " COLORS "//////////////////////////////////////////////////////////////// Plug 'luochen1990/rainbow', { 'commit': '1c45e0f' } " Rainbow parens. Locked to an older commit that still works fine on my PC. Plug 'vim-airline/vim-airline-themes' if IsWindows() Plug 'godlygeek/csapprox' " Try to make gvim themes look decent on Windows. endif "////////////////////////////// " SYNTAX HIGHLIGHTING "////////////////////////////// Plug 'tpope/vim-markdown' " Markdown Plug 'bfrg/vim-cpp-modern' " C/C++ Plug 'vim-ruby/vim-ruby' " Ruby Plug 'fatih/vim-go' " Go Plug 'rust-lang/rust.vim' " Rust Plug 'jdonaldson/vaxe' " Haxe Plug 'pprovost/vim-ps1' " PowerShell Plug 'fedorenchik/fasm.vim' " Flat Assembler Plug 'elixir-editors/vim-elixir' " Elixir "////////////////////////////// call plug#end() filetype plugin indent on "--------------------------------------------------------------------------------------------------- "################################################################ "################################################################ "################################################################ "#2 BASE CONFIG "################################################################ "################################################################ "################################################################ """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " BASIC EDITING CONFIGURATION """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" set hidden set history=10000 set expandtab set tabstop=4 set shiftwidth=4 set softtabstop=4 set autoindent set laststatus=2 set showcmd " Display incomplete commands. set showmatch set incsearch " Highlight matches as you type. set hlsearch " Highlight matches. set dictionary+=/usr/share/dict/words "set clipboard=unnamed " Yank and paste with the system clipboard. set number set ignorecase smartcase " Make searches case-sensitive only if they contain upper-case characters. set visualbell " No bell sounds. set ttyfast set cmdheight=2 set switchbuf=useopen,split set numberwidth=5 set showtabline=2 set winwidth=79 " Use abbreviations. set shortmess=a " Remove gvim Menubar and Toolbar "set guioptions -=m "set guioptions -=T " @warning: This must come AFTER `set ignorecase smartcase` otherwise vim spews out a ton of errors. No idea why! if IsWindows() " Just assume we don't have a zsh shell set shell=bash else "set shell=zsh set shell=bash endif set t_ti= t_te= " Prevent Vim from clobbering the scrollback buffer. See http://www.shallowsky.com/linux/noaltscreen.html set scrolloff=3 " keep more context when scrolling off the end of a buffer set cursorline set cursorcolumn " " Store swap, backup and undo files in a central spot. I have my settings in a " `vimrc.private` so that my drive paths aren't in this config. If you want to " set them here then add: " " set directory= " set backupdir= " if has('persistent_undo') " set undodir= " endif " " And make sure those directories exist before opening vim. " set backup set backupcopy=yes augroup backupCmds autocmd! autocmd BufWritePre * let &bex = '.' . strftime("%Y-%m-%d-%T") . '.bak' augroup END set writebackup " Make buckup before overwriting the current buffer. " " Keep undo history across sessions by storing it in a file. The undo save " location is set in the vimrc.private file. You can also set it here with: " set undodir= " set undolevels=1000 " Allow undo when going back into a closed file set undoreload=10000 if has('persistent_undo') set undofile endif set backspace=indent,eol,start " Allow backspacing over everything in insert mode. set complete+=kspell " Spell checking autocomplete. set complete-=i " Don't scan all included files since it's really slow. set termguicolors syntax on " Enable highlighting for syntax set wildmenu set wildmode=longest,list,full set wildignore+=*/log/*,*.so,*.swp,*.zip,*/rdoc/* set grepprg=rg\ --vimgrep " Requires ripgrep to be installed. set list listchars=tab:»·,trail:· " Show trailing whitespace. set timeoutlen=300 ttimeoutlen=0 " Adding this since the esc remap on the 'i' key had a long delay when pressed. " @fixme might be broken if lowered to 100 from original value of 4000. Will " first try 500 and tweak from there. " UPDATE: I lowered this to 250 and eventually started seeing some plugin " errors related to paren formatting. I think 800 might be the sweet spot. set updatetime=800 " I lowered this to make git-gutter updates faster. " Fix vim's background colour erase - http://snk.tuxfamily.org/log/vim-256color-bce.html if &term =~ '256color' " Disable Background Color Erase (BCE) so that color schemes " work properly when Vim is used inside tmux and GNU screen. " See also http://snk.tuxfamily.org/log/vim-256color-bce.html set t_ut= endif " Disable arrow keys. noremap noremap noremap noremap inoremap inoremap inoremap inoremap "--------------------------------------------------------------- " Load vimrc.private " This should be done after above base settings that don't use the global " campo variables. " " You can further customize things in a private vimrc. I use this for things " that I don't want included in my public dotfiles repo such as temp file settings. if filereadable($HOME . "/.vimrc.private") source $HOME/.vimrc.private endif " Settings that use the global campo variables. let &colorcolumn=g:campo_max_line_length """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " CUSTOM AUTOCMDS """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" augroup campoCmds " Clear all autocmds in the group. autocmd! " Automatically wrap at N characters. autocmd FileType gitcommit setlocal colorcolumn=72 autocmd BufRead,BufNewFile *.{md,txt,plan} exec "setlocal textwidth=".g:campo_max_line_length " Spell checking. autocmd FileType gitcommit,markdown,text setlocal spell " Jump to last cursor position unless it's invalid or in an event handler. autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif " Properly indent schemes (scheme, racket, etc). autocmd BufRead,BufNewFile *.{lisp,scm,rkt} setlocal equalprg=scmindent.rkt " Elixir indent autocmd FileType elixir setlocal tabstop=2 | setlocal shiftwidth=2 | setlocal softtabstop=2 " Fasm indent; uses the fedorenchik/fasm.vim plugin. autocmd BufReadPre *.asm let g:asmsyntax = "fasm" " Auto reload VIM when settings changed. " Need to use silent! now because the save commands call a custom function " and without the silent we would see an error after sourcing saying that " we can't redefine the function that initiated the save. :ReloadVimrcError " " @fixme Reload lvimrc after sourcing this file on a save. I tried calling " a function that does the source and a call to lvimrc's API but got an " error complaining that the function cannot be created while it's in use. autocmd BufWritePost .vimrc silent! source $MYVIMRC autocmd BufWritePost *.vim silent! source $MYVIMRC autocmd BufWritePost ~/.vimrc.private silent! source $MYVIMRC " Remove trailing whitespace when saving any file. function! s:StripTrailingWhitespaces() let l:filename = expand('%:t') if g:campo_strip_trailing_whitespace == 1 if len(g:campo_files_to_ignore_when_stripping_trailing_whitespace) for ignore in g:campo_files_to_ignore_when_stripping_trailing_whitespace if ignore == l:filename return endif endfor endif else if len(g:campo_files_to_force_stripping_trailing_whitespace) let l:found_match = 0 for name in g:campo_files_to_force_stripping_trailing_whitespace if name == l:filename let l:found_match = 1 break endif endfor if l:found_match == 0 return endif else return endif endif let l = line(".") let c = col(".") %s/\s\+$//e call cursor(l, c) endfun autocmd BufWritePre * call s:StripTrailingWhitespaces() "//////////////////////////////////////////////////////////////// " FILE TEMPLATES "//////////////////////////////////////////////////////////////// " C/C++ template. function! s:CFileTemplate() let s:env = { \ 'filename': expand('%:t'), \ 'creation_date': strftime('%Y-%m-%d'), \ 'year': strftime('%Y'), \ 'copyright_owner': 'Jelly Pixel, Inc. All Rights Reserved.' \} let l:template =<< trim EOS /*================================================================================================== File: ${filename} Creation Date: ${creation_date} Creator: Michael Campagnaro Notice!: (C) Copyright ${year} by ${copyright_owner} ================================================================================================*/ //////////////////////////////////////////////////////////////////////////////////////////////////// // # Defines //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // # Globals //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // # Structs //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // # Macros //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // # Private API //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // # Public API //////////////////////////////////////////////////////////////////////////////////////////////////// EOS return map(l:template, { _, line -> substitute(line, '${\(.\{-}\)}', '\=get(s:env, submatch(1), submatch(1))', 'g') } ) endfunction function! s:InsertCHeaderGates() let l:gatename = substitute(toupper(expand("%:t")), "\\.", "_", "g") call append(0, '#ifndef '. l:gatename) call append(line('$'), '#define '. l:gatename) call append(line('$'), '#endif') endfunction " sh template function! s:ShellScriptTemplate() let l:template =<< trim EOS #!/usr/bin/env bash if which tput >/dev/null 2>&1; then ncolors=$(tput colors) fi if [ -t 1 ] && [ -n "$ncolors" ] && [ "$ncolors" -ge 8 ]; then RED="$(tput setaf 1)" GREEN="$(tput setaf 2)" YELLOW="$(tput setaf 3)" BLUE="$(tput setaf 4)" MAGENTA="$(tput setaf 5)" CYAN="$(tput setaf 6)" BOLD="$(tput bold)" DIM="\e[2m" NORMAL="$(tput sgr0)" else RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" BOLD="" NORMAL="" fi error() { printf "${BOLD}${RED}$1${NORMAL}\n" } abort() { error "\nAborting..." exit 1 } set -e cwd=$PWD uname_s="$(uname -s)" case "${uname_s}" in Linux*) machine=Linux;; Darwin*) machine=MacOS;; CYGWIN*) machine=Cygwin;; MINGW*) machine=MinGw;; *) machine="UNKNOWN:${uname_s}" esac printf "${YELLOW}Platform: $machine${NORMAL}\n" EOS return l:template endfunction " Shell script template. autocmd BufNewFile *.sh call append(0, s:ShellScriptTemplate()) " C/C++ file. autocmd BufNewFile *.{c,cc,cpp,h,hpp} call append(0, s:CFileTemplate()) autocmd BufNewFile *.{h,hpp} call s:InsertCHeaderGates() augroup END """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " MISC KEY MAPS """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Lowercase the e (have a habit of making it uppercase). :ca E e " Mapping ESC in insert mode and command mode to double i. imap jj " Suspend vim process and return to the shell. Can return to vim with `fg`. nnoremap z " Open the vimrc file for editing / reload vimrc file. nnoremap ev :vsp $MYVIMRC nnoremap pv :vsp ~/.vimrc.private nnoremap rv :source $MYVIMRC " Type %/ in the command bar to have it replaced with the current buffer's " path if the file is on disk. One thing I noticed is that you have to type " the full %/ quickly otherwise it won't replace it. cmap %/ %:p:h/ "------------------------------------------------------------ " Remap saving and quiting. " " I used to have a BufWritePost autocommand that ran the ctags generator but " it ends up running multiple times when saving multiple buffers at once. In " order to only call it once for a group of saves I've had to remap the " various save commands to a function call. function! s:CreateCtags() " Only allow one instance of ctags to run in this directory at any given time. let l:lock_file = "ctags.lock" if filereadable(l:lock_file) || filereadable("newtags") " Don't print a warning because this will always show when saving multiple files at the same time with a :wa or :xa return endif let l:extension = tolower(expand('%:e')) if (g:campo_force_ctags_regardless_of_extension == 0) && (index(g:campo_extensions_that_run_ctags, l:extension) < 0) echo "Skipping ctags generation" return endif " Abort if we're editing a text file. This won't be an exhaustive " filter. We can restrict what goes into the tag file " First determine if we're in a root drive directory. If we are then " we bail because we don't want to recurse across the entire drive! let l:path = expand('%:p:h') let l:path_without_slashes = substitute(l:path, "/", "", "g") if (strchars(l:path) - strchars(l:path_without_slashes)) <= 1 call PrintError("Not going to run ctags because the file is in a root drive directory") return endif " Always ignore .git and node_modules let g:campo_ctags_exclude = g:campo_ctags_exclude + ['.git', 'node_modules'] let l:exclude_list = "" for name in g:campo_ctags_exclude let l:exclude_list = l:exclude_list . "--exclude=" . name . " " endfor " Include local variables for C-like languages. let l:ctags_cmd = 'ctags '.l:exclude_list.' '.g:campo_custom_ctags_args.' --c-types=+l --c++-types=+l -o newtags' " Add the filename to the ctags command if not running in recursive mode. let l:recursive = matchstr(g:campo_custom_ctags_args, '\(-R\s\)\|\(-R$\)\|\(--recurse=yes\)\|\(--recurse\s\)\|\(--recurse$\)') if l:recursive == '' let l:ctags_cmd = l:ctags_cmd . ' ' . expand('%:t') echo "Creating non-recursive ctags" else echo "Creating recursive ctags" endif " The ampersand at the end is to make this run in the background. I had to group the " commands in parens to make the chained commands run in the background. let l:cmd = '!(touch '.l:lock_file.'; '.l:ctags_cmd.'; mv newtags tags &>/dev/null; rm '.l:lock_file.') &' silent! exec l:cmd endfun " @note We have an autocmd that reloads the vimrc files after they're saved. " These write functions below will not be reloaded because they initiate the " save. So if you make changes to them then you need to manually reload this " file using rv or whatever. :ReloadVimrcError function! DoSingleWrite() write! call s:CreateCtags() endfunction function! DoSingleWriteThenQuit() write! call s:CreateCtags() quit endfunction " @fixme Sometimes a :wa that saves multiple files causes vim to hang and use " a lot of CPU. function! DoMultiWrite() let l:current_buffer = bufnr("%") bufdo wa " Restore the last buffer because it may have changed. exec "buffer " . l:current_buffer call s:CreateCtags() endfunction cnoreabbrev w :call DoSingleWrite() cnoreabbrev W :call DoSingleWrite() cnoreabbrev wa :call DoMultiWrite() cnoreabbrev Wa :call DoMultiWrite() cnoreabbrev WA :call DoMultiWrite() cnoreabbrev wq :call DoSingleWriteThenQuit() cnoreabbrev Wq :call DoSingleWriteThenQuit() cnoreabbrev WQ :call DoSingleWriteThenQuit() nnoremap w :call DoSingleWrite() nnoremap x :call DoSingleWriteThenQuit() nnoremap q :q cnoreabbrev Q q cnoreabbrev Qa qa command! Qa qall " Disable Ex mode. noremap Q "------------------------------------------------------------ " Open a terminal within vim. Use `exit` to close it. if exists(':terminal') noremap t :terminal tnoremap e tnoremap h tnoremap j tnoremap k tnoremap l nnoremap h nnoremap j nnoremap k nnoremap l endif " Jump to other buffers. noremap noremap noremap noremap " Make it easier to jump around the command line. The default behaviour is " using the arrow keys with or without shift. :cnoremap :cnoremap " Window splitting - couldn't figure out how to remap v & n to " & noremap m :vsplit noremap mm :split " Forward delete and replace a word. noremap d ciw " Allow fast pasting by accessing the system clipboard register. noremap p "+p " Likely won't need to use this if pasting with p, but just in case here ya go. noremap pp :set paste! paste? " Toggle line numbers. noremap o :set number! number? " Show spell checking. " You can add new entries to the dict by moving the cursor over the word and pressing `zg`. noremap j :exec &spell==&spell? "se spell! spelllang=en_us" : "se spell!" noremap = z= " Clear the search buffer (highlighting) when hitting return. nnoremap :nohlsearch " Switch to the previous file. nnoremap " Replace currently selected text with default register without yanking it. vnoremap p "_dP " Switch between C++ source and header files. noremap v :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp, "noremap vv :e %:p:s,.h$,.X123X,:s,.c$,.h,:s,.X123X$,.c, "noremap vvv :e %:p:s,.h$,.X123X,:s,.cc$,.h,:s,.X123X$,.cc, " Replace all instances of the highlighted text with whatever you enter. nnoremap :%s///g "//////////////////////////////////////////////////////////////// " QUICKLY OPEN C++ SOURCE OR HEADER FILE "//////////////////////////////////////////////////////////////// function! s:CompleteFilenameWithoutExtension(ArgLead, CmdLine, CursorPos) " Returns a matching filename without the period that separates the name " from the extension. let l:file = substitute(glob(a:ArgLead.'*', 0, 0), "[\.].*", "", "*") return l:file endfunction " Custom command to open cpp and h files without typing an extension "command! -nargs=+ -complete=custom,s:CompleteFilenameWithoutExtension "OpenCppSource exec ':e .cpp' ":ca c OpenCppSource ":ca C OpenCppSource "command! -nargs=+ -complete=custom,s:CompleteFilenameWithoutExtension "OpenCppHeader exec ':e .h' ":ca h OpenCppHeader ":ca H OpenCppHeader "command! -nargs=+ -complete=custom,s:CompleteFilenameWithoutExtension "OpenCppSourceAndHeader exec ':vsp | :e .h | :sp .cpp' ":ca b OpenCppSourceAndHeader ":ca B OpenCppSourceAndHeader "//////////////////////////////////////////////////////////////// " MULTIPURPOSE TAB KEY "//////////////////////////////////////////////////////////////// function! InsertTabWrapper() let l:col = col('.') - 1 if !l:col || getline('.')[l:col - 1] !~ '\k' return "\" else return "\" endif endfunction inoremap =InsertTabWrapper() inoremap "--------------------------------------------------------------------------------------------------- "################################################################ "################################################################ "################################################################ "#3 PLUGIN CONFIGS "################################################################ "################################################################ "################################################################ """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " LOCAL VIMRC """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" let g:localvimrc_sandbox = 0 let g:localvimrc_ask = 0 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " TAGBAR """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" noremap :TagbarToggle " Sort tags by their order in the source file. Press 's' to sort them alphabetically. let g:tagbar_sort = 0 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " GITGUTTER """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" let g:gitgutter_enabled = 1 let g:gitgutter_highlight_lines = 0 nnoremap hh :GitGutterToggle nmap ha (GitGutterStageHunk) nmap [h (GitGutterNextHunk) nmap ]h GitGutterPrevHunk augroup gitGutterPluginCmds autocmd! " Update marks on save autocmd BufWritePost * GitGutter augroup END """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " SYNTASTIC """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " NOTE: there is a status line config in the status line section let g:syntastic_always_populate_loc_list = 1 let g:syntastic_auto_loc_list = 1 let g:syntastic_check_on_open = 0 let g:syntastic_check_on_wq = 0 " Customize Rust " https://github.com/rust-lang/rust.vim/issues/130 " Can remove once this Syntastic PR is merged https://github.com/rust-lang/rust.vim/pull/132 "let g:syntastic_rust_rustc_exe = 'cargo check' "let g:syntastic_rust_rustc_fname = '' "let g:syntastic_rust_checkers = ['rustc'] """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " RIPGREP """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" let g:rg_highlight = 1 let g:rg_window_height = g:quickfix_pane_height """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " CTRL-P """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " keybindings: " " leader-f = open CtrlP in tag searching mode. " leader-g = open CtrlP in file searching mode. " ctrl-f = toggle search mode " enter = open result in a current buffer or in an already open buffer for that file. " ctrl-v = open result in a vertically-split buffer. " ctrl-h = open result in a horizontally-split buffer. " ctrl-t = open result in a new tab. " ctrl-j | ctrl-k = move up and down the search results. " ctrl-y = create file and open it. " ctrl-z = mark multiple file search results to open (I think you can only use ctrl-v or ctrl-x and not enter). " ctrl-o = ask how to open a file search result. " ctrl-o = ask how to open a file search result. " ctrl-p | ctrl-n = traverse search history. noremap g :CtrlP let g:ctrlp_map = 'f' let g:ctrlp_cmd = 'CtrlPTag' " Search tags by default. let g:ctrlp_by_filename = 1 " File search by filename as opposed to full path. let g:ctrlp_match_window = 'bottom,order:ttb,min:10,max:'.g:quickfix_pane_height.',results:'.g:quickfix_pane_height let g:ctrlp_use_caching = 1 let g:ctrlp_clear_cache_on_exit = 1 " No need to keep cache for now since I mostly work in git repos. Press F5 inside CtrlP to rebuild the cache. let g:ctrlp_working_path_mode = 'ra' " Search from nearest ancestor of the current file that contains .git OR directory of the current file unless it's a subdirectory of the cwd let g:ctrlp_switch_buffer = 'et' " If a file is already open, open it again in a new pane instead of switching to the existing pane let g:ctrlp_custom_ignore = '\v[\/]\.(git|hg|svn)$' let g:ctrlp_user_command = ['.git', 'cd %s && git ls-files -co --exclude-standard'] " If a git repo, use checked in files (ignore things in .gitignore); fallback to globpath() " @fixme Not sure why I can't get these new mappings (c-m, c-cr) to register... "let g:ctrlp_prompt_mappings = { " \ 'AcceptSelection("h")': ['', ''], " \ 'AcceptSelection("v")': ['', ''], " \ } """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " GIT """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" noremap gb :Git blame -w " Ignore whitespace changes; follow renames and copies. command! -bar -bang -nargs=* Blame :Git blame -wCM command! -bar -bang -nargs=* Gblame :Git blame -wCM """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " GIST VIM """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" let g:gist_detect_filetype = 1 let g:gist_open_browser_after_post = 1 let g:gist_show_privates = 1 let g:gist_post_private = 1 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " VIM-CLOJURE-STATIC """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Default let g:clojure_fuzzy_indent = 1 let g:clojure_align_multiline_strings = 1 let g:clojure_fuzzy_indent_patterns = ['^match', '^with', '^def', '^let'] let g:clojure_fuzzy_indent_blacklist = ['-fn$', '\v^with-%(meta|out-str|loading-context)$'] """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " RUST.VIM """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" "let g:rustfmt_autosave = 1 " auto run rust formatter when saving """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " RAINBOW """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" let g:rainbow_active = 1 " Always on let s:light_rainbow = ['red', 'green', 'magenta', 'cyan', 'yellow', 'white', 'gray', 'blue'] let s:dark_rainbow = ['darkblue', 'red', 'black', 'darkgreen', 'darkyellow', 'darkred', 'darkgray'] let s:rainbow_theme = g:campo_default_bg_mode function! UpdateRainbowConf() let g:rainbow_conf = { \ 'ctermfgs': (s:rainbow_theme == "light"? s:dark_rainbow : s:light_rainbow) \} "\ 'separately': { "\ '*': 0, " Disable all "\ 'c++': {} " Only enable c++ "\ } endfunction call UpdateRainbowConf() function! ReloadRainbow() if g:campo_theme_use_rainbow_parens if exists(':RainbowToggle') call UpdateRainbowConf() call rainbow#clear() | call rainbow#hook() endif else let g:rainbow_active = 0 if exists(':RainbowToggle') call UpdateRainbowConf() call rainbow#clear() endif endif endfunction """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " C-TAGS """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" set tags+=tags;$HOME "--------------------------------------------------------------------------------------------------- "################################################################ "################################################################ "################################################################ "#4 VISUALS "################################################################ "################################################################ "################################################################ """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " LAYOUT """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" "//////////////////////////////////////////////////////////////// " CENTER THE BUFFER "//////////////////////////////////////////////////////////////// function! CenterPane() " centers the current pane as the middle 2 of 4 imaginary columns " should be called in a window with a single pane " Taken from https://dev.to/vinneycavallo/easily-center-content-in-vim lefta vnew wincmd w exec 'vertical resize' string(&columns * 0.75) endfunction nnoremap c :call CenterPane() function! RemoveCenterPane() wincmd w close endfunction nnoremap cw :call RemoveCenterPane() "//////////////////////////////////////////////////////////////// " TEXT ALIGNMENT PLUGIN "//////////////////////////////////////////////////////////////// let b:lion_squeeze_spaces = 1 "//////////////////////////////////////////////////////////////// " STATUS LINE "//////////////////////////////////////////////////////////////// set statusline=%<%f\ (%{&ft})\ %-4(%m%)%=%-19(%3l,%02c%03V%) """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " COLORS """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" exec "autocmd ColorScheme " . g:campo_dark_theme . " call ReloadRainbow()" exec "autocmd ColorScheme " . g:campo_light_theme . " call ReloadRainbow()" " Switch between light and dark themes. noremap l :call ChangeBgTheme('light', 0) noremap ll :call ChangeBgTheme('dark', 0) function! ChangeBgTheme(bg, onlySetTheme) if a:bg =~ 'light' let s:rainbow_theme = 'light' let s:theme = g:campo_light_theme exe 'colorscheme ' . s:theme set background=light else let s:rainbow_theme = 'dark' let s:theme = g:campo_dark_theme " We have to set the theme twice in order to get its correct dark-theme colors. " Weird stuff. exe 'colorscheme ' . s:theme set background=dark exe 'colorscheme ' . s:theme endif if !a:onlySetTheme exec 'AirlineTheme' a:bg endif endfunction if g:campo_default_bg_mode =~ 'light' call ChangeBgTheme('light', 1) else call ChangeBgTheme('dark', 1) endif """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " HIGHLIGHTS - TODO, NOTE, FIXME, etc """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " NOTE: These depend on custom color names (Bugs, Notes and Notices) defined " in the campo color themes. Since most themes won't define these, you can " use WildMenu as substitution. " " FIXME: the custom Bugs, Notes and Notices highlighting for campo-light isn't " working... augroup vimrc_bugs autocmd! autocmd Syntax * syn match MyBugs /\v<(FIXME|BUG|DEPRECATED):/ \ containedin=.*Comment,vimCommentTitle augroup END hi def link MyBugs Bugs augroup vimrc_notes autocmd! autocmd Syntax * syn match MyNotes /\v<(IDEA|NOTE|QUESTION|WARNING|IMPORTANT):/ \ containedin=.*Comment,vimCommentTitle augroup END hi def link MyNotes Notes augroup vimrc_notices au! autocmd Syntax * syn match MyNotices /\v<(WARNING|IMPORTANT):/ \ containedin=.*Comment,vimCommentTitle augroup END hi def link MyNotices Notices augroup vimrc_annotated_todo autocmd! " This was a major pain in the ass to get working... autocmd Syntax * syn match cTodo /@\S\+/ \ containedin=.*Comment,vimCommentTitle augroup END augroup vimrc_annotated_notes autocmd! autocmd Syntax * syn match cTodo /#\+ .\+$/ \ containedin=.*Comment,vimCommentTitle augroup END "--------------------------------------------------------------------------------------------------- "################################################################ "################################################################ "################################################################ "#5 CUSTOM FUNCTIONS / COMMANDS "################################################################ "################################################################ "################################################################ """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " BUILD COMMANDS """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " AsyncRun status line let g:airline_section_error = airline#section#create_right(['%{g:asyncrun_status}']) " Display error highlighting in source after running GCC with AsyncRun " NOTE: error results can be cleared with cr or by hiding the build " result window. let g:asyncrun_auto = "make" let errormarker_errortext = "E" let errormarker_warningtext = "W" " Thanks to https://forums.handmadehero.org/index.php/forum?view=topic&catid=4&id=704#3982 " for the error message formats " " Microsoft MSBuild errors set errorformat+=\\\ %#%f(%l\\\,%c):\ %m " Microsoft compiler: cl.exe set errorformat+=\\\ %#%f(%l)\ :\ %#%t%[A-z]%#\ %m " Microsoft HLSL compiler: fxc.exe set errorformat+=\\\ %#%f(%l\\\,%c-%*[0-9]):\ %#%t%[A-z]%#\ %m function! HideBuildResultsAndClearErrors() RemoveErrorMarkers call asyncrun#quickfix_toggle(g:quickfix_pane_height, 0) endfunction function! HideAsyncResults() call asyncrun#quickfix_toggle(g:quickfix_pane_height, 0) endfunction function! ToggleBuildResults() call asyncrun#quickfix_toggle(g:quickfix_pane_height) endfunction function! StopRunTask() AsyncStop call HideAsyncResults() endfunction function! ExecuteRunScript() exec "AsyncRun! -post=call\\ StopRunTask() ./run %" endfunction function! SilentBuild() AsyncStop exec "AsyncRun! -save=2 -post=call\\ HideAsyncResults() ./build* %" endfunction " Show results window the moment the async job starts augroup asyncPluginCmds autocmd! autocmd User AsyncRunStart call asyncrun#quickfix_toggle(g:quickfix_pane_height, 1) augroup END " Toggle build results noremap :call ToggleBuildResults() nnoremap bc :call ToggleBuildResults() " Hide build results and clear errors noremap :call HideBuildResultsAndClearErrors() " Execute build script nnoremap b :AsyncRun! -save=2 ./build* % nnoremap :call SilentBuild() " Execute run script nnoremap br :call ExecuteRunScript() nnoremap :call ExecuteRunScript() nnoremap bs :AsyncStop "Go to next build error nnoremap :cn nnoremap :cn "Go to previous build error nnoremap :cp nnoremap :cp """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " SEARCH """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Search using ripgrep (first install with Rust: cargo install ripgrep). function! Search(case_sensitive, search_args) let l:helper = "Search [" . a:search_args . " | " . (a:case_sensitive ? "case-sensitive" : "case-insensitive") . "]: " let l:term = input(l:helper) if empty(l:term) return endif "@note --pretty (i.e. colors) is not enabled in vim-ripgrep because the "quickfix window doesn't seem to parse the ansi color codes. let l:rg_args = "--column --line-number --no-heading --fixed-strings --no-ignore --hidden --follow --trim -g \"!tags\" " . a:search_args if !a:case_sensitive let l:rg_args .= " --ignore-case" endif exec 'Rg ' . l:rg_args . ' "' . l:term . '"' endfunction " Case insensitive noremap s :call Search(0, g:campo_custom_search_args) noremap :call Search(0, g:campo_custom_search_args_2) " Case sensitive noremap ss :call Search(1, g:campo_custom_search_args) noremap :call Search(1, g:campo_custom_search_args_2) " Navigation for the vim-ripgrep search results. " Hit o on a result line to open the file at that line. " Hit p on a result line to open the file at that line and return to the results pane. nnoremap o (&buftype is# "quickfix" ? "\|:lopen" : "o") nnoremap p (&buftype is# "quickfix" ? "\|:copen" : "p") """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " SEARCH & REPLACE """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Replace text in a git repo's committed files. " The range identifier allows us to run this once when multiple lines are selected in a file. function! GlobalReplaceIt(confirm_replacement) range if exists(':Ggrep') call inputsave() if a:confirm_replacement let l:term = input('Enter search term (w/ confirmation): ') else let l:term = input('Enter search term (no confirmation): ') endif call inputrestore() if empty(l:term) return endif call inputsave() let l:replacement = input('Enter replacement: ') call inputrestore() if empty(l:replacement) return endif if a:confirm_replacement let l:confirm = 'c' else let l:confirm = 'e' endif " Capture opened buffers and windows so that we can restore everything after running cdo. exec 'mksession! _replace_session.vim' " Search all committed files in the current directory " Ignoring binary files (-I) " Only including a matching filename once (--name-only) exec 'Ggrep --threads 4 -I --name-only' l:term '.' " cdo will run the command foreach file in the grep results. It opens " the file in a window so we immediately write the changes and then " wipe the buffer (closing the window). If we don't close it then vim " will run out of space when modifying a lot of files. This will " likely close buffers that were open before running the replace, but " we will restore them below from the saved session file. " Note that we don't run autocommands for optimization purposes! :noautocmd exec 'cdo' '%s/'.l:term.'/'.l:replacement.'/g'.l:confirm '| write | bwipe' " Restore the session. if filereadable('_replace_session.vim') silent! exec 'source _replace_session.vim | !rm _replace_session.vim &>/dev/null' endif call s:CreateCtags() else call PrintError("Unable to search since you're not in a git repo!") endif endfunction noremap r :call GlobalReplaceIt(0) noremap rr :call GlobalReplaceIt(1) """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " RENAME CURRENT FILE """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! RenameFile() let l:old_name = expand('%') let l:new_name = input('New file name: ', expand('%'), 'file') if l:new_name != '' && l:new_name != l:old_name exec 'saveas' l:new_name exec '!rm' l:old_name redraw! endif endfunction noremap n :call RenameFile() "---------------------------------------------------------------------------------------------------