1 " nrrwrgn.vim - Narrow Region plugin for Vim
2 " -------------------------------------------------------------
4 " Maintainer: Christian Brabandt <cb@256bit.org>
5 " Last Change: Sat, 16 Feb 2013 22:28:31 +0100
7 " Script: http://www.vim.org/scripts/script.php?script_id=3075
8 " Copyright: (c) 2009, 2010, 2011, 2012, 2013 by Christian Brabandt
9 " The VIM LICENSE applies to NrrwRgn.vim
10 " (see |copyright|) except use "NrrwRgn.vim"
12 " No warranty, express or implied.
13 " *** *** Use At-Your-Own-Risk! *** ***
14 " GetLatestVimScripts: 3075 31 :AutoInstall: NrrwRgn.vim
18 fun! <sid>WarningMsg(msg) "{{{1
19 let msg = "NarrowRegion: ". a:msg
21 if exists(":unsilent") == 2
31 fun! <sid>Init() "{{{1
38 if !exists("g:nrrw_custom_options") || empty(g:nrrw_custom_options)
39 let s:opts=<sid>Options('local to buffer')
42 " Prevent accidently overwriting windows with instn_id set
43 " back to an already existing instn_id
44 let s:instn = (s:instn==0 ? 1 : s:instn)
45 while (has_key(s:nrrw_rgn_lines, s:instn))
50 if exists("b:nrrw_aucmd_create")
51 let s:nrrw_aucmd["create"] = b:nrrw_aucmd_create
53 if exists("b:nrrw_aucmd_close")
54 let s:nrrw_aucmd["close"] = b:nrrw_aucmd_close
56 if !exists("s:nrrw_rgn_lines")
57 let s:nrrw_rgn_lines = {}
59 let s:nrrw_rgn_lines[s:instn] = {}
60 " show some debugging messages
61 let s:nrrw_winname='Narrow_Region'
64 let s:nrrw_rgn_vert = (exists("g:nrrw_rgn_vert") ? g:nrrw_rgn_vert : 0)
65 let s:nrrw_rgn_wdth = (exists("g:nrrw_rgn_wdth") ? g:nrrw_rgn_wdth : 20)
66 let s:nrrw_rgn_hl = (exists("g:nrrw_rgn_hl") ? g:nrrw_rgn_hl :
68 let s:nrrw_rgn_nohl = (exists("g:nrrw_rgn_nohl") ? g:nrrw_rgn_nohl : 0)
70 let s:debug = (exists("s:debug") ? s:debug : 0)
74 fun! <sid>NrrwRgnWin(bang) "{{{1
75 let local_options = <sid>GetOptions(s:opts)
76 let nrrw_winname = s:nrrw_winname. '_'. s:instn
77 let nrrw_win = bufwinnr('^'.nrrw_winname.'$')
79 exe ":noa ". nrrw_win. 'wincmd w'
80 " just in case, a global nomodifiable was set
81 " disable this for the narrowed window
85 if !exists('g:nrrw_topbot_leftright')
86 let g:nrrw_topbot_leftright = 'topleft'
89 exe g:nrrw_topbot_leftright s:nrrw_rgn_wdth.
90 \(s:nrrw_rgn_vert?'v':''). "sp ". nrrw_winname
93 " if hidden is set, set the original buffer to be modified, so
94 " that :q won't accidently quit vim
99 if bufexists(s:nrrw_winname. '_'. s:instn)
101 exe 'bw' s:nrrw_winname. '_'. s:instn
103 exe 'f' s:nrrw_winname. '_'. s:instn
104 catch /^Vim\%((\a\+)\)\=:E37/ " catch error E37
105 " Fall back and use a new window
106 exe g:nrrw_topbot_leftright s:nrrw_rgn_wdth.
107 \(s:nrrw_rgn_vert?'v':''). "sp ". nrrw_winname
111 " just in case, a global nomodifiable was set
112 " disable this for the narrowed window
116 " Set up some options like 'bufhidden', 'noswapfile',
117 " 'buftype', 'bufhidden', when enabling Narrowing.
118 call <sid>NrrwSettings(1)
119 let nrrw_win = bufwinnr("")
121 call <sid>SetOptions(local_options)
122 " We are in the narrowed buffer now!
126 fun! <sid>CleanRegions() "{{{1
127 let s:nrrw_rgn_line=[]
128 unlet! s:nrrw_rgn_last
129 unlet! s:nrrw_rgn_buf
132 fun! <sid>CompareNumbers(a1,a2) "{{{1
133 return (a:a1+0) == (a:a2+0) ? 0
134 \ : (a:a1+0) > (a:a2+0) ? 1
138 fun! <sid>ParseList(list) "{{{1
139 " for a given list of line numbers, return those line numbers
140 " in a format start:end for continous items, else [start, next]
145 for item in sort(a:list, "<sid>CompareNumbers")
149 let result[i]=[start,temp]
156 " add all items from the list to the result
157 " list consists only of consecutive items
158 let result[i] = [a:list[0], a:list[-1]]
160 " Make sure the last item is included in the selection
161 if get(result, (i-1), 0)[0] && result[i-1][1] != item
162 let result[i]=[start,item]
167 fun! <sid>WriteNrrwRgn(...) "{{{1
168 " if argument is given, write narrowed buffer back
169 " else destroy the narrowed window
170 let nrrw_instn = exists("b:nrrw_instn") ? b:nrrw_instn : s:instn
171 if exists("b:orig_buf") && (bufwinnr(b:orig_buf) == -1) &&
172 \ !<sid>BufInTab(b:orig_buf) &&
173 \ !bufexists(b:orig_buf)
174 call s:WarningMsg("Original buffer does no longer exist! Aborting!")
177 if &l:mod && exists("a:1") && a:1
178 " Write the buffer back to the original buffer
181 if bufname('') !~# 'Narrow_Region' && bufwinnr(s:nrrw_winname. '_'. s:instn) > 0
182 exe ':noa'. bufwinnr(s:nrrw_winname. '_'. s:instn). 'wincmd w'
185 call <sid>StoreLastNrrwRgn(nrrw_instn)
186 let winnr = bufwinnr(b:orig_buf)
188 if bufname('') =~# 'Narrow_Region' && winnr > 0
189 exe ':noa'. winnr. 'wincmd w'
192 " close narrowed buffer
193 call <sid>NrrwRgnAuCmd(nrrw_instn)
198 fun! <sid>SaveRestoreRegister(values) "{{{1
201 let reg = ['a', getreg('a'), getregtype('a') ]
202 let fold = [ &fen, &l:fdm ]
206 let visual = [getpos("'<"), getpos("'>")]
207 return [ reg, fold, visual ]
211 call <sid>WarningMsg("Error setting options back!")
214 call call('setreg', a:values[0])
217 let &l:fdm=a:values[1][1]
219 if v:version > 703 || (v:version == 703 && has("patch590"))
220 " settable '< and '> marks
221 call setpos("'<", a:values[2][0])
222 call setpos("'>", a:values[2][1])
227 fun! <sid>NrrwRgnAuCmd(instn) "{{{1
228 " If a:instn==0, then enable auto commands
229 " else disable auto commands for a:instn
231 exe "aug NrrwRgn". b:nrrw_instn
233 au BufWriteCmd <buffer> nested :call s:WriteNrrwRgn(1)
234 au BufWinLeave,BufWipeout,BufDelete <buffer> nested
235 \ :call s:WriteNrrwRgn()
238 exe "aug NrrwRgn". a:instn
241 exe "aug! NrrwRgn". a:instn
243 if !has_key(s:nrrw_rgn_lines, a:instn)
244 " narrowed buffer was already cleaned up
245 call <sid>DeleteMatches(a:instn)
246 call s:WarningMsg("Window was already cleaned up. Nothing to do.")
250 " make the original buffer modifiable, if possible
251 let buf = s:nrrw_rgn_lines[a:instn].orig_buf
252 if !getbufvar(buf, '&l:ma') && !getbufvar(buf, 'orig_buf_ro')
253 call setbufvar(s:nrrw_rgn_lines[a:instn].orig_buf, '&ma', 1)
257 echo printf("bufnr: %d a:instn: %d\n", bufnr(''), a:instn)
258 echo "bwipe " s:nrrw_winname. '_'. a:instn
260 if (has_key(s:nrrw_rgn_lines[a:instn], 'disable') &&
261 \ !s:nrrw_rgn_lines[a:instn].disable ) ||
262 \ !has_key(s:nrrw_rgn_lines[a:instn], 'disable')
263 call <sid>DeleteMatches(a:instn)
264 " bwipe! throws E855 (catching does not work)
265 " but because of 'bufhidden' wipeing will happen anyways
266 "exe "bwipe! " bufnr(s:nrrw_winname. '_'. a:instn)
267 " if has_key(s:nrrw_rgn_lines[a:instn], 'single') &&
268 " \ s:nrrw_rgn_lines[a:instn].single
269 " If there is only a single window open don't clean up now
270 " because we can't put the narrowed lines back, so do not
271 " clean up now. We need to clean up then later. But how?
274 call <sid>CleanUpInstn(a:instn)
279 fun! <sid>CleanUpInstn(instn) "{{{1
280 if s:instn>=1 && has_key(s:nrrw_rgn_lines, a:instn)
281 unlet s:nrrw_rgn_lines[a:instn]
286 fun! <sid>StoreLastNrrwRgn(instn) "{{{1
287 " Only store the last region, when the narrowed instance is still valid
288 if !has_key(s:nrrw_rgn_lines, a:instn)
289 call <sid>WarningMsg("Error storing the last Narrowed Window,".
294 let s:nrrw_rgn_lines['last'] = []
295 if !exists("b:orig_buf")
296 let orig_buf = s:nrrw_rgn_lines[a:instn].orig_buf
298 let orig_buf = b:orig_buf
300 if has_key(s:nrrw_rgn_lines[a:instn], 'multi')
301 call add(s:nrrw_rgn_lines['last'], [ orig_buf,
302 \ s:nrrw_rgn_lines[a:instn]['multi']])
304 " Linewise narrowed region, pretend it was done like a visual
306 let s:nrrw_rgn_lines['last'] = [ [ orig_buf,
307 \ s:nrrw_rgn_lines[a:instn].start[1:]],
308 \ [ orig_buf, s:nrrw_rgn_lines[a:instn].end[1:]]]
309 call add(s:nrrw_rgn_lines['last'],
310 \ has_key(s:nrrw_rgn_lines[a:instn], 'vmode') ?
311 \ s:nrrw_rgn_lines[a:instn].vmode : 'V')
315 fun! <sid>RetVisRegionPos() "{{{1
316 if v:version > 703 || (v:version == 703 && has("patch590"))
317 return [ getpos("'<"), getpos("'>") ]
319 return [ getpos("'<")[0:1] + [virtcol("'<"), 0],
320 \ getpos("'>")[0:1] + [virtcol("'>"), 0] ]
324 fun! <sid>GeneratePattern(startl, endl, mode, ...) "{{{1
325 if exists("a:1") && a:1
330 " This is just a best guess, the highlighted block could still be wrong
331 " (a " rectangle has been selected, but the complete lines are
333 if a:mode ==# '
\16' && a:startl[0] > 0 && a:startl[1] > 0 && block
334 return '\%>'. (a:startl[0]-1). 'l\&\%>'. (a:startl[1]-1).
335 \ 'v\&\%<'. (a:endl[0]+1). 'l'
336 elseif a:mode ==# '
\16' && a:startl[0] > 0 && a:startl[1] > 0
337 return '\%>'. (a:startl[0]-1). 'l\&\%>'. (a:startl[1]-1).
338 \ 'v\&\%<'. (a:endl[0]+1). 'l\&\%<'. (a:endl[1]+1). 'v'
339 elseif a:mode ==# 'v' && a:startl[0] > 0 && a:startl[1] > 0
340 return '\%>'. (a:startl[0]-1). 'l\&\%>'. (a:startl[1]-1).
341 \ 'v\_.*\%<'. (a:endl[0]+1). 'l\&\%<'. (a:endl[1]+1). 'v'
342 elseif a:startl[0] > 0
343 return '\%>'. (a:startl[0]-1). 'l\&\%<'. (a:endl[0]+1). 'l'
349 fun! <sid>Options(search) "{{{1
353 " empty search pattern
357 silent noa sview $VIMRUNTIME/doc/options.txt
358 " for whatever reasons $VIMRUNTIME/doc/options.txt
359 " does not exist, return empty list
366 call add(reg_a,getreg('a'))
367 call add(reg_a, getregtype('a'))
369 exe "silent :g/". '\v'.escape(a:search, '\\/'). "/-y A"
370 let b=split(@a, "\n")
371 call call('setreg', reg_a)
372 "call setreg('a', reg_a[0], reg_a[1])
373 call filter(b, 'v:val =~ "^''"')
374 " the following options should be set
375 let filter_opt='\%(modifi\%(ed\|able\)\|readonly\|swapfile\|'.
376 \ 'buftype\|bufhidden\|foldcolumn\|buflisted\)'
377 call filter(b, 'v:val !~ "^''".filter_opt."''"')
379 let item=substitute(item, '''', '', 'g')
380 call add(c, split(item, '\s\+')[0])
383 if fnamemodify(bufname(''),':p') ==
384 \expand("$VIMRUNTIME/doc/options.txt")
387 exe "noa " bufwinnr(buf) "wincmd w"
392 fun! <sid>GetOptions(opt) "{{{1
393 if exists("g:nrrw_custom_options") && !empty(g:nrrw_custom_options)
394 let result = g:nrrw_custom_options
399 exe "let result[item]=&l:".item
407 fun! <sid>SetOptions(opt) "{{{1
408 if type(a:opt) == type({})
409 for [option, result] in items(a:opt)
410 exe "let &l:". option " = " string(result)
416 fun! <sid>CheckProtected() "{{{1
417 " Protect the original window, unless the user explicitly defines not to
419 if exists("g:nrrw_rgn_protect") && g:nrrw_rgn_protect =~? 'n'
425 call s:WarningMsg("Buffer is protected, won't be able to write".
426 \ "the changes back!")
428 " Protect the original buffer,
429 " so you won't accidentally modify those lines,
430 " that might later be overwritten
435 fun! <sid>DeleteMatches(instn) "{{{1
436 " Make sure, we are in the correct buffer
437 " Not needed, prevents recursively narrowing
438 " if bufname('') =~# 'Narrow_Region'
439 " exe ':noa'. bufwinnr(b:orig_buf). 'wincmd w'
441 if exists("s:nrrw_rgn_lines[a:instn].matchid")
442 " if you call :NarrowRegion several times, without widening
443 " the previous region, b:matchid might already be defined so
444 " make sure, the previous highlighting is removed.
445 for item in s:nrrw_rgn_lines[a:instn].matchid
447 " If the match has been deleted, discard the error
448 exe (s:debug ? "" : "silent!") "call matchdelete(item)"
451 let s:nrrw_rgn_lines[a:instn].matchid=[]
455 fun! <sid>HideNrrwRgnLines() "{{{1
456 let cnc = has("Conceal")
457 let cmd='syn match NrrwRgnStart "^# Start NrrwRgn\d\+$" '.
458 \ (cnc ? 'conceal' : '')
460 let cmd='syn match NrrwRgnEnd "^# End NrrwRgn\d\+$" '.
461 \ (cnc ? 'conceal' : '')
463 syn region NrrwRgn start="^# Start NrrwRgn\z(\d\+\).*$"
464 \ end="^# End NrrwRgn\z1$" fold transparent
471 fun! <sid>ReturnCommentFT() "{{{1
475 " Perl, PHP, Ruby, Python, Sh
476 elseif &l:ft=~'^\(perl\|php\|ruby\|python\|sh\)$'
479 elseif &l:ft=~'^\(c\%(pp\)\?\|java\)'
482 elseif &l:ft=~'^\(ht\|x\)ml\?$'
485 elseif &l:ft=~'^\(la\)tex'
493 fun! <sid>CheckRectangularRegion(reg) "{{{1
494 " Check whether the region that was pasted into
495 " register a:reg has always the same length
496 " This is needed, to be able to select the correct region
497 " when writing back the changes.
499 let list=split(a:reg, "\n")
500 call map(list, 'substitute(v:val, ".", "x", "g")')
501 let llen = len(list)/2
502 call map(list, 'len(v:val)')
504 if has_key(result, item)
505 let result[item] += 1
510 for [key, value] in items(result)
518 fun! <sid>WidenRegionMulti(content, instn) "{{{1
519 " for single narrowed windows, the original narrowed buffer will be closed,
520 " so don't renew the highlighting and clean up (later in
521 " nrrwrgn#WidenRegion)
522 if empty(s:nrrw_rgn_lines[a:instn].multi)
528 let [c_s, c_e] = <sid>ReturnComments()
529 let lastline = line('$')
530 " We must put the regions back from top to bottom,
531 " otherwise, changing lines in between messes up the list of lines that
532 " still need to put back from the narrowed buffer to the original buffer
533 for key in sort(keys(s:nrrw_rgn_lines[a:instn].multi),
534 \ "<sid>CompareNumbers")
535 let adjust = line('$') - lastline
536 let range = s:nrrw_rgn_lines[a:instn].multi[key]
537 let last = (len(range)==2) ? range[1] : range[0]
539 let indexs = index(a:content, c_s.' Start NrrwRgn'.key.c_e) + 1
540 let indexe = index(a:content, c_s.' End NrrwRgn'.key.c_e) - 1
541 if indexs <= 0 || indexe < -1
542 call s:WarningMsg("Skipping Region ". key)
545 " Adjust line numbers. Changing the original buffer, might also
546 " change the regions we have remembered. So we must adjust these
548 " This only works, if we put the regions from top to bottom!
551 if last == line('$') && first == 1
552 let delete_last_line=1
554 let delete_last_line=0
556 exe ':silent :'. first. ','. last. 'd _'
557 call append((first-1), a:content[indexs : indexe])
558 " Recalculate the start and end positions of the narrowed window
559 " so subsequent calls will adjust the region accordingly
560 let last = first + len(a:content[indexs : indexe]) - 1
564 if !has_key(s:nrrw_rgn_lines[a:instn].multi, 'single')
565 " original narrowed buffer is going to be closed
566 " so don't renew the matches
567 call <sid>AddMatches(<sid>GeneratePattern([first, 0 ],
568 \ [last, 0], 'V'), a:instn)
576 fun! <sid>AddMatches(pattern, instn) "{{{1
577 if !s:nrrw_rgn_nohl || empty(a:pattern)
578 if !exists("s:nrrw_rgn_lines[a:instn].matchid")
579 let s:nrrw_rgn_lines[a:instn].matchid=[]
581 call add(s:nrrw_rgn_lines[a:instn].matchid,
582 \matchadd(s:nrrw_rgn_hl, a:pattern))
586 fun! <sid>BufInTab(bufnr) "{{{1
587 " returns tabpage of buffer a:bufnr
588 for tab in range(1,tabpagenr('$'))
589 if !empty(filter(tabpagebuflist(tab), 'v:val == a:bufnr'))
596 fun! <sid>JumpToBufinTab(tab,buf) "{{{1
600 let win = bufwinnr(a:buf)
602 exe ':noa '. bufwinnr(a:buf). 'wincmd w'
606 fun! <sid>RecalculateLineNumbers(instn, adjust) "{{{1
607 " This only matters, if the original window isn't protected
608 if !exists("g:nrrw_rgn_protect") || g:nrrw_rgn_protect !~# 'n'
612 for instn in filter(keys(s:nrrw_rgn_lines), 'v:val != a:instn')
613 " Skip narrowed instances, when they are before
614 " the region, we are currently putting back
615 if s:nrrw_rgn_lines[instn].start[1] <=
616 \ s:nrrw_rgn_lines[a:instn].start[1]
620 let s:nrrw_rgn_lines[instn].start[1] += a:adjust
621 let s:nrrw_rgn_lines[instn].end[1] += a:adjust
623 if s:nrrw_rgn_lines[instn].start[1] < 1
624 let s:nrrw_rgn_lines[instn].start[1] = 1
626 if s:nrrw_rgn_lines[instn].end[1] < 1
627 let s:nrrw_rgn_lines[instn].end[1] = 1
629 call <sid>DeleteMatches(instn)
630 call <sid>AddMatches(<sid>GeneratePattern(
631 \s:nrrw_rgn_lines[instn].start[1:2],
632 \s:nrrw_rgn_lines[instn].end[1:2],
639 fun! <sid>NrrwSettings(on) "{{{1
641 setl noswapfile buftype=acwrite foldcolumn=0
643 let instn = matchstr(bufname(''), '_\zs\d\+')+0
644 if !&hidden && !has_key(s:nrrw_rgn_lines[instn], "single")
650 setl swapfile buftype= bufhidden= buflisted
654 fun! <sid>SetupBufLocalCommands() "{{{1
655 com! -buffer -bang WidenRegion :call nrrwrgn#WidenRegion(<bang>0)
656 com! -buffer NRSyncOnWrite :call nrrwrgn#ToggleSyncWrite(1)
657 com! -buffer NRNoSyncOnWrite :call nrrwrgn#ToggleSyncWrite(0)
660 fun! <sid>ReturnComments() "{{{1
661 let cmt = <sid>ReturnCommentFT()
662 let c_s = split(cmt)[0]
663 let c_e = (len(split(cmt)) == 1 ? "" : " ". split(cmt)[1])
667 fun! nrrwrgn#NrrwRgnDoPrepare(...) "{{{1
668 let bang = (a:0 > 0 && !empty(a:1))
669 if !exists("s:nrrw_rgn_line")
670 call <sid>WarningMsg("You need to first select the lines to".
671 \ " narrow using :NRP!")
674 if empty(s:nrrw_rgn_line) && !exists("s:nrrw_rgn_buf")
675 call <sid>WarningMsg("No lines selected from :NRP, aborting!")
678 if !exists("s:nrrw_rgn_buf")
679 let s:nrrw_rgn_buf = <sid>ParseList(s:nrrw_rgn_line)
681 if empty(s:nrrw_rgn_buf)
682 call <sid>WarningMsg("An error occured when selecting all lines. Please report as bug")
689 let orig_buf=bufnr('')
691 " initialize Variables
693 call <sid>CheckProtected()
694 let s:nrrw_rgn_lines[s:instn].start = []
695 let s:nrrw_rgn_lines[s:instn].end = []
696 let s:nrrw_rgn_lines[s:instn].multi = s:nrrw_rgn_buf
697 let s:nrrw_rgn_lines[s:instn].orig_buf = orig_buf
698 call <sid>DeleteMatches(s:instn)
704 let keys = keys(s:nrrw_rgn_buf)
705 call sort(keys,"<sid>CompareNumbers")
706 "for [ nr,lines] in items(s:nrrw_rgn_buf)
707 let [c_s, c_e] = <sid>ReturnComments()
709 let lines = s:nrrw_rgn_buf[nr]
711 let end = len(lines)==2 ? lines[1] : lines[0]
713 call <sid>AddMatches(<sid>GeneratePattern([start,0], [end,0], 'V'),
716 call add(buffer, c_s.' Start NrrwRgn'.nr.c_e)
717 let buffer = buffer +
718 \ getline(start,end) +
719 \ [c_s.' End NrrwRgn'.nr.c_e, '']
722 let win=<sid>NrrwRgnWin(bang)
724 let s:nrrw_rgn_lines[s:instn].single = 1
726 let b:orig_buf = orig_buf
727 call setline(1, buffer)
729 let b:nrrw_instn = s:instn
730 call <sid>SetupBufLocalCommands()
731 call <sid>NrrwRgnAuCmd(0)
732 call <sid>CleanRegions()
733 call <sid>HideNrrwRgnLines()
739 fun! nrrwrgn#NrrwRgn(...) range "{{{1
743 let orig_buf=bufnr('')
744 let bang = (a:0 > 0 && !empty(a:1))
746 " initialize Variables
748 call <sid>CheckProtected()
749 let first = a:firstline
750 let last = a:lastline
751 " If first line is in a closed fold,
752 " include complete fold in Narrowed window
753 if first == last && foldclosed(first) != -1
754 let first = foldclosed(first)
755 let last = foldclosedend(last)
757 let s:nrrw_rgn_lines[s:instn].start = [ 0, first, 0, 0 ]
758 let s:nrrw_rgn_lines[s:instn].end = [ 0, last , 0, 0 ]
759 let s:nrrw_rgn_lines[s:instn].orig_buf = orig_buf
761 \s:nrrw_rgn_lines[s:instn].start[1],
762 \s:nrrw_rgn_lines[s:instn].end[1])
763 call <sid>DeleteMatches(s:instn)
764 let win=<sid>NrrwRgnWin(bang)
766 let s:nrrw_rgn_lines[s:instn].single = 1
769 " Set highlighting in original window
770 call <sid>AddMatches(<sid>GeneratePattern(
771 \s:nrrw_rgn_lines[s:instn].start[1:2],
772 \s:nrrw_rgn_lines[s:instn].end[1:2],
774 " move back to narrowed window
777 let b:orig_buf = orig_buf
780 let b:nrrw_instn = s:instn
781 call <sid>SetupBufLocalCommands()
782 call <sid>NrrwRgnAuCmd(0)
783 if has_key(s:nrrw_aucmd, "create")
784 exe s:nrrw_aucmd["create"]
786 if has_key(s:nrrw_aucmd, "close")
787 let b:nrrw_aucmd_close = s:nrrw_aucmd["close"]
794 fun! nrrwrgn#Prepare() "{{{1
795 let ltime = localtime()
796 if (!exists("s:nrrw_rgn_last") || s:nrrw_rgn_last + 10 < ltime)
797 let s:nrrw_rgn_last = ltime
798 let s:nrrw_rgn_line = []
800 if !exists("s:nrrw_rgn_line") | let s:nrrw_rgn_line=[] | endif
801 call add(s:nrrw_rgn_line, line('.'))
804 fun! nrrwrgn#WidenRegion(force) "{{{1
805 " a:close: original narrowed window is going to be closed
806 " so, clean up, don't renew highlighting, etc.
807 let nrw_buf = bufnr('')
808 let orig_buf = b:orig_buf
809 let orig_tab = tabpagenr()
810 let instn = b:nrrw_instn
811 let close = has_key(s:nrrw_rgn_lines[instn], 'single')
812 let vmode = has_key(s:nrrw_rgn_lines[instn], 'vmode')
813 " Execute autocommands
814 if has_key(s:nrrw_aucmd, "close")
815 exe s:nrrw_aucmd["close"]
817 let cont = getline(1,'$')
819 let tab=<sid>BufInTab(orig_buf)
820 if tab != tabpagenr() && tab > 0
823 let orig_win = bufwinnr(orig_buf)
824 " Should be in the right tab now!
826 if bufexists(orig_buf)
827 " buffer not in current window, switch to it!
828 exe "noa" orig_buf "b!"
829 " Make sure highlighting will be removed
830 let close = (&g:hid ? 0 : 1)
832 call s:WarningMsg("Original buffer does no longer exist!".
837 exe ':noa'. orig_win. 'wincmd w'
839 let _opts = <sid>SaveRestoreRegister([])
840 let wsv=winsaveview()
841 call <sid>DeleteMatches(instn)
842 if exists("b:orig_buf_ro") && b:orig_buf_ro && !a:force
843 call s:WarningMsg("Original buffer protected. Can't write changes!")
844 call <sid>JumpToBufinTab(orig_tab, nrw_buf)
847 if !&l:ma && !( exists("b:orig_buf_ro") && b:orig_buf_ro)
850 " This is needed to adjust all other narrowed regions
851 " in case we have several narrowed regions within the same buffer
852 if exists("g:nrrw_rgn_protect") && g:nrrw_rgn_protect =~? 'n'
853 let adjust_line_numbers = len(cont) - 1 - (
854 \s:nrrw_rgn_lines[instn].end[1] -
855 \s:nrrw_rgn_lines[instn].start[1])
858 " Make sure the narrowed buffer is still valid (happens, when 2 split
859 " window of the narrowed buffer is opened.
860 if !has_key(s:nrrw_rgn_lines, instn)
861 call <sid>WarningMsg("Error writing changes back,".
862 \ "Narrowed Window invalid!")
866 " Now copy the content back into the original buffer
868 " 1) Check: Multiselection
869 if has_key(s:nrrw_rgn_lines[instn], 'multi')
870 call <sid>WidenRegionMulti(cont, instn)
871 " 2) Visual Selection
873 "charwise, linewise or blockwise selection
874 call setreg('a', join(cont, "\n"). "\n",
875 \ s:nrrw_rgn_lines[instn].vmode)
876 if s:nrrw_rgn_lines[instn].vmode == 'v' &&
877 \ s:nrrw_rgn_lines[instn].end[1] -
878 \ s:nrrw_rgn_lines[instn].start[1] + 1 == len(cont) + 1
879 " in characterwise selection, remove trailing \n
880 call setreg('a', substitute(@a, '\n$', '', ''),
881 \ s:nrrw_rgn_lines[instn].vmode)
883 if v:version > 703 || (v:version == 703 && has("patch590"))
884 " settable '< and '> marks
886 " store actual values
887 let _v = [getpos("'<"), getpos("'>"), [visualmode(1)]]
888 " set the mode for the gv command
889 exe "norm! ". s:nrrw_rgn_lines[instn].vmode."\<ESC>"
890 call setpos("'<", s:nrrw_rgn_lines[instn].start)
891 call setpos("'>", s:nrrw_rgn_lines[instn].end)
893 if !empty(_v[2][0]) && (_v[2][0] != visualmode())
894 exe 'norm!' _v[2][0]. "\<ESC>"
895 call setpos("'<", _v[0])
896 call setpos("'>", _v[1])
899 exe "keepj" s:nrrw_rgn_lines[instn].start[1]
900 exe "keepj norm!" s:nrrw_rgn_lines[instn].start[2]. '|'
901 exe "keepj norm!" s:nrrw_rgn_lines[instn].vmode
902 exe "keepj" s:nrrw_rgn_lines[instn].end[1]
903 if s:nrrw_rgn_lines[instn].blockmode
904 exe "keepj norm!" s:nrrw_rgn_lines[instn].end[2]. '|'
908 " overwrite the visually selected region with the contents from
909 " the narrowed buffer
912 " Recalculate the start and end positions of the narrowed window
913 " so subsequent calls will adjust the region accordingly
914 let [ s:nrrw_rgn_lines[instn].start,
915 \s:nrrw_rgn_lines[instn].end ] = <sid>RetVisRegionPos()
916 " make sure the visual selected lines did not add a new linebreak,
917 " this messes up the characterwise selected regions and removes lines
918 " on further writings
919 if s:nrrw_rgn_lines[instn].end[1] - s:nrrw_rgn_lines[instn].start[1]
920 \ + 1 > len(cont) && s:nrrw_rgn_lines[instn].vmode == 'v'
921 let s:nrrw_rgn_lines[instn].end[1] =
922 \ s:nrrw_rgn_lines[instn].end[1] - 1
923 let s:nrrw_rgn_lines[instn].end[2] = virtcol('$')
926 " also, renew the highlighted region
927 if !has_key(s:nrrw_rgn_lines[instn], 'single')
928 call <sid>AddMatches(<sid>GeneratePattern(
929 \ s:nrrw_rgn_lines[instn].start[1:2],
930 \ s:nrrw_rgn_lines[instn].end[1:2],
931 \ s:nrrw_rgn_lines[instn].vmode),
936 " 3) :NR started selection
938 " linewise selection because we started the NarrowRegion with the
939 " command NarrowRegion(0)
941 " if the endposition of the narrowed buffer is also the last line of
942 " the buffer, the append will add an extra newline that needs to be
944 if s:nrrw_rgn_lines[instn].end[1]==line('$') &&
945 \ s:nrrw_rgn_lines[instn].start[1] == 1
946 let delete_last_line=1
948 let delete_last_line=0
950 exe ':silent :'.s:nrrw_rgn_lines[instn].start[1].','
951 \.s:nrrw_rgn_lines[instn].end[1].'d _'
952 call append((s:nrrw_rgn_lines[instn].start[1]-1),cont)
953 " Recalculate the start and end positions of the narrowed window
954 " so subsequent calls will adjust the region accordingly
955 " so subsequent calls will adjust the region accordingly
956 let s:nrrw_rgn_lines[instn].end[1] =
957 \ s:nrrw_rgn_lines[instn].start[1] + len(cont) -1
958 if s:nrrw_rgn_lines[instn].end[1] > line('$')
959 let s:nrrw_rgn_lines[instn].end[1] = line('$')
961 if !has_key(s:nrrw_rgn_lines[instn], 'single')
962 call <sid>AddMatches(<sid>GeneratePattern(
963 \s:nrrw_rgn_lines[instn].start[1:2],
964 \s:nrrw_rgn_lines[instn].end[1:2],
972 " Recalculate start- and endline numbers for all other Narrowed Windows.
973 " This matters, if you narrow different regions of the same file and
974 " write your changes back.
975 if exists("g:nrrw_rgn_protect") && g:nrrw_rgn_protect =~? 'n'
976 call <sid>RecalculateLineNumbers(instn, adjust_line_numbers)
978 " if close && !has_key(s:nrrw_rgn_lines[instn], 'single')
979 " For narrowed windows that have been created using !,
980 " don't clean up yet, or else we loose all data and can't write
982 " (e.g. :NR! createas a new single window, do :sp
983 " and you can only write one of the windows back, the other will
984 " become invalid, if CleanUp is executed)
986 call <sid>SaveRestoreRegister(_opts)
988 call winrestview(wsv)
989 if !close && has_key(s:nrrw_rgn_lines[instn], 'single')
990 " move back to narrowed buffer
993 call <sid>CleanUpInstn(instn)
995 " jump back to narrowed window
996 call <sid>JumpToBufinTab(orig_tab, nrw_buf)
999 " trigger auto command
1004 fun! nrrwrgn#VisualNrrwRgn(mode, ...) "{{{1
1005 " bang: open the narrowed buffer in the current window and don't open a
1008 " in case, visual mode wasn't entered, visualmode()
1009 " returns an empty string and in that case, we finish
1011 call <sid>WarningMsg("There was no region visually selected!")
1014 " This beeps, when called from command mode
1015 " e.g. by using :NRV, so using :sil!
1016 " else exiting visual mode
1017 exe "sil! norm! \<ESC>"
1018 let bang = (a:0 > 0 && !empty(a:1))
1024 let s:nrrw_rgn_lines[s:instn].vmode=a:mode
1025 " Protect the original buffer,
1026 " so you won't accidentally modify those lines,
1027 " that will later be overwritten
1028 let orig_buf=bufnr('')
1029 let _opts = <sid>SaveRestoreRegister([])
1031 call <sid>CheckProtected()
1032 let [ s:nrrw_rgn_lines[s:instn].start,
1033 \s:nrrw_rgn_lines[s:instn].end ] = <sid>RetVisRegionPos()
1034 call <sid>DeleteMatches(s:instn)
1036 if len(split(@a, "\n", 1)) !=
1037 \ (s:nrrw_rgn_lines[s:instn].end[1] -
1038 \ s:nrrw_rgn_lines[s:instn].start[1] + 1)
1039 " remove trailing "\n"
1040 let @a=substitute(@a, '\n$', '', '')
1043 if a:mode == '
\16' && <sid>CheckRectangularRegion(@a)
1044 " Rectangular selection
1045 let s:nrrw_rgn_lines[s:instn].blockmode = 1
1047 " Non-Rectangular selection
1048 let s:nrrw_rgn_lines[s:instn].blockmode = 0
1050 let win=<sid>NrrwRgnWin(bang)
1052 let s:nrrw_rgn_lines[s:instn].single = 1
1054 " Set the highlighting
1056 call <sid>AddMatches(<sid>GeneratePattern(
1057 \s:nrrw_rgn_lines[s:instn].start[1:2],
1058 \s:nrrw_rgn_lines[s:instn].end[1:2],
1059 \s:nrrw_rgn_lines[s:instn].vmode,
1060 \s:nrrw_rgn_lines[s:instn].blockmode),
1064 let b:orig_buf = orig_buf
1065 let s:nrrw_rgn_lines[s:instn].orig_buf = orig_buf
1067 let b:nrrw_instn = s:instn
1070 call <sid>SetupBufLocalCommands()
1071 " Setup autocommands
1072 call <sid>NrrwRgnAuCmd(0)
1073 " Execute autocommands
1074 if has_key(s:nrrw_aucmd, "create")
1075 exe s:nrrw_aucmd["create"]
1077 call <sid>SaveRestoreRegister(_opts)
1083 fun! nrrwrgn#UnifiedDiff() "{{{1
1084 let save_winposview=winsaveview()
1085 let orig_win = winnr()
1086 " close previous opened Narrowed buffers
1087 silent! windo | if bufname('')=~'^Narrow_Region' &&
1088 \ &diff |diffoff|q!|endif
1090 " this is disabled, because this might be useful, to see everything
1091 "exe "vert resize -999999"
1093 " move to current start of chunk of unified diff
1094 if search('^@@', 'bcW') > 0
1095 call search('^@@', 'bc')
1097 call search('^@@', 'c')
1099 let curpos=getpos('.')
1101 if search('^@@', 'nW') > 0
1104 " Last chunk in file
1117 call setpos('.', curpos)
1119 call winrestview(save_winposview)
1122 fun! nrrwrgn#ToggleSyncWrite(enable) "{{{1
1123 let s:nrrw_rgn_lines[b:nrrw_instn].disable = !a:enable
1124 " Enable syncing of bufers
1126 " Enable Narrow settings and autocommands
1127 call <sid>NrrwSettings(1)
1128 call <sid>NrrwRgnAuCmd(0)
1131 " Disable Narrow settings and autocommands
1132 call <sid>NrrwSettings(0)
1133 " b:nrrw_instn should always be available
1134 call <sid>NrrwRgnAuCmd(b:nrrw_instn)
1138 fun! nrrwrgn#LastNrrwRgn(bang) "{{{1
1139 let bang = !empty(a:bang)
1140 if !exists("s:nrrw_rgn_lines") || !has_key(s:nrrw_rgn_lines, 'last')
1141 call <sid>WarningMsg("There is no last region to re-select")
1144 let orig_buf = s:nrrw_rgn_lines['last'][0][0] + 0
1145 let tab = <sid>BufInTab(orig_buf)
1146 if tab != tabpagenr() && tab > 0
1149 let orig_win = bufwinnr(orig_buf)
1150 " Should be in the right tab now!
1152 call s:WarningMsg("Original buffer does no longer exist! Aborting!")
1155 if orig_win != winnr()
1156 exe "noa" orig_win "wincmd w"
1158 if len(s:nrrw_rgn_lines['last']) == 1
1160 let s:nrrw_rgn_buf = s:nrrw_rgn_lines['last'][0][1]
1161 call nrrwrgn#NrrwRgnDoPrepare('')
1163 exe "keepj" s:nrrw_rgn_lines['last'][0][1][0]
1164 exe "keepj norm!" s:nrrw_rgn_lines['last'][0][1][1]. '|'
1166 exe "keepj norm!" s:nrrw_rgn_lines['last'][2]
1167 exe "keepj" s:nrrw_rgn_lines['last'][1][1][0]
1168 if col(s:nrrw_rgn_lines['last'][1][1][1]) == col('$') &&
1169 \ s:nrrw_rgn_lines['last'][2] == '
\16'
1173 exe "keepj norm!" s:nrrw_rgn_lines['last'][1][1][1]. '|'
1175 " Call VisualNrrwRgn()
1176 call nrrwrgn#VisualNrrwRgn(visualmode(), bang)
1179 " Debugging options "{{{1
1180 fun! nrrwrgn#Debug(enable) "{{{1
1183 fun! <sid>NrrwRgnDebug() "{{{2
1185 com! NI :call <sid>WarningMsg("Instance: ".s:instn)
1186 com! NJ :call <sid>WarningMsg("Data: ".string(s:nrrw_rgn_lines))
1187 com! -nargs=1 NOutput :if exists("s:".<q-args>)|redraw!|
1188 \ :exe 'echo s:'.<q-args>|else|
1189 \ echo "s:".<q-args>. " does not exist!"|endif
1191 call <sid>NrrwRgnDebug()
1194 delf <sid>NrrwRgnDebug
1202 " vim: ts=4 sts=4 fdm=marker com+=l\:\" fdl=0