mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
340 lines
11 KiB
VimL
340 lines
11 KiB
VimL
" ingo/actions/iterations.vim: Repeated action execution over several targets.
|
|
"
|
|
" DEPENDENCIES:
|
|
" - ingo/actions.vim autoload script
|
|
" - ingo/escape/file.vim autoload script
|
|
"
|
|
" Copyright: (C) 2016 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
"
|
|
" REVISION DATE REMARKS
|
|
" 1.025.001 29-Jul-2016 file creation
|
|
|
|
function! ingo#actions#iterations#WinDo( alreadyVisitedBuffers, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Invoke a:Action on each window in the current tab page, unless the buffer is
|
|
" in a:alreadyVisitedBuffers.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:alreadyVisitedBuffers Dictionary with already visited buffer numbers
|
|
" as keys. Will be added to, and the same buffers
|
|
" in other windows will be skipped. Pass 0 to
|
|
" visit _all_ windows, regardless of the buffers
|
|
" they display.
|
|
" a:Action Either a Funcref or Ex commands to be executed
|
|
" in each window.
|
|
" ... Arguments passed to an a:Action Funcref.
|
|
"* RETURN VALUES:
|
|
" None.
|
|
"******************************************************************************
|
|
let l:originalWinNr = winnr()
|
|
let l:previousWinNr = winnr('#') ? winnr('#') : 1
|
|
|
|
" By entering a window, its height is potentially increased from 0 to 1 (the
|
|
" minimum for the current window). To avoid any modification, save the window
|
|
" sizes and restore them after visiting all windows.
|
|
let l:originalWindowLayout = winrestcmd()
|
|
let l:didSwitchWindows = 0
|
|
|
|
try
|
|
for l:winNr in range(1, winnr('$'))
|
|
let l:bufNr = winbufnr(l:winNr)
|
|
if a:alreadyVisitedBuffers is# 0 || ! has_key(a:alreadyVisitedBuffers, l:bufNr)
|
|
if l:winNr != winnr()
|
|
execute 'noautocmd' l:winNr . 'wincmd w'
|
|
let l:didSwitchWindows = 1
|
|
endif
|
|
if type(a:alreadyVisitedBuffers) == type({}) | let a:alreadyVisitedBuffers[bufnr('')] = 1 | endif
|
|
|
|
call call(function('ingo#actions#ExecuteOrFunc'), a:000)
|
|
endif
|
|
endfor
|
|
finally
|
|
if l:didSwitchWindows
|
|
noautocmd execute l:previousWinNr . 'wincmd w'
|
|
noautocmd execute l:originalWinNr . 'wincmd w'
|
|
silent! execute l:originalWindowLayout
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
function! ingo#actions#iterations#TabWinDo( alreadyVisitedTabPages, alreadyVisitedBuffers, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Invoke a:Action on each window in each tab page, unless the buffer is in
|
|
" a:alreadyVisitedBuffers.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:alreadyVisitedTabPages Dictionary with already visited tabpage numbers
|
|
" as keys. Will be added to, those tab pages will
|
|
" be skipped. Pass empty Dictionary to visit _all_
|
|
" tab pages.
|
|
" a:alreadyVisitedBuffers Dictionary with already visited buffer numbers
|
|
" as keys. Will be added to, and the same buffers
|
|
" in other windows / tab pages will be skipped.
|
|
" Pass 0 to visit _all_ windows and tab pages,
|
|
" regardless of the buffers they display.
|
|
" a:Action Either a Funcref or Ex commands to be executed
|
|
" in each window.
|
|
" ... Arguments passed to an a:Action Funcref.
|
|
"* RETURN VALUES:
|
|
" None.
|
|
"******************************************************************************
|
|
let l:originalTabNr = tabpagenr()
|
|
let l:didSwitchTabs = 0
|
|
try
|
|
for l:tabNr in range(1, tabpagenr('$'))
|
|
if ! has_key(a:alreadyVisitedTabPages, l:tabNr)
|
|
let a:alreadyVisitedTabPages[l:tabNr] = 1
|
|
if ! empty(a:alreadyVisitedBuffers) && ingo#collections#differences#ContainsLoosely(keys(a:alreadyVisitedBuffers), tabpagebuflist(l:tabNr))
|
|
" All buffers of that tab page have already been visited; no
|
|
" need to go there.
|
|
continue
|
|
endif
|
|
|
|
if l:tabNr != tabpagenr()
|
|
execute 'noautocmd' l:tabNr . 'tabnext'
|
|
let l:didSwitchTabs = 1
|
|
endif
|
|
|
|
let l:originalWinNr = winnr()
|
|
let l:previousWinNr = winnr('#') ? winnr('#') : 1
|
|
" By entering a window, its height is potentially increased from 0 to 1 (the
|
|
" minimum for the current window). To avoid any modification, save the window
|
|
" sizes and restore them after visiting all windows.
|
|
let l:originalWindowLayout = winrestcmd()
|
|
let l:didSwitchWindows = 0
|
|
|
|
try
|
|
for l:winNr in range(1, winnr('$'))
|
|
let l:bufNr = winbufnr(l:winNr)
|
|
if a:alreadyVisitedBuffers is# 0 || ! has_key(a:alreadyVisitedBuffers, l:bufNr)
|
|
execute 'noautocmd' l:winNr . 'wincmd w'
|
|
|
|
let l:didSwitchWindows = 1
|
|
if type(a:alreadyVisitedBuffers) == type({}) | let a:alreadyVisitedBuffers[bufnr('')] = 1 | endif
|
|
|
|
call call(function('ingo#actions#ExecuteOrFunc'), a:000)
|
|
endif
|
|
endfor
|
|
finally
|
|
if l:didSwitchWindows
|
|
noautocmd execute l:previousWinNr . 'wincmd w'
|
|
noautocmd execute l:originalWinNr . 'wincmd w'
|
|
silent! execute l:originalWindowLayout
|
|
endif
|
|
endtry
|
|
endif
|
|
endfor
|
|
finally
|
|
if l:didSwitchTabs
|
|
noautocmd execute l:originalTabNr . 'tabnext'
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:GetNextArgNr( argNr, alreadyVisitedBuffers )
|
|
let l:argNr = a:argNr + 1 " Try next argument.
|
|
while l:argNr <= argc()
|
|
let l:bufNr = bufnr(ingo#escape#file#bufnameescape(argv(a:argNr - 1)))
|
|
if l:bufNr == -1 || type(a:alreadyVisitedBuffers) != type({}) || ! has_key(a:alreadyVisitedBuffers, l:bufNr)
|
|
return l:argNr
|
|
endif
|
|
|
|
" That one was already visited; continue searching.
|
|
let l:argNr += 1
|
|
endwhile
|
|
return -1
|
|
endfunction
|
|
function! ingo#actions#iterations#ArgDo( alreadyVisitedBuffers, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Invoke a:Action on each argument in the argument list, unless the buffer is
|
|
" in a:alreadyVisitedBuffers.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" Prints any Vim exception as error message.
|
|
"* INPUTS:
|
|
" a:alreadyVisitedBuffers Dictionary with already visited buffer numbers
|
|
" as keys. Will be added to, and the same buffers
|
|
" in other arguments will be skipped. Pass 0 to
|
|
" visit _all_ arguments.
|
|
" a:Action Either a Funcref or Ex commands to be executed
|
|
" in each window.
|
|
" ... Arguments passed to an a:Action Funcref.
|
|
"* RETURN VALUES:
|
|
" Number of Vim exceptions raised while iterating through the argument list
|
|
" (e.g. errors when loading buffers) or from executing a:Action.
|
|
"******************************************************************************
|
|
let l:originalBufNr = bufnr('')
|
|
let l:originalWindowLayout = winrestcmd()
|
|
let l:originalWinNr = winnr()
|
|
let l:previousWinNr = winnr('#') ? winnr('#') : 1
|
|
|
|
let l:nextArgNr = s:GetNextArgNr(0, a:alreadyVisitedBuffers)
|
|
if l:nextArgNr == -1
|
|
return | " No arguments left.
|
|
endif
|
|
|
|
let l:didSplit = 0
|
|
let l:failureCnt = 0
|
|
try
|
|
try
|
|
execute 'noautocmd silent keepalt leftabove' l:nextArgNr . 'sargument'
|
|
let l:didSplit = 1
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
if bufnr('') == l:originalBufNr
|
|
" We failed to split to the target buffer; bail out, as we need
|
|
" the split.
|
|
return l:failureCnt
|
|
endif
|
|
endtry
|
|
|
|
while 1
|
|
let l:bufNr = bufnr('')
|
|
if type(a:alreadyVisitedBuffers) == type({}) | let a:alreadyVisitedBuffers[bufnr('')] = 1 | endif
|
|
|
|
try
|
|
call call(function('ingo#actions#ExecuteOrFunc'), a:000)
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
endtry
|
|
|
|
let l:nextArgNr = s:GetNextArgNr(l:nextArgNr, a:alreadyVisitedBuffers)
|
|
if l:nextArgNr == -1
|
|
break
|
|
endif
|
|
|
|
try
|
|
execute 'noautocmd silent keepalt' l:nextArgNr . 'argument'
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
endtry
|
|
endwhile
|
|
finally
|
|
if l:didSplit
|
|
noautocmd silent! close!
|
|
noautocmd execute l:previousWinNr . 'wincmd w'
|
|
noautocmd execute l:originalWinNr . 'wincmd w'
|
|
silent! execute l:originalWindowLayout
|
|
endif
|
|
endtry
|
|
|
|
return l:failureCnt
|
|
endfunction
|
|
|
|
function! s:GetNextBufNr( bufNr, alreadyVisitedBuffers )
|
|
let l:bufNr = a:bufNr + 1 " Try next buffer.
|
|
let l:lastBufNr = bufnr('$')
|
|
while l:bufNr <= l:lastBufNr
|
|
if buflisted(l:bufNr) && (type(a:alreadyVisitedBuffers) != type({}) || ! has_key(a:alreadyVisitedBuffers, l:bufNr))
|
|
return l:bufNr
|
|
endif
|
|
|
|
" That one was already visited; continue searching.
|
|
let l:bufNr += 1
|
|
endwhile
|
|
return -1
|
|
endfunction
|
|
function! ingo#actions#iterations#BufDo( alreadyVisitedBuffers, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Invoke a:Action on each listed buffer, unless the buffer is in
|
|
" a:alreadyVisitedBuffers.
|
|
"* SEE ALSO:
|
|
" To execute an Action in a single visible buffer, use
|
|
" ingo#buffer#visible#Execute() / ingo#buffer#visible#Call().
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" Prints any Vim exception as error message.
|
|
"* INPUTS:
|
|
" a:alreadyVisitedBuffers Dictionary with already visited buffer numbers
|
|
" as keys. Will be added to. Pass 0 or {} to visit
|
|
" _all_ buffers.
|
|
" a:Action Either a Funcref or Ex commands to be executed
|
|
" in each buffer.
|
|
" ... Arguments passed to an a:Action Funcref.
|
|
"* RETURN VALUES:
|
|
" Number of Vim exceptions raised while iterating through the buffer list
|
|
" (e.g. errors when loading buffers) or from executing a:Action.
|
|
"******************************************************************************
|
|
let l:originalWindowLayout = winrestcmd()
|
|
let l:originalWinNr = winnr()
|
|
let l:previousWinNr = winnr('#') ? winnr('#') : 1
|
|
|
|
let l:nextBufNr = s:GetNextBufNr(0, a:alreadyVisitedBuffers)
|
|
if l:nextBufNr == -1
|
|
return | " No buffers left.
|
|
endif
|
|
|
|
let l:didSplit = 0
|
|
let l:failureCnt = 0
|
|
let l:save_switchbuf = &switchbuf | set switchbuf= | " :sbuffer should always open a new split (so we can :close it without checking).
|
|
try
|
|
try
|
|
execute 'noautocmd silent keepalt leftabove' l:nextBufNr . 'sbuffer'
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
if bufnr('') != l:nextBufNr
|
|
" We failed to split to the target buffer; bail out, as we need
|
|
" the split.
|
|
return l:failureCnt
|
|
endif
|
|
finally
|
|
let &switchbuf = l:save_switchbuf
|
|
endtry
|
|
|
|
let l:didSplit = 1
|
|
while 1
|
|
let l:bufNr = bufnr('')
|
|
if type(a:alreadyVisitedBuffers) == type({}) | let a:alreadyVisitedBuffers[bufnr('')] = 1 | endif
|
|
|
|
try
|
|
call call(function('ingo#actions#ExecuteOrFunc'), a:000)
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
endtry
|
|
|
|
let l:nextBufNr = s:GetNextBufNr(l:nextBufNr, a:alreadyVisitedBuffers)
|
|
if l:nextBufNr == -1
|
|
break
|
|
endif
|
|
|
|
try
|
|
execute 'noautocmd silent keepalt' l:nextBufNr . 'buffer'
|
|
catch
|
|
call ingo#msg#VimExceptionMsg()
|
|
let l:failureCnt += 1
|
|
endtry
|
|
endwhile
|
|
finally
|
|
if l:didSplit
|
|
noautocmd silent! close!
|
|
noautocmd execute l:previousWinNr . 'wincmd w'
|
|
noautocmd execute l:originalWinNr . 'wincmd w'
|
|
silent! execute l:originalWindowLayout
|
|
endif
|
|
endtry
|
|
|
|
return l:failureCnt
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|