root/dotfiles/vim/cho45/.vim/plugin/yankring.vim @ 7845

Revision 7845, 58.2 kB (checked in by cho45, 5 years ago)

/dotfiles/vim/cho45/.vim/doc/yankring.txt,
/dotfiles/vim/cho45/.vim/plugin/yankring.vim,
/dotfiles/vim/cho45/.vimrc,
/dotfiles/zsh/cho45/.zshrc:

YankRing? をいれました。
todo.pl を t にしてるので autocd がうざい。

Line 
1" yankring.vim - Yank / Delete Ring for Vim
2" ---------------------------------------------------------------
3" Version:  3.0
4" Authors:  David Fishburn <fishburn@ianywhere.com>
5" Last Modified: Fri 07 Sep 2007 11:20:49 PM Eastern Daylight Time
6" Script:   http://www.vim.org/scripts/script.php?script_id=1234
7" Based On: Mocked up version by Yegappan Lakshmanan
8"           http://groups.yahoo.com/group/vim/post?act=reply&messageNum=34406
9"  License: GPL (Gnu Public License)
10" GetLatestVimScripts: 1234 1 :AutoInstall: yankring.vim
11"
12" Perl integration with the clipboard:
13"     http://www.xav.com/perl/site/lib/Win32/Clipboard.html
14"
15
16if exists('loaded_yankring') || &cp
17    finish
18endif
19
20if v:version < 700
21  echomsg 'yankring: You need at least Vim 7.0'
22  finish
23endif
24
25let loaded_yankring = 30
26
27" Allow the user to override the # of yanks/deletes recorded
28if !exists('g:yankring_max_history')
29    let g:yankring_max_history = 100
30elseif g:yankring_max_history < 0
31    let g:yankring_max_history = 100
32endif
33
34" Allow the user to specify if the plugin is enabled or not
35if !exists('g:yankring_enabled')
36    let g:yankring_enabled = 1
37endif
38
39" Specify a separation character for the key maps
40if !exists('g:yankring_separator')
41    let g:yankring_separator = ','
42endif
43
44" Specify max display length for each element for YRShow
45if !exists('g:yankring_max_display')
46    let g:yankring_max_display = 0
47endif
48
49" Check if yankring should persist between Vim instances
50if !exists('g:yankring_persist')
51    let g:yankring_persist = 1
52endif
53
54" Specify whether the results of the ring should be displayed
55" in a separate buffer window instead of the use of echo
56if !exists('g:yankring_window_use_separate')
57    let g:yankring_window_use_separate = 1
58endif
59
60" Specifies whether the window is closed after an action
61" is performed
62if !exists('g:yankring_window_auto_close')
63    let g:yankring_window_auto_close = 1
64endif
65
66" When displaying the buffer, how many lines should it be
67if !exists('g:yankring_window_height')
68    let g:yankring_window_height = 8
69endif
70
71" When displaying the buffer, how many lines should it be
72if !exists('g:yankring_window_width')
73    let g:yankring_window_width = 30
74endif
75
76" When displaying the buffer, where it should be placed
77if !exists('g:yankring_window_use_horiz')
78    let g:yankring_window_use_horiz = 1
79endif
80
81" When displaying the buffer, where it should be placed
82if !exists('g:yankring_window_use_bottom')
83    let g:yankring_window_use_bottom = 1
84endif
85
86" When displaying the buffer, where it should be placed
87if !exists('g:yankring_window_use_right')
88    let g:yankring_window_use_right = 1
89endif
90
91" If the user presses <space>, toggle the width of the window
92if !exists('g:yankring_window_increment')
93    let g:yankring_window_increment = 50
94endif
95
96" Controls whether the . operator will repeat yank operations
97" The default is based on cpoptions: |cpo-y|
98"       y       A yank command can be redone with ".".
99if !exists('g:yankring_dot_repeat_yank')
100    let g:yankring_dot_repeat_yank = (&cpoptions=~'y'?1:0)
101endif
102
103" Only adds unique items to the yankring.
104" If the item already exists, that element is set as the
105" top of the yankring.
106if !exists('g:yankring_ignore_duplicate')
107    let g:yankring_ignore_duplicate = 1
108endif
109
110" Vim automatically manages the numbered registers:
111" 0   - last yanked text
112" 1-9 - last deleted items
113" If this option is turned on, the yankring will manage the
114" values in them.
115if !exists('g:yankring_manage_numbered_reg')
116    let g:yankring_manage_numbered_reg = 0
117endif
118
119" Allow the user to specify what characters to use for the mappings.
120if !exists('g:yankring_n_keys')
121    let g:yankring_n_keys = 'x,yy,dd,yw,dw,ye,de,yE,dE,yiw,diw,yaw,daw,y$,d$,Y,D,yG,dG,ygg,dgg'
122endif
123
124" Whether we sould map the . operator
125if !exists('g:yankring_map_dot')
126    let g:yankring_map_dot = 1
127endif
128
129" Whether we sould map the "g" paste operators
130if !exists('g:yankring_paste_using_g')
131    let g:yankring_paste_using_g = 1
132endif
133
134if !exists('g:yankring_v_key')
135    let g:yankring_v_key = 'y'
136endif
137
138if !exists('g:yankring_del_v_key')
139    let g:yankring_del_v_key = 'd'
140endif
141
142if !exists('g:yankring_paste_n_bkey')
143    let g:yankring_paste_n_bkey = 'P'
144endif
145
146if !exists('g:yankring_paste_n_akey')
147    let g:yankring_paste_n_akey = 'p'
148endif
149
150if !exists('g:yankring_paste_v_bkey')
151    let g:yankring_paste_v_bkey = 'P'
152endif
153
154if !exists('g:yankring_paste_v_akey')
155    let g:yankring_paste_v_akey = 'p'
156endif
157
158if !exists('g:yankring_replace_n_pkey')
159    let g:yankring_replace_n_pkey = '<C-P>'
160endif
161
162if !exists('g:yankring_replace_n_nkey')
163    let g:yankring_replace_n_nkey = '<C-N>'
164endif
165
166" Script variables for the yankring buffer
167let s:yr_buffer_name       = '__YankRing__'
168let s:yr_buffer_last_winnr = -1
169let s:yr_buffer_last       = -1
170let s:yr_buffer_id         = -1
171let s:yr_elements          = []
172let s:yr_element_type      = []
173let s:yr_search            = ""
174
175" Vim window size is changed by the yankring plugin or not
176let s:yankring_winsize_chgd = 0
177
178" If the we are persisting the yankring between Vim instances
179" set the scope to be global instead of script level.
180let s:yr_scope = (g:yankring_persist==1?'g':'s')
181
182" Enables or disables the yankring
183function! s:YRToggle(...)
184    " Default the current state to toggle
185    let new_state = ((g:yankring_enabled == 1) ? 0 : 1)
186
187    " Allow the user to specify if enabled
188    if a:0 > 0
189        let new_state = ((a:1 == 1) ? 1 : 0)
190    endif
191           
192    " YRToggle accepts an integer value to specify the state
193    if new_state == g:yankring_enabled
194        return
195    elseif new_state == 1
196        call YRMapsCreate()
197    else
198        call YRMapsDelete()
199    endif
200endfunction
201 
202
203" Enables or disables the yankring
204function! s:YRDisplayElem(disp_nbr, script_var)
205    if g:yankring_max_display == 0
206        if g:yankring_window_use_separate == 1
207            let max_display = 500
208        else
209            let max_display = g:yankring_window_width +
210                        \ g:yankring_window_increment -
211                        \ 12
212        endif
213    else
214        let max_display = g:yankring_max_display
215    endif
216
217    if exists(s:yr_scope.':YR_ELEM_'.a:script_var)
218        let length = strlen({s:yr_scope}:YR_ELEM_{a:script_var})
219        " Fancy trick to align them all regardless of how many
220        " digits the element # is
221        return a:disp_nbr.
222                    \ strtrans(
223                    \ strpart("      ",0,(6-strlen(a:disp_nbr+1))).
224                    \ (
225                    \ (length>max_display)?
226                    \ (strpart({s:yr_scope}:YR_ELEM_{a:script_var},0,max_display).
227                    \ '...'):
228                    \ ({s:yr_scope}:YR_ELEM_{a:script_var})
229                    \ )
230                    \ )
231    endif
232    return ""
233endfunction
234 
235
236" Enables or disables the yankring
237function! s:YRShow(...)
238    " If no parameter was provided assume the user wants to
239    " toggle the display.
240    let toggle = 1
241    if a:0 > 0
242        let toggle = matchstr(a:1, '\d\+')
243    endif
244
245    if toggle == 1
246        if bufwinnr(s:yr_buffer_id) > -1
247            exec bufwinnr(s:yr_buffer_id) . "wincmd w"
248            hide
249            return
250        endif
251    endif
252
253    " Reset the search string, since this is automatically called
254    " if the yankring window is open.  A previous search must be
255    " cleared since we do not want to show new items.  The user can
256    " always run the search again.
257    let s:yr_search = ""
258
259    " List is shown in order of replacement
260    " assuming using previous yanks
261    let output = "--- YankRing ---\n"
262    let output = output . "Elem  Content\n"
263
264    let disp_item_nr = 1
265    for elem in s:yr_elements
266        let output  = output . s:YRDisplayElem(disp_item_nr, elem) . "\n"
267        let disp_item_nr   += 1
268    endfor
269
270    if g:yankring_window_use_separate == 1
271        call s:YRWindowOpen(output)
272    else
273        echo output
274    endif
275endfunction
276 
277
278" Paste a certain item from the yankring
279" If no parameter is provided, this function becomes interactive.  It will
280" display the list (using YRShow) and allow the user to choose an element.
281function! s:YRGetElem(...)
282    if s:yr_count == 0
283        call s:YRWarningMsg('YR: yankring is empty')
284        return -1
285    endif
286
287    let default_buffer = ((&clipboard=='unnamed')?'*':'"')
288
289    let direction = 'p'
290    if a:0 > 1
291        " If the user indicated to paste above or below
292        " let direction = ((a:2 ==# 'P') ? 'P' : 'p')
293        if a:2 =~ '\(p\|gp\|P\|gP\)'
294            let direction = a:2
295        endif
296    endif
297
298    " Check to see if a specific value has been provided
299    let elem = 0
300    if a:0 > 0
301        " Ensure we get only the numeric value (trim it)
302        let elem = matchstr(a:1, '\d\+')
303    else
304        " If no parameter was supplied display the yankring
305        " and prompt the user to enter the value they want pasted.
306        call s:YRShow(0)
307
308        if g:yankring_window_use_separate == 1
309            " The window buffer is used instead of command line
310            return
311        endif
312
313        let elem = input("Enter # to paste:")
314
315        " Ensure we get only the numeric value (trim it)
316        let elem = matchstr(elem, '\d\+')
317
318        if elem == ''
319            " They most likely pressed enter without entering a value
320            return
321        endif
322    endif
323
324    if elem < 1 || elem > s:yr_count
325        call s:YRWarningMsg("YR: Invalid choice:".elem)
326        return -1
327    endif
328
329    if !exists(s:yr_scope.':YR_ELEM_'.elem)
330        call s:YRWarningMsg("YR: Elem:".elem." does not exist")
331        return -1
332    endif
333
334    let default_buffer = ((&clipboard=='unnamed')?'*':'"')
335    " let save_reg = getreg(default_buffer)
336    " let save_reg_type = getregtype(default_buffer)
337    call setreg(default_buffer
338                \ , s:YRGetValElemNbr((elem-1), 'v')
339                \ , s:YRGetValElemNbr((elem-1), 't')
340                \ )
341    exec "normal! ".direction
342    " call setreg(default_buffer, save_reg, save_reg_type)
343
344    " Set the previous action as a paste in case the user
345    " press . to repeat
346    call s:YRSetPrevOP('p', '', default_buffer)
347
348endfunction
349 
350
351" Starting the top of the ring it will paste x items from it
352function! s:YRGetMultiple(reverse_order, ...)
353    if s:yr_count == 0
354        call s:YRWarningMsg('YR: yankring is empty')
355        return
356    endif
357
358    " If the user provided a range, exit after that many
359    " have been displayed
360    let iter = 0
361    let elem = 0
362    if a:0 > 0
363        " If no yank command has been supplied, assume it is
364        " a full line yank
365        let iter = matchstr(a:1, '\d\+')
366    endif
367    if a:0 > 1
368        " If no yank command has been supplied, assume it is
369        " a full line yank
370        let elem = matchstr(a:2, '\d\+')
371    endif
372    if iter < 1
373        " The default to only 1 item if no argument is specified
374        let iter = 1
375    endif
376    if iter > s:yr_count
377        " Default to all items if they specified a very high value
378        let iter = s:yr_count
379    endif
380    if elem < 1 || elem > s:yr_count
381        " The default to only 1 item if no argument is specified
382        let elem = 1
383    endif
384
385    " Base the increment on the sort order of the results
386    let increment = ((a:reverse_order==0)?(1):(-1))
387
388    if a:reverse_order != 0
389        " If there are 5 elements in the ring
390        " User wants the top 3 in reverse order
391        " We need to set the starting element to 3, because 3,4,5
392        " Starting at the current element 5, we need to:
393        " 1 + (3 * -1 * 1)
394        " 1 + (-3)
395        " -2
396        " So start 2 elements below the current position
397        let elem = s:YRGetNextElem(elem, ((iter*-1*increment)-1) )
398    endif
399
400    while iter > 0
401        " Paste the first item, and move on to the next.
402        " digits the element # is
403        call s:YRGetElem(elem)
404        let elem = s:YRGetNextElem(elem, increment)
405        let iter = iter - 1
406    endwhile
407endfunction
408 
409
410" Given a regular expression, check each element within
411" the yankring, display only the matching items and prompt
412" the user for which item to paste
413function! s:YRSearch(...)
414    if s:yr_count == 0
415        call s:YRWarningMsg('YR: yankring is empty')
416        return
417    endif
418
419    let s:yr_search = ""
420    " If the user provided a range, exit after that many
421    " have been displayed
422    if a:0 == 0 || (a:0 == 1 && a:1 == "")
423        let s:yr_search = input('Enter [optional] regex:')
424    else
425        let s:yr_search = a:1
426    endif
427
428    if s:yr_search == ""
429        " Show the entire yankring
430        call s:YRShow(0)
431        return
432    endif
433
434    let disp_item_nr = 1
435
436    " List is shown in order of replacement
437    " assuming using previous yanks
438    let output        = "--- YankRing ---\n"
439    let output        = output . "Elem  Content\n"
440    let search_result = ""
441    let valid_choices = ','
442
443    for elem in s:yr_elements
444        let v:errmsg = ''
445        if exists(s:yr_scope.':YR_ELEM_'.elem)
446            if match({s:yr_scope}:YR_ELEM_{elem}, s:yr_search) > -1
447                let search_result = search_result . s:YRDisplayElem(disp_item_nr, elem) . "\n"
448                let valid_choices = valid_choices . disp_item_nr . ','
449            endif
450            if v:errmsg != ''
451                " If an error is report due to the regular expression
452                " abort the checks
453                return -1
454            endif
455        endif
456        let disp_item_nr += 1
457    endfor
458
459    if search_result == ""
460        let output = output . "Search for [".s:yr_search."] did not match any items "
461    else
462        let output = output . search_result
463    endif
464
465    if g:yankring_window_use_separate == 1
466        call s:YRWindowOpen(output)
467    else
468        if valid_choices != ','
469            let elem = input("Enter # to paste:")
470
471            " Ensure we get only the numeric value (trim it)
472            let elem = matchstr(elem, '\d\+')
473
474            if elem == ''
475                " They most likely pressed enter without entering a value
476                return
477            endif
478
479            if valid_choices =~ ','.elem.','
480                exec 'YRGetElem ' . elem
481            else
482                " User did not choose one of the elements that were found
483                " Remove leading ,
484                call s:YRWarningMsg( "YR: Only valid choices are:" .
485                            \ strpart(valid_choices, 1)
486                            \ )
487                return -1
488            endif
489
490        else
491            call s:YRWarningMsg( "YR: The pattern [" .
492                        \ s:yr_search .
493                        \ "] does not match any items in the yankring"
494                        \ )
495        endif
496    endif
497
498endfunction
499 
500
501" Resets the common script variables for managing the ring.
502function! s:YRReset()
503    let s:yr_next_idx              = 0
504    let s:yr_last_paste_idx        = 0
505    let s:yr_count                 = 0
506    let s:yr_paste_dir             = 'p'
507
508    " For the . op support
509    let s:yr_prev_op_code          = ''
510    let s:yr_prev_count            = ''
511    let s:yr_prev_reg              = ''
512    let s:yr_prev_reg_unnamed      = ''
513    let s:yr_prev_reg_small        = ''
514    let s:yr_prev_reg_insert       = ''
515    let s:yr_prev_reg_expres       = ''
516    let s:yr_prev_vis_lstart       = 0
517    let s:yr_prev_vis_lend         = 0
518    let s:yr_prev_vis_cstart       = 0
519    let s:yr_prev_vis_cend         = 0
520
521    " This is used to determine if the visual selection should be
522    " reset prior to issuing the YRReplace
523    let s:yr_prev_vis_mode         = 0
524endfunction
525 
526
527" Clears the yankring by simply setting the # of items in it to 0.
528" There is no need physically unlet each variable.
529function! s:YRInit()
530    call s:YRReset()
531
532    " This is the MRU list of items in the yankring
533    if !exists( s:yr_scope.':YR_ELEM_ORDER' )
534        let {s:yr_scope}:YR_ELEM_ORDER = ""
535    endif
536    call s:YRMRUInit( 's:yr_elements', g:yankring_max_history, {s:yr_scope}:YR_ELEM_ORDER )
537
538    let s:yr_count    = s:YRMRUSize('s:yr_elements')
539
540    " We can potentially keep creating new global variables forever
541    " since as the rings rotates through values, it will always be
542    " increasing.  Add some additional logic to:
543    "    a) Check for the current highest value in the ring
544    "       by sorting the list and adding 1
545    "    b) If the lowest value is larger than g:yankring_max_history,
546    "       set the next index item to be 1.
547    if !empty(s:yr_elements)
548        let sorted_mru    = sort(copy(s:yr_elements))
549        let s:yr_next_idx = get(sorted_mru, -1) + 1
550
551        if get(sorted_mru, 1) > g:yankring_max_history
552            let s:yr_next_idx = 0
553        endif
554    endif
555endfunction
556 
557
558" Clears the yankring by simply setting the # of items in it to 0.
559" There is no need physically unlet each variable.
560function! s:YRClear()
561    call s:YRReset()
562
563    " This is the MRU list of items in the yankring
564    call s:YRMRUReset( 's:yr_elements' )
565endfunction
566 
567
568" Determine which register the user wants to use
569" For example the 'a' register:  "ayy
570function! s:YRRegister()
571    let user_register = v:register
572    if &clipboard == 'unnamed' && user_register == '"'
573        let user_register = '*'
574    endif
575    return user_register
576endfunction
577
578
579" Allows you to push a new item on the yankring.  Useful if something
580" is in the clipboard and you want to add it to the yankring.
581" Or if you yank something that is not mapped.
582function! s:YRPush(...)
583    let user_register = s:YRRegister()
584
585    if a:0 > 0
586        " If no yank command has been supplied, assume it is
587        " a full line yank
588        let user_register = ((a:1 == '') ? user_register : a:1)
589    endif
590
591    " If we are pushing something on to the yankring, add it to
592    " the default buffer as well so the next item pasted will
593    " be the item pushed
594    let default_buffer = ((&clipboard=='unnamed')?'*':'"')
595    call setreg(default_buffer, getreg(user_register),
596                \ getregtype(user_register))
597
598    call s:YRSetPrevOP('', '', '')
599    call s:YRRecord(user_register)
600endfunction
601
602
603" Allows you to pop off any element from the yankring.
604" If no parameters are provided the first element is removed.
605" If a vcount is provided, that many elements are removed
606" from the top.
607function! s:YRPop(...)
608    if s:yr_count == 0
609        call s:YRWarningMsg('YR: yankring is empty')
610        return
611    endif
612
613    let v_count = 1
614    if a:0 > 1
615        let v_count = a:2
616    endif
617
618    " If the user provided a parameter, remove that element
619    " from the yankring. 
620    " If no parameter was provided assume the first element.
621    let elem = 1
622    if a:0 > 0
623        " Get the element # from the parameter
624        let elem = matchstr(a:1, '\d\+')
625    endif
626   
627    " If the user entered a count, then remove that many
628    " elements from the ring.
629    while v_count > 0
630        call s:YRMRUDel('s:yr_elements', elem)
631        let v_count = v_count - 1
632    endwhile
633
634    " If the yankring window is open, refresh it
635    call s:YRWindowUpdate()
636endfunction
637
638
639" Adds this value to the yankring.
640function! s:YRRecord(value)
641
642    " Add item to list
643    " This will also account for duplicates.
644    call s:YRMRUAdd( 's:yr_elements'
645                \ , getreg(a:value)
646                \ , getregtype(a:value)
647                \ )
648
649    " If the yankring window is open, refresh it
650    call s:YRWindowUpdate()
651
652    " Manage the numbered registers
653    if g:yankring_manage_numbered_reg == 1
654        call s:YRSetNumberedReg()
655    endif
656endfunction
657
658
659" Record the operation for the dot operator
660function! s:YRSetPrevOP(op_code, count, reg)
661    let s:yr_prev_op_code     = a:op_code
662    let s:yr_prev_count       = a:count
663    let s:yr_prev_reg         = a:reg
664    let s:yr_prev_reg_unnamed = getreg('"')
665    let s:yr_prev_reg_small   = getreg('-')
666    let s:yr_prev_reg_insert  = getreg('.')
667    let s:yr_prev_vis_lstart  = line("'<")
668    let s:yr_prev_vis_lend    = line("'>")
669    let s:yr_prev_vis_cstart  = col("'<")
670    let s:yr_prev_vis_cend    = col("'>")
671    let s:yr_prev_chg_lstart  = line("'[")
672    let s:yr_prev_chg_lend    = line("']")
673    let s:yr_prev_chg_cstart  = col("'[")
674    let s:yr_prev_chg_cend    = col("']")
675    let s:yr_prev_reg_expres  = histget('=', -1)
676
677    " If storing the last change position (using '[, '])
678    " is not good enough, then another option is to:
679    " Use :redir on the :changes command
680    " and grab the last item.  Store this value
681    " and compare it is YRDoRepeat.
682    "
683endfunction
684
685
686" Adds this value to the yankring.
687function! s:YRDoRepeat()
688    let dorepeat = 0
689
690    if g:yankring_manage_numbered_reg == 1
691        " When resetting the numbered register we are
692        " must ignore the comparision of the " register.
693        if s:yr_prev_reg_small  == getreg('-') &&
694                    \ s:yr_prev_reg_insert == getreg('.') &&
695                    \ s:yr_prev_reg_expres == histget('=', -1) &&
696                    \ s:yr_prev_vis_lstart == line("'<") &&
697                    \ s:yr_prev_vis_lend   == line("'>") &&
698                    \ s:yr_prev_vis_cstart == col("'<") &&
699                    \ s:yr_prev_vis_cend   == col("'>") &&
700                    \ s:yr_prev_chg_lstart == line("'[") &&
701                    \ s:yr_prev_chg_lend   == line("']") &&
702                    \ s:yr_prev_chg_cstart == col("'[") &&
703                    \ s:yr_prev_chg_cend   == col("']")
704            let dorepeat = 1
705        endif
706    else
707        " Check the previously recorded value of the registers
708        " if they are the same, we need to reissue the previous
709        " yankring command.
710        " If any are different, the user performed a command
711        " command that did not involve the yankring, therefore
712        " we should just issue the standard "normal! ." to repeat it.
713        if s:yr_prev_reg_unnamed == getreg('"') &&
714                    \ s:yr_prev_reg_small  == getreg('-') &&
715                    \ s:yr_prev_reg_insert == getreg('.') &&
716                    \ s:yr_prev_reg_expres == histget('=', -1) &&
717                    \ s:yr_prev_vis_lstart == line("'<") &&
718                    \ s:yr_prev_vis_lend   == line("'>") &&
719                    \ s:yr_prev_vis_cstart == col("'<") &&
720                    \ s:yr_prev_vis_cend   == col("'>") &&
721                    \ s:yr_prev_chg_lstart == line("'[") &&
722                    \ s:yr_prev_chg_lend   == line("']") &&
723                    \ s:yr_prev_chg_cstart == col("'[") &&
724                    \ s:yr_prev_chg_cend   == col("']")
725            let dorepeat = 1
726        endif
727    endif
728    " If we are going to repeat check to see if the
729    " previous command was a yank operation.  If so determine
730    " if yank operations are allowed to be repeated.
731    if dorepeat == 1 && s:yr_prev_op_code =~ '^y'
732        " This value be default is set based on cpoptions.
733        if g:yankring_dot_repeat_yank == 0
734            let dorepeat = 0
735        endif
736    endif
737    return dorepeat
738endfunction
739
740
741" Manages the Vim's numbered registers
742function! s:YRSetNumberedReg()
743
744    let i = 1
745
746    while i <= 10
747        if i > s:yr_count
748            break
749        endif
750
751        call setreg( (i-1)
752                    \ , s:YRGetValElemNbr((i-1),'v')
753                    \ , s:YRGetValElemNbr((i-1),'t')
754                    \ )
755        let i += 1
756    endwhile
757endfunction
758
759
760" This internal function will add and subtract values from a starting
761" point and return the correct element number.  It takes into account
762" the circular nature of the yankring.
763function! s:YRGetNextElem(start, iter)
764
765    let needed_elem = a:start + a:iter
766
767    " The yankring is a ring, so if an element is
768    " requested beyond the number of elements, we
769    " must wrap around the ring.
770    if needed_elem > s:yr_count
771        let needed_elem = needed_elem % s:yr_count
772    endif
773
774    if needed_elem == 0
775        " Can happen at the end or beginning of the ring
776        if a:iter == -1
777            " Wrap to the bottom of the ring
778            let needed_elem = s:yr_count
779        else
780            " Wrap to the top of the ring
781            let needed_elem = 1
782        endif
783    elseif needed_elem < 1
784        " As we step backwards through the ring we could ask for a negative
785        " value, this will wrap it around to the end
786        let needed_elem = s:yr_count
787    endif
788
789    return needed_elem
790
791endfunction
792
793
794" Lets Vim natively perform the operation and then stores what
795" was yanked (or deleted) into the yankring.
796" Supports this for example -   5"ayy
797function! s:YRYankCount(...) range
798
799    let user_register = s:YRRegister()
800    let v_count = v:count
801
802    " Default yank command to the entire line
803    let op_code = 'yy'
804    if a:0 > 0
805        " If no yank command has been supplied, assume it is
806        " a full line yank
807        let op_code = ((a:1 == '') ? op_code : a:1)
808    endif
809
810    if op_code == '.'
811        if s:YRDoRepeat() == 1
812            if s:yr_prev_op_code != ''
813                let op_code       = s:yr_prev_op_code
814                let v_count       = s:yr_prev_count
815                let user_register = s:yr_prev_reg
816            endif
817        else
818            exec "normal! ."
819            return
820        endif
821    endif
822
823    " Supports this for example -   5"ayy
824    " A delete operation will still place the items in the
825    " default registers as well as the named register
826    exec "normal! ".
827                \ ((v_count > 0)?(v_count):'').
828                \ (user_register=='"'?'':'"'.user_register).
829                \ op_code
830
831    if user_register == '_'
832        " Black hole register, ignore
833        return
834    endif
835   
836    call s:YRSetPrevOP(op_code, v_count, user_register)
837
838    call s:YRRecord(user_register)
839endfunction
840 
841
842" Handles ranges.  There are visual ranges and command line ranges.
843" Visual ranges are easy, since we pass through and let Vim deal
844" with those directly.
845" Command line ranges means we must yank the entire line, and not
846" just a portion of it.
847function! s:YRYankRange(do_delete_selection, ...) range
848
849    let user_register  = s:YRRegister()
850    let default_buffer = ((&clipboard=='unnamed')?'*':'"')
851
852    " Default command mode to normal mode 'n'
853    let cmd_mode = 'n'
854    if a:0 > 0
855        " Change to visual mode, if command executed via
856        " a visual map
857        let cmd_mode = ((a:1 == 'v') ? 'v' : 'n')
858    endif
859
860    if cmd_mode == 'v'
861        " We are yanking either an entire line, or a range
862        exec "normal! gv".
863                    \ (user_register==default_buffer?'':'"'.user_register).
864                    \ 'y'
865        if a:do_delete_selection == 1
866            exec "normal! gv".
867                        \ (user_register==default_buffer?'':'"'.user_register).
868                        \ 'd'
869        endif
870    else
871        " In normal mode, always yank the complete line, since this
872        " command is for a range.  YRYankCount is used for parts
873        " of a single line
874        if a:do_delete_selection == 1
875            exec a:firstline . ',' . a:lastline . 'delete '.user_register
876        else
877            exec a:firstline . ',' . a:lastline . 'yank ' . user_register
878        endif
879    endif
880
881    if user_register == '_'
882        " Black hole register, ignore
883        return
884    endif
885   
886    call s:YRSetPrevOP('', '', user_register)
887    call s:YRRecord(user_register)
888endfunction
889 
890
891" Paste from either the yankring or from a specified register
892" Optionally a count can be provided, so paste the same value 10 times
893function! s:YRPaste(replace_last_paste_selection, nextvalue, direction, ...)
894    " Disabling the yankring removes the default maps.
895    " But there are some maps the user can create on their own, and
896    " these would most likely call this function.  So place an extra
897    " check and display a message.
898    if g:yankring_enabled == 0
899        call s:YRWarningMsg(
900                    \ 'YR: The yankring is currently disabled, use YRToggle.'
901                    \ )
902        return
903    endif
904   
905    let user_register  = s:YRRegister()
906    let default_buffer = ((&clipboard == 'unnamed')?'*':'"')
907    let v_count        = v:count
908
909    " Default command mode to normal mode 'n'
910    let cmd_mode = 'n'
911    if a:0 > 0
912        " Change to visual mode, if command executed via
913        " a visual map
914        let cmd_mode = ((a:1 == 'v') ? 'v' : 'n')
915    endif
916
917    " User has decided to bypass the yankring and specify a specific
918    " register
919    if user_register != default_buffer
920        if a:replace_last_paste_selection == 1
921            echomsg 'YR: A register cannot be specified in replace mode'
922            return
923        else
924            " Check for the expression register, in this special case
925            " we must copy it's content into the default buffer and paste
926            if user_register == '='
927                let user_register = ''
928                call setreg(default_buffer, histget('=', -1) )
929            else
930                let user_register = '"'.user_register
931            endif
932            exec "normal! ".
933                        \ ((cmd_mode=='n') ? "" : "gv").
934                        \ ((v_count > 0)?(v_count):'').
935                        \ user_register.
936                        \ a:direction
937            " In this case, we have bypassed the yankring
938            " If the user hits next or previous we want the
939            " next item pasted to be the top of the yankring.
940            let s:yr_last_paste_idx = 0
941        endif
942        let s:yr_paste_dir     = a:direction
943        let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1)
944        return
945    endif
946
947    " Try to second guess the user to make these mappings less intrusive.
948    " If the user hits paste, compare the contents of the paste register
949    " to the current entry in the yankring.  If they are different, lets
950    " assume the user wants the contents of the paste register.
951    " So if they pressed [yt ] (yank to space) and hit paste, the yankring
952    " would not have the word in it, so assume they want the word pasted.
953    if a:replace_last_paste_selection != 1
954        if s:yr_count > 0
955            if getreg(default_buffer) != s:YRGetValElemNbr(0,'v')
956                " The user has performed a yank / delete operation
957                " outside of the yankring maps.  First, add this
958                " value to the yankring.
959                call s:YRRecord(default_buffer)
960                " Now, use the most recently yanked text, rather than the
961                " value from the yankring.
962                exec "normal! ".
963                            \ ((cmd_mode=='n') ? "" : "gv").
964                            \ ((v_count > 0)?(v_count):'').
965                            \ a:direction
966                let s:yr_paste_dir     = a:direction
967                let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1)
968
969                " In this case, we have bypassed the yankring
970                " If the user hits next or previous we want the
971                " next item pasted to be the top of the yankring.
972                let s:yr_last_paste_idx = 0
973                return
974            endif
975        else
976            exec "normal! ".
977                        \ ((cmd_mode=='n') ? "" : "gv").
978                        \ ((v_count > 0)?(v_count):'').
979                        \ a:direction
980            let s:yr_paste_dir     = a:direction
981            let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1)
982            return
983        endif
984    endif
985
986    if s:yr_count == 0
987        echomsg 'YR: yankring is empty'
988        " Nothing to paste
989        return
990    endif
991
992    if a:replace_last_paste_selection == 1
993        " Replacing the previous put
994        let start = line("'[")
995        let end = line("']")
996
997        if start != line('.')
998            echomsg 'YR: You must paste text first, before you can replace'
999            return
1000        endif
1001
1002        if start == 0 || end == 0
1003            return
1004        endif
1005
1006        " If a count was provided (ie 5<C-P>), multiply the
1007        " nextvalue accordingly and position the next paste index
1008        let which_elem = a:nextvalue * ((v_count > 0)?(v_count):1) * -1
1009        let s:yr_last_paste_idx = s:YRGetNextElem(
1010                    \ s:yr_last_paste_idx, which_elem
1011                    \ )
1012
1013        let save_reg            = getreg(default_buffer)
1014        let save_reg_type       = getregtype(default_buffer)
1015        call setreg( default_buffer
1016                    \ , s:YRGetValElemNbr((s:yr_last_paste_idx-1),'v')
1017                    \ , s:YRGetValElemNbr((s:yr_last_paste_idx-1),'t')
1018                    \ )
1019
1020        " First undo the previous paste
1021        exec "normal! u"
1022        " Check if the visual selection should be reselected
1023        " Next paste the correct item from the ring
1024        " This is done as separate statements since it appeared that if
1025        " there was nothing to undo, the paste never happened.
1026        exec "normal! ".
1027                    \ ((s:yr_prev_vis_mode==0) ? "" : "gv").
1028                    \ s:yr_paste_dir
1029        call setreg(default_buffer, save_reg, save_reg_type)
1030        call s:YRSetPrevOP('', '', '')
1031    else
1032        " User hit p or P
1033        " Supports this for example -   5"ayy
1034        " And restores the current register
1035        let save_reg            = getreg(default_buffer)
1036        let save_reg_type       = getregtype(default_buffer)
1037        let s:yr_last_paste_idx = 1
1038        call setreg(default_buffer
1039                    \ , s:YRGetValElemNbr(0,'v')
1040                    \ , s:YRGetValElemNbr(0,'t')
1041                    \ )
1042        exec "normal! ".
1043                    \ ((cmd_mode=='n') ? "" : "gv").
1044                    \ (
1045                    \ ((v_count > 0)?(v_count):'').
1046                    \ a:direction
1047                    \ )
1048        call setreg(default_buffer, save_reg, save_reg_type)
1049        call s:YRSetPrevOP(
1050                    \ a:direction
1051                    \ , v_count
1052                    \ , default_buffer)
1053        let s:yr_paste_dir     = a:direction
1054        let s:yr_prev_vis_mode = ((cmd_mode=='n') ? 0 : 1)
1055    endif
1056
1057endfunction
1058 
1059
1060" Create the default maps
1061function! YRMapsCreate()
1062
1063    " Iterate through a comma separated list of mappings and create
1064    " calls to the YRYankCount function
1065    if g:yankring_n_keys != ''
1066        let index = 0
1067        while index > -1
1068            " Retrieve the keystrokes for the mappings
1069            let sep_end = match(g:yankring_n_keys, g:yankring_separator, index)
1070            if sep_end > 0
1071                let cmd = strpart(g:yankring_n_keys, index, (sep_end - index))
1072            else
1073                let cmd = strpart(g:yankring_n_keys, index)
1074            endif
1075            " Creating the mapping and pass the key strokes into the
1076            " YRYankCount function so it knows how to replay the same
1077            " command
1078            if strlen(cmd) > 0
1079                exec 'nnoremap <silent>'.cmd." :<C-U>YRYankCount '".cmd."'<CR>"
1080            endif
1081            " Move onto the next entry in the comma separated list
1082            let index = index + strlen(cmd) + strlen(g:yankring_separator)
1083            if index >= strlen(g:yankring_n_keys)
1084                break
1085            endif
1086        endwhile
1087    endif
1088    if g:yankring_map_dot == 1
1089        exec "nnoremap <silent> .  :<C-U>YRYankCount '.'<CR>"
1090    endif
1091    if g:yankring_v_key != ''
1092        exec 'vnoremap <silent>'.g:yankring_v_key." :YRYankRange 'v'<CR>"
1093    endif
1094    if g:yankring_del_v_key != ''
1095        exec 'vnoremap <silent>'.g:yankring_del_v_key." :YRDeleteRange 'v'<CR>"
1096    endif
1097    if g:yankring_paste_n_bkey != ''
1098        exec 'nnoremap <silent>'.g:yankring_paste_n_bkey." :<C-U>YRPaste 'P'<CR>"
1099        if g:yankring_paste_using_g == 1
1100            exec 'nnoremap <silent> g'.g:yankring_paste_n_bkey." :<C-U>YRPaste 'gP'<CR>"
1101        endif
1102    endif
1103    if g:yankring_paste_n_akey != ''
1104        exec 'nnoremap <silent>'.g:yankring_paste_n_akey." :<C-U>YRPaste 'p'<CR>"
1105        if g:yankring_paste_using_g == 1
1106            exec 'nnoremap <silent> g'.g:yankring_paste_n_akey." :<C-U>YRPaste 'gp'<CR>"
1107        endif
1108    endif
1109    if g:yankring_paste_v_bkey != ''
1110        exec 'vnoremap <silent>'.g:yankring_paste_v_bkey." :<C-U>YRPaste 'P', 'v'<CR>"
1111    endif
1112    if g:yankring_paste_v_akey != ''
1113        exec 'vnoremap <silent>'.g:yankring_paste_v_akey." :<C-U>YRPaste 'p', 'v'<CR>"
1114    endif
1115    if g:yankring_replace_n_pkey != ''
1116        exec 'nnoremap <silent>'.g:yankring_replace_n_pkey." :<C-U>YRReplace '-1', 'P'<CR>"
1117    endif
1118    if g:yankring_replace_n_nkey != ''
1119        exec 'nnoremap <silent>'.g:yankring_replace_n_nkey." :<C-U>YRReplace '1', 'p'<CR>"
1120    endif
1121
1122    let g:yankring_enabled = 1
1123endfunction
1124 
1125
1126" Create the default maps
1127function! YRMapsDelete()
1128
1129    " Iterate through a comma separated list of mappings and create
1130    " calls to the YRYankCount function
1131    if g:yankring_n_keys != ''
1132        let index = 0
1133        while index > -1
1134            " Retrieve the keystrokes for the mappings
1135            let sep_end = match(g:yankring_n_keys, g:yankring_separator, index)
1136            if sep_end > 0
1137                let cmd = strpart(g:yankring_n_keys, index, (sep_end - index))
1138            else
1139                let cmd = strpart(g:yankring_n_keys, index)
1140            endif
1141            " Creating the mapping and pass the key strokes into the
1142            " YRYankCount function so it knows how to replay the same
1143            " command
1144            if strlen(cmd) > 0
1145                exec 'nunmap '.cmd
1146            endif
1147            " Move onto the next entry in the comma separated list
1148            let index = index + strlen(cmd) + strlen(g:yankring_separator)
1149            if index >= strlen(g:yankring_n_keys)
1150                break
1151            endif
1152        endwhile
1153    endif
1154    if g:yankring_map_dot == 1
1155        exec "nunmap ."
1156    endif
1157    if g:yankring_v_key != ''
1158        exec 'vunmap '.g:yankring_v_key
1159    endif
1160    if g:yankring_del_v_key != ''
1161        exec 'vunmap '.g:yankring_del_v_key
1162    endif
1163    if g:yankring_paste_n_bkey != ''
1164        exec 'nunmap '.g:yankring_paste_n_bkey
1165        if g:yankring_paste_using_g == 1
1166            exec 'nunmap g'.g:yankring_paste_n_bkey
1167        endif
1168    endif
1169    if g:yankring_paste_n_akey != ''
1170        exec 'nunmap '.g:yankring_paste_n_akey
1171        if g:yankring_paste_using_g == 1
1172            exec 'nunmap g'.g:yankring_paste_n_akey
1173        endif
1174    endif
1175    if g:yankring_paste_v_bkey != ''
1176        exec 'vunmap '.g:yankring_paste_v_bkey
1177    endif
1178    if g:yankring_paste_v_akey != ''
1179        exec 'vunmap '.g:yankring_paste_v_akey
1180    endif
1181    if g:yankring_replace_n_pkey != ''
1182        exec 'nunmap '.g:yankring_replace_n_pkey
1183    endif
1184    if g:yankring_replace_n_nkey != ''
1185        exec 'nunmap '.g:yankring_replace_n_nkey
1186    endif
1187
1188    let g:yankring_enabled = 0
1189endfunction
1190
1191function! s:YRGetValElemNbr( position, type )
1192
1193    let needed_elem = a:position
1194
1195    if needed_elem > s:yr_count
1196        " The yankring is a ring, so if an element is
1197        " requested beyond the number of elements, we
1198        " must wrap around the ring.
1199        let needed_elem = needed_elem % s:yr_count
1200    endif
1201
1202    if needed_elem < 0
1203        " The yankring is a ring, so if an element is
1204        " requested beyond the number of elements, we
1205        " must wrap around the ring.
1206        " let needed_elem = s:yr_count + needed_elem + 1
1207        let needed_elem = s:yr_count + needed_elem
1208    endif
1209
1210    " The MRU stores the *order* of the items in the
1211    " yankring, not the value.  These are stored within
1212    " script variables.
1213    let elem = get(s:yr_elements, needed_elem)
1214
1215    if elem >= 0
1216        if a:type == 't'
1217            if exists(s:yr_scope.':YR_ELEM_TYPE_'.elem)
1218                return {s:yr_scope}:YR_ELEM_TYPE_{elem}
1219            endif
1220        else
1221            if exists(s:yr_scope.':YR_ELEM_'.elem)
1222                return {s:yr_scope}:YR_ELEM_{elem}
1223            endif
1224        endif
1225    else
1226        return -1
1227    endif
1228
1229    return ""
1230endfunction
1231
1232function! s:YRMRUInit( mru_list, max_size, initial_values )
1233
1234    " Create the list if required
1235    if empty({a:mru_list}) && a:initial_values != ''
1236        let {a:mru_list} = split(a:initial_values, ',')
1237    endif
1238
1239    if len({a:mru_list}) > a:max_size
1240        " Ensure list does not exceed maximum size
1241        call remove({a:mru_list}, a:max_size, -1)
1242    endif
1243
1244    call s:YRMRUExport(a:mru_list)
1245
1246    return 0
1247endfunction
1248
1249function! s:YRMRUReset( mru_list )
1250
1251    let {a:mru_list} = []
1252
1253    call s:YRMRUExport(a:mru_list)
1254
1255    return 1
1256endfunction
1257
1258function! s:YRMRUSize( mru_list )
1259    return len({a:mru_list})
1260endfunction
1261
1262function! s:YRMRUHas( mru_list, find_str )
1263    " This function will find a string and return the element #
1264    let find_idx = index({a:mru_list}, a:find_str)
1265
1266    return find_idx
1267endfunction
1268
1269function! s:YRMRUGet( mru_list, position )
1270    " This function will return the value of the item at a:position
1271    " Find the value of one element
1272    let value = get({a:mru_list}, a:position, -2)
1273
1274    return value
1275endfunction
1276
1277function! s:YRMRUAdd( mru_list, element, element_type )
1278    " Only add new items if they do not already exist in the MRU.
1279    " If the item is found, move it to the start of the MRU.
1280    let found      = -1
1281    let elem       = ''
1282    let elem_index = 0
1283    for elem_item in {a:mru_list}
1284        let elem = s:YRGetValElemNbr(elem_index, 'v')
1285        " If the item has been found, move it to the start of the list
1286        " unless of course it is already at the start
1287        if elem == a:element && elem_item > 0
1288            call remove({a:mru_list}, elem_index)
1289            call insert({a:mru_list}, elem_item)
1290            let found = elem_index
1291            break
1292        endif
1293        let elem_index += 1
1294    endfor
1295
1296    if found == -1
1297        let {s:yr_scope}:YR_ELEM_{s:yr_next_idx}       = a:element
1298        let {s:yr_scope}:YR_ELEM_TYPE_{s:yr_next_idx}  = a:element_type
1299        let elem_index                                 = s:yr_next_idx
1300        let s:yr_next_idx                             += 1
1301        call insert({a:mru_list}, elem_index)
1302    endif
1303
1304    " Allow (retain) only g:yankring_max_history in the MRU list.
1305    " Remove/discard the remaining entries. As we are adding a one entry to
1306    " the list, the list should have only g:yankring_max_history - 1 in it.
1307    let curr_cnt = s:YRMRUSize(a:mru_list)
1308
1309    if curr_cnt > g:yankring_max_history
1310        " Since the yankring is full, set the s:yr_next_idx
1311        " to the last item in the MRU list
1312        " Return -1 if this fails
1313        let s:yr_next_idx = get({a:mru_list}, -1, -1)
1314        call remove({a:mru_list}, -1)
1315
1316        if s:yr_next_idx == -1
1317            call s:YRErrorMsg(
1318                        \ 'YRMRUAdd: Last element not found: '.
1319                        \ {a:mru_list}.':'.
1320                        \ {a:mru_list}
1321                        \ )
1322        endif
1323    endif
1324
1325    let s:yr_count = s:YRMRUSize(a:mru_list)
1326
1327    call s:YRMRUExport(a:mru_list)
1328
1329    return 1
1330endfunction
1331
1332function! s:YRMRUDel( mru_list, elem_nbr )
1333
1334    " This regex determines how many elements to keep
1335    " at the front of the yankring
1336    call remove({a:mru_list}, a:elem_nbr)
1337
1338    let s:yr_count = s:YRMRUSize(a:mru_list)
1339
1340    call s:YRMRUExport(a:mru_list)
1341
1342    return 1
1343endfunction
1344
1345function! s:YRMRUExport( mru_list )
1346    " Export the List into the variable.  If this is a global variable
1347    " a comma separated list is created as a global variable so that
1348    " it can be saved within the viminfo file.
1349    if s:yr_scope == 'g'
1350        let {s:yr_scope}:YR_ELEM_ORDER = join({a:mru_list}, ',')
1351    endif
1352endfunction
1353
1354" YRWindowUpdate
1355" Checks if the yankring window is already open.
1356" If it is, it will refresh it.
1357function! s:YRWindowUpdate()
1358    let orig_win_bufnr = bufwinnr('%')
1359
1360    " Switch to the yankring buffer
1361    " only if it is already visible
1362    if bufwinnr(s:yr_buffer_id) != -1
1363        call s:YRShow(0)
1364        " Switch back to the original buffer
1365        exec orig_win_bufnr . "wincmd w"
1366    endif
1367endfunction
1368
1369" YRWindowStatus
1370" Displays a brief command list and option settings.
1371" It also will toggle the Help text.
1372function! s:YRWindowStatus(show_help)
1373
1374    let orig_win_bufnr = bufwinnr('%')
1375    let yr_win_bufnr   = bufwinnr(s:yr_buffer_id)
1376
1377    if yr_win_bufnr == -1
1378        " Do not update the window status since the
1379        " yankring is not currently displayed.
1380        return ""
1381    endif
1382    " Switch to the yankring buffer
1383    if orig_win_bufnr != yr_win_bufnr
1384        " If the buffer is visible, switch to it
1385        exec yr_win_bufnr . "wincmd w"
1386    endif
1387
1388    let msg = 'AutoClose='.g:yankring_window_auto_close.
1389                \ ';Cmds:<enter>,[g]p,[p]P,d,r,a,u,q,<space>;Help=?'.
1390                \ (s:yr_search==""?"":';SearchRegEx='.s:yr_search)
1391
1392    " Toggle help by checking the first line of the buffer
1393    if a:show_help == 1 && getline(1) !~ 'selection'
1394        let msg =
1395                    \ '" <enter>      : [p]aste selection'."\n".
1396                    \ '" double-click : [p]aste selection'."\n".
1397                    \ '" [g]p         : [g][p]aste selection'."\n".
1398                    \ '" [g]P         : [g][P]aste selection'."\n".
1399                    \ '" r            : [p]aste selection in reverse order'."\n".
1400                    \ '" u            : update display'."\n".
1401                    \ '" a            : toggle autoclose setting'."\n".
1402                    \ '" q            : Close the yankring window'."\n".
1403                    \ '" ?            : Remove help text'."\n".
1404                    \ '" <space>      : toggles the width of the window'."\n".
1405                    \ '" Visual mode is supported for above commands'."\n".
1406                    \ msg
1407    endif
1408
1409    let saveMod = &modifiable
1410
1411    " Go to the top of the buffer and remove any previous status
1412    " Use the blackhole register so it does not affect the yankring
1413    setlocal modifiable
1414    exec 0
1415    silent! exec 'norm! "_d/^---'."\n"
1416    call histdel("search", -1)
1417
1418    silent! 0put =msg
1419
1420    let &modifiable = saveMod
1421
1422    if orig_win_bufnr != s:yr_buffer_id
1423        exec orig_win_bufnr . "wincmd w"
1424    endif
1425endfunction
1426
1427" YRWindowOpen
1428" Display the Most Recently Used file list in a temporary window.
1429function! s:YRWindowOpen(results)
1430
1431    " Setup the cpoptions properly for the maps to work
1432    let old_cpoptions = &cpoptions
1433    set cpoptions&vim
1434    setlocal cpoptions-=a,A
1435
1436    " Save the current buffer number. The yankring will switch back to
1437    " this buffer when an action is taken.
1438    let s:yr_buffer_last       = bufnr('%')
1439    let s:yr_buffer_last_winnr = winnr()
1440
1441    if bufwinnr(s:yr_buffer_id) == -1
1442
1443        if g:yankring_window_use_horiz == 1
1444            if g:yankring_window_use_bottom == 1
1445                let location = 'botright'
1446            else
1447                let location = 'topleft'
1448            endif
1449            let win_size = g:yankring_window_height
1450        else
1451            " Open a horizontally split window. Increase the window size, if
1452            " needed, to accomodate the new window
1453            if g:yankring_window_width &&
1454                        \ &columns < (80 + g:yankring_window_width)
1455                " one extra column is needed to include the vertical split
1456                let &columns             = &columns + g:yankring_window_width + 1
1457                let s:yankring_winsize_chgd = 1
1458            else
1459                let s:yankring_winsize_chgd = 0
1460            endif
1461
1462            if g:yankring_window_use_right == 1
1463                " Open the window at the rightmost place
1464                let location = 'botright vertical'
1465            else
1466                " Open the window at the leftmost place
1467                let location = 'topleft vertical'
1468            endif
1469            let win_size = g:yankring_window_width
1470        endif
1471
1472        " Special consideration was involved with these sequence
1473        " of commands. 
1474        "     First, split the current buffer.
1475        "     Second, edit a new file.
1476        "     Third record the buffer number.
1477        " If a different sequence is followed when the yankring
1478        " buffer is closed, Vim's alternate buffer is the yanking
1479        " instead of the original buffer before the yankring
1480        " was shown.
1481        silent exec location. ' ' . win_size . 'split '
1482        " Using :e and hide prevents the alternate buffer
1483        " from being changed.
1484        exec ":e " . escape(s:yr_buffer_name, ' ')
1485        " Save buffer id
1486        let s:yr_buffer_id = bufnr('%')
1487    else
1488        " If the buffer is visible, switch to it
1489        exec bufwinnr(s:yr_buffer_name) . "wincmd w"
1490    endif
1491
1492    " Mark the buffer as scratch
1493    setlocal buftype=nofile
1494    setlocal bufhidden=hide
1495    setlocal noswapfile
1496    setlocal nowrap
1497    setlocal nonumber
1498    setlocal nobuflisted
1499    setlocal modifiable
1500
1501    " Clear all existing maps for this buffer
1502    " We should do this for all maps, but I am not sure how to do
1503    " this for this buffer/window only without affecting all the
1504    " other buffers.
1505    mapclear <buffer>
1506    " Create a mapping to act upon the yankring
1507    nnoremap <buffer> <silent> <2-LeftMouse> :call <SID>YRWindowActionN('p','n')<CR>
1508    nnoremap <buffer> <silent> <CR>          :call <SID>YRWindowActionN('p','n')<CR>
1509    vnoremap <buffer> <silent> <CR>          :call <SID>YRWindowAction('p','v')<CR>
1510    nnoremap <buffer> <silent> p             :call <SID>YRWindowActionN('p','n')<CR>
1511    vnoremap <buffer> <silent> p             :call <SID>YRWindowAction('p','v')<CR>
1512    nnoremap <buffer> <silent> P             :call <SID>YRWindowActionN('P','n')<CR>
1513    vnoremap <buffer> <silent> P             :call <SID>YRWindowAction('P','v')<CR>
1514    nnoremap <buffer> <silent> gp            :call <SID>YRWindowActionN('gp','n')<CR>
1515    vnoremap <buffer> <silent> gp            :call <SID>YRWindowAction('gp','v')<CR>
1516    nnoremap <buffer> <silent> gP            :call <SID>YRWindowActionN('gP','n')<CR>
1517    vnoremap <buffer> <silent> gP            :call <SID>YRWindowAction('gP','v')<CR>
1518    nnoremap <buffer> <silent> d             :call <SID>YRWindowActionN('d','n')<CR>
1519    vnoremap <buffer> <silent> d             :call <SID>YRWindowAction('d','v')<CR>
1520    vnoremap <buffer> <silent> r             :call <SID>YRWindowAction('r','v')<CR>
1521    nnoremap <buffer> <silent> a             :call <SID>YRWindowAction('a','n')<CR>
1522    nnoremap <buffer> <silent> ?             :call <SID>YRWindowAction('?','n')<CR>
1523    nnoremap <buffer> <silent> u             :call <SID>YRShow(0)<CR>
1524    nnoremap <buffer> <silent> q             :call <SID>YRWindowAction('q','n')<CR>
1525    nnoremap <buffer> <silent> <space>     \|:silent exec 'vertical resize '.
1526                \ (
1527                \ g:yankring_window_use_horiz!=1 && winwidth('.') > g:yankring_window_width
1528                \ ?(g:yankring_window_width)
1529                \ :(winwidth('.') + g:yankring_window_increment)
1530                \ )<CR>
1531
1532    " Erase it's contents to the blackhole
1533    %delete _
1534
1535    " Display the status line / help
1536    call s:YRWindowStatus(0)
1537
1538    " Display the contents of the yankring
1539    silent! put =a:results
1540
1541    " Move the cursor to the first line with an element
1542    exec 0
1543    call search('^\d','W')
1544
1545    setlocal nomodifiable
1546    "
1547    " Restore the previous cpoptions settings
1548    let &cpoptions = old_cpoptions
1549
1550endfunction
1551
1552function! s:YRWindowActionN(op, cmd_mode)
1553    let v_count    = v:count
1554    " If no count was specified it will have a value of 0
1555    " so set it to at least 1
1556    let v_count = ((v_count > 0)?(v_count):1)
1557
1558    if v_count > 1
1559        if !exists("b:yankring_show_range_error")
1560            let b:yankring_show_range_error = v_count
1561        else
1562            let b:yankring_show_range_error = b:yankring_show_range_error - 1
1563        endif
1564
1565        if b:yankring_show_range_error == 1
1566            call s:YRWarningMsg("YR:Use visual mode if you need to specify a count")
1567            unlet b:yankring_show_range_error
1568        endif
1569        return
1570    endif
1571   
1572    " while v_count > 0
1573        call s:YRWindowAction(a:op, a:cmd_mode)
1574        let v_count = v_count - 1
1575    " endwhile
1576
1577    if g:yankring_window_auto_close == 1 && v_count == 0
1578        exec 'bdelete '.bufnr(s:yr_buffer_name)
1579        return ""
1580    endif
1581
1582    return ""
1583endfunction
1584
1585function! s:YRWindowAction(op, cmd_mode) range
1586    let default_buffer = ((&clipboard=='unnamed')?'*':'"')
1587    let opcode     = a:op
1588    let saveA      = getreg('a')
1589    let saveA_t    = getregtype('a')
1590    let saveD      = getreg(default_buffer)
1591    let saveD_t    = getregtype(default_buffer)
1592    let lines      = ""
1593    let v_count    = v:count
1594    let cmd_mode   = a:cmd_mode
1595    let firstline  = a:firstline
1596    let lastline   = a:lastline
1597
1598    if cmd_mode == 'n'
1599        let v_count = 1
1600        " if v_count > 1
1601        "     call s:YRWarningMsg('Use visual mode to apply a count')
1602        "     return
1603        " endif
1604        " If a count was provided (5p), we want to repeat the paste
1605        " 5 times, but this also alters the a:firstline and a:lastline
1606        " ranges, which while in normal mode we do not want
1607        let lastline = firstline
1608    endif
1609    " If no count was specified it will have a value of 0
1610    " so set it to at least 1
1611    let v_count = ((v_count > 0)?(v_count):1)
1612
1613    if '[dr]' =~ opcode
1614        " Reverse the order of the lines to act on
1615        let begin = lastline
1616        while begin >= firstline
1617            let lines = lines."\n".getline(begin)
1618            let begin = begin - 1
1619        endwhile
1620    else
1621        " Process the selected items in order
1622        exec firstline.','.lastline.'yank a'
1623        let lines = "\n".@a
1624    endif
1625    call setreg('a', saveA, saveA_t)
1626    call setreg(default_buffer, saveD, saveD_t)
1627
1628    if opcode ==# 'q'
1629        " Close the yankring window
1630        if s:yankring_winsize_chgd == 1
1631            " Adjust the Vim window width back to the width
1632            " it was before we showed the yankring window
1633            let &columns= &columns - (g:yankring_window_width)
1634        endif
1635
1636        hide
1637        return
1638    elseif opcode ==# 'u'
1639        call s:YRShow(0)
1640        return
1641    elseif opcode ==# 'a'
1642        let l:curr_line = line(".")
1643        " Toggle the auto close setting
1644        let g:yankring_window_auto_close =
1645                    \ (g:yankring_window_auto_close == 1?0:1)
1646        " Display the status line / help
1647        call s:YRWindowStatus(0)
1648        call cursor(l:curr_line,0)
1649        return
1650    elseif opcode ==# '?'
1651        " Display the status line / help
1652        call s:YRWindowStatus(1)
1653        return
1654    endif
1655
1656    " Switch back to the original buffer
1657    exec s:yr_buffer_last_winnr . "wincmd w"
1658   
1659    " Intentional case insensitive comparision
1660    if opcode =~? 'p'
1661        let cmd   = 'YRGetElem '
1662        let parms = ", '".opcode."' "
1663    elseif opcode ==? 'r'
1664        let opcode = 'p'
1665        let cmd    = 'YRGetElem '
1666        let parms  = ", 'p' "
1667    elseif opcode ==# 'd'
1668        let cmd   = 'YRPop '
1669        let parms = ""
1670    endif
1671
1672    " Only execute this code if we are operating on elements
1673    " within the yankring
1674    if '[auq?]' !~# opcode
1675        while v_count > 0
1676            let iter  = 0
1677            let index = 0
1678            let index = match(lines, "\n".'\d\+', index)
1679            while index > -1
1680                " Retrieve the keystrokes for the mappings
1681                let index = match(lines, "\n".'\d\+', index)
1682                let elem  = matchstr(lines, "\n".'\zs\d\+', index)
1683
1684                if elem > 0 && elem <= s:yr_count
1685                    if iter > 0 && opcode =~# 'p'
1686                        " Move to the end of the last pasted item
1687                        " only if pasting after (not above)
1688                        ']
1689                    endif
1690                    exec cmd . elem . parms
1691                    let iter += 1
1692                endif
1693                " Search for the next element beginning with a newline character
1694                " Add +2, 1 to go by the number, 1 for the newline character
1695                let index = index + strlen(elem) + 2
1696                if index >= strlen(lines)
1697                    break
1698                endif
1699                let index = match(lines, "\n".'\d\+', index)
1700            endwhile
1701            let v_count = v_count - 1
1702        endwhile
1703
1704        if opcode ==# 'd'
1705            call s:YRShow(0)
1706            return ""
1707        endif
1708
1709        if g:yankring_window_auto_close == 1 && cmd_mode == 'v'
1710            exec 'bdelete '.bufnr(s:yr_buffer_name)
1711            return ""
1712        endif
1713
1714    endif
1715
1716    return ""
1717
1718endfunction
1719     
1720function! s:YRWarningMsg(msg)
1721    echohl WarningMsg
1722    echomsg a:msg
1723    echohl None
1724endfunction
1725     
1726function! s:YRErrorMsg(msg)
1727    echohl ErrorMsg
1728    echomsg a:msg
1729    echohl None
1730endfunction
1731     
1732function! s:YRWinLeave()
1733    " Track which window we are last in.  We will use this information
1734    " to determine where we need to paste any contents, or which
1735    " buffer to return to.
1736   
1737    if s:yr_buffer_id < 0
1738        " The yankring window has never been activated
1739        return
1740    endif
1741
1742    if winbufnr(winnr()) == s:yr_buffer_id
1743        " Ignore leaving the yankring window
1744        return
1745    endif
1746
1747    if bufwinnr(s:yr_buffer_id) != -1
1748        " YankRing window is visible, so save off the previous buffer ids
1749        let s:yr_buffer_last_winnr = winnr()
1750        let s:yr_buffer_last       = winbufnr(s:yr_buffer_last_winnr)
1751    " else
1752    "     let s:yr_buffer_last_winnr = -1
1753    "     let s:yr_buffer_last       = -1
1754    endif
1755endfunction
1756
1757" Deleting autocommands first is a good idea especially if we want to reload
1758" the script without restarting vim.
1759" Call YRInit in the VimEnter event so that the global variables have been
1760" restored via the viminfo file.  Calling YRInit prior to this will
1761" not restore the yankring order or items.
1762augroup YankRing
1763    autocmd!
1764    autocmd VimEnter * :call <SID>YRInit()
1765    autocmd WinLeave * :call <SID>YRWinLeave()
1766augroup END
1767
1768
1769" Public commands
1770command!                           YRClear       call s:YRClear()
1771command! -range -bang     -nargs=? YRDeleteRange <line1>,<line2>call s:YRYankRange(<bang>1, <args>)
1772command!                  -nargs=* YRGetElem     call s:YRGetElem(<args>)
1773command!        -bang     -nargs=? YRGetMultiple call s:YRGetMultiple(<bang>0, <args>)
1774command! -count -register -nargs=* YRPaste       call s:YRPaste(0,1,<args>)
1775command!                  -nargs=? YRPop         <line1>,<line2>call s:YRPop(<args>)
1776command!        -register -nargs=? YRPush        call s:YRPush(<args>)
1777command! -count -register -nargs=* YRReplace     call s:YRPaste(1,<args>)
1778command!                  -nargs=? YRSearch      call s:YRSearch(<q-args>)
1779" command!                  -nargs=1 YRSetTop      call s:YRSetTop(<args>)
1780command!                  -nargs=? YRShow        call s:YRShow(<args>)
1781command!                  -nargs=? YRToggle      call s:YRToggle(<args>)
1782command! -count -register -nargs=* YRYankCount   call s:YRYankCount(<args>)
1783command! -range -bang     -nargs=? YRYankRange   <line1>,<line2>call s:YRYankRange(<bang>0, <args>)
1784
1785" Verify the viminfo setting has !, which is required to
1786" persist global variables
1787if s:yr_scope == 'g'
1788    if &viminfo !~ '!'
1789        call s:YRWarningMsg('YR: The yankring can only persist if the viminfo setting has a "!" ')
1790    endif
1791endif
1792
1793if g:yankring_enabled == 1
1794    " Create YankRing Maps
1795    call YRMapsCreate()
1796endif
1797
1798if exists('*YRRunAfterMaps')
1799    " This will allow you to override the default maps if necessary
1800    call YRRunAfterMaps()
1801endif
1802
1803call s:YRInit()
1804     
1805
1806" vim:fdm=marker:nowrap:ts=4:expandtab:
Note: See TracBrowser for help on using the browser.