Created
October 14, 2010 08:09
-
-
Save h1mesuke/625865 to your computer and use it in GitHub Desktop.
Vim - My autload/util.vim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
" 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