1 " World.vim -- The World prototype for tlib#input#List()
2 " @Author: Thomas Link (micathom AT gmail com?subject=[vim])
3 " @Website: http://members.a1.net/t.link/
4 " @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
5 " @Created: 2007-05-01.
6 " @Last Change: 2008-03-08.
10 " A prototype used by |tlib#input#List|.
11 " Inherits from |tlib#Object#New|.
14 if &cp || exists("loaded_tlib_world_autoload")
17 let loaded_tlib_world_autoload = 1
20 let s:prototype = tlib#Object#New({
26 \ 'display_format': '',
28 \ 'filter_format': '',
29 \ 'follow_cursor': '',
31 \ 'initial_filter': [['']],
37 \ 'numeric_chars': tlib#var#Get('tlib_numeric_chars', 'bg'),
39 \ 'offset_horizontal': 0,
40 \ 'pick_last_item': tlib#var#Get('tlib_pick_last_item', 'bg'),
41 \ 'post_handlers': [],
44 \ 'resize_vertical': 0,
45 \ 'retrieve_eval': '',
48 \ 'scratch': '__InputList__',
49 \ 'scratch_filetype': 'tlibInputList',
50 \ 'scratch_vertical': 0,
54 \ 'state_handlers': [],
57 \ 'timeout_resolution': 2,
63 function! tlib#World#New(...)
64 let object = s:prototype.New(a:0 >= 1 ? a:1 : {})
69 function! s:prototype.Set_display_format(value) dict "{{{3
70 if a:value == 'filename'
71 call self.Set_highlight_filename()
72 let self.display_format = 'world.FormatFilename(%s)'
74 let self.display_format = a:value
79 function! s:prototype.Set_highlight_filename() dict "{{{3
80 let self.tlib_UseInputListScratch = 'call world.Highlight_filename()'
81 " \ 'syntax match TLibMarker /\%>'. (1 + eval(g:tlib_inputlist_width_filename)) .'c |.\{-}| / | hi def link TLibMarker Special'
82 " let self.tlib_UseInputListScratch .= '| syntax match TLibDir /\%>'. (4 + eval(g:tlib_inputlist_width_filename)) .'c\S\{-}[\/].*$/ | hi def link TLibDir Directory'
86 function! s:prototype.Highlight_filename() dict "{{{3
87 " exec 'syntax match TLibDir /\%>'. (3 + eval(g:tlib_inputlist_width_filename)) .'c \(\S:\)\?[\/].*$/ contained containedin=TLibMarker'
88 exec 'syntax match TLibDir /\(\a:\|\.\.\..\{-}\)\?[\/][^&<>*|]*$/ contained containedin=TLibMarker'
89 exec 'syntax match TLibMarker /\%>'. (1 + eval(g:tlib_inputlist_width_filename)) .'c |\( \|[[:alnum:]%*+-]*\)| \S.*$/ contains=TLibDir'
90 hi def link TLibMarker Special
91 hi def link TLibDir Directory
95 function! s:prototype.FormatFilename(file) dict "{{{3
96 let fname = fnamemodify(a:file, ":p:t")
97 " let fname = fnamemodify(a:file, ":t")
98 " if isdirectory(a:file)
101 let dname = fnamemodify(a:file, ":h")
102 " let dname = pathshorten(fnamemodify(a:file, ":h"))
103 let dnmax = &co - max([eval(g:tlib_inputlist_width_filename), len(fname)]) - 11 - self.index_width - &fdc
104 if len(dname) > dnmax
105 let dname = '...'. strpart(fnamemodify(a:file, ":h"), len(dname) - dnmax)
108 if g:tlib_inputlist_filename_indicators
109 let bnr = bufnr(a:file)
110 " TLogVAR a:file, bnr, self.bufnr
113 call add(marker, '%')
115 call add(marker, ' ')
116 " elseif buflisted(a:file)
117 " if getbufvar(a:file, "&mod")
118 " call add(marker, '+')
120 " call add(marker, 'B')
122 " elseif bufloaded(a:file)
123 " call add(marker, 'h')
125 " call add(marker, 'u')
128 call add(marker, ' ')
131 call insert(marker, '|')
132 call add(marker, '|')
133 return printf("%-". eval(g:tlib_inputlist_width_filename) ."s %s %s", fname, join(marker, ''), dname)
137 function! s:prototype.GetSelectedItems(current) dict "{{{3
138 if stridx(self.type, 'i') != -1
139 let rv = copy(self.sel_idx)
141 let rv = map(copy(self.sel_idx), 'self.GetBaseItem(v:val)')
144 let ci = index(rv, a:current)
148 call insert(rv, a:current)
150 if stridx(self.type, 'i') != -1
151 if !empty(self.index_table)
152 " TLogVAR rv, self.index_table
153 call map(rv, 'self.index_table[v:val - 1]')
161 function! s:prototype.SelectItem(mode, index) dict "{{{3
162 let bi = self.GetBaseIdx(a:index)
163 " if self.RespondTo('MaySelectItem')
164 " if !self.MaySelectItem(bi)
169 let si = index(self.sel_idx, bi)
170 " TLogVAR self.sel_idx
173 call add(self.sel_idx, bi)
174 elseif a:mode == 'toggle'
175 call remove(self.sel_idx, si)
181 function! s:prototype.FormatArgs(format_string, arg) dict "{{{3
182 let nargs = len(substitute(a:format_string, '%%\|[^%]', '', 'g'))
183 return [a:format_string] + repeat([string(a:arg)], nargs)
187 function! s:prototype.GetRx(filter) dict "{{{3
188 return '\('. join(filter(copy(a:filter), 'v:val[0] != "!"'), '\|') .'\)'
192 function! s:prototype.GetRx0(...) dict "{{{3
193 exec tlib#arg#Let(['negative'])
195 for filter in self.filter
197 let rx = join(reverse(filter(copy(filter), '!empty(v:val)')), '\|')
199 if !empty(rx) && (negative ? rx[0] == g:tlib_inputlist_not : rx[0] != g:tlib_inputlist_not)
203 let rx0s = join(rx0, '\|')
207 return '\V\('. rx0s .'\)'
212 function! s:prototype.GetItem(idx) dict "{{{3
213 return self.list[a:idx - 1]
217 function! s:prototype.GetListIdx(baseidx) dict "{{{3
218 " if empty(self.index_table)
219 let baseidx = a:baseidx
221 " let baseidx = 0 + self.index_table[a:baseidx - 1]
222 " " TLogVAR a:baseidx, baseidx, self.index_table
224 let rv = index(self.table, baseidx)
225 " TLogVAR rv, self.table
230 function! s:prototype.GetBaseIdx(idx) dict "{{{3
231 " TLogVAR a:idx, self.table, self.index_table
232 if !empty(self.table) && a:idx > 0 && a:idx <= len(self.table)
233 return self.table[a:idx - 1]
240 function! s:prototype.GetBaseItem(idx) dict "{{{3
241 return self.base[a:idx - 1]
245 function! s:prototype.SetBaseItem(idx, item) dict "{{{3
246 let self.base[a:idx - 1] = a:item
250 function! s:prototype.GetCurrentItem() dict "{{{3
251 let idx = self.prefidx
252 if stridx(self.type, 'i') != -1
254 elseif !empty(self.list)
255 if len(self.list) >= idx
256 return self.list[idx - 1]
264 function! s:prototype.CurrentItem() dict "{{{3
265 if stridx(self.type, 'i') != -1
266 return self.GetBaseIdx(self.llen == 1 ? 1 : self.prefidx)
271 elseif self.prefidx > 0
272 " TLogVAR self.prefidx
273 return self.GetCurrentItem()
279 function! s:prototype.SetFilter() dict "{{{3
280 " let mrx = '\V'. (a:0 >= 1 && a:1 ? '\C' : '')
281 let mrx = '\V'. self.filter_format
282 let self.filter_pos = []
283 let self.filter_neg = []
284 for filter in self.filter
286 let rx = join(reverse(filter(copy(filter), '!empty(v:val)')), '\|')
288 if rx[0] == g:tlib_inputlist_not
290 call add(self.filter_neg, mrx .'\('. rx[1:-1] .'\)')
293 call add(self.filter_pos, mrx .'\('. rx .'\)')
299 function! s:prototype.Match(text, ...) dict "{{{3
300 for rx in self.filter_neg
305 for rx in self.filter_pos
310 " for filter in self.filter
312 " let rx = join(reverse(filter(copy(filter), '!empty(v:val)')), '\|')
314 " if rx[0] == g:tlib_inputlist_not
315 " if len(rx) > 1 && a:text =~ mrx .'\('. rx[1:-1] .'\)'
318 " elseif a:text !~ mrx .'\('. rx .'\)'
321 " " if a:text !~ mrx. self.GetRx(filter)
329 function! s:prototype.MatchBaseIdx(idx) dict "{{{3
330 let text = self.GetBaseItem(a:idx)
331 if !empty(self.filter_format)
332 let text = eval(call(function("printf"), self.FormatArgs(self.filter_format, text)))
334 return self.Match(text)
338 function! s:prototype.BuildTable() dict "{{{3
339 call self.SetFilter()
340 let self.table = filter(range(1, len(self.base)), 'self.MatchBaseIdx(v:val)')
344 function! s:prototype.ReduceFilter() dict "{{{3
345 " TLogVAR self.filter
346 if self.filter[0] == [''] && len(self.filter) > 1
347 call remove(self.filter, 0)
348 elseif empty(self.filter[0][0]) && len(self.filter[0]) > 1
349 call remove(self.filter[0], 0)
351 let self.filter[0][0] = self.filter[0][0][0:-2]
356 function! s:prototype.SetInitialFilter(filter) dict "{{{3
357 " let self.initial_filter = [[''], [a:filter]]
358 let self.initial_filter = [[a:filter]]
362 function! s:prototype.PopFilter() dict "{{{3
363 " TLogVAR self.filter
364 if len(self.filter[0]) > 1
365 call remove(self.filter[0], 0)
366 elseif len(self.filter) > 1
367 call remove(self.filter, 0)
369 let self.filter[0] = ['']
374 function! s:prototype.FilterIsEmpty() dict "{{{3
375 " TLogVAR self.filter
376 return self.filter == copy(self.initial_filter)
380 function! s:prototype.DisplayFilter() dict "{{{3
381 " TLogVAR self.filter
382 let filter1 = map(deepcopy(self.filter), '"(". join(reverse(v:val), " OR ") .")"')
384 return join(reverse(filter1), ' AND ')
388 function! s:prototype.UseScratch() dict "{{{3
389 return tlib#scratch#UseScratch(self)
393 function! s:prototype.CloseScratch(...) dict "{{{3
394 TVarArg ['reset_scratch', 0]
395 " TVarArg ['reset_scratch', 1]
396 " TLogVAR reset_scratch
397 return tlib#scratch#CloseScratch(self, reset_scratch)
401 function! s:prototype.UseInputListScratch() dict "{{{3
402 let scratch = self.UseScratch()
404 syntax match InputlListIndex /^\d\+:/
405 syntax match InputlListCursor /^\d\+\* .*$/ contains=InputlListIndex
406 syntax match InputlListSelected /^\d\+# .*$/ contains=InputlListIndex
407 hi def link InputlListIndex Constant
408 hi def link InputlListCursor Search
409 hi def link InputlListSelected IncSearch
410 " exec "au BufEnter <buffer> call tlib#input#Resume(". string(self.name) .")"
412 " hi def link InputlListIndex Special
413 " let b:tlibDisplayListMarks = {}
414 let b:tlibDisplayListMarks = []
415 call tlib#hook#Run('tlib_UseInputListScratch', self)
420 " :def: function! s:prototype.Reset(?initial=0)
421 function! s:prototype.Reset(...) dict "{{{3
422 TVarArg ['initial', 0]
424 let self.state = 'display'
426 let self.filter = deepcopy(self.initial_filter)
429 call self.UseInputListScratch()
430 call self.ResetSelected()
431 call self.Retrieve(!initial)
436 function! s:prototype.ResetSelected() dict "{{{3
437 let self.sel_idx = []
441 function! s:prototype.Retrieve(anyway) dict "{{{3
442 " TLogVAR a:anyway, self.base
443 " TLogDBG (a:anyway || empty(self.base))
444 if (a:anyway || empty(self.base))
445 let ra = self.retrieve_eval
448 let back = self.SwitchWindow('win')
450 let self.base = eval(ra)
460 function! s:prototype.DisplayHelp() dict "{{{3
463 \ 'Mouse ... Pick an item Letter ... Filter the list',
464 \ printf('Number ... Pick an item "%s", "%s", %sWORD ... AND, OR, NOT',
465 \ g:tlib_inputlist_and, g:tlib_inputlist_or, g:tlib_inputlist_not),
466 \ 'Enter ... Pick the current item <bs>, <c-bs> ... Reduce filter',
467 \ '<c|m-r> ... Reset the display Up/Down ... Next/previous item',
468 \ '<c|m-q> ... Edit top filter string Page Up/Down ... Scroll',
472 if self.allow_suspend
474 \ '<c|m-z> ... Suspend/Resume <c-o> ... Switch to origin')
477 if stridx(self.type, 'm') != -1
479 \ '#, <c-space> ... (Un)Select the current item',
480 \ '<c|m-a> ... (Un)Select all currently visible items',
481 \ '<s-up/down> ... (Un)Select items',
483 " \ '<c-\> ... Show only selected',
485 for handler in self.key_handlers
486 let key = get(handler, 'key_name', '')
488 let desc = get(handler, 'help', '')
489 call add(help, printf('%-12s ... %s', key, desc))
495 \ 'Please don''t resize the window with the mouse.',
497 \ 'Note on filtering:',
498 \ 'The filter is prepended with "\V". Basically, filtering is case-insensitive.',
499 \ 'Letters at word boundaries or upper-case lettes in camel-case names is given',
500 \ 'more weight. If an OR-joined pattern start with "!", matches will be excluded.',
502 \ 'Press any key to continue.',
507 call self.Resize(len(help), 0)
511 function! s:prototype.Resize(hsize, vsize) dict "{{{3
512 " TLogVAR self.scratch_vertical, a:hsize, a:vsize
513 if self.scratch_vertical
515 exec 'vert resize '. eval(a:vsize)
519 exec 'resize '. eval(a:hsize)
525 " :def: function! s:prototype.DisplayList(query, ?list)
526 function! s:prototype.DisplayList(query, ...) dict "{{{3
529 let list = a:0 >= 1 ? a:1 : []
530 call self.UseScratch()
531 " TLogVAR self.scratch
532 " TAssert IsNotEmpty(self.scratch)
533 if self.state == 'scroll'
534 exec 'norm! '. self.offset .'zt'
535 elseif self.state == 'help'
536 call self.DisplayHelp()
540 " let x = len(ll) + 1
541 let x = self.index_width + 1
543 if self.state =~ '\<display\>'
544 let resize = get(self, 'resize', 0)
546 let resize = resize == 0 ? ll : min([ll, resize])
547 let resize = min([resize, (&lines * g:tlib_inputlist_pct / 100)])
548 " TLogVAR resize, ll, &lines
549 call self.Resize(resize, get(self, 'resize_vertical', 0))
551 let w = winwidth(0) - &fdc
552 " let w = winwidth(0) - &fdc - 1
553 let lines = copy(list)
554 let lines = map(lines, 'printf("%-'. w .'.'. w .'s", substitute(v:val, ''[[:cntrl:][:space:]]'', " ", "g"))')
556 call append(0, lines)
559 " TLogVAR self.prefidx
560 let base_pref = self.GetBaseIdx(self.prefidx)
562 if self.state =~ '\<redisplay\>'
563 call filter(b:tlibDisplayListMarks, 'index(self.sel_idx, v:val) == -1 && v:val != base_pref')
564 " TLogVAR b:tlibDisplayListMarks
565 call map(b:tlibDisplayListMarks, 'self.DisplayListMark(x, v:val, ":")')
566 " let b:tlibDisplayListMarks = map(copy(self.sel_idx), 'self.DisplayListMark(x, v:val, "#")')
567 " call add(b:tlibDisplayListMarks, self.prefidx)
568 " call self.DisplayListMark(x, self.GetBaseIdx(self.prefidx), '*')
570 let b:tlibDisplayListMarks = map(copy(self.sel_idx), 'self.DisplayListMark(x, v:val, "#")')
571 call add(b:tlibDisplayListMarks, base_pref)
572 call self.DisplayListMark(x, base_pref, '*')
573 call self.SetOffset()
574 " TLogVAR self.offset
575 " TLogDBG winheight('.')
576 " if self.prefidx > winheight(0)
577 " let lt = len(list) - winheight('.') + 1
578 " if self.offset > lt
579 " exec 'norm! '. lt .'zt'
581 exec 'norm! '. self.offset .'zt'
586 let rx0 = self.GetRx0()
588 if !empty(g:tlib_inputlist_higroup)
592 exec 'match '. g:tlib_inputlist_higroup .' /\c'. escape(rx0, '/') .'/'
598 call add(options, 's')
601 let query .= printf(' [%s]', join(options))
603 let &statusline = query
609 function! s:prototype.SetOffset() dict "{{{3
610 " TLogVAR self.prefidx, self.offset
611 " TLogDBG winheight(0)
612 " TLogDBG self.prefidx > self.offset + winheight(0) - 1
613 let listtop = len(self.list) - winheight(0) + 1
617 if self.prefidx > listtop
618 let self.offset = listtop
619 elseif self.prefidx > self.offset + winheight(0) - 1
620 let listoff = self.prefidx - winheight(0) + 1
621 let self.offset = min([listtop, listoff])
622 " TLogVAR self.prefidx
623 " TLogDBG len(self.list)
624 " TLogDBG winheight(0)
625 " TLogVAR listtop, listoff, self.offset
626 elseif self.prefidx < self.offset
627 let self.offset = self.prefidx
629 " TLogVAR self.offset
633 function! s:prototype.DisplayListMark(x, y, mark) dict "{{{3
634 " TLogVAR a:y, a:mark
635 if a:x > 0 && a:y >= 0
636 " TLogDBG a:x .'x'. a:y .' '. a:mark
637 let sy = self.GetListIdx(a:y) + 1
640 call setpos('.', [0, sy, a:x, 0])
641 exec 'norm! r'. a:mark
642 " exec 'norm! '. a:y .'gg'. a:x .'|r'. a:mark
649 function! s:prototype.SwitchWindow(where) dict "{{{3
650 let wnr = get(self, a:where.'_wnr')
652 return tlib#win#Set(wnr)
656 function! s:prototype.FollowCursor() dict "{{{3
657 if !empty(self.follow_cursor)
658 let back = self.SwitchWindow('win')
662 call call(self.follow_cursor, [self, [self.CurrentItem()]])
670 function! s:prototype.SetOrigin(...) dict "{{{3
671 TVarArg ['winview', 0]
672 let self.win_wnr = winnr()
673 let self.bufnr = bufnr('%')
674 let self.cursor = getpos('.')
676 let self.winview = tlib#win#GetLayout()
682 function! s:prototype.RestoreOrigin(...) dict "{{{3
683 TVarArg ['winview', 0]
685 call tlib#win#SetLayout(self.winview)
687 " TLogVAR self.win_wnr, self.bufnr, self.cursor
688 if self.win_wnr != winnr()
689 exec self.win_wnr .'wincmd w'
691 exec 'buffer! '. self.bufnr
692 call setpos('.', self.cursor)