blob: b6a0ae7f27d5acfc3d81953bd3ec231d4ef7df11 [file] [log] [blame]
chatsiri rattana12c30d42012-04-23 22:33:39 +07001"=============================================================================
2" Copyright (c) 2009-2010 Takeshi NISHIDA
3"
4"=============================================================================
5" LOAD GUARD {{{1
6
7if exists('g:loaded_autoload_l9')
8 finish
9endif
10let g:loaded_autoload_l9 = 1
11
12" }}}1
13"=============================================================================
14" COMPATIBILITY TEST {{{1
15
16"
17let s:L9_VERSION_CURRENT = 101
18let s:L9_VERSION_PASSABLE = 101
19
20" returns true if given version is compatible.
21function l9#isCompatible(ver)
22 return
23endfunction
24
25let s:VERSION_FACTOR = str2float('0.01')
26
27" returns false if the caller script should finish.
28" a:vimVersion: if 0, don't check vim version
29" a:l9Version: same rule as v:version
30function l9#guardScriptLoading(path, vimVersion, l9Version, exprs)
31 let loadedVarName = 'g:loaded_' . substitute(a:path, '\W', '_', 'g')
32 if exists(loadedVarName)
33 return 0
34 elseif a:vimVersion > 0 && a:vimVersion > v:version
35 echoerr a:path . ' requires Vim version ' . string(a:vimVersion * s:VERSION_FACTOR)
36 return 0
37 elseif a:l9Version > 0 && (a:l9Version > s:L9_VERSION_CURRENT ||
38 \ a:l9Version < s:L9_VERSION_PASSABLE)
39 echoerr a:path . ' requires L9 library version ' . string(a:l9Version * s:VERSION_FACTOR)
40 return 0
41 endif
42 for expr in a:exprs
43 if !eval(expr)
44 echoerr a:path . ' requires: ' . expr
45 return 0
46 endif
47 endfor
48 let {loadedVarName} = 1
49 return 1
50endfunction
51
52"
53function l9#getVersion()
54 return s:L9_VERSION_CURRENT
55endfunction
56
57" }}}1
58"=============================================================================
59" LIST {{{1
60
61" Removes duplicates (unstable)
62" This function doesn't change the list of argument.
63function l9#unique(items)
64 let sorted = sort(a:items)
65 if len(sorted) < 2
66 return sorted
67 endif
68 let last = remove(sorted, 0)
69 let result = [last]
70 for item in sorted
71 if item != last
72 call add(result, item)
73 let last = item
74 endif
75 endfor
76 return result
77endfunction
78
79" Removes duplicates (stable)
80" This function doesn't change the list of argument.
81function l9#uniqueStably(items)
82 let result = []
83 for item in a:items
84 if count(result, item, &ignorecase) == 0
85 call add(result, item)
86 endif
87 endfor
88 return result
89endfunction
90
91" [ [0], [1,2], [3] ] -> [ 0, 1, 2, 3 ]
92" This function doesn't change the list of argument.
93function l9#concat(items)
94 let result = []
95 for l in a:items
96 let result += l
97 endfor
98 return result
99endfunction
100
101" [ [0,1,2], [3,4], [5,6,7,8] ] -> [ [0,3,5],[1,4,6] ]
102" This function doesn't change the list of argument.
103function l9#zip(items)
104 let result = []
105 for i in range(min(map(copy(a:items), 'len(v:val)')))
106 call add(result, map(copy(a:items), 'v:val[i]'))
107 endfor
108 return result
109endfunction
110
111" filter() with the maximum number of items
112" This function doesn't change the list of argument.
113function l9#filterWithLimit(items, expr, limit)
114 if a:limit <= 0
115 return filter(copy(a:items), a:expr)
116 endif
117 let result = []
118 let stride = a:limit * 3 / 2 " x1.5
119 for i in range(0, len(a:items) - 1, stride)
120 let result += filter(a:items[i : i + stride - 1], a:expr)
121 if len(result) >= a:limit
122 return remove(result, 0, a:limit - 1)
123 endif
124 endfor
125 return result
126endfunction
127
128" Removes if a:expr is evaluated as non-zero and returns removed items.
129" This function change the list of argument.
130function l9#removeIf(items, expr)
131 let removed = filter(copy(a:items), a:expr)
132 call filter(a:items, '!( ' . a:expr . ')')
133 return removed
134endfunction
135
136" }}}1
137"=============================================================================
138" NUMERIC {{{1
139
140" }}}1
141"=============================================================================
142" STRING {{{1
143
144" Snips a:str and add a:mask if the length of a:str is more than a:len
145function l9#snipHead(str, len, mask)
146 if a:len >= len(a:str)
147 return a:str
148 elseif a:len <= len(a:mask)
149 return a:mask
150 endif
151 return a:mask . a:str[-a:len + len(a:mask):]
152endfunction
153
154" Snips a:str and add a:mask if the length of a:str is more than a:len
155function l9#snipTail(str, len, mask)
156 if a:len >= len(a:str)
157 return a:str
158 elseif a:len <= len(a:mask)
159 return a:mask
160 endif
161 return a:str[:a:len - 1 - len(a:mask)] . a:mask
162endfunction
163
164" Snips a:str and add a:mask if the length of a:str is more than a:len
165function l9#snipMid(str, len, mask)
166 if a:len >= len(a:str)
167 return a:str
168 elseif a:len <= len(a:mask)
169 return a:mask
170 endif
171 let len_head = (a:len - len(a:mask)) / 2
172 let len_tail = a:len - len(a:mask) - len_head
173 return (len_head > 0 ? a:str[: len_head - 1] : '') . a:mask .
174 \ (len_tail > 0 ? a:str[-len_tail :] : '')
175endfunction
176
177"
178function l9#hash224(str)
179 let a = 0x00000800 " shift 11 bit (if unsigned)
180 let b = 0x001fffff " extract 11 bit (if unsigned)
181 let nHash = 7
182 let hashes = repeat([0], nHash)
183 for i in range(len(a:str))
184 let iHash = i % nHash
185 let hashes[iHash] = hashes[iHash] * a + hashes[iHash] / b
186 let hashes[iHash] += char2nr(a:str[i])
187 endfor
188 return join(map(hashes, 'printf("%08x", v:val)'), '')
189endfunction
190
191" wildcard -> regexp
192function l9#convertWildcardToRegexp(expr)
193 let re = escape(a:expr, '\')
194 for [pat, sub] in [ [ '*', '\\.\\*' ], [ '?', '\\.' ], [ '[', '\\[' ], ]
195 let re = substitute(re, pat, sub, 'g')
196 endfor
197 return '\V' . re
198endfunction
199
200" }}}1
201"=============================================================================
202" LINES {{{1
203
204" Removes from the line matching with a:begin first to the line matching with
205" a:end next and returns removed lines.
206" If matching range is not found, returns []
207function l9#removeLinesBetween(lines, begin, end)
208 for i in range(len(a:lines) - 1)
209 if a:lines[i] =~ a:begin
210 break
211 endif
212 endfor
213 for j in range(i + 1, len(a:lines) - 1)
214 if a:lines[j] =~ a:end
215 let g:l0 += [a:lines[i : j]]
216 return remove(a:lines, i, j)
217 endif
218 endfor
219 return []
220endfunction
221
222" }}}1
223"=============================================================================
224" PATH {{{1
225
226" returns the path separator charactor.
227function l9#getPathSeparator()
228 return (!&shellslash && (has('win32') || has('win64')) ? '\' : '/')
229endfunction
230
231" [ 'a', 'b/', '/c' ] -> 'a/b/c'
232function l9#concatPaths(paths)
233 let result = ''
234 for p in a:paths
235 if empty(p)
236 continue
237 elseif empty(result)
238 let result = p
239 else
240 let result = substitute(result, '[/\\]$', '', '') . l9#getPathSeparator()
241 \ . substitute(p, '^[/\\]', '', '')
242 endif
243 endfor
244 return result
245endfunction
246
247" path: '/a/b/c/d', dir: '/a/b' => 'c/d'
248function l9#modifyPathRelativeToDir(path, dir)
249 let pathFull = fnamemodify(a:path, ':p')
250 let dirFull = fnamemodify(a:dir, ':p')
251 if len(pathFull) < len(dirFull) || pathFull[:len(dirFull) - 1] !=# dirFull
252 return pathFull
253 endif
254 return pathFull[len(dirFull):]
255endfunction
256
257" }}}1
258"=============================================================================
259" FILE {{{1
260
261" Almost same as readfile().
262function l9#readFile(...)
263 let args = copy(a:000)
264 let args[0] = expand(args[0])
265 try
266 return call('readfile', args)
267 catch
268 endtry
269 return []
270endfunction
271
272" Almost same as writefile().
273function l9#writeFile(...)
274 let args = copy(a:000)
275 let args[1] = expand(args[1])
276 let dir = fnamemodify(args[1], ':h')
277 try
278 if !isdirectory(dir)
279 call mkdir(dir, 'p')
280 endif
281 return call('writefile', args)
282 catch
283 endtry
284 return -1 " -1 is error code.
285endfunction
286
287" }}}1
288"=============================================================================
289" BUFFER {{{1
290
291" :wall/:wall! wrapper. Useful for writing readonly buffers.
292function l9#writeAll()
293 try
294 silent update " NOTE: avoiding a problem with a buftype=acwrite buffer.
295 silent wall
296 catch /^Vim/ " E45, E505
297 if l9#inputHl('Question', v:exception . "\nWrite readonly files? (Y/N) : ", 'Y') ==? 'y'
298 redraw
299 :wall!
300 endif
301 endtry
302endfunction
303
304" Loads given files with :edit command
305function l9#loadFilesToBuffers(files)
306 for file in filter(copy(a:files), '!bufloaded(v:val)')
307 execute 'edit ' . fnameescape(file)
308 if !exists('bufNrFirst')
309 let bufNrFirst = bufnr('%')
310 endif
311 endfor
312 if exists('bufNrFirst')
313 execute bufNrFirst . 'buffer'
314 endif
315endfunction
316
317" Deletes all buffers except given files with :bdelete command
318function l9#deleteAllBuffersExcept(files)
319 let bufNrExcepts = map(copy(a:files), 'bufnr("^" . v:val . "$")')
320 for bufNr in filter(range(1, bufnr('$')), 'bufloaded(v:val)')
321 if count(bufNrExcepts, bufNr) == 0
322 execute bufNr . 'bdelete'
323 endif
324 endfor
325endfunction
326
327" }}}1
328"=============================================================================
329" WINDOW {{{1
330
331" move current window to next tabpage.
332function l9#shiftWinNextTabpage()
333 if tabpagenr('$') < 2
334 return
335 endif
336 let bufnr = bufnr('%')
337 tabnext
338 execute bufnr . 'sbuffer'
339 tabprevious
340 if winnr('$') > 1
341 close
342 tabnext
343 else
344 close " if tabpage is closed, next tabpage will become current
345 endif
346endfunction
347
348" move current window to previous tabpage.
349function l9#shiftWinPrevTabpage()
350 if tabpagenr('$') < 2
351 return
352 endif
353 let bufnr = bufnr('%')
354 tabprevious
355 execute bufnr . 'sbuffer'
356 tabnext
357 close
358 tabprevious
359endfunction
360
361" move to a window containing specified buffer.
362" returns 0 if the buffer is not found.
363function l9#moveToBufferWindowInCurrentTabpage(bufNr)
364 if bufnr('%') == a:bufNr
365 return 1
366 elseif count(tabpagebuflist(), a:bufNr) == 0
367 return 0
368 endif
369 execute bufwinnr(a:bufNr) . 'wincmd w'
370 return 1
371endfunction
372
373" returns 0 if the buffer is not found.
374function s:moveToOtherTabpageOpeningBuffer(bufNr)
375 for tabNr in range(1, tabpagenr('$'))
376 if tabNr != tabpagenr() && count(tabpagebuflist(tabNr), a:bufNr) > 0
377 execute 'tabnext ' . tabNr
378 return 1
379 endif
380 endfor
381 return 0
382endfunction
383
384" move to a window containing specified buffer.
385" returns 0 if the buffer is not found.
386function l9#moveToBufferWindowInOtherTabpage(bufNr)
387 if !s:moveToOtherTabpageOpeningBuffer(a:bufNr)
388 return 0
389 endif
390 return l9#moveToBufferWindowInCurrentTabpage(a:bufNr)
391endfunction
392
393" }}}1
394"=============================================================================
395" COMMAND LINE {{{1
396
397" echo/echomsg with highlighting.
398function l9#echoHl(hl, msg, prefix, addingHistory)
399 let echoCmd = (a:addingHistory ? 'echomsg' : 'echo')
400 execute "echohl " . a:hl
401 try
402 for l in (type(a:msg) == type([]) ? a:msg : split(a:msg, "\n"))
403 execute echoCmd . ' a:prefix . l'
404 endfor
405 finally
406 echohl None
407 endtry
408endfunction
409
410" input() with highlighting.
411" This function can take list as {completion} argument.
412function l9#inputHl(hl, ...)
413 execute "echohl " . a:hl
414 try
415 let args = copy(a:000)
416 if len(args) > 2 && type(args[2]) == type([])
417 let s:candidatesForInputHl = args[2]
418 let args[2] = 'custom,l9#completeForInputHl'
419 endif
420 let s = call('input', args)
421 unlet! s:candidatesForInputHl
422 finally
423 echohl None
424 endtry
425 redraw " needed to show following echo to next line.
426 return s
427endfunction
428
429" only called by l9#inputHl() for completion.
430function l9#completeForInputHl(lead, line, pos)
431 return join(s:candidatesForInputHl, "\n")
432endfunction
433
434" }}}1
435"=============================================================================
436" VISUAL MODE {{{1
437
438" returns last selected text in Visual mode.
439function l9#getSelectedText()
440 let reg_ = [@", getregtype('"')]
441 let regA = [@a, getregtype('a')]
442 if mode() =~# "[vV\<C-v>]"
443 silent normal! "aygv
444 else
445 let pos = getpos('.')
446 silent normal! gv"ay
447 call setpos('.', pos)
448 endif
449 let text = @a
450 call setreg('"', reg_[0], reg_[1])
451 call setreg('a', regA[0], regA[1])
452 return text
453endfunction
454
455
456" }}}1
457"=============================================================================
458" EVAL {{{1
459
460" loads given text as Vim script with :source command
461function l9#loadScript(text)
462 let lines = (type(a:text) == type([]) ? a:text : split(a:text, "\n"))
463 let fname = tempname()
464 call writefile(lines, fname)
465 source `=fname`
466 call delete(fname)
467endfunction
468
469
470" }}}1
471"=============================================================================
472" VARIABLES {{{1
473
474"
475function l9#defineVariableDefault(name, default)
476 if !exists(a:name)
477 let {a:name} = a:default
478 endif
479endfunction
480
481" }}}1
482"=============================================================================
483" GREP {{{1
484
485" Execute :vimgrep and opens the quickfix window if matches are found.
486"
487" a:pattern: search pattern. If ommitted, last search pattern (@/) is used.
488" a:files: List of files
489function l9#grepFiles(pattern, files)
490 let target = join(map(a:files, 'escape(v:val, " ")'), ' ')
491 let pattern = (a:pattern[0] ==# '/' ? a:pattern[1:] : a:pattern)
492 let pattern = (empty(pattern) ? @/ : pattern)
493 try
494 execute printf('vimgrep/%s/j %s', pattern, target)
495 catch /^Vim/
496 call setqflist([])
497 endtry
498 call l9#quickfix#sort()
499 call l9#quickfix#openIfNotEmpty(1, 0)
500endfunction
501
502" Execute :vimgrep for buffers using l9#grepFiles()
503" See also: :L9GrepBuffer :L9GrepBufferAll
504function l9#grepBuffers(pattern, bufNrs)
505 let files = map(filter(a:bufNrs, 'bufloaded(v:val)'), 'bufname(v:val)')
506 call l9#grepFiles(a:pattern, files)
507endfunction
508
509" }}}1
510"=============================================================================
511" SIGN {{{1
512
513" Highlights lines using :sign define and :sign place.
514"
515" a:linehl, a:text, a:texthl: See |signs|. Ignored if empty string.
516" a:locations: List of [{buffer number}, {line number}] for highlighting
517function l9#placeSign(linehl, text, texthl, locations)
518 let argLinehl = (empty(a:linehl) ? '' : 'linehl=' . a:linehl)
519 let argText = (empty(a:text) ? '' : 'text=' . a:text)
520 let argTexthl = (empty(a:texthl) ? '' : 'texthl=' . a:texthl)
521 let name = 'l9--' . a:linehl . '--' . a:text . '--' . a:texthl
522 execute printf('sign define %s linehl=%s text=%s texthl=%s',
523 \ name, a:linehl, a:text, a:texthl)
524 for [bufNr, lnum] in a:locations
525 execute printf('sign place 1 line=%d name=%s buffer=%d', lnum, name, bufNr)
526 endfor
527endfunction
528
529" }}}1
530"=============================================================================
531" NOTIFY EXTERNALLY {{{1
532
533" Notify a message using an external program.
534" Currently supports Balloonly, Screen, and Tmux.
535function l9#notifyExternally(msg)
536 return l9#notifyBalloonly(a:msg)
537 \ || l9#notifyScreen(a:msg)
538 \ || l9#notifyTmux(a:msg)
539endfunction
540
541"
542function l9#notifyBalloonly(msg)
543 if !(has('win32') || has('win64')) || !executable(g:l9_balloonly)
544 return 0
545 endif
546 execute 'silent !start ' . shellescape(g:l9_balloonly) . ' 4000 "l9" ' . shellescape(a:msg)
547 return 1
548endfunction
549
550"
551function l9#notifyScreen(msg)
552 if !has('unix') || has('gui_running') || $WINDOW !~ '\d' || !executable('screen')
553 return 0
554 endif
555 call system('screen -X wall ' . shellescape('l9: ' . a:msg))
556 return 1
557endfunction
558
559"
560function l9#notifyTmux(msg)
561 if !has('unix') || has('gui_running') || empty($TMUX) || !executable('tmux')
562 return 0
563 endif
564 call system('tmux display-message ' . shellescape('l9: ' . a:msg))
565 return 1
566endfunction
567
568" }}}1
569"=============================================================================
570" vim: set fdm=marker: