Skip to content

Instantly share code, notes, and snippets.

@h1mesuke
Created October 14, 2010 08:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save h1mesuke/625865 to your computer and use it in GitHub Desktop.
Save h1mesuke/625865 to your computer and use it in GitHub Desktop.
Vim - My autload/util.vim
" NOTE: THIS FILE CONTAINS MANY EXPERIMENTAL CODES!
"-----------------------------------------------------------------------------
" Abbrev
function! util#abbrev(words)
let table = {}
let seen = {}
for word in a:words
let abbrev = substitute(word, '.$', '', '')
let ablen = strlen(abbrev)
while ablen > 0
if !has_key(seen, abbrev)
let seen[abbrev] = 0
endif
let seen[abbrev] += 1
if seen[abbrev] == 1
let table[abbrev] = word
elseif seen[abbrev] == 2
unlet table[abbrev]
else
break
endif
let abbrev = substitute(abbrev, '.$', '', '')
let ablen = strlen(abbrev)
endwhile
endfor
for word in a:words
let table[word] = word
endfor
return table
endfunction
"-----------------------------------------------------------------------------
" Buffers
function! util#buffers(...)
let filter = (a:0 ? a:1 : 'listed')
let buffers = []
let bufnr = 1
while bufnr <= bufnr('$')
if filter =~# '^e\(xists\=\)\=$'
if bufexists(bufnr)
call add(buffers, bufnr)
endif
elseif filter =~# '^l\(isted\)\=$'
if buflisted(bufnr)
call add(buffers, bufnr)
endif
elseif filter =~# '^\(L\|loaded\)$'
if bufloaded(bufnr)
call add(buffers, bufnr)
endif
else " if filter =~# '^a\(ll\)\=$'
call add(buffers, bufnr)
endif
let bufnr += 1
endwhile
return buffers
endfunction
function! util#buffer_count(...)
let filter = (a:0 ? a:1 : 'listed')
return len(util#buffers(filter))
endfunction
function! util#buffer_names(...)
let filter = (a:0 ? a:1 : 'listed')
return map(util#buffers(filter), "bufname(v:val)")
endfunction
"-----------------------------------------------------------------------------
" Environment
" Saves the current environment and returns a Dictionary which is the snapshot
" of it. The environment means a set of positions of marks, states of options,
" values and regtypes of registers whose names are given as arguments.
"
" == Example
"
" let saved_env = util#save_env("'m", '&l:et', '@r', visualmode())
"
" " do something
"
" call util#restore_env(saved_env)
"
function! util#save_env(...)
let save_pos = 0
let save_marks = []
let save_opts = []
let save_regs = []
let save_vsel = 0
" parse args
for arg in a:000
if arg == '.'
let save_pos = 1
elseif match(arg, "^'[a-zA-Z[\\]']$") >= 0
call add(save_marks, substitute(arg, '^.', '', ''))
elseif match(arg, '^&\(l:\)\=\w\+') >= 0
call add(save_opts, substitute(arg, '^.', '', ''))
elseif match(arg, '^@[\"a-z01-9\-*+~/]$') >= 0
call add(save_regs, substitute(arg, '^.', '', ''))
elseif arg ==# 'R'
let save_regs += ['1', '2', '3', '4', '5', '6', '7', '8', '9', '-']
elseif arg ==? 'v' || arg == "\<C-v>"
let save_vsel = 1
let visualmode = arg
else
throw "ArgumentError: invalid name " . string(arg)
endif
endfor
let saved_env = {}
if save_pos
let saved_env.cursor = getpos('.')
endif
" save marks
if !empty(save_marks)
let saved_marks = {}
for mark_name in save_marks
let saved_marks[mark_name] = getpos("'".mark_name)
endfor
let saved_env.marks = saved_marks
endif
" save options
if !empty(save_opts)
let saved_opts = {}
for opt_name in save_opts
execute 'let saved_opts[opt_name] = &'.opt_name
endfor
let saved_env.options = saved_opts
endif
" save registers
if !empty(save_regs)
let saved_regs = {}
for reg_name in save_regs
let reg_data = {
\ 'value': getreg(reg_name),
\ 'type' : getregtype(reg_name),
\ }
let saved_regs[reg_name] = reg_data
endfor
let saved_env.registers = saved_regs
endif
" save the visual selection
if save_vsel
let saved_vsel = {
\ 'mode' : visualmode,
\ 'start': getpos("'<"),
\ 'end' : getpos("'>"),
\ }
let saved_env.selection = saved_vsel
endif
return saved_env
endfunction
" Restore the environment from {saved_env}, which is a snapshot Dictionary
" created by a previous call of util#save_env().
"
function! util#restore_env(saved_env)
" restore marks
if has_key(a:saved_env, 'marks')
for [mark_name, mark_pos] in items(a:saved_env.marks)
call setpos("'".mark_name, mark_pos)
endfor
endif
" restore options
if has_key(a:saved_env, 'options')
for [opt_name, opt_val] in items(a:saved_env.options)
execute 'let &'.opt_name.' = opt_val'
endfor
endif
" restore registers
if has_key(a:saved_env, 'registers')
for [reg_name, reg_data] in items(a:saved_env.registers)
call setreg(reg_name, reg_data.value, reg_data.type)
endfor
" NOTE: As a side effect, setreg() to numbered or named registers always
" updates @" too. So, we need to setreg() to @" again at the last.
if has_key(a:saved_env.registers, '"')
let reg_data = a:saved_env.registers['"']
call setreg('"', reg_data.value, reg_data.type)
endif
endif
" restore the visual selection
if has_key(a:saved_env, 'selection')
let vsel = a:saved_env.selection
let saved_pos = getpos('.')
call setpos('.', vsel.start)
execute 'normal!' vsel.mode
call setpos('.', vsel.end)
execute 'normal!' vsel.mode
call setpos('.', saved_pos)
endif
" restore the cursor
if has_key(a:saved_env, 'cursor')
call setpos('.', a:saved_env.cursor)
endif
endfunction
"-----------------------------------------------------------------------------
" Helpers
function! util#map(modes, lhs, rhs, ...)
let attr = (a:0 ? a:1 : '')
for mode in split(a:modes, '\zs')
execute mode.'map' attr a:lhs a:rhs
endfor
endfunction
function! util#noremap(modes, lhs, rhs, ...)
let attr = (a:0 ? a:1 : '')
for mode in split(a:modes, '\zs')
execute mode.'noremap' attr a:lhs a:rhs
endfor
endfunction
function! util#register_names()
let reg_names = []
let i = char2nr('a')
let z = char2nr('z')
while i <= z
call add(reg_names, nr2char(i))
let i += 1
endwhile
return reg_names
endfunction
function! util#toggle_option(opt)
execute 'setlocal' a:opt.'!'
execute 'setlocal' a:opt.'?'
endfunction
function! util#toggle_flag(var)
if eval(a:var)
execute 'let' a:var.' = 0'
else
execute 'let' a:var.' = 1'
endif
echo printf('%s=%s', a:var, eval(a:var))
endfunction
function! util#print_error(msg)
echohl ErrorMsg | echomsg a:msg | echohl None
endfunction
function! util#print_warning(msg)
echohl WarningMsg | echomsg a:msg | echohl None
endfunction
"-----------------------------------------------------------------------------
" Parser
" Parses {flags}, which is a String consists of flag characters, and returns
" a Dictionary that describes the options specified by {flags}.
"
" == Example
"
" function! s:search_command(flags)
" let opts = util#parse_flags(a:flags, {
" \ '?': 'search_backward',
" \ 'u': 'search_input',
" \ 'w': 'search_cword',
" \ 'W': 'search_cWORD',
" \ 's': 'search_selection',
" \ '<': 'match_word',
" \ 'c': 'force_ignorecase',
" \ 'C': 'force_noignorecase',
" \ 'b': 'against_buffer',
" \ 'V': 'against_selected',
" \ 'p': 'use_pcre',
" \ },
" \ ['uwWs', 'u'], ['bV', 'b'])
"
" if opts.search_backward
" " eregex doesn't have :M? command
" let opts.use_pcre = 0
" endif
"
function! util#parse_flags(flags, flag_maps, ...)
let ex_group_chars = {}
let ex_group_defaults = []
for ex_group in a:000
let chars = split(ex_group[0], '\zs')
let default = ex_group[1]
for ch in chars
let ex_group_chars[ch] = chars
endfor
call add(ex_group_defaults, default)
endfor
let opts = {}
for ch in keys(a:flag_maps)
if match(ex_group_defaults, ch) >= 0
let opts[a:flag_maps[ch]] = 1
else
let opts[a:flag_maps[ch]] = 0
endif
endfor
for ch in keys(a:flag_maps)
if match(a:flags, '\C'.ch) >= 0
if has_key(ex_group_chars, ch)
for ex_ch in ex_group_chars[ch]
let opts[a:flag_maps[ex_ch]] = 0
endfor
endif
let opts[a:flag_maps[ch]] = 1
endif
endfor
return opts
endfunction
"-----------------------------------------------------------------------------
" Paths
if has("ruby")
function! util#path_expand(path)
ruby << EOF
path = File.expand_path(VIM.evaluate('a:path'))
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
return path
endfunction
function! util#path_split(path)
ruby << EOF
dir, base = File.split(VIM.evaluate('a:path'))
dir .gsub!(/(["\\])/) { '\\' + $1 }
base.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let dir = "' + dir + '"')
VIM.command('let base = "' + base + '"')
EOF
return [dir, base]
endfunction
function! util#path_join(...)
let args = join(a:000, "\n")
ruby << EOF
path = File.join(*VIM.evaluate('args').split("\n"))
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
return path
endfunction
function! util#path_dir(path)
ruby << EOF
path = File.dirname(VIM.evaluate('a:path'))
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
return path
endfunction
function! util#path_base(path, ...)
if a:0
ruby << EOF
path = File.basename(VIM.evaluate('a:path'), ".*")
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
else
ruby << EOF
path = File.basename(VIM.evaluate('a:path'))
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
end
return path
endfunction
function! util#path_ext(path)
ruby << EOF
path = File.extname(VIM.evaluate('a:path'))
path.gsub!(/(["\\])/) { '\\' + $1 }
VIM.command('let path = "' + path + '"')
EOF
return path
endfunction
endif
"-----------------------------------------------------------------------------
" Ruby
if has("ruby")
function! util#ruby_version()
ruby << EOF
VIM.command("let ver = '#{RUBY_VERSION}'")
EOF
return ver
endfunction
function! util#ruby_platform()
ruby << EOF
VIM.command("let str = '#{RUBY_PLATFORM}'")
EOF
return str
endfunction
endif
"-----------------------------------------------------------------------------
" Texts
function! util#string_is_ascii(str)
return (match(a:str, '^[\x00-\x7f]*$') >= 0)
endfunction
function! util#string_width(str)
if util#string_is_ascii(a:str)
let len = strlen(a:str)
else
" borrowed from Charles E. Campbell, Jr's Align.vim
let saved_mod = &l:modified
execute "normal! o\<Esc>"
call setline(line('.'), a:str)
let len = virtcol('$') - 1
silent delete
let &l:modified= saved_mod
endif
return len
endfunction
function! util#escape_pregex(str, ...)
let chars = '^$()|[]{}.*+?\'
if a:0
let chars .= a:1
end
return escape(a:str, chars)
endfunction
function! util#escape_vregex(str, ...)
let chars = '^$[].*\~'
if a:0
let chars .= a:1
end
return escape(a:str, chars)
endfunction
function! util#get_selection()
let sel = util#get_region(visualmode(1))
return join(sel.lines, "\n")
endfunction
function! util#filter_selection(cmd)
let sel = util#get_region(visualmode(1))
let sel.lines = split(system(a:cmd, join(sel.lines, "\n")), "\n")
call util#set_region(sel)
endfunction
function! util#get_region(...)
let mode = 'n'
if a:0 == 1
if a:1 ==? 'v' || a:1 ==# "\<C-v>"
" from the visual mode; util#get_region(visualmode())
let mode = 'v'
let type = { 'v': 'char', 'V': 'line', "\<C-v>": 'block' }[a:1]
let range = [line("'<"), line("'>")]
let vsel_range = [getpos("'<"), getpos("'>")]
elseif a:1 ==# 'char' || a:1 ==# 'line' || a:1 ==# 'block'
" from the operator-pending mode
let mode = 'o'
let type = a:1
let range = [line("'["), line("']")]
let vsel_range = [getpos("'["), getpos("']")]
endif
elseif a:0 == 2
let type = 'line'
let range = [a:1, a:2]
else
throw "ArgumentError: wrong number of arguments (" . a:0 . " for 1..2)"
endif
let regtype = { 'char': 'v', 'line': 'V', 'block': "\<C-v>" }[type]
call s:sort_numbers(range)
if type ==# 'line'
let lines = getline(range[0], range[1])
else
" char or block
" get the visual selection via register 'v'
let saved_env = util#save_env('.', '&selection', '@v', regtype)
if mode ==# 'o'
set selection=inclusive
endif
call setpos('.', range[0])
execute 'normal!' regtype
call setpos('.', range[1])
execute 'silent normal! "vy'
let lines = split(@v, "\n")
" NOTE: If a line ends before the left most column of a block, the block
" will be filled with spaces for the line. It's "ragged right".
"
if type ==# 'block'
" collect and copy lines that cause ragged rights to avoid the extra
" spaces issue on util#set_region()
let block_cols = [virtcol("'<"), virtcol("'>")]
call s:sort_numbers(block_cols)
let block_left = block_cols[0]
let block_width = block_cols[1] - block_cols[0]
let regtype .= block_width
let ragged = {}
let saved_pos = getpos('.')
let nr = range[0]
while nr <= range[1]
execute ':'.nr
if virtcol('$') <= block_left
let ragged[nr] = getline(nr)
let lines[nr - range[0]] = ""
endif
let nr += 1
endwhile
endif
call util#restore_env(saved_env)
" for restoring the visual selection after
let range = vsel_range
endif
let region_data = {
\ 'type' : type,
\ 'regtype': regtype,
\ 'range' : range,
\ 'lines' : lines,
\ }
if type ==# 'block' && !empty(ragged)
let region_data.ragged = ragged
endif
return region_data
endfunction
function! util#set_region(region_data)
let data = a:region_data
if data.type ==# 'line'
call setline(data.range[0], data.lines)
else
let saved_env = util#save_env('.', '@v')
call setreg('v', join(data.lines, "\n"), data.regtype)
call setpos('.', range[0])
execute 'normal!' regtype
call setpos('.', range[1])
silent normal! "_d"vP`<
call util#restore_env(saved_env)
" NOTE: Pasting a block with ragged rights appends extra white spaces to
" the ends of their corresponding lines. To avoid this behavior, we
" overwrite the lines with their saved copies if they are still blank.
"
if data.type ==# 'block' && has_key(data, 'ragged')
let range = [data.range[0][1], data.range[1][1]]
for [nr, line] in items(data.ragged)
if data.lines[nr - range[0]] == ""
call setline(nr, line)
endif
endfor
endif
endif
endfunction
function! s:sort_numbers(list)
call sort(a:list, 's:compare_numbers')
endfunction
function! s:compare_numbers(n1, n2)
return a:n1 == a:n2 ? 0 : a:n1 > a:n2 ? 1 : -1
endfunction
" vim: filetype=vim
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment