Skip to content

Instantly share code, notes, and snippets.

@victor-iyi
Last active May 19, 2021 11:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save victor-iyi/f30b8f8a46a6e3711e4ade16a8179a01 to your computer and use it in GitHub Desktop.
Save victor-iyi/f30b8f8a46a6e3711e4ade16a8179a01 to your computer and use it in GitHub Desktop.
My Vim configuration file
" File: ~/.vimrc
"
" Author: Victor I. Afolabi
syntax enable
colorscheme desert
" highlight Normal guibg=none
" =============================================================================
" =============================================================================
" # PLUGINS
" =============================================================================
" =============================================================================
" Vim Plug
" Specify a directory for plugins
" - For Neovim: stdpath('data') . '/plugged'
" - Avoid using standard vim directory names like 'plugin'
call plug#begin('~/.vim/plugged')
" Load plugins
" VIM enhancements
Plug 'ciaranm/securemodelines'
Plug 'editorconfig/editorconfig-vim'
Plug 'justinmk/vim-sneak'
" GUI enhancements
Plug 'itchyny/lightline.vim'
Plug 'machakann/vim-highlightedyank'
Plug 'andymass/vim-matchup'
" NERDTree
Plug 'preservim/nerdtree'
Plug 'ryanoasis/vim-devicons'
" Plug 'tiagofumo/vim-nerdtree-syntax-highlight'
" Fuzzy finder
Plug 'airblade/vim-rooter'
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
Plug 'junegunn/fzf.vim'
" Semantic language support
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'neoclide/mycomment.vim'
Plug 'sansyrox/vim-python-virtualenv'
" Language client
Plug 'autozimu/LanguageClient-neovim', { 'branch': 'next', 'do': './install.sh' }
" Syntactic language support
Plug 'cespare/vim-toml'
Plug 'stephpy/vim-yaml'
Plug 'rust-lang/rust.vim'
Plug 'rhysd/vim-clang-format'
Plug 'fatih/vim-go'
Plug 'dag/vim-fish'
Plug 'godlygeek/tabular'
Plug 'plasticboy/vim-markdown'
Plug 'kevinoid/vim-jsonc'
" ThePrimeagen
Plug 'jremmen/vim-ripgrep'
Plug 'tpope/vim-fugitive'
Plug 'vim-utils/vim-man'
Plug 'lyuts/vim-rtags'
Plug 'git@github.com:kien/ctrlp.vim.git'
Plug 'mbbill/undotree'
" Telescope
Plug 'nvim-lua/popup.nvim'
Plug 'nvim-lua/plenary.nvim'
Plug 'nvim-telescope/telescope.nvim'
Plug 'nvim-telescope/telescope-fzy-native.nvim'
call plug#end()
if has('nvim')
set guicursor=n-v-c:block-Cursor/lCursor-blinkon0,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor
set inccommand=nosplit
noremap <C-q> :confirm qall<CR>
end
" See https://github.com/junegunn/vim-plug for more info
"
" PlugInstall [name ...] - Install plugins
" PlugUpdate [name ...] - Install or update plugins
" PlugClean [!] - Remove unlisted plugins
" PlugUpgrade - Upgrade vim-plug itself
" PlugStatus - Check the status of plugins
" PlugDiff - Examine changes from previous update
" and pending changes
" PlugSnapshot [!] [output path] - Generate script for restoring current
" snapshot of the plugins.
" =============================================================================
" =============================================================================
" # Editor settings
" =============================================================================
" =============================================================================
filetype plugin indent on
set autoindent
set timeoutlen=300 " http://stackoverflow.com/questions/2158516/delay-before-o-opens-a-new-line
set encoding=utf-8
set scrolloff=8
set noshowmode
set hidden
set nowrap
set nojoinspaces
set printfont=:h10
set printencoding=utf-8
set printoptions=paper:letter
set signcolumn=yes " Always show the sign column.
" Otherwise it would shift the
" text each time diagnostics
" appear/become resolved.
" Settings needed for .lvimrc
set exrc
set secure
" Sane splits
set splitright
set splitbelow
" No backup & swp files.
set noswapfile
set nobackup
set nowritebackup
" Permanent undo
set undodir=~/.vim/undodir
set undofile
" Decent wildmenu
set wildmode=list:longest
set wildignore=.hg,.svn,*~,*.png,*.jpg,*.gif,*.settings,Thumbs.db,.DS_Store,*.min.js,*.swp,publish/*,intermediate/*,*.o,*.hi,Zend,vendor
" Use narrow tabs
set shiftwidth=2
set softtabstop=2
set tabstop=2
set expandtab
set autoindent
" Wrapping options
set formatoptions=tc " Wrap text and comments using textwidth
set formatoptions+=r " continue comments when pressing ENTER in I mode
set formatoptions+=q " enable formatting of comments with gq
set formatoptions+=n " detect list for formatting
set formatoptions+=b " auto-wrap insert mode, and do not wrap old long lines
" Proper search
set incsearch
set ignorecase
set smartcase
set gdefault
" =============================================================================
" =============================================================================
" # GUI settings
" =============================================================================
" =============================================================================
set guifont=FiraCode_Nerd_Font:h11
set guioptions-=T " Remove toolbar
set vb t_vb= " No more beeps
set backspace=indent,eol,start
set nofoldenable
set ttyfast
" https://github.com/vim/vim/issues/1735#issuescomment-383353563
set lazyredraw
set synmaxcol=500
set laststatus=2
set relativenumber " Relative line numbers
set number " Also show current absolute line
"set diffopt+=iwhite " No whitespace in vimdiff
" Make diffing better: https://vimways.org/2018/the-power-of-diff/
"set diffopt+=algorithm:patience
"set diffopt+=indent-heuristic
set colorcolumn=80 " and give me a colored column
set showcmd " Show (partial) command in status line.
set mouse=a " Enable mouse usage (all modes) in terminals
set shortmess+=c " Don't give |ins-completion-menu| messages.
" Show those damn hidden characters
" Verbose: set listchars=nbsp:¬,eol:¶,extends:»,precedes:«,trail:•
set listchars=nbsp:¬,extends:»,precedes:«,trail:•
" LanguageClient server commands.
"
" Required for operations modifying multiple buffers like rename.
set hidden
" Completion
" Better display for messages
set cmdheight=2
" You will have bad experience for diagnostic messages when it's default 4000.
set updatetime=300
" Add (Neo)Vim's native statusline support.
" NOTE: Please see `:h coc-status` for integrations with external plugins that
" provide custom statusline: lightline.vim, vim-airline.
" set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}
" =============================================================================
" =============================================================================
" # Let
" =============================================================================
" =============================================================================
"
"
" Button used for `<leader>`.
let mapleader = "\<Space>"
" Markdown
let g:sneak#s_next = 1
let g:vim_markdown_new_list_item_indent = 0
let g:vim_markdown_auto_insert_bullets = 0
let g:vim_markdown_frontmatter = 1
" netrw
let g:netrw_browse_split=2
let g:netrw_banner = 0
let g:netrw_winsize = 25
" ctrlp
let g:ctrlp_user_command = ['.git/', 'git --git-dir=%s/.git ls-files -oc --exclude-standard']
let g:ctrlp_use_caching = 0
" Airline
" let g:airline_powerline_fonts = 1
" Server commands.
"
" rust - L.S. can be changed to use 'stable' | 'beta' | 'nightly'
"
let g:LanguageClient_serverCommands = {
\ 'rust': ['~/.cargo/bin/rustup', 'run', 'stable', 'rls'],
\ 'javascript': ['/usr/local/bin/javascript-typescript-stdio'],
\ 'javascript.jsx': ['tcp://127.0.0.1:2089'],
\ 'python': ['/Library/Frameworks/Python.framework/Versions/3.8/bin/pyls'],
\ }
" NOTE: If you're using Plug mapping, you shouldn't use `noremap` mappings.
"
" nnoremap <silent> K :call LanguageClient#textDocument_hover()<CR>
" nnoremap <silent> gd :call LanguageClient#textDocument_definition()<CR>
" nnoremap <silent> <F2> :call LanguageClient#textDocument_rename()<CR>
" Plugin settings
let g:secure_modelines_allowed_items = [
\ "textwidth", "tw",
\ "softtabstop", "sts",
\ "shiftwidth", "sw",
\ "expandtab", "et", "noexpandtab", "noet",
\ "filetype", "ft",
\ "foldmethod", "fdm",
\ "readonly", "ro", "noreadonly", "noro",
\ "rightleft", "rl", "norightleft", "norl",
\ "colorcolumn"
\ ]
" Lightline
let g:lightline = {
\ 'active': {
\ 'left': [ [ 'mode', 'paste' ],
\ [ 'cocstatus', 'readonly', 'filename', 'modified' ] ]
\ },
\ 'component_function': {
\ 'filename': 'LightlineFilename',
\ 'cocstatus': 'coc#status'
\ },
\ }
function! LightlineFilename()
return expand('%:t') !=# '' ? @% : '[No Name]'
endfunction
" use autocmd to force lightline update
autocmd User CocStatusChange,CocDiagnosticChange call lightline#update()
" from http://sheerun.net/2014/03/21/how-to-boost-your-vim-productivity/
if executable('ag')
set grepprg=ag\ --nogroup\ --nocolor
endif
if executable('rg')
set grepprg=rg\ --no-heading\ --vimgrep
set grepformat=%f:%l:%c:%m
endif
" JavaScript
let javascript_fold=0
" Java
let java_ignore_javadoc=1
" Latex
let g:latex_indent_enabled = 1
let g:latex_fold_envs = 0
let g:latex_fold_sections = []
" Rust
let g:rustfmt_autosave = 1
let g:rustfmt_emit_files = 1
let g:rustfmt_fail_silently = 0
if has('macunix')
let g:rust_clip_command = 'pbcopy'
elseif has('win32')
let g:rust_clip_command = 'clip'
else
let g:rust_clip_command = 'xclip -selection clipboard'
endif
" Python
let g:python3_host_prog='/usr/local/bin/python3'
" Don't confirm .lvimrc
let g:localvimrc_ask = 0
" =============================================================================
" =============================================================================
" # Keyboard shortcuts | Mappings
" =============================================================================
" =============================================================================
" See https://stackoverflow.com/questions/3776117/what-is-the-difference-between-the-remap-noremap-nnoremap-and-vnoremap-mapping
" for more info on `nnoremap`.
" Source .vimrc
map <C-s> :source ~/.vimrc<CR>
" Search result centered please
nnoremap <silent> n nzz
nnoremap <silent> N Nzz
nnoremap <silent> * *zz
nnoremap <silent> # #zz
nnoremap <silent> g* g*zz
" Very magic by default
nnoremap ? ?\v
nnoremap / /\v
cnoremap %s/ %sm/
" Open hotkeys
map <C-p> :Files<CR>
nmap <leader>; :Buffers<CR>
" Quick-save
nmap <leader>w :w<CR>
" ; as :
nnoremap ; :
" Ctrl+j and Ctrl+k as Esc
" Ctrl-j is a little awkward unfortunately:
" https://github.com/neovim/neovim/issues/5916
" So we also map Ctrl+k
nnoremap <C-j> <Esc>
inoremap <C-j> <Esc>
vnoremap <C-j> <Esc>
snoremap <C-j> <Esc>
xnoremap <C-j> <Esc>
cnoremap <C-j> <C-c>
onoremap <C-j> <Esc>
lnoremap <C-j> <Esc>
tnoremap <C-j> <Esc>
nnoremap <C-k> <Esc>
inoremap <C-k> <Esc>
vnoremap <C-k> <Esc>
snoremap <C-k> <Esc>
xnoremap <C-k> <Esc>
cnoremap <C-k> <C-c>
onoremap <C-k> <Esc>
lnoremap <C-k> <Esc>
tnoremap <C-k> <Esc>
" Ctrl+h to stop searching
vnoremap <C-h> :nohlsearch<cr>
nnoremap <C-h> :nohlsearch<cr>
" Suspend with Ctrl+f
inoremap <C-f> :sus<cr>
vnoremap <C-f> :sus<cr>
nnoremap <C-f> :sus<cr>
" =============================================================================
" Window Mappings.
" =============================================================================
" Window command (focus)
nnoremap <leader>h :wincmd h<CR>
nnoremap <leader>j :wincmd j<CR>
nnoremap <leader>k :wincmd k<CR>
nnoremap <leader>l :wincmd l<CR>
" Window resize
nnoremap <silent> <Leader>+ :vertical resize +5<CR>
nnoremap <silent> <Leader>- :vertical resize -5<CR>
nnoremap <silent> <Leader>vr :vertical resize 30<CR>
" Move selected lines up or down.
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv
" Project history tree.
nnoremap <leader>u :UndotreeShow<CR>
" project view (project file tree)
" nnoremap <leader>pv :wincmd v<bar> :Ex <bar> :vertical resize 30<CR>
""NERDTree
noremap <leader>s :Rg
let g:fzf_layout = { 'down': '~20%' }
command! -bang -nargs=* Rg
nnoremap <Leader>pt :NERDTreeToggle<Enter>
nnoremap <silent> <Leader>pv :NERDTreeFind<CR>
nnoremap <leader>n :NERDTreeFocus<CR>
""
" =============================================================================
" Editor Mappings.
" =============================================================================
vmap <Leader>y "+y
vmap <Leader>= <C-W><C-=>
" Jump to the start and end of line using the home row keys
map H ^
map L $
" Neat X clipboard integration
" ,p will paste clipboard into buffer
" ,c will copy entire buffer into clipboard
" noremap <leader>p :read !xsel --clipboard --output<cr>
" noremap <leader>c :w !xsel -ib<cr><cr>
if has('macunix')
noremap <leader>p :read !pbpaste<cr>
" noremap <leader>c :w !pbcopy<cr><cr>
else
noremap <leader>p :read !xsel --clipboard --output<cr>
" noremap <leader>c :w !xsel -ib<cr><cr>
endif
" project search (search entire project for ...)
" <leader>s for Rg search
noremap <leader>s :Rg<SPACE>
let g:fzf_layout = { 'down': '~20%' }
command! -bang -nargs=* Rg
\ call fzf#vim#grep(
\ 'rg --column --line-number --no-heading --color=always '.shellescape(<q-args>), 1,
\ <bang>0 ? fzf#vim#with_preview('up:60%')
\ : fzf#vim#with_preview('right:50%:hidden', '?'),
\ <bang>0)
function! s:list_cmd()
let base = fnamemodify(expand('%'), ':h:.:S')
return base == '.' ? 'fd --type file --follow' : printf('fd --type file --follow | proximity-sort %s', shellescape(expand('%')))
endfunction
command! -bang -nargs=? -complete=dir Files
\ call fzf#vim#files(<q-args>, {'source': s:list_cmd(),
\ 'options': '--tiebreak=index'}, <bang>0)
" Open new file adjacent to current file
nnoremap <leader>e :e <C-R>=expand("%:p:h") . "/" <CR>
" No arrow keys -- force yourself to use the home row hjkl
nnoremap <up> <nop>
nnoremap <down> <nop>
inoremap <up> <nop>
inoremap <down> <nop>
inoremap <left> <nop>
inoremap <right> <nop>
" Left and right can switch buffers
nnoremap <left> :bp<CR>
nnoremap <right> :bn<CR>
" Move by line
nnoremap j gj
nnoremap k gk
" =============================================================================
" # CoC (Conquer of Completion) - https://github.com/neoclide/coc.nvim
" =============================================================================
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 'Smart' navigation
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <TAB>
\ pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ coc#refresh()
function! s:check_back_space() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
" Use <c-.> to trigger completion.
inoremap <silent><expr> <c-.> coc#refresh()
" Use <cr> to confirm completion, `<C-g>u` means break undo chain at current
" position. Coc only does snippet and additional edit on confirm.
if exists('*complete_info')
inoremap <expr> <cr> complete_info()["selected"] != "-1" ? "\<C-y>" : "\<C-g>u\<CR>"
else
imap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
endif
" Use `[g` and `]g` to navigate diagnostics
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
" GoTo code navigation.
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
" Use K to show documentation in preview window.
nnoremap <silent> K :call <SID>show_documentation()<CR>
function! s:show_documentation()
if (index(['vim','help'], &filetype) >= 0)
execute 'h '.expand('<cword>')
elseif (coc#rpc#ready())
call CocActionAsync('doHover')
else
execute '!' . &keywordprg . " " . expand('<cword>')
" call CocAction('doHover')
endif
endfunction
" Highlight the symbol and its references when holding the cursor.
autocmd CursorHold * silent call CocActionAsync('highlight')
" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)
" Formatting selected code.
xmap <leader>f <Plug>(coc-format-selected)
nmap <leader>f <Plug>(coc-format-selected)
" Applying codeAction to the selected region.
" Example: `<leader>aap` for current paragraph
xmap <leader>a <Plug>(coc-codeaction-selected)
nmap <leader>a <Plug>(coc-codeaction-selected)
" Remap keys for applying codeAction to the current buffer.
nmap <leader>ac <Plug>(coc-codeaction)
" Apply AutoFix to problem on the current line.
nmap <leader>qf <Plug>(coc-fix-current)
" Introduce function text object
" NOTE: Requires 'textDocument.documentSumbol' support from the language
" server.
xmap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap if <Plug>(coc-funcobj-i)
omap af <Plug>(coc-funcobj-a)
" Use <TAB> for selections ranges.
nmap <silent> <TAB> <Plug>(coc-range-select)
xmap <silent> <TAB> <Plug>(coc-range-select)
" Find symbol of current document.
nnoremap <silent> <space>o :<C-u>CocList outline<cr>
" Search workspace symbols.
nnoremap <silent> <space>s :<C-u>CocList -I symbols<cr>
" Implement methods for trait
nnoremap <silent> <space>i :call CocActionAsync('codeAction', '', 'Implement missing members')<cr>
" Show actions available at this location
nnoremap <silent> <space>a :CocAction<cr>
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Mapping for CoCList
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" " Show all diagnostics.
" nnoremap <silent><nowait> <space>a :<C-u>CocList diagnostics<cr>
" " Manage extensions.
" nnoremap <silent><nowait> <space>e :<C-u>CocList extensions<cr>
" " Show commands.
" nnoremap <silent><nowait> <space>c :<C-u>CocList commands<cr>
" " Find symbol of current document.
" nnoremap <silent><nowait> <space>o :<C-u>CocList outline<cr>
" " Search workspace symbols.
" nnoremap <silent><nowait> <space>s :<C-u>CocList -I symbols<cr>
" " Do default action for next item.
" nnoremap <silent><nowait> <space>j :<C-u>CocNext<CR>
" " Do default action for previous item.
" nnoremap <silent><nowait> <space>k :<C-u>CocPrev<CR>
" " Resume latest coc list.
" nnoremap <silent><nowait> <space>p :<C-u>CocListResume<CR>
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" <leader><leader> toggles between buffers
nnoremap <leader><leader> <c-^>
" <leader>, shows/hides hidden characters
nnoremap <leader>, :set invlist<cr>
" <leader>q shows stats
nnoremap <leader>q g<c-g>
" Keymap for replacing up to next _ or -
noremap <leader>m ct+
" I can type :help on my own, thanks :)
map <F1> <Esc>
imap <F1> <Esc>
" =============================================================================
" =============================================================================
" # Autocommands
" =============================================================================
" =============================================================================
" Prevent accidental writes to buffers that shouldn't be edited
autocmd BufRead *.orig set readonly
autocmd BufRead *.pacnew set readonly
" Leave paste mode when leaving insert mode
autocmd InsertLeave * set nopaste
" Jump to last edit position on opening file
if has("autocmd")
" https://stackoverflow.com/questions/31449496/vim-ignore-specific-file-in-autocommand
au BufReadPost * if expand('%:p') !~# '\m/\.git/' && line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif
" Follow Python code style rules
au Filetype python source ~/.config/nvim/scripts/fourspaces.vim
au Filetype python set colorcolumn=79
" Help filetype detection
autocmd BufRead *.plot set filetype=gnuplot
autocmd BufRead *.md set filetype=markdown
autocmd BufRead *.lds set filetype=ld
autocmd BufRead *.tex set filetype=text
autocmd BufRead *trm set filetype=c
autocmd BufRead *.cc set filetype=cpp
autocmd BufRead *.xlsx.axlsx set filetype=ruby
" Script plugins
autocmd Filetype html,xml,xsl,php source ~/.config/nvim/scripts/closetag.vim
" =============================================================================
" # Footer
" =============================================================================
" nvim
if has('nvim')
runtime! plugin/python_setup.vim
endif
" File: ~/.config/nvim/scripts/closetag.vim
"
" Summary: Functions and mappings to close open HTML/XML tags
" Uses: <C-_> -- close matching open tag
" Author: Steven Mueller <diffusor@ugcs.caltech.edu>
" Last Modified: Tue May 24 13:29:48 PDT 2005
" Version: 0.9.1
" XXX - breaks if close attempted while XIM is in preedit mode
" TODO - allow usability as a global plugin -
" Add g:unaryTagsStack - always contains html tags settings
" and g:closetag_default_xml - user should define this to default to xml
" When a close is attempted but b:unaryTagsStack undefined,
" use b:closetag_html_style to determine if the file is to be treated
" as html or xml. Failing that, check the filetype for xml or html.
" Finally, default to g:closetag_html_style.
" If the file is html, let b:unaryTagsStack=g:unaryTagsStack
" otherwise, let b:unaryTagsStack=""
" TODO - make matching work for all comments
" -- kinda works now, but needs syn sync minlines to be very long
" -- Only check whether in syntax in the beginning, then store comment tags
" in the tagstacks to determine whether to move into or out of comment mode
" TODO - The new normal mode mapping clears recent messages with its <ESC>, and
" it doesn't fix the null-undo issue for vim 5.7 anyway.
" TODO - make use of the following neat features:
" -- the ternary ?: operator
" -- :echomsg and :echoerr
" -- curly brace expansion for variables and function name definitions?
" -- check up on map <blah> \FuncName
"
" Description:
" This script eases redundant typing when writing html or xml files (even if
" you're very good with ctrl-p and ctrl-n :). Hitting ctrl-_ will initiate a
" search for the most recent open tag above that is not closed in the
" intervening space and then insert the matching close tag at the cursor. In
" normal mode, the close tag is inserted one character after cursor rather than
" at it, as if a<C-_> had been used. This allows putting close tags at the
" ends of lines while in normal mode, but disallows inserting them in the
" first column.
"
" For HTML, a configurable list of tags are ignored in the matching process.
" By default, the following tags will not be matched and thus not closed
" automatically: area, base, br, dd, dt, hr, img, input, link, meta, and
" param.
"
" For XML, all tags must have a closing match or be terminated by />, as in
" <empty-element/>. These empty element tags are ignored for matching.
"
" Comment checking is now handled by vim's internal syntax checking. If tag
" closing is initiated outside a comment, only tags outside of comments will
" be matched. When closing tags in comments, only tags within comments will
" be matched, skipping any non-commented out code (wee!). However, the
" process of determining the syntax ID of an arbitrary position can still be
" erroneous if a comment is not detected because the syntax highlighting is
" out of sync, or really slow if syn sync minlines is large.
" Set the b:closetag_disable_synID variable to disable this feature if you
" have really big chunks of comment in your code and closing tags is too slow.
"
" If syntax highlighting is not enabled, comments will not be handled very
" well. Commenting out HTML in certain ways may cause a "tag mismatch"
" message and no completion. For example, '<!--a href="blah">link!</a-->'
" between the cursor and the most recent unclosed open tag above causes
" trouble. Properly matched well formed tags in comments don't cause a
" problem.
"
" Install:
" To use, place this file in your standard vim scripts directory, and source
" it while editing the file you wish to close tags in. If the filetype is not
" set or the file is some sort of template with embedded HTML, you may force
" HTML style tag matching by first defining the b:closetag_html_style buffer
" variable. Otherwise, the default is XML style tag matching.
"
" Example:
" :let b:closetag_html_style=1
" :source ~/.vim/scripts/closetag.vim
"
" For greater convenience, load this script in an autocommand:
" :au Filetype html,xml,xsl source ~/.vim/scripts/closetag.vim
"
" Also, set noignorecase for html files or edit b:unaryTagsStack to match your
" capitalization style. You may set this variable before or after loading the
" script, or simply change the file itself.
"
" Configuration Variables:
"
" b:unaryTagsStack Buffer local string containing a whitespace
" seperated list of element names that should be
" ignored while finding matching closetags. Checking
" is done according to the current setting of the
" ignorecase option.
"
" b:closetag_html_style Define this (as with let b:closetag_html_style=1)
" and source the script again to set the
" unaryTagsStack to its default value for html.
"
" b:closetag_disable_synID Define this to disable comment checking if tag
" closing is too slow. This can be set or unset
" without having to source again.
"
" Changelog:
" May 24, 2005 Tuesday
" * Changed function names to be script-local to avoid conflicts with other
" scripts' stack implementations.
"
" June 07, 2001 Thursday
" * Added comment handling. Currently relies on synID, so if syn sync
" minlines is small, the chance for failure is high, but if minlines is
" large, tagclosing becomes rather slow...
"
" * Changed normal mode closetag mapping to use <C-R> in insert mode
" rather than p in normal mode. This has 2 implications:
" - Tag closing no longer clobbers the unnamed register
" - When tag closing fails or finds no match, no longer adds to the undo
" buffer for recent vim 6.0 development versions.
" - However, clears the last message when closing tags in normal mode
"
" * Changed the closetag_html_style variable to be buffer-local rather than
" global.
"
" * Expanded documentation
"------------------------------------------------------------------------------
" User configurable settings
"------------------------------------------------------------------------------
" if html, don't close certain tags. Works best if ignorecase is set.
" otherwise, capitalize these elements according to your html editing style
if !exists("b:unaryTagsStack") || exists("b:closetag_html_style")
if &filetype == "html" || exists("b:closetag_html_style")
let b:unaryTagsStack="area base br dd dt hr img input link meta param"
else " for xsl and xsl
let b:unaryTagsStack=""
endif
endif
" Has this already been loaded?
if exists("loaded_closetag")
finish
endif
let loaded_closetag=1
" set up mappings for tag closing
inoremap <C-_> <C-R>=GetCloseTag()<CR>
map <C-_> a<C-_><ESC>
"------------------------------------------------------------------------------
" Tag closer - uses the stringstack implementation below
"------------------------------------------------------------------------------
" Returns the most recent unclosed tag-name
" (ignores tags in the variable referenced by a:unaryTagsStack)
function! GetLastOpenTag(unaryTagsStack)
" Search backwards through the file line by line using getline()
" Overall strategy (moving backwards through the file from the cursor):
" Push closing tags onto a stack.
" On an opening tag, if the tag matches the stack top, discard both.
" -- if the tag doesn't match, signal an error.
" -- if the stack is empty, use this tag
let linenum=line(".")
let lineend=col(".") - 1 " start: cursor position
let first=1 " flag for first line searched
let b:TagStack="" " main stack of tags
let startInComment=s:InComment()
let tagpat='</\=\(\k\|[-:]\)\+\|/>'
" Search for: closing tags </tag, opening tags <tag, and unary tag ends />
while (linenum>0)
" Every time we see an end-tag, we push it on the stack. When we see an
" open tag, if the stack isn't empty, we pop it and see if they match.
" If no, signal an error.
" If yes, continue searching backwards.
" If stack is empty, return this open tag as the one that needs closing.
let line=getline(linenum)
if first
let line=strpart(line,0,lineend)
else
let lineend=strlen(line)
endif
let b:lineTagStack=""
let mpos=0
let b:TagCol=0
" Search the current line in the forward direction, pushing any tags
" onto a special stack for the current line
while (mpos > -1)
let mpos=matchend(line,tagpat)
if mpos > -1
let b:TagCol=b:TagCol+mpos
let tag=matchstr(line,tagpat)
if exists("b:closetag_disable_synID") || startInComment==s:InCommentAt(linenum, b:TagCol)
let b:TagLine=linenum
call s:Push(matchstr(tag,'[^<>]\+'),"b:lineTagStack")
endif
"echo "Tag: ".tag." ending at position ".mpos." in '".line."'."
let lineend=lineend-mpos
let line=strpart(line,mpos,lineend)
endif
endwhile
" Process the current line stack
while (!s:EmptystackP("b:lineTagStack"))
let tag=s:Pop("b:lineTagStack")
if match(tag, "^/") == 0 "found end tag
call s:Push(tag,"b:TagStack")
"echo linenum." ".b:TagStack
elseif s:EmptystackP("b:TagStack") && !s:Instack(tag, a:unaryTagsStack) "found unclosed tag
return tag
else
let endtag=s:Peekstack("b:TagStack")
if endtag == "/".tag || endtag == "/"
call s:Pop("b:TagStack") "found a open/close tag pair
"echo linenum." ".b:TagStack
elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error
echohl Error
echon "\rError:"
echohl None
echo " tag mismatch: <".tag."> doesn't match <".endtag.">. (Line ".linenum." Tagstack: ".b:TagStack.")"
return ""
endif
endif
endwhile
let linenum=linenum-1 | let first=0
endwhile
" At this point, we have exhausted the file and not found any opening tag
echo "No opening tags."
return ""
endfunction
" Returns closing tag for most recent unclosed tag, respecting the
" current setting of b:unaryTagsStack for tags that should not be closed
function! GetCloseTag()
let tag=GetLastOpenTag("b:unaryTagsStack")
if tag == ""
return ""
else
return "</".tag.">"
endif
endfunction
" return 1 if the cursor is in a syntactically identified comment field
" (fails for empty lines: always returns not-in-comment)
function! s:InComment()
return synIDattr(synID(line("."), col("."), 0), "name") =~ 'Comment'
endfunction
" return 1 if the position specified is in a syntactically identified comment field
function! s:InCommentAt(line, col)
return synIDattr(synID(a:line, a:col, 0), "name") =~ 'Comment'
endfunction
"------------------------------------------------------------------------------
" String Stacks
"------------------------------------------------------------------------------
" These are strings of whitespace-separated elements, matched using the \< and
" \> patterns after setting the iskeyword option.
"
" The sname argument should contain a symbolic reference to the stack variable
" on which method should operate on (i.e., sname should be a string containing
" a fully qualified (ie: g:, b:, etc) variable name.)
" Helper functions
function! s:SetKeywords()
let g:IsKeywordBak=&iskeyword
let &iskeyword="33-255"
endfunction
function! s:RestoreKeywords()
let &iskeyword=g:IsKeywordBak
endfunction
" Push el onto the stack referenced by sname
function! s:Push(el, sname)
if !s:EmptystackP(a:sname)
exe "let ".a:sname."=a:el.' '.".a:sname
else
exe "let ".a:sname."=a:el"
endif
endfunction
" Check whether the stack is empty
function! s:EmptystackP(sname)
exe "let stack=".a:sname
if match(stack,"^ *$") == 0
return 1
else
return 0
endif
endfunction
" Return 1 if el is in stack sname, else 0.
function! s:Instack(el, sname)
exe "let stack=".a:sname
call s:SetKeywords()
let m=match(stack, "\\<".a:el."\\>")
call s:RestoreKeywords()
if m < 0
return 0
else
return 1
endif
endfunction
" Return the first element in the stack
function! s:Peekstack(sname)
call s:SetKeywords()
exe "let stack=".a:sname
let top=matchstr(stack, "\\<.\\{-1,}\\>")
call s:RestoreKeywords()
return top
endfunction
" Remove and return the first element in the stack
function! s:Pop(sname)
if s:EmptystackP(a:sname)
echo "Error! Stack ".a:sname." is empty and can't be popped."
return ""
endif
exe "let stack=".a:sname
" Find the first space, loc is 0-based. Marks the end of 1st elt in stack.
call s:SetKeywords()
let loc=matchend(stack,"\\<.\\{-1,}\\>")
exe "let ".a:sname."=strpart(stack, loc+1, strlen(stack))"
let top=strpart(stack, match(stack, "\\<"), loc)
call s:RestoreKeywords()
return top
endfunction
function! s:Clearstack(sname)
exe "let ".a:sname."=''"
endfunction
" File: ~/.config/nvim/scripts/fourspaces.vim
"
" Use 4 spaces instead of default 2
set shiftwidth=4
set softtabstop=4
set tabstop=4
set expandtab
" File: ~/.vim/plugged/mycomment.vim
"
" Comment: (also supports toggle).
" Normal mode: <leader>c OR <leader>cc
" Visual mode: <leader>c
"
" Toggle:
" Visual mode: <leader>t
if get(g:, "comment_loaded", 0) || v:version < 704
finish
endif
let g:comment_loaded = 1
let s:save_cpo = &cpo
set cpo&vim
" Mapping
vnoremap <silent> <leader>c :call <SID>CommentFromSelected('visual')<CR>
vnoremap <silent> <leader>t :call <SID>ToggleCommentFromSelected()<CR>
nnoremap <silent> <leader>c :<C-u>set operatorfunc=<SID>CommentFromSelected<CR>g@
nnoremap <silent> <leader>cc :<C-u>set opfunc=<SID>CommentFromSelected<Bar>exe 'normal! 'v:count1.'g@_'<CR>
let s:xmls = ['html', 'xhtml', 'xml', 'eruby', 'wxml']
function! s:ToggleCommentFromSelected()
let line = getline('.')
if empty(line) | return | endif
if index(s:xmls, &ft) != -1 | return | endif
let com_beg = get(b:, 'comment_begin', get(s:comment_begin, &ft, '#'))
let com_end = get(b:, 'comment_end', get(s:comment_end, &ft, ''))
let hasComment = substitute(getline('.'), s:regex, '', '')[0 : len(com_beg) - 1] ==# com_beg ? 1 : 0
let str = s:CommentToggle(line, com_beg, com_end, hasComment, indent('.'))
call setline('.', str)
endfunction
function! s:CommentFromSelected(type, ...) range
let pos = getcurpos()
let sel_save = &selection
let &selection = "inclusive"
if index(s:xmls, &ft) != -1 && exists('*emmet#toggleComment')
call emmet#toggleComment()
return
endif
if a:type ==# 'visual'
let start = a:firstline
autocmd BufRead *.pacnew set readonly
let end = a:lastline
else
normal! '[
let start = line('.')
normal! ']
let end = line('.')
endif
call s:CommentLines(start, end)
let &selection = sel_save
call setpos('.', pos)
endfunction
" Line comments... (start of the comment)
let s:comment_begin = {
\"c" : "// ",
\"cpp" : "// ",
\"css" : "/*",
\"wxss" : "/*",
\"scss" : "/*",
\"default" : "# ",
\"python" : "# ",
\"make" : "# ",
\"sh" : "# ",
\"conf" : "# ",
\"zsh" : "# ",
\"dosini" : "# ",
\"yaml" : "# ",
\"toml" : "# ",
\"rust" : "// ",
\"go" : "// ",
\"java" : "// ",
\"swift" : "// ",
\"json" : "// ",
\"jsonc" : "// ",
\"javascript" : "// ",
\"typescript" : "// ",
\"plaintex" : "% ",
\"tex" : "% ",
\"vim" : "\" ",
\"markdown" : "<!--",
\"xhtml" : "<!--",
\"xml" : "<!--",
\"wxml" : "<!--",
\"html" : "<!--",
\"eruby" : "<!--",
\}
" (optional)
let s:comment_end = {
\"css" : "*/",
\"wxss" : "*/",
\"scss" : "*/",
\"default" : "",
\"xhtml" : "-->",
\"xml" : "-->",
\"wxml" : "-->",
\"html" : "-->",
\"markdown" : "-->",
\"eruby" : "-->",
\}
let s:regex = '^\s*'
function! s:CommentToggle(line, com_beg, com_end, hasComment, min)
let indent = matchstr(a:line, s:regex)
let sl = len(indent)
let has = a:line[sl : len(a:com_beg) + sl - 1] ==# a:com_beg
if a:hasComment && has
let str = indent . a:line[len(a:com_beg) + sl : len(a:line)-len(a:com_end) - 1]
elseif !a:hasComment
let str = a:com_beg . a:line[a:min : ] . a:com_end
if a:min | let str = a:line[0 : a:min - 1] . str | endif
else
let str = a:line
endif
return str
endfunction
function! s:CommentLines(start, end)
let g:s = a:start
let g:e = a:end
let lines = []
let indents = []
let com_begin = get(b:, 'comment_begin', get(s:comment_begin, &ft, '#'))
let com_end = get(b:, 'comment_end', get(s:comment_end, &ft, ''))
let hasComment = substitute(getline('.'), s:regex, '', '')[0 : len(com_begin) - 1] ==# com_begin ?
\ 1 : 0
for lnum in range(a:start, a:end)
call add(indents, indent(lnum))
call add(lines, getline(lnum))
endfor
let min_indent = min(indents)
if min_indent && getline(a:start)[0] =~# '\t'
let min_indent = min_indent/&tabstop
endif
call map(lines, 's:CommentToggle(v:val, com_begin, com_end, hasComment, min_indent)')
call setline(a:start, lines)
endfunction
command! -range -nargs=0 Comment :call s:CommentLines(<line1>, <line2>)
let &cpo = s:save_cpo
unlet s:save_cpo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment