Skip to content

Instantly share code, notes, and snippets.

@yowu
Last active May 29, 2025 11:48
Show Gist options
  • Save yowu/30b93d50505dfb2ced42d9cbe993847a to your computer and use it in GitHub Desktop.
Save yowu/30b93d50505dfb2ced42d9cbe993847a to your computer and use it in GitHub Desktop.
.vimrc
vim9script
# Basic Settings
set nocp # No compability
set number # Show line numbers
set tabstop=4 # Number of spaces for a tab
set shiftwidth=4 # Number of spaces for auto-indent
set expandtab # Use spaces instead of tabs
set ignorecase # Case-insensitive search
set smartcase # Case-sensitive if uppercase is used
set hlsearch # Highlight search matches
set incsearch # Enable incremental search
set splitbelow # Always split below
set splitright # Always split right
set ruler
set shortmess=atI # Shorten common messages
set fillchars=vert:\│
set completeopt=menuone,noinsert
set bs=2 # backspace
set ai # AutoIndent
set hidden # Not close really but stay in memory
set encoding=utf-8
set clipboard=unnamed
set belloff=all
set report=0
set timeoutlen=500
syntax on
filetype plugin on
filetype plugin indent on
set tags="./tags;,./tags;./.tags;,./.tmp_tags;"
# Enable true color support
if $TERM_PROGRAM != "Apple_Terminal"
set termguicolors
endif
# Leader keys
g:mapleader = ' '
g:maplocalleader = ','
# Platform specific settings
if has("win32")
source $VIMRUNTIME/mswin.vim
behave mswin
command Eb browse e
command Ew browse w
set renderoptions=type:directx
endif
# GUI settings
if has('gui_running')
set lines=60 columns=140
set guioptions-=T
set guifont=LiterationMono_Nerd_Font_Mono:h14:cANSI:qDRAFT
set linespace=2
endif
# Unified vim directory - always use ~/.vim regardless of platform/shell
var vim_dir = $HOME .. '/.vim/'
# Add unified vim directory to runtime path if not already present
if stridx(&runtimepath, vim_dir) == -1
set runtimepath^=~/.vim
set runtimepath+=~/.vim/after
endif
# Install vim-plug if not found
var plug_file = vim_dir .. 'autoload/plug.vim'
if !filereadable(plug_file)
system('curl -fLo ' .. plug_file .. ' --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim')
if v:shell_error == 0
execute 'source ' .. plug_file
g:plug_fresh_installed = 1
else
echoerr "Failed to install vim-plug! Exit code: " .. v:shell_error
quit
endif
endif
# Plugin Manager
call plug#begin($MYVIMDIR .. 'plugged')
# UI and Theme
Plug 'joshdick/onedark.vim'
Plug 'vim-airline/vim-airline'
Plug 'ryanoasis/vim-devicons'
Plug 'Yggdroot/indentLine'
# File Navigation
Plug 'preservim/nerdtree'
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
Plug 'airblade/vim-rooter'
# Editor Enhancement
Plug 'machakann/vim-highlightedyank'
Plug 'Raimondi/delimitMate'
Plug 'easymotion/vim-easymotion'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-repeat'
Plug 'tpope/vim-commentary'
Plug 'liuchengxu/vim-which-key'
Plug 'girishji/vimcomplete'
# Git Integration
Plug 'tpope/vim-fugitive'
Plug 'airblade/vim-gitgutter'
# Misc
Plug 'mhinz/vim-startify'
Plug 'romainl/vim-cool'
call plug#end()
# Auto-install missing plugins if plug is fresh installed.
if exists('g:plug_fresh_installed') && len(filter(values(g:plugs), '!isdirectory(v:val.dir)')) > 0
echo "Installing missing plugins..."
PlugInstall --sync
echo "Plugins installed! Please restart vim to load them properly."
quit
endif
# Plugin Configurations
# Theme
colorscheme onedark
# Startify
g:startify_change_to_dir = 0
# Rooter
g:rooter_cd_cmd = 'lcd'
g:rooter_patterns = ['.git', 'Makefile', 'package.json', '.vs', '*.sln', 'lazy-lock.json']
# IndentLine
g:indentLine_char = '┊'
# NERDTree
g:NERDTreeShowHidden = 1
g:NERDTreeRespectWildIgnore = 1
g:NERDTreeIgnore = ['.git$', '__pycache__$']
g:NERDTreeDirArrowExpandable = ''
g:NERDTreeDirArrowCollapsible = ''
# Airline
g:airline_theme = 'dark'
g:airline_left_sep = ''
g:airline_right_sep = ''
g:airline_left_alt_sep = ''
g:airline_right_alt_sep = 'ᚲ'
g:airline_powerline_fonts = 1
g:airline#extensions#whitespace#enabled = 0
g:airline#extensions#wordcount#enabled = 0
g:airline#extensions#tabline#enabled = 1
g:airline#extensions#tabline#fnamemod = ':t'
# Git Gutter
g:gitgutter_enabled = 0
g:gitgutter_sign_added = '▎'
g:gitgutter_sign_modified = '▎'
g:gitgutter_sign_removed = '▎'
g:gitgutter_sign_removed_first_line = '▎'
g:gitgutter_sign_removed_above_and_below = '▎'
g:gitgutter_sign_modified_removed = '▎'
# FZF
g:fzf_vim = {}
g:fzf_vim.preview_window = ['hidden,right,50%,<70(up,40%)', 'ctrl-/']
g:fzf_layout = { 'down': '~40%' }
# Complete
g:vimcomplete_tab_enable = 1
var vcoptions = {
completor: {
noNewlineInCompletionEver: true,
triggerWordLen: 2,
setCompleteOpt: false,
},
}
autocmd VimEnter * g:VimCompleteOptionsSet(vcoptions)
# Which-Key
# Which-key base configuration
g:which_key_use_floating_win = 0
g:which_key_timeout = 100
# Main mappings
g:which_key_map = {
'f': ['Files', 'Files'],
'b': ['Buffers', 'Buffers'],
'e': ['NERDTreeToggle', 'Toggle explorer'],
'q': 'Smart close',
}
# Local mappings
g:which_key_local_map = {
'g': {
'name': 'Goto',
'a': 'Goto related files',
'f': 'Goto file',
't': 'Goto tag',
},
}
# FZF pickers
g:which_key_map[' '] = {
'name': '+Fzf Pickers',
'c': [':Commands', 'Commands'],
'h': [':History', 'History'],
'm': [':Marks', 'Marks'],
'w': [':Windows', 'Windows'],
'l': [':Lines', 'Lines'],
'C': [':Colors', 'Colors'],
'H': [':Helptags', 'Helptags'],
't': [':Filetypes', 'Filetypes'],
'T': [':Tags', 'Tags'],
'B': [':BCommits', 'Buffer Commits'],
'g': [':GFiles', 'Git Files'],
'L': [':Locate', 'Locate'],
'a': [':Ag', 'Ag'],
'r': [':Rg', 'Ripgrep'],
}
# Plugin management
g:which_key_map.p = {
'name': '+Plug',
'i': [':PlugInstall', 'Install plugins'],
'u': [':PlugUpdate', 'Update plugins'],
'c': [':PlugClean', 'Clean plugins'],
'e': [':e $MYVIMRC', 'Edit vimrc'],
'r': [':source $MYVIMRC', 'Reload all'],
}
# Git operations
g:which_key_map.g = {
'name': '+Git',
'b': [':Gblame', 'fugitive-blame'],
'C': [':Gcommit', 'fugitive-commit'],
'd': [':Gdiff', 'fugitive-diff'],
'l': [':Glog', 'fugitive-log'],
'r': [':Gread', 'fugitive-read'],
'w': [':Gwrite', 'fugitive-write'],
'y': [':Goyo', 'goyo-mode'],
}
# Edit operations
g:which_key_map.e = {
'name': '+Edit',
'a': {
'name': 'add surrounding',
"1": ["ysiw'", "surround with '"],
'2': ['ysiw\"', 'surround with "'],
'3': ['ysiwb', 'surround with ('],
'4': ['ysiwr', 'surround with ['],
'5': ['ysiw\{', 'surround with {'],
"6": ["yss'", "surround line with '"],
'7': ['yss\"', 'surround line with "'],
'8': ['yssb', 'surround line with ('],
'9': ['yssr', 'surround line with ['],
'0': ['yss\{', 'surround line with {'],
},
'd': {
'name': '+remove surrounding',
"1": ["ds'", "remove surrounding '"],
'2': ['ds\"', 'remove surrounding "'],
'3': ['dsb', 'remove surrounding ('],
'4': ['dsr', 'remove surrounding ['],
'5': ['ds\{', 'remove surround with {'],
},
'c': {
'name': '+change surrounding',
'1': ['csbr', 'change ( to ['],
'2': ['csb\{', 'change ( to {'],
'3': ['csrb', 'change [ to ('],
'4': ['csr\{', 'change [ to {' ],
'5': ['cs\{b', 'change { to ('],
'6': ['cs\{r', 'change { to [' ],
},
}
# Movement operations
g:which_key_map.m = {
'name': '+Move',
'/': ['<Plug>(easymotion-sn)', 'search'],
'l': ['<Plug>(easymotion-lineforward)', 'beginning/ending of WORD forward'],
'h': ['<Plug>(easymotion-linebackward)', 'beginning/ending of WORD backward'],
'f': ['<plug>(easymotion-prefix)f', 'find {char} to the right'],
'F': ['<plug>(easymotion-prefix)F', 'find {char} to the left'],
't': ['<plug>(easymotion-prefix)t', 'till before the {char} to the right'],
'T': ['<plug>(easymotion-prefix)T', 'till after the {char} to the left'],
'w': ['<plug>(easymotion-prefix)w', 'beginning of word forward'],
'W': ['<plug>(easymotion-prefix)W', 'beginning of WORD forward'],
'b': ['<plug>(easymotion-prefix)b', 'beginning of word backward'],
'B': ['<plug>(easymotion-prefix)B', 'beginning of WORD backward'],
'e': ['<plug>(easymotion-prefix)e', 'end of word forward'],
'E': ['<plug>(easymotion-prefix)E', 'end of WORD forward'],
'g': {
'name': '+Backwards',
'e': ['<plug>(easymotion-prefix)ge', 'end of word backward'],
'E': ['<plug>(easymotion-prefix)gE', 'end of WORD backward'],
},
'j': ['<plug>(easymotion-prefix)j', 'line downward'],
'k': ['<plug>(easymotion-prefix)k', 'line upward'],
'n': ['<plug>(easymotion-prefix)n', 'jump to latest "/" or "?" forward'],
'N': ['<plug>(easymotion-prefix)N', 'jump to latest "/" or "?" backward'],
's': ['<plug>(easymotion-prefix)s', 'find(search) {char} forward and backward'],
' ': {
'name': 'Move in buffer',
'h': ['h', 'Left'],
'j': ['j', 'Down'],
'k': ['k', 'Up'],
'l': ['l', 'Right'],
'0': ['0', 'Start of line'],
'^': ['^', 'Start of line (non-blank)'],
'$': ['$', 'End of line'],
'b': ['b', 'Previous word'],
'w': ['w', 'Next word'],
'e': ['e', 'Next end of word'],
'%': ['%', 'Match (), [], and {}'],
'G': ['G', 'Last line'],
'{': ['{', 'Previous empty line'],
'}': ['}', 'Next empty line'],
},
}
which_key#register('<Space>', g:which_key_map)
which_key#register(',', g:which_key_local_map)
# AutoCommands
# Hide status line for specific filetypes
augroup HideStatusLine
autocmd!
autocmd FileType fzf,which_key set laststatus=0 noshowmode noruler
\| autocmd BufLeave <buffer> set laststatus=2 showmode ruler
augroup END
# Auto map 'q' to close buffer for specific buffer types
augroup QClose
autocmd!
autocmd BufWinEnter * if index(['nofile', 'help', 'quickfix'], &buftype) >= 0 && maparg('q', 'n') ==# ''
\| nnoremap <buffer> q :close<CR>
\| endif
autocmd BufWipeout * if index(['nofile', 'help', 'quickfix'], &buftype) >= 0
\| silent! nunmap <buffer> q
\| endif
augroup END
# Close vim if help/quickfix is the last buffer
autocmd BufEnter * if winnr('$') == 1 && (
\ &buftype ==# 'help' ||
\ &buftype ==# 'quickfix' ||
\ &filetype ==# 'nerdtree'
\ )
\| quit
\| endif
#
# Key Mappings
#
# File Operations
nnoremap <C-s> :wi<CR> # Save file
nnoremap <C-S-s> :wa<CR> # Save all
nnoremap <C-q> :close<CR> # Close Window
# Window Navigation
nnoremap <silent> <C-h> <C-w>h
nnoremap <silent> <C-j> <C-w>j
nnoremap <silent> <C-k> <C-w>k
nnoremap <silent> <C-l> <C-w>l
# Window Resize
nnoremap <M-Up> :resize +2<CR>
nnoremap <M-Down> :resize -2<CR>
nnoremap <M-Left> :vertical resize -4<CR>
nnoremap <M-Right> :vertical resize +4<CR>
# Line Movement
nnoremap <M-j> :move .+1<CR>==
nnoremap <M-k> :move .-2<CR>==
xnoremap <M-j> :move '>+1<CR>gv=gv
xnoremap <M-k> :move '<-2<CR>gv=gv
snoremap <M-j> <Esc>:move '>+1<CR>gv=gv
snoremap <M-k> <Esc>:move '<-2<CR>gv=gv
# FZF Mappings
nnoremap <silent> <localleader>gt :execute 'Tags ' .. expand('<cword>')<CR>
nnoremap <silent> <localleader>ga :call FindFilesWithPattern(expand('%:t:r') .. '*')<CR>
nnoremap <silent> <localleader>gf :call FindFilesWithPattern(expand('<cfile>'))<CR>
nmap <silent> <Leader>q :call SmartCloseWindow()<CR> # QClose
# which-key
nnoremap <silent> <Leader> :<c-u>WhichKey ' '<CR>
vnoremap <silent> <Leader> :<c-u>WhichKeyVisual ' '<CR>
nnoremap <localleader> :<c-u>WhichKey ','<CR>
vnoremap <localleader> :<c-u>WhichKeyVisual ','<CR>
# Functions
# Custom FZF Functions
def g:FindFilesWithPattern(pattern: string)
fzf#run(fzf#wrap({
'source': 'rg --files --iglob ' .. pattern,
'sink': 'e',
'options': '--select-1 --exit-0',
}))
enddef
# Smart Close Window
def g:SmartCloseWindow()
# Step 1: Do nothing if only 1 window
if winnr('$') == 1
return
endif
# Define tool buftypes and special filetypes
var tool_buftypes = ['nofile', 'help', 'quickfix']
var special_filetypes = ['nerdtree', 'startify']
# Step 2: Close a tool windows (high priority)
for winid in range(winnr('$'), 1, -1)
var bufnr = winbufnr(winid)
var bt = getbufvar(bufnr, '&buftype')
if index(tool_buftypes, bt) >= 0
execute ':' .. winid .. 'wincmd c'
return
endif
endfor
# Step 3: Close a special filetype windows (secondary priority)
for winid in range(winnr('$'), 1, -1)
var bufnr = winbufnr(winid)
var ft = getbufvar(bufnr, '&filetype')
if index(special_filetypes, ft) >= 0
execute ':' .. winid .. 'wincmd c'
return
endif
endfor
# Step 4: close one other than current
var curwin = winnr()
for winid in range(winnr('$'), 1, -1)
if winid != curwin
execute ':' .. winid .. 'wincmd c'
return
endif
endfor
enddef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment