You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
571 lines
15 KiB
VimL
571 lines
15 KiB
VimL
"=============================================================================
|
|
" Copyright (c) 2009-2010 Takeshi NISHIDA
|
|
"
|
|
"=============================================================================
|
|
" LOAD GUARD {{{1
|
|
|
|
if exists('g:loaded_autoload_l9')
|
|
finish
|
|
endif
|
|
let g:loaded_autoload_l9 = 1
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" COMPATIBILITY TEST {{{1
|
|
|
|
"
|
|
let s:L9_VERSION_CURRENT = 101
|
|
let s:L9_VERSION_PASSABLE = 101
|
|
|
|
" returns true if given version is compatible.
|
|
function l9#isCompatible(ver)
|
|
return
|
|
endfunction
|
|
|
|
let s:VERSION_FACTOR = str2float('0.01')
|
|
|
|
" returns false if the caller script should finish.
|
|
" a:vimVersion: if 0, don't check vim version
|
|
" a:l9Version: same rule as v:version
|
|
function l9#guardScriptLoading(path, vimVersion, l9Version, exprs)
|
|
let loadedVarName = 'g:loaded_' . substitute(a:path, '\W', '_', 'g')
|
|
if exists(loadedVarName)
|
|
return 0
|
|
elseif a:vimVersion > 0 && a:vimVersion > v:version
|
|
echoerr a:path . ' requires Vim version ' . string(a:vimVersion * s:VERSION_FACTOR)
|
|
return 0
|
|
elseif a:l9Version > 0 && (a:l9Version > s:L9_VERSION_CURRENT ||
|
|
\ a:l9Version < s:L9_VERSION_PASSABLE)
|
|
echoerr a:path . ' requires L9 library version ' . string(a:l9Version * s:VERSION_FACTOR)
|
|
return 0
|
|
endif
|
|
for expr in a:exprs
|
|
if !eval(expr)
|
|
echoerr a:path . ' requires: ' . expr
|
|
return 0
|
|
endif
|
|
endfor
|
|
let {loadedVarName} = 1
|
|
return 1
|
|
endfunction
|
|
|
|
"
|
|
function l9#getVersion()
|
|
return s:L9_VERSION_CURRENT
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" LIST {{{1
|
|
|
|
" Removes duplicates (unstable)
|
|
" This function doesn't change the list of argument.
|
|
function l9#unique(items)
|
|
let sorted = sort(a:items)
|
|
if len(sorted) < 2
|
|
return sorted
|
|
endif
|
|
let last = remove(sorted, 0)
|
|
let result = [last]
|
|
for item in sorted
|
|
if item != last
|
|
call add(result, item)
|
|
let last = item
|
|
endif
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" Removes duplicates (stable)
|
|
" This function doesn't change the list of argument.
|
|
function l9#uniqueStably(items)
|
|
let result = []
|
|
for item in a:items
|
|
if count(result, item, &ignorecase) == 0
|
|
call add(result, item)
|
|
endif
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" [ [0], [1,2], [3] ] -> [ 0, 1, 2, 3 ]
|
|
" This function doesn't change the list of argument.
|
|
function l9#concat(items)
|
|
let result = []
|
|
for l in a:items
|
|
let result += l
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" [ [0,1,2], [3,4], [5,6,7,8] ] -> [ [0,3,5],[1,4,6] ]
|
|
" This function doesn't change the list of argument.
|
|
function l9#zip(items)
|
|
let result = []
|
|
for i in range(min(map(copy(a:items), 'len(v:val)')))
|
|
call add(result, map(copy(a:items), 'v:val[i]'))
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" filter() with the maximum number of items
|
|
" This function doesn't change the list of argument.
|
|
function l9#filterWithLimit(items, expr, limit)
|
|
if a:limit <= 0
|
|
return filter(copy(a:items), a:expr)
|
|
endif
|
|
let result = []
|
|
let stride = a:limit * 3 / 2 " x1.5
|
|
for i in range(0, len(a:items) - 1, stride)
|
|
let result += filter(a:items[i : i + stride - 1], a:expr)
|
|
if len(result) >= a:limit
|
|
return remove(result, 0, a:limit - 1)
|
|
endif
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" Removes if a:expr is evaluated as non-zero and returns removed items.
|
|
" This function change the list of argument.
|
|
function l9#removeIf(items, expr)
|
|
let removed = filter(copy(a:items), a:expr)
|
|
call filter(a:items, '!( ' . a:expr . ')')
|
|
return removed
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" NUMERIC {{{1
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" STRING {{{1
|
|
|
|
" Snips a:str and add a:mask if the length of a:str is more than a:len
|
|
function l9#snipHead(str, len, mask)
|
|
if a:len >= len(a:str)
|
|
return a:str
|
|
elseif a:len <= len(a:mask)
|
|
return a:mask
|
|
endif
|
|
return a:mask . a:str[-a:len + len(a:mask):]
|
|
endfunction
|
|
|
|
" Snips a:str and add a:mask if the length of a:str is more than a:len
|
|
function l9#snipTail(str, len, mask)
|
|
if a:len >= len(a:str)
|
|
return a:str
|
|
elseif a:len <= len(a:mask)
|
|
return a:mask
|
|
endif
|
|
return a:str[:a:len - 1 - len(a:mask)] . a:mask
|
|
endfunction
|
|
|
|
" Snips a:str and add a:mask if the length of a:str is more than a:len
|
|
function l9#snipMid(str, len, mask)
|
|
if a:len >= len(a:str)
|
|
return a:str
|
|
elseif a:len <= len(a:mask)
|
|
return a:mask
|
|
endif
|
|
let len_head = (a:len - len(a:mask)) / 2
|
|
let len_tail = a:len - len(a:mask) - len_head
|
|
return (len_head > 0 ? a:str[: len_head - 1] : '') . a:mask .
|
|
\ (len_tail > 0 ? a:str[-len_tail :] : '')
|
|
endfunction
|
|
|
|
"
|
|
function l9#hash224(str)
|
|
let a = 0x00000800 " shift 11 bit (if unsigned)
|
|
let b = 0x001fffff " extract 11 bit (if unsigned)
|
|
let nHash = 7
|
|
let hashes = repeat([0], nHash)
|
|
for i in range(len(a:str))
|
|
let iHash = i % nHash
|
|
let hashes[iHash] = hashes[iHash] * a + hashes[iHash] / b
|
|
let hashes[iHash] += char2nr(a:str[i])
|
|
endfor
|
|
return join(map(hashes, 'printf("%08x", v:val)'), '')
|
|
endfunction
|
|
|
|
" wildcard -> regexp
|
|
function l9#convertWildcardToRegexp(expr)
|
|
let re = escape(a:expr, '\')
|
|
for [pat, sub] in [ [ '*', '\\.\\*' ], [ '?', '\\.' ], [ '[', '\\[' ], ]
|
|
let re = substitute(re, pat, sub, 'g')
|
|
endfor
|
|
return '\V' . re
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" LINES {{{1
|
|
|
|
" Removes from the line matching with a:begin first to the line matching with
|
|
" a:end next and returns removed lines.
|
|
" If matching range is not found, returns []
|
|
function l9#removeLinesBetween(lines, begin, end)
|
|
for i in range(len(a:lines) - 1)
|
|
if a:lines[i] =~ a:begin
|
|
break
|
|
endif
|
|
endfor
|
|
for j in range(i + 1, len(a:lines) - 1)
|
|
if a:lines[j] =~ a:end
|
|
let g:l0 += [a:lines[i : j]]
|
|
return remove(a:lines, i, j)
|
|
endif
|
|
endfor
|
|
return []
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" PATH {{{1
|
|
|
|
" returns the path separator charactor.
|
|
function l9#getPathSeparator()
|
|
return (!&shellslash && (has('win32') || has('win64')) ? '\' : '/')
|
|
endfunction
|
|
|
|
" [ 'a', 'b/', '/c' ] -> 'a/b/c'
|
|
function l9#concatPaths(paths)
|
|
let result = ''
|
|
for p in a:paths
|
|
if empty(p)
|
|
continue
|
|
elseif empty(result)
|
|
let result = p
|
|
else
|
|
let result = substitute(result, '[/\\]$', '', '') . l9#getPathSeparator()
|
|
\ . substitute(p, '^[/\\]', '', '')
|
|
endif
|
|
endfor
|
|
return result
|
|
endfunction
|
|
|
|
" path: '/a/b/c/d', dir: '/a/b' => 'c/d'
|
|
function l9#modifyPathRelativeToDir(path, dir)
|
|
let pathFull = fnamemodify(a:path, ':p')
|
|
let dirFull = fnamemodify(a:dir, ':p')
|
|
if len(pathFull) < len(dirFull) || pathFull[:len(dirFull) - 1] !=# dirFull
|
|
return pathFull
|
|
endif
|
|
return pathFull[len(dirFull):]
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" FILE {{{1
|
|
|
|
" Almost same as readfile().
|
|
function l9#readFile(...)
|
|
let args = copy(a:000)
|
|
let args[0] = expand(args[0])
|
|
try
|
|
return call('readfile', args)
|
|
catch
|
|
endtry
|
|
return []
|
|
endfunction
|
|
|
|
" Almost same as writefile().
|
|
function l9#writeFile(...)
|
|
let args = copy(a:000)
|
|
let args[1] = expand(args[1])
|
|
let dir = fnamemodify(args[1], ':h')
|
|
try
|
|
if !isdirectory(dir)
|
|
call mkdir(dir, 'p')
|
|
endif
|
|
return call('writefile', args)
|
|
catch
|
|
endtry
|
|
return -1 " -1 is error code.
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" BUFFER {{{1
|
|
|
|
" :wall/:wall! wrapper. Useful for writing readonly buffers.
|
|
function l9#writeAll()
|
|
try
|
|
silent update " NOTE: avoiding a problem with a buftype=acwrite buffer.
|
|
silent wall
|
|
catch /^Vim/ " E45, E505
|
|
if l9#inputHl('Question', v:exception . "\nWrite readonly files? (Y/N) : ", 'Y') ==? 'y'
|
|
redraw
|
|
:wall!
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
" Loads given files with :edit command
|
|
function l9#loadFilesToBuffers(files)
|
|
for file in filter(copy(a:files), '!bufloaded(v:val)')
|
|
execute 'edit ' . fnameescape(file)
|
|
if !exists('bufNrFirst')
|
|
let bufNrFirst = bufnr('%')
|
|
endif
|
|
endfor
|
|
if exists('bufNrFirst')
|
|
execute bufNrFirst . 'buffer'
|
|
endif
|
|
endfunction
|
|
|
|
" Deletes all buffers except given files with :bdelete command
|
|
function l9#deleteAllBuffersExcept(files)
|
|
let bufNrExcepts = map(copy(a:files), 'bufnr("^" . v:val . "$")')
|
|
for bufNr in filter(range(1, bufnr('$')), 'bufloaded(v:val)')
|
|
if count(bufNrExcepts, bufNr) == 0
|
|
execute bufNr . 'bdelete'
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" WINDOW {{{1
|
|
|
|
" move current window to next tabpage.
|
|
function l9#shiftWinNextTabpage()
|
|
if tabpagenr('$') < 2
|
|
return
|
|
endif
|
|
let bufnr = bufnr('%')
|
|
tabnext
|
|
execute bufnr . 'sbuffer'
|
|
tabprevious
|
|
if winnr('$') > 1
|
|
close
|
|
tabnext
|
|
else
|
|
close " if tabpage is closed, next tabpage will become current
|
|
endif
|
|
endfunction
|
|
|
|
" move current window to previous tabpage.
|
|
function l9#shiftWinPrevTabpage()
|
|
if tabpagenr('$') < 2
|
|
return
|
|
endif
|
|
let bufnr = bufnr('%')
|
|
tabprevious
|
|
execute bufnr . 'sbuffer'
|
|
tabnext
|
|
close
|
|
tabprevious
|
|
endfunction
|
|
|
|
" move to a window containing specified buffer.
|
|
" returns 0 if the buffer is not found.
|
|
function l9#moveToBufferWindowInCurrentTabpage(bufNr)
|
|
if bufnr('%') == a:bufNr
|
|
return 1
|
|
elseif count(tabpagebuflist(), a:bufNr) == 0
|
|
return 0
|
|
endif
|
|
execute bufwinnr(a:bufNr) . 'wincmd w'
|
|
return 1
|
|
endfunction
|
|
|
|
" returns 0 if the buffer is not found.
|
|
function s:moveToOtherTabpageOpeningBuffer(bufNr)
|
|
for tabNr in range(1, tabpagenr('$'))
|
|
if tabNr != tabpagenr() && count(tabpagebuflist(tabNr), a:bufNr) > 0
|
|
execute 'tabnext ' . tabNr
|
|
return 1
|
|
endif
|
|
endfor
|
|
return 0
|
|
endfunction
|
|
|
|
" move to a window containing specified buffer.
|
|
" returns 0 if the buffer is not found.
|
|
function l9#moveToBufferWindowInOtherTabpage(bufNr)
|
|
if !s:moveToOtherTabpageOpeningBuffer(a:bufNr)
|
|
return 0
|
|
endif
|
|
return l9#moveToBufferWindowInCurrentTabpage(a:bufNr)
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" COMMAND LINE {{{1
|
|
|
|
" echo/echomsg with highlighting.
|
|
function l9#echoHl(hl, msg, prefix, addingHistory)
|
|
let echoCmd = (a:addingHistory ? 'echomsg' : 'echo')
|
|
execute "echohl " . a:hl
|
|
try
|
|
for l in (type(a:msg) == type([]) ? a:msg : split(a:msg, "\n"))
|
|
execute echoCmd . ' a:prefix . l'
|
|
endfor
|
|
finally
|
|
echohl None
|
|
endtry
|
|
endfunction
|
|
|
|
" input() with highlighting.
|
|
" This function can take list as {completion} argument.
|
|
function l9#inputHl(hl, ...)
|
|
execute "echohl " . a:hl
|
|
try
|
|
let args = copy(a:000)
|
|
if len(args) > 2 && type(args[2]) == type([])
|
|
let s:candidatesForInputHl = args[2]
|
|
let args[2] = 'custom,l9#completeForInputHl'
|
|
endif
|
|
let s = call('input', args)
|
|
unlet! s:candidatesForInputHl
|
|
finally
|
|
echohl None
|
|
endtry
|
|
redraw " needed to show following echo to next line.
|
|
return s
|
|
endfunction
|
|
|
|
" only called by l9#inputHl() for completion.
|
|
function l9#completeForInputHl(lead, line, pos)
|
|
return join(s:candidatesForInputHl, "\n")
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" VISUAL MODE {{{1
|
|
|
|
" returns last selected text in Visual mode.
|
|
function l9#getSelectedText()
|
|
let reg_ = [@", getregtype('"')]
|
|
let regA = [@a, getregtype('a')]
|
|
if mode() =~# "[vV\<C-v>]"
|
|
silent normal! "aygv
|
|
else
|
|
let pos = getpos('.')
|
|
silent normal! gv"ay
|
|
call setpos('.', pos)
|
|
endif
|
|
let text = @a
|
|
call setreg('"', reg_[0], reg_[1])
|
|
call setreg('a', regA[0], regA[1])
|
|
return text
|
|
endfunction
|
|
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" EVAL {{{1
|
|
|
|
" loads given text as Vim script with :source command
|
|
function l9#loadScript(text)
|
|
let lines = (type(a:text) == type([]) ? a:text : split(a:text, "\n"))
|
|
let fname = tempname()
|
|
call writefile(lines, fname)
|
|
source `=fname`
|
|
call delete(fname)
|
|
endfunction
|
|
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" VARIABLES {{{1
|
|
|
|
"
|
|
function l9#defineVariableDefault(name, default)
|
|
if !exists(a:name)
|
|
let {a:name} = a:default
|
|
endif
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" GREP {{{1
|
|
|
|
" Execute :vimgrep and opens the quickfix window if matches are found.
|
|
"
|
|
" a:pattern: search pattern. If ommitted, last search pattern (@/) is used.
|
|
" a:files: List of files
|
|
function l9#grepFiles(pattern, files)
|
|
let target = join(map(a:files, 'escape(v:val, " ")'), ' ')
|
|
let pattern = (a:pattern[0] ==# '/' ? a:pattern[1:] : a:pattern)
|
|
let pattern = (empty(pattern) ? @/ : pattern)
|
|
try
|
|
execute printf('vimgrep/%s/j %s', pattern, target)
|
|
catch /^Vim/
|
|
call setqflist([])
|
|
endtry
|
|
call l9#quickfix#sort()
|
|
call l9#quickfix#openIfNotEmpty(1, 0)
|
|
endfunction
|
|
|
|
" Execute :vimgrep for buffers using l9#grepFiles()
|
|
" See also: :L9GrepBuffer :L9GrepBufferAll
|
|
function l9#grepBuffers(pattern, bufNrs)
|
|
let files = map(filter(a:bufNrs, 'bufloaded(v:val)'), 'bufname(v:val)')
|
|
call l9#grepFiles(a:pattern, files)
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" SIGN {{{1
|
|
|
|
" Highlights lines using :sign define and :sign place.
|
|
"
|
|
" a:linehl, a:text, a:texthl: See |signs|. Ignored if empty string.
|
|
" a:locations: List of [{buffer number}, {line number}] for highlighting
|
|
function l9#placeSign(linehl, text, texthl, locations)
|
|
let argLinehl = (empty(a:linehl) ? '' : 'linehl=' . a:linehl)
|
|
let argText = (empty(a:text) ? '' : 'text=' . a:text)
|
|
let argTexthl = (empty(a:texthl) ? '' : 'texthl=' . a:texthl)
|
|
let name = 'l9--' . a:linehl . '--' . a:text . '--' . a:texthl
|
|
execute printf('sign define %s linehl=%s text=%s texthl=%s',
|
|
\ name, a:linehl, a:text, a:texthl)
|
|
for [bufNr, lnum] in a:locations
|
|
execute printf('sign place 1 line=%d name=%s buffer=%d', lnum, name, bufNr)
|
|
endfor
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" NOTIFY EXTERNALLY {{{1
|
|
|
|
" Notify a message using an external program.
|
|
" Currently supports Balloonly, Screen, and Tmux.
|
|
function l9#notifyExternally(msg)
|
|
return l9#notifyBalloonly(a:msg)
|
|
\ || l9#notifyScreen(a:msg)
|
|
\ || l9#notifyTmux(a:msg)
|
|
endfunction
|
|
|
|
"
|
|
function l9#notifyBalloonly(msg)
|
|
if !(has('win32') || has('win64')) || !executable(g:l9_balloonly)
|
|
return 0
|
|
endif
|
|
execute 'silent !start ' . shellescape(g:l9_balloonly) . ' 4000 "l9" ' . shellescape(a:msg)
|
|
return 1
|
|
endfunction
|
|
|
|
"
|
|
function l9#notifyScreen(msg)
|
|
if !has('unix') || has('gui_running') || $WINDOW !~ '\d' || !executable('screen')
|
|
return 0
|
|
endif
|
|
call system('screen -X wall ' . shellescape('l9: ' . a:msg))
|
|
return 1
|
|
endfunction
|
|
|
|
"
|
|
function l9#notifyTmux(msg)
|
|
if !has('unix') || has('gui_running') || empty($TMUX) || !executable('tmux')
|
|
return 0
|
|
endif
|
|
call system('tmux display-message ' . shellescape('l9: ' . a:msg))
|
|
return 1
|
|
endfunction
|
|
|
|
" }}}1
|
|
"=============================================================================
|
|
" vim: set fdm=marker:
|