[!NOTE|label:references:]

expr

[!NOTE|label:references:]

- USE 'ignorecase' MATCH CASE CASE INSENSITIVE
equal == ==# ==?
not equal != !=# !=?
greater than > ># >?
greater than or equal >= >=# >=?
smaller than < <# <?
smaller than or equal <= <=# <=?
regexp matches =~ =~# =~?
regexp doesn't match !~ !~# !~?
same instance is is# is?
different instance isnot isnot# isnot?

builtin function details

math

:echo abs(1.456)
1.456
:echo abs(-5.456)
5.456

:echo acos(0)
1.570796
:echo acos(-0.5)
2.094395

:echo asin(0.8)
0.927295
:echo asin(-0.5)
-0.523599

:echo cosh(0.5)
1.127626
:echo cosh(-0.5)
1.127626

" exponential
:echo exp(2)
7.389056
:echo exp(-1)
0.367879

" x/y
:echo fmod(12.33, 1.22)
0.13

" smallest integral value greater than or equal to {expr} as a Float
:echo ceil(1.456)
2.0
:echo ceil(-5.456)
-5.0

" largest integral value less than or equal to
:echo floor(1.856)
1.0
:echo floor(-5.456)
-6.0

assert

:echo assert_equal( 'foo', 'bar' )
1

:echo assert_match( '^f.*o$', 'foobar' )
1

list

:echo blob2list(0z0102.0304)
[1, 2, 3, 4]
:echo blob2list(0z)
[]

" string to list
:echo str2list("ABC")
[65, 66, 67]
" exists
:let l = [1, 2, 3]
:echo exists("l[5]")
0
:echo exists("l[2]")
1
" basic
:let newlist = [1, 2, 3] + [4, 5]
:echo newlist
[1, 2, 3, 4, 5]
:call extend(newlist, [2, 3], 1)
:echo newlist
[1, 2, 3, 2, 3, 4, 5]

" sort
:echo sort(extend(newlist, [7, 5]))
[1, 2, 3, 4, 5, 5, 7]

" flatten
:echo flatten([1, [2, [3, 4]], 5])
[1, 2, 3, 4, 5]
:echo flatten([1, [2, [3, 4]], 5], 1)
[1, 2, [3, 4], 5]

system info

" whether if exists
:echo exists("&mouse")
1
:echo exists("$HOSTNAME")
0
:echo exists("*strftime")
1
:echo exists("*s:MyFunc")
0
:echo exists("*MyFunc")
0
:echo exists("*v:lua.Func")
0
:echo exists("bufcount")
0
:echo exists(":Make")
0
:echo exists(":make")
2
:echo exists("#CursorHold")
1
:echo exists("#BufReadPre#*.gz")
1
:echo exists("#filetypeindent")
1
:echo exists("#filetypeindent#FileType")
1
:echo exists("#filetypeindent#FileType#*")
1
:echo exists("##ColorScheme")
1
" file
:echo filereadable('~/.vimrc')
0
:echo filereadable(expand('~/.vimrc'))
1

" get first line
:getline(1)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
" get current line
:echo getline('.')

" get matches
:echo getmatches()
" more
:let m = getmatches()
:call clearmatches()
:echo getmatches()
" returns the character index of the column position
"               + cursor
:echo charcoal('.')
22
:echo col('.')
22
" line length
" cursor can be anywhere of following line
:echo col("$")
17
" get environment variable
:echo has_key( environ(), 'HOME' )
1
:echo index( keys(environ()), 'HOME', 0, 1 ) != -1
1

:echo escape( 'c:\program files\vim', ' \' )
c:\\program\ files\\vim
" execute
:echo execute( 'echon "foo"' )
foo

:echo execute([ 'echon "foo"', 'echon "bar"' ])
foobar

:echo execute('args')->split("\n")
['[nvim.md] ']
" paths
:echo expandcmd( 'make %<.o' )
make nvim.o
:echo expandcmd( 'make %<.o', {'errmsg': v:true} )
make nvim.o

:echo fnameescape( '+some str%nge|name' )
\+some\ str\%nge\|name
:let fname = '+some str%nge|name'
:exe "edit " .. fnameescape(fname)

:echo fnamemodify( "main.c", ":p:h" )
/home/marslo/ibook/docs/vim

others

  • buffer name

    :echo bufname("#")
    nvim.md
    
    :echo bufname("#")
    cmd
    
  • buffer number

    :echo "A window containing buffer 1 is " .. (bufwinnr(1))
    A window containing buffer 1 is 1
    
    :echo "A window containing buffer 1 is " .. (bufwinid(1))
    A window containing buffer 1 is 1000
    
  • others

    :echo byteidx('a😊😊', 2)
    5
    :echo byteidx('a😊😊', 2, 1)
    1
    :echo byteidx('a😊😊', 3, 1)
    5
    
    :let s = 'e' .. nr2char(0x301)
    :echo byteidx(s, 1)
    3
    :echo byteidxcomp(s, 1)
    1
    :echo byteidxcomp(s, 2)
    3
    

autocmd

[!TIP|label:references:]

  • Triggering a function with BufEnter
    autocmd BufEnter *.p[lm]  nmap <buffer> ;t :call RunPerlTests()<CR>
    "       |        |        |     |       |  |
    "       |        |        |     |       |  +-- `{rhs}` of the mapping
    "       |        |        |     |       +-- `{lhs}` of the mapping
    "       |        |        |     +-- argument to pass to `:nmap`; limits the scope of the mapping to the current buffer
    "       |        |        +-- mapping command to execute
    "       |        +-- pattern to limit the scope of the autocmd to certain filetypes
    "       +-- event of the autocmd
    
  • Vim’s event model
  • Listing 1. Event sequence in a simple Vim editing session
    • BufWinEnter : create a default window
    • BufEnter : create a default buffer
    • VimEnter : start the Vim session):edit demo.txt
    • BufNew : create a new buffer to contain demo.txt
    • BufAdd : add that new buffer to the session’s buffer list
    • BufLeave : exit the default buffer
    • BufWinLeave : exit the default window
    • BufUnload : remove the default buffer from the buffer list
    • BufDelete : deallocate the default buffer
    • BufReadCmd : read the contexts of demo.txt into the new buffer
    • BufEnter : activate the new buffer
    • BufWinEnter : activate the new buffer's window
    • InsertEnter : swap into Insert mode
  • references

autocmd BufWritePre except

  • functions

    fun! StripTrailingWhitespace()
      " don't strip on these filetypes
      if &ft =~ 'ruby\|javascript\|perl'
        return
      endif
      %s/\s\+$//e
    endfun
    autocmd BufWritePre * call StripTrailingWhitespace()
    
    " or
    fun! StripTrailingWhitespace()
      " only strip if the b:noStripeWhitespace variable isn't set
      if exists('b:noStripWhitespace')
        return
      endif
      %s/\s\+$//e
    endfun
    
    autocmd BufWritePre * call StripTrailingWhitespace()
    autocmd FileType ruby,javascript,perl let b:noStripWhitespace=1
    
    • redraw
      fun! ReplaceTabToSpace()
        # don't strip on these filetypes
        if &ft =~ 'ruby\|javascript\|perl\|ltsv'
          return
        endif
        %s/\s\+$//e
      endfun
      autocmd BufWritePre * call ReplaceTabToSpace()
      
  • blacklist

    let blacklist = ['rb', 'js', 'pl']
    autocmd BufWritePre  *  if index(blacklist, &ft) < 0 | do something you like | endif
    
  • @<!

    autocmd BufWritePre  *\(.out\|.diffs\)\@<!  <your_command>
    
    " i.e.:
    autocmd Syntax       *\(^rst\)\@<!          :redraw!
    
    • redraw
      autocmd BufWritePre *\(.ltsv\|.diffs\)\@<! :retab!    " automatic retab
      

stop gitblame in diff mode

[!NOTE|label:references:]

autocmd BufEnter * if &diff         | let g:blamer_enabled=0 | endif    " ╮ disable diff mode
autocmd BufEnter * if ! empty(&key) | let g:blamer_enabled=0 | endif    " ╯ and encrypt mode

disable line number in terminal

[!NOTE|label:references:]

augroup numbertoggle
        autocmd!
        autocmd BufEnter,FocusGained,InsertLeave * if &buftype != 'terminal' |  set relativenumber | endif
        autocmd BufLeave,FocusLost,InsertEnter * if &buftype != 'termina' | set norelativenumber | endif
        au BufEnter * if &buftype == 'terminal' | :set nonumber | endif
 augroup END
  • or
    let b:visualnum_enabled = 0
    

automatic cleanup tailing space when save

autocmd BufWritePre              *                      :%s/\s\+$//e | %s/\r$//e   " automatic remove trailing space

# others
autocmd FileType                 sh,bash,shell  silent  :redraw
autocmd BufWritePre,BufWritePost *.sh           silent  :redraw                    " for shellcheck lint

automatic save

[!NOTE|label:references:]

autocmd  FocusLost  *.txt   :    if &modified && g:autosave_on_focus_change
autocmd  FocusLost  *.txt   :    write
autocmd  FocusLost  *.txt   :    echo "Autosaved file while you were absent"
autocmd  FocusLost  *.txt   :    endif
  • clean way to handle multiple autocommands

    function! Highlight_cursor ()
        set cursorline
        redraw
        sleep 1
        set nocursorline
    endfunction
    function! Autosave ()
       if &modified && g:autosave_on_focus_change
           write
           echo "Autosaved file while you were absent"
       endif
    endfunction
    
    autocmd  FocusGained  *.txt   :call Highlight_cursor()
    autocmd  FocusLost    *.txt   :call Autosave()
    

BufWritePost

[!NOTE|label:references:]

# source $MYVIMRC or `~/.vimrc` if the libs changed
if index( ['vimrc.d'], split(expand("%:p:h"), "/")[-1] ) >= 0
  autocmd! BufWritePost ~/.marslo/vimrc.d/* silent! source $MYVIMRC
        \| echohl WarningMsg
        \| echom expand('%:p') . " changed! " . $MYVIMRC . " sourced!"
        \| echohl None
        \| silent !redraw
endif

# source $MYVIMRC or `~/.vimrc` if the lua.x.x changed. nvim only
if has('nvim') && '/'.join(split(expand('%:p'), '/')[0:3], '/') == stdpath('config')
  autocmd! BufWritePost ~/.config/nvim/*    silent! source $MYVIMRC
        \| echohl WarningMsg
        \| echom expand('%:p') . " changed! " . $MYVIMRC . " sourced!"
        \| echohl None
        \| execute 'silent !redraw'
endif

edit binary using xxd-format

[!NOTE|label:manual:]

" If one has a particular extension that one uses for binary files (such as exe,
" bin, etc), you may find it helpful to automate the process with the following
" bit of autocmds for your <.vimrc>.  Change that "*.bin" to whatever
" comma-separated list of extension(s) you find yourself wanting to edit:

" vim -b : edit binary using xxd-format!
augroup Binary
  au!
  au BufReadPre   *.bin let &bin=1
  au BufReadPost  *.bin if &bin    | %!xxd
  au BufReadPost  *.bin set ft=xxd | endif
  au BufWritePre  *.bin if &bin    | %!xxd -r
  au BufWritePre  *.bin endif
  au BufWritePost *.bin if &bin    | %!xxd
  au BufWritePost *.bin set nomod  | endif
augroup END

set option and value in autocmd

let s:ft_settings = {
      \ 'help': {'foldenable': 0},
      \ 'vim-plug': {'number': 0, 'relativenumber': 0},
      \ 'markdown': {'spell': 1},
      \ }

function! s:ApplyFTSettings() abort
  if has_key(s:ft_settings, &filetype)
    for [opt, val] in items(s:ft_settings[&filetype])
      if getbufvar('%', '&'.opt) != val
        execute 'setlocal' opt.'='.val
      endif
    endfor
  endif
endfunction

augroup LAST_OVERRIDE
  autocmd!
  autocmd BufWinEnter * call s:ApplyFTSettings()
augroup END
" to support 'option': v:true
let s:ft_settings = {
    \ 'help': {'foldenable': 0},
    \ 'vim-plug': {'foldenable': 0, 'relativenumber': 0, 'number': v:false },
    \ 'markdown': {'spell': 1},
    \ }

function! s:ApplyFTSettings() abort
  if has_key(s:ft_settings, &filetype)
    for [opt, val] in items(s:ft_settings[&filetype])
      let current_val = getbufvar('%', '&' . opt)
      if type(val) == v:t_bool
        " if `val = v:false`, the option will be `no{opt}`
        execute 'setlocal' (val ? '' : 'no') . opt
      else
        if current_val != val | execute 'setlocal ' . opt . '=' . val | endif
      endif
    endfor
  else
    setlocal foldmethod=indent
  endif
endfunction

augroup LAST_OVERRIDE
autocmd!
autocmd FileType    *        ++nested call s:ApplyFTSettings()
autocmd BufWinEnter *        call s:ApplyFTSettings()
autocmd BufWinEnter __Plug__ setlocal nofoldenable nonumber relativenumber=0
augroup END
  • simple version
    augroup LAST_OVERRIDE
      autocmd!
      autocmd BufWinEnter *
              \ if &filetype ==# 'help' && &foldenable | setlocal nofoldenable | endif
    augroup END
    

system

system file path

[!TIP]

  • slice string
    :echo strcharpart('abc', -1, 2)
    a
    
substitute(system('<cmd>'), '\n\+$', '', '')
" or
expand(trim( system('<cmd>') ))

" i.e.:
substitute(system('brew --prefix fzf'), '\n\+$', '', '')
" or
expand(trim( system('command -v python3') ))

filetype in vim language

if index(['vim', 'c', 'cpp'], &filetype) != -1
  echom "hello!"
endif

" or: https://stackoverflow.com/a/29407473/2940319
let fts = ['c', 'cpp']
if index(fts, &filetype) == -1
  " do stuff
endif

putting the current file on the windows clipboard

[!NOTE|label:references:]

command! Copyfile let @*=substitute(expand("%:p"), '/', '\', 'g')
:map <Leader>cf :Copyfile<CR>

" or
nn <silent><C-G> :let @*=expand('%:p')<CR>:f<CR>

map overview

[!NOTE|label:references:]

COMMANDS COMMANDS REMOVE MODES
:map :noremap :unmap Normal, Visual, Select, Operator-pending
:nmap :nnoremap :nunmap Normal
:vmap :vnoremap :vunmap Visualm Select
:smap :snoremap :sunmap Select
:xmap :xnoremap :xunmap Visual
:omap :onoremap :ounmap Operator-pending
:map! :noremap! :unmap! Insert, Command-line
:imap :inoremap :iunmap Insert
:lmap :lnoremap :lunmap Insert, Command-line, Lang-Arg
:cmap :cnoremap :cunmap Command-line
:tmap :tnoremap :tunmap Terminal-Job
MODE COMAMDN NORM INS CMD VIS SEL OPR TERM LANG
[nore]map yes - - yes yes yes - -
n[nore]map yes - - - - - - -
[nore]map! - yes yes - - - - -
i[nore]map - yes - - - - - -
c[nore]map - - yes - - - - -
v[nore]map - - - yes yes - - -
x[nore]map - - - yes - - - -
s[nore]map - - - - yes - - -
o[nore]map - - - - - yes - -
t[nore]map - - - - - - yes -
l[nore]map - yes yes - - - - yes
COMMANDS COMMANDS COMMANDS COMMANDS Normal Visual+Select Operator-pending
:map :noremap :unmap :mapclear yes yes yes
:nmap :nnoremap :nunmap :nmapclear yes - -
:vmap :vnoremap :vunmap :vmapclear - yes -
:omap :onoremap :ounmap :omapclear - - yes

check MACHTYPE

[!NOTE]

  • MACHTYPE A string that fully describes the system type on which Bash is executing, in the standard GNU cpu-company-system format
  • system()
  • :help system()
" git bash
:echo system('echo -n $MACHTYPE')
x86_64-pc-msys

" wsl
:echo system('echo -n $MACHTYPE')
x86_64-pc-linux-gnu

" centos
:echo system('echo -n $MACHTYPE')
x86_64-redhat-linux-gnu

" osx
:echo system('echo -n $MACHTYPE')
aarch64-apple-darwin25.6.0

" more for `system()`
:echo system('ls ' .. expand('%:h:S'))
color.md
install.md
plugins.md
tricky.md
troubleshooting.md
vim.md
viml.md
windows.md

Press ENTER or type command to continue

time

:echo strftime("%c")
Tue May  6 12:00:53 2025

:echo strftime("%Y %b %d %X")
2025 May 06 12:02:27

:echo strftime("%y%m%d %T")
250506 12:03:16

:echo strftime("%H:%M")
12:03

:echo strftime("%c", getftime("file.c"))
Wed Dec 31 15:59:59 1969

functions

yank highlight

vim.cmd( 'autocmd TextYankPost * silent! lua vim.highlight.on_yank {on_visual=true}' )

-- or --
vim.api.nvim_create_autocmd("TextYankPost", {
  pattern = "*",
  callback = function()
    vim.highlight.on_yank({ timeout = 150, on_visual = true })
  end,
})

-- with different highlight group (Search) --
vim.api.nvim_create_autocmd("TextYankPost", {
  pattern = "*",
  callback = function()
    vim.highlight.on_yank({ higroup = "Search", on_visual = true })
  end,
})

-- or with callback --
vim.api.nvim_create_autocmd("TextYankPost", {
  callback = function(args)
    local event = args.event
    local regtype = vim.v.event.regtype
    local lines = vim.v.event.regcontents
    local lnum = vim.fn.line(".")
    local col = vim.fn.col(".")

    local ns = vim.api.nvim_create_namespace("yank_hl")

    if regtype == "v" then
      local text = table.concat(lines, "\n")
      vim.highlight.range(0, ns, "IncSearch", {lnum - 1, col - 1}, {lnum - 1, col - 1 + #text}, {inclusive = true})
    elseif regtype == "V" then
      vim.highlight.range(0, ns, "IncSearch", {lnum - 1, 0}, {lnum - 1 + #lines, 0}, {inclusive = true})
    end

    vim.defer_fn(function()
      vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
    end, 200)
  end,
})

-- or create new namespace everytime --
vim.api.nvim_create_autocmd("TextYankPost", {
  callback = function()
    local regtype = vim.v.event.regtype
    local lines = vim.v.event.regcontents
    local lnum = vim.fn.line(".")
    local col = vim.fn.col(".")

    if not lines or vim.tbl_isempty(lines) then return end

    local ns = vim.api.nvim_create_namespace("")  -- 不传 name = 每次新 ID
    local bufnr = 0
    local hl_group = "IncSearch"

    if regtype == "v" then
      local text = table.concat(lines, "\n")
      vim.api.nvim_buf_add_highlight(bufnr, ns, hl_group, lnum - 1, col - 1, col - 1 + #text)
    elseif regtype == "V" then
      for i = 0, #lines - 1 do
        vim.api.nvim_buf_add_highlight(bufnr, ns, hl_group, lnum - 1 + i, 0, -1)
      end
    end

    vim.defer_fn(function()
      vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
    end, 200)
  end,
})

paste highlight

" highlight whole line
function! PasteHighlight() abort
  let l:start = getpos("'[")
  let l:end   = getpos("']")
  if l:start[1] == 0 || l:end[1] == 0 | return | endif
  let l:pattern = '\%'.l:start[1].'l\_.*\%'.l:end[1].'l'
  let l:match_id = matchadd('Search', l:pattern)
  call timer_start(150, {-> matchdelete(l:match_id)})
  call setpos('.', l:start)
endfunction
nnoremap <silent> p p:call PasteHighlight()<CR>
nnoremap <silent> P P:call PasteHighlight()<CR>

" full version
function! PasteHighlight() abort
  let l:start = getpos("'[")
  let l:end   = getpos("']")

  if l:start[1] == 0 || l:end[1] == 0 | return | endif

  let l:start_lnum = l:start[1]
  let l:start_col  = l:start[2]
  let l:end_lnum   = l:end[1]
  let l:end_col    = l:end[2]

  if l:start_lnum == l:end_lnum
    let l:len = l:end_col - l:start_col + 1
    let l:match_id = matchaddpos('IncSearch', [[l:start_lnum, l:start_col, l:len]])
  else
    let l:pattern = '\%'.l:start_lnum.'l\_.*\%'.l:end_lnum.'l'
    let l:match_id = matchadd('IncSearch', l:pattern)
  endif

  call timer_start(150, {-> matchdelete(l:match_id)})
  call setpos('.', l:start)
endfunction

TwiddleCase

[!TIP|label:references:]

  • change.txt
    • :help s/\u : next character made uppercase
    • examples:
      • :s/a\|b/xxx\0xxx/g : modifies "a b" to "xxxaxxx xxxbxxx"
      • `:s/([abc])([efg])/\2\1/g modifies "af fa bg" to "fa fa gb"
      • :s/abcde/abc^Mde/ : modifies "abcde"to "abc", "de" (two lines)
      • :s/$/\^M/ : modifies "abcde" to "abcde^M"
      • :s/\w\+/\u\0/g : modifies "bla bla" to "Bla Bla"
      • :s/\w\+/\L\u\0/g : modifies "BLA bla" to "Bla Bla"
  • cmd:

    • :s/\<\(\w\)\(\w*\)\>/\u\1\L\2/g
    • :s/\<\(\w\)\(\S*\)/\u\1\L\2/g
    • :s#\v(\w)(\S*)#\u\1\L\2#g
  • * iMarslo : switching case of characters via shortcuts

function! TwiddleCase(str)
  if a:str ==# toupper(a:str)
    let result = tolower(a:str)
Learn Vimscript the Hard Way  elseif a:str ==# tolower(a:str)
    let result = substitute(a:str,'\(\<\w\+\>\)', '\u\1', 'g')
  else
    let result = toupper(a:str)
  endif
  return result
endfunction
vnoremap ~ y:call setreg('', TwiddleCase(@"), getregtype(''))<CR>gv""Pgv
twiddle case
1.3.6.1 -- twiddle case

open html in terminal

[!NOTE|label:references:]

" brew install felinks
" which elinks: /usr/local/bin/elinks
function! ViewHtmlText(url)
  if !empty(a:url)
    new
    setlocal buftype=nofile bufhidden=hide noswapfile
    execute 'r !elinks ' . a:url . ' -dump -dump-width ' . winwidth(0)
    1d
  endif
endfunction
" save and view text for current html file.
nnoremap <Leader>H :update<Bar>call ViewHtmlText(expand('%:p'))<CR>
" view text for visually selected url.
vnoremap <Leader>h y:call ViewHtmlText(@@)<CR>
" View text for URL from clipboard.
" on linux, use @* for current selection or @+ for text in clipboard.
nnoremap <Leader>h :call ViewHtmlText(@+)<CR>

OpenInFreshWindowOrNewTab

function! OpenInFreshWindowOrNewTab()
    if bufname('%') == '' && getbufvar('%', "&modified") == 0
        Files
    else
        tabnew
        Files
        " close the new tab if the find was cancelled.
        if bufname('%') == '' | tabclose | endif
    endif
endfunction
nnoremap ; :call OpenInFreshWindowOrNewTab()<cr>

GetFiletypes

function! GetFiletypes()
  " https://vi.stackexchange.com/a/5782/7389
  " Get a list of all the runtime directories by taking the value of that
  " option and splitting it using a comma as the separator.
  let rtps      = split( &runtimepath, "," )
  " This will be the list of filetypes that the function returns
  let filetypes = []

  " Loop through each individual item in the list of runtime paths
  for rtp in rtps
    let syntax_dir = rtp . "/syntax"
    " Check to see if there is a syntax directory in this runtimepath.
    if ( isdirectory(syntax_dir) )
      " Loop through each vimscript file in the syntax directory
      for syntax_file in split( glob(syntax_dir . "/*.vim"), "\n" )
        " Add this file to the filetypes list with its everything
        " except its name removed.
        call add( filetypes, fnamemodify(syntax_file, ":t:r") )
      endfor
    endif
  endfor

  " This removes any duplicates and returns the resulting list.
  " NOTE: This might not be the best way to do this, suggestions are welcome.
  return uniq( sort(filetypes) )
endfunction

IgnoreSpells

" spell
" set spellcamelcase=1
" ignore CamelCase words when spell checking
function! IgnoreSpells()
  syntax match Url "\w\+:\/\/[:/?#[\]@!$&'()*+,;=0-9[:lower:][:upper:]_\-.~]\+" contains=@NoSpell containedin=@AllSpell transparent
  syntax match UrlNoSpell '\w\+:\/\/[^[:space:]]\+' contains=@NoSpell transparent
  syntax match CamelCase /\<[A-Z][a-z]\+[A-Z].\{-}\>/ contains=@NoSpell transparent
  " or syn match myExNonWords +\<\p*[^A-Za-z \t]\p*\>+ contains=@NoSpell
  " or syn match myExCapitalWords +\<\w*[A-Z]\K*\>\|'s+ contains=@NoSpell
  syntax match mixedCase /\<[a-z]\+[A-Z].\{-}\>/ contains=@NoSpell transparent
  syntax cluster Spell add=Url
  syntax cluster Spell add=UrlNoSpell
  syntax cluster Spell add=CamelCase
  syntax cluster Spell add=mixedCase
endfunction
autocmd BufRead,BufNewFile * :call IgnoreSpells()
" ignore capital check
set spellcapcheck=

IgnoreCamelCaseSpell

" spell
" set spellcamelcase=1
" Ignore CamelCase words when spell checking
fun! IgnoreCamelCaseSpell()
  syn match CamelCase /\<[A-Z][a-z]\+[A-Z].\{-}\>/ contains=@NoSpell transparent
  syn match mixedCase /\<[a-z]\+[A-Z].\{-}\>/ contains=@NoSpell transparent
  syn cluster Spell add=CamelCase
  syn cluster Spell add=mixedCase
endfun
autocmd BufRead,BufNewFile * :call IgnoreCamelCaseSpell()
syn match UrlNoSpell '\w\+:\/\/[^[:space:]]\+' contains=@NoSpell

TabMessage

" redir into new tab: https://vim.fandom.com/wiki/Capture_ex_command_output; https://vim.fandom.com/wiki/Capture_ex_command_output
" `gt`, `:tabfirst`, `:tabnext`, `:tablast` ... to switch tabs : https://vim.fandom.com/wiki/Alternative_tab_navigation
function! TabMessage(cmd)
  redir => message
  silent execute a:cmd
  redir END
  if empty(message)
    echoerr "no output"
  else
    " use "new" instead of "tabnew" below if you prefer split windows instead of tabs
    tabnew
    setlocal buftype=nofile bufhidden=wipe noswapfile nobuflisted nomodified
    silent put=message
  endif
endfunction
command! -nargs=+ -complete=command TabMessage call TabMessage(<q-args>)

BSkipQuickFix

" switch avoid quickfix : https://vi.stackexchange.com/a/19420/7389
function! BSkipQuickFix(command)
  let start_buffer = bufnr('%')
  execute a:command
  while &buftype ==# 'quickfix' && bufnr('%') != start_buffer
    execute a:command
  endwhile
endfunction

nnoremap <Tab>      :call BSkipQuickFix("bn")<CR>
nnoremap <S-Tab>    :call BSkipQuickFix("bp")<CR>
nnoremap <leader>bp :call BSkipQuickFix("bn")<CR>
nnoremap <leader>bn :call BSkipQuickFix("bp")<CR>

TriggerYCM

function! TriggerYCM()
  if g:loaded_youcompleteme == 1
    let g:loaded_youcompleteme = 0
  else
    let g:loaded_youcompleteme = 1
  endif
endfunction
nnoremap <C-y> :call TriggerYCM()<CR>

DeleteCurBufferNotCloseWindow

[!NOTE]

function! BufferDelete()
    if &modified
        echohl ErrorMsg
        echomsg "No write since last change. Not closing buffer."
        echohl NONE
    else
        let s:total_nr_buffers = len(filter(range(1, bufnr('$')), 'buflisted(v:val)'))

        if s:total_nr_buffers == 1
            bdelete
            echo "Buffer deleted. Created new buffer."
        else
            bprevious
            bdelete #
            echo "Buffer deleted."
        endif
    endif
endfunction

" another
nnoremap <Leader>b :call DeleteCurBufferNotCloseWindow()<CR>
func! DeleteCurBufferNotCloseWindow() abort
    if &modified
        echohl ErrorMsg
        echom "E89: no write since last change"
        echohl None
    elseif winnr('$') == 1
        bd
    else  " multiple window
        let oldbuf = bufnr('%')
        let oldwin = winnr()
        while 1   " all windows that display oldbuf will remain open
            if buflisted(bufnr('#'))
                b#
            else
                bn
                let curbuf = bufnr('%')
                if curbuf == oldbuf
                    new    " oldbuf is the only buffer, create one
                endif
            endif
            let win = bufwinnr(oldbuf)
            if win == -1
                break
            else        " there are other window that display oldbuf
                exec win 'wincmd w'
            endif
        endwhile
        " delete oldbuf and restore window to oldwin
        exec oldbuf 'bd'
        exec oldwin 'wincmd w'
    endif
endfunc
  • AdjustWindowHeight
    function! AdjustWindowHeight(minheight, maxheight)
      exe max([min([line("$"), a:maxheight]), a:minheight]) . "wincmd _"
    endfunction
    autocmd FileType qf call AdjustWindowHeight( 3, 10 )
    

WordCount

[!NOTE]

function! WordCount()
   let s:old_status = v:statusmsg
   let position = getpos(".")
   exe ":silent normal g\<c-g>"
   let stat = v:statusmsg
   let s:word_count = 0
   if stat != '--No lines in buffer--'
     let s:word_count = str2nr(split(v:statusmsg)[11])
     let v:statusmsg = s:old_status
   end
   call setpos('.', position)
   return s:word_count
endfunction
set statusline=wc:%{WordCount()}

" or
let g:word_count="<unknown>"
fun! WordCount()
    return g:word_count
endfun
fun! UpdateWordCount()
    let s = system("wc -w ".expand("%p"))
    let parts = split(s, ' ')
    if len(parts) > 1
        let g:word_count = parts[0]
    endif
endfun

augroup WordCounter
    au! CursorHold  * call UpdateWordCount()
    au! CursorHoldI * call UpdateWordCount()
augroup END

" how eager are you? (default is 4000 ms)
set updatetime=500

" modify as you please...
set statusline=%{WordCount()}\ words

[!NOTE|label:references:]

function! VSetSearch()
  let l:cursor = getpos('.')       " save cursor position
  let l:temp = @s                  " save to @s (s register)
  " gv: virtual mode selection; "s: register name is `s`; y: yank (yank the visual selection to @s)
  normal! gv"sy
  let @/ = '\V' . substitute( escape(@s, '\/|'), '\n', '\\n', 'g' )
  let @s = l:temp                  " recover @s (s register)
  call setpos('.', l:cursor)       " recover cursor position
endfunction
xnoremap * :<C-u>call VSetSearch()<CR>/<C-R>=@/<CR><CR>:normal! N<CR>
xnoremap # :<C-u>call VSetSearch()<CR>?<C-R>=@/<CR><CR>:normal! n<CR>
"                                     +----+--+---+
"                                       |   |   + `<CR>` ⩵  Enter
"                                       |   + `@/` ⩵  @/
"                                       + `?<C-R>=` ⩵  ? → Ctrl+R → =

" g* and g# for regex search
function! VSetRegexSearch()
  let temp = @s
  normal! gv"sy
  let @/ = '\v' . substitute( escape(@s, '/'), '\n', '\\n', 'g' )
  let @s = temp
endfunction
xnoremap g* :<C-u>call VSetRegexSearch()<CR>/<C-R>=@/<CR><CR>:normal! N<CR>
xnoremap g# :<C-u>call VSetRegexSearch()<CR>?<C-R>=@/<CR><CR>:normal! n<CR>

commands

[!NOTE|label:references:]

execute doctoc in vim via command :Toc

command! -nargs=0 Toc execute 'silent ! /usr/local/bin/doctoc --github --maxlevel 3 %' | execute 'redraw!'

" for automatic cmd
autocmd BufWritePost *\(.md\)  silent :Toc                 " automatic build doctoc when save it

get path

[!NOTE|label:references:] references:

MODIFIERS COMMENTS
:p expand to full path
:h head (last path component removed)
:t tail (last path component only)
:r root (one extension removed)
:e extension only
MODIFIERS RESULTS
:p /home/mool/vim/src/version.c
:p:. src/version.c
:p:~ ~/vim/src/version.c
:h src
:p:h /home/mool/vim/src
:p:h:h /home/mool/vim
:t version.c
:p:t version.c
:r src/version
:p:r /home/mool/vim/src/version
:t:r version
:e c
:s?version?main? src/main.c
:s?version?main?:p /home/mool/vim/src/main.c
:p:gs?/?\\? \home\mool\vim\src\version.c
MODIFIES EXPLANATION
% current file name
%< current file name without extension
# alternate file name for current window
#< idem, without extension
#31 alternate file number 31
#31< idem, without extension
<cword> word under the cursor
<cWORD> WORD under the cursor (see WORD )
<cfile> path name under the cursor
<cfile>< idem, without extension
EXPRESSION RESULT COMMENTS
expand('%:p') /Users/marslo/path/filename.txt absolute path, full path
expand('%:p:~') ~/path/filename.txt absolute path with $HOME shortened to ~ == ${ABSOLUTE_FILE_PATH/#$HOME\//\~\/}
expand('%:~') ~/path/filename.txt relative path with $HOME shortened to ~
expand('%:.') path/filename.txt relative path with $cwd
expand('%:t') filename.txt filename
expand('%:t:r') filename filename without extension
expand('%:e') txt extension
expand('%:r') path/filename (:set noautochdir)
filename (:set autochdir)
% without extension
expand('%:p:r') /Users/marslo/path/filename absolute path without extension
expand('%:p:h') /Users/marslo/path dirname ( == dirname )
expand('%:p:~:h') ~/path dirname with $HOME shortened to ~ ( == dirname | sed "s:^$HOME/:~/:" )
expand('%:h') path directory name ( == dirname | sed "s:^$cwd/::" )
expand('%:p:h:t') path directory name ( == dirname | sed "s:^$cwd/::" )
expand('%:p:h:h') /Users/marslo parent directory (absolute path) of dirname ( == dirname $(dirname) )

[!TIP] % == "${ABSOLUTE_FILE_PATH/#$cwd\//}" == ABSOLUTE_FILE_PATH - $cwd/ (:pwd print current $cwd):

  • if $ABSOLUTE_FILE_PATH starts with $cwd, then remove $cwd/ from $ABSOLUTE_FILE_PATH
  • if $ABSOLUTE_FILE_PATH does not start with $cwd, then return $ABSOLUTE_FILE_PATH way to change $cwd in vim/nvim - Set working directory to the current file
  • :set autochdir ( VS. :set noautochdir ) changes $cwd to the directory of the current file == :cd %:p:h == :cd $(dirname $ABSOLUTE_FILE_PATH)
  • :cd /path/to/dir change $cwd to /path/to/dir
  • :lcd /path/to/dir change $cwd to /path/to/dir for current window only
  • :tcd /path/to/dir change $cwd to /path/to/dir for current tab only

others

- KEYS COMMANDS
SHORTCUTS ctrl + g "viml.md" 1454 lines --47%--
COMMAND :f "viml.md" 1454 lines --47%--
:echo expand("%:p")
/Users/marslo/ibook/docs/vim/viml.md
:echo expand("%:p:~")
~/ibook/docs/vim/viml.md
:echo getcwd()
/Users/marslo/ibook/docs/vim

:echo fnamemodify( '.', ':p:h:t' )               " https://stackoverflow.com/a/13940563/2940319
vim
:echo fnamemodify( getcwd(), ':t' )              " https://vi.stackexchange.com/a/15047/7389
vim
:echo substitute( getcwd(), '^.*/', '', '' )
vim

:echo expand("%:t")
viml.md
:echo expand("%:r")
viml

settings

[!NOTE|label:sample dot-vimrc]

  • Capitalize

    " https://vim.fandom.com/wiki/Capitalize_words_and_regions_easily
    if ( &tildeop )
      nnoremap gcw  guw~l
      nnoremap gcW  guW~l
      nnoremap gciw guiw~l
      nnoremap gciW guiW~l
      nnoremap gcis guis~l
      nnoremap gc$  gu$~l
      nnoremap gcgc guu~l
      nnoremap gcc  guu~l
      vnoremap gc   gu~l
    else
      nnoremap gcw  guw~h
      nnoremap gcW  guW~h
      nnoremap gciw guiw~h
      nnoremap gciW guiW~h
      nnoremap gcis guis~h
      nnoremap gc$  gu$~h
      nnoremap gcgc guu~h
      nnoremap gcc  guu~h
      vnoremap gc   gu~h
    endif
    nnoremap gcc :s/\v<(.)(\w*)/\u\1\L\2/g<CR>
    nnoremap gcgc gcc
    
  • comments

    " Commenting blocks of code.
    augroup commenting_blocks_of_code
      autocmd!
      autocmd FileType c,cpp,java,scala let b:comment_leader = '// '
      autocmd FileType sh,ruby,python   let b:comment_leader = '# '
      autocmd FileType conf,fstab       let b:comment_leader = '# '
      autocmd FileType tex              let b:comment_leader = '% '
      autocmd FileType mail             let b:comment_leader = '> '
      autocmd FileType vim              let b:comment_leader = '" '
    augroup END
    noremap <silent> ,cc :<C-B>silent <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:nohlsearch<CR>
    noremap <silent> ,cu :<C-B>silent <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:nohlsearch<CR>
    

theme

""" solarized
colorscheme solarized
set termguicolors
let g:solarized_termcolors      = 256
let &t_8f                       = "\<esc>[38;2;%lu;%lu;%lum"
let &t_8b                       = "\<esc>[48;2;%lu;%lu;%lum"
let g:solarized_termtrans       = 1
let g:solarized_extra_hi_groups = 1
let g:solarized_visibility      = "high"
let g:solarized_contrast        = "high"
let s:base03                    = "255"

" dynamic theme settings
set background=dark
if exists('$SSH_CONNECTION') || exists('$SSH_TTY') || exists('$SSH_CLIENT')
  let g:everforest_background = 'hard'
  colorscheme everforest                                          " sainnhe/everforest
else
  let g:gruvbox_material_background = 'hard'
  let g:gruvbox_material_better_performance = 1
  colorscheme gruvbox-material                                    " sainnhe/gruvbox-material
endif

" -- highlight for different colorschemes"
if get(g:, 'colors_name', '') =~# 'gruvbox'
  highlight! CursorColumn   ctermbg=236  guibg=#32302F
  highlight! CursorLine     ctermbg=236  guibg=#32302F
  highlight! Folded         cterm=italic ctermfg=241  ctermbg=236 gui=italic guifg=#616161 guibg=#2E2D2C
  highlight! ColorColumn    term=NONE    ctermfg=NONE ctermbg=235 gui=NONE   guifg=NONE    guibg=#2A2A2A
elseif get(g:, 'colors_name', '') =~# 'everforest'
  highlight! CursorColumn   ctermbg=237  guibg=#2F393D
  highlight! CursorLine     ctermbg=237  guibg=#2F393D
  highlight! Folded         cterm=italic ctermfg=241  ctermbg=237 gui=italic guifg=#616161 guibg=#303A3E
  highlight! ColorColumn    term=NONE    ctermfg=NONE ctermbg=237 gui=NONE   guifg=NONE    guibg=#353F43
endif

tricky

Copyright © marslo 2020-2026 all right reserved,powered by GitbookLast Modified: 2026-09-25 12:44:43

results matching ""

    No results matching ""