"--------------- Buffer Registration Interface
--------------------------------------------------------
"--------------------------------------------------------------------------------------
" Function:
" "RegisterAllValidBuffersForSession()"
" Collect the basenames of all suitable buffers and save them in a file
" that is named after the session-file with a special suffix
" Function:
" "MarkAllNonRegisteredBuffersToBeDeleted()"
" The correpsonding buffer numbers are collected in a list for later deletion
" Function:
" "GetBufferDeletionCommandIfAppropriateAndConcatCommand( concatCommand )"
" I best give a mapping as an example to show how it can be used.
" :map <F8> :call
MarkAllNonRegisteredBuffersToBeDeleted()<CR>:ls<CR>:<C-R>=GetBufferDeletionCommandIfAppropriateAndConcatCommand("Mks")<CR>
" - call MarkAllNonRegisteredBuffersToBeDeleted()
" - :ls
" - Use this function to get something like this ":bd 28 29 | mks" on
" the commandline and be able to inspect it
" ... I tryed to simplify it but, I think buffer deletion is not possible
" when pulling the expressions register.
"--------------- Remote Instance Interaction Interface
--------------------------------------------------------
"--------------------------------------------------------------------------------------
" Command:
" "TurnOnForeignBufferAutoHandling()"
" This sets the autocommand for the SwapExistsEvent. From then on every attempt
to
" open an already edited file, will record a remote command, and unless
interrupted when
" prompted, will TryToDispatchPendingRemoteCommand()
" Command:
" "TryToDispatchPendingRemoteCommand()"
" Normally done automatically, but can also be done manually with this function
" Command:
" "ReturnToTheSourceServerOfTheLastRemoteCommand()"
" Exactly!
" Command:
" "ShowRegisteredCommand()"
" "ShowBuffersEditedByAnotherSession()"
" "ShowLastRemoteCommandSourceServer()"
" "ShowLastReceivedRemoteCommand()"
" Display information only
" Command:
" "SetRemoteInteractionContext( contextSpecifier )"
" The intention for a context was to clone the quickfix buffer remotely.
" Currently it's not interpreted.
" Command:
" "SetPendingRemoteCommand(RemoteBuffer,RemoteCommand,Context)"
" Needed only if you want to issue remote commands yourself.
" RemoteCommand is :executed with "normal " prepended. Notes relating to the
" corresponding manual entry:
" - The opening angle-bracket of vim-keycodes eg <ESC> must be escpaped
"\<ESC>"
" - Mappings will be used. Executed without '!' (If the [!] is given,
mappings will not be used.) TODO???
" - (The display isn't updated while ":normal" is busy.) The {cmd} is
aborted automatically
" if not completed, but that does not happen until you do
something else (press a key, ...)
" - A ":" command must be completed as well.
"---------------------------------------------------------------------------------------------------------
"--------------- Tools
----------------------------------------------------------------
"---------------------------------------------------------------------------------------------------------
fun! GetBasenameOfPath( path )
let s:lastPathSepIdx = strridx( a:path , "/" )
if s:lastPathSepIdx == -1
return a:path
else
return strpart( a:path , s:lastPathSepIdx + 1 )
endif
endfun
fun! ColorMessageOutput( putItThatWay )
echohl WarningMsg | echomsg a:putItThatWay | echohl None
endfun
fun! ColorMessageOutputConfirm( putItThatWay )
echohl WarningMsg | call input( a:putItThatWay ) | echohl None
endfun
"---------------------------------------------------------------------------------------------------------
"--------------- Buffer Registration Impl
----------------------------------------------------------------
"---------------------------------------------------------------------------------------------------------
" This is appended to 'v:this_session' to form the filename for the registered
" buffers
let s:BufferListFilenameExtension = ".RegisteredBuffers"
if ! exists("g:RegisteredBuffersForSession")
let g:RegisteredBuffersForSession = []
endif
if ! exists("g:ToBeDeletedBufferKludge")
" had some problems with deletion. Maybe
" iteration-list manipulation, or similar. Anyway, this is used to
" aggregate the buffernumbers to be deleted by ':bd' with one call.
let g:ToBeDeletedBufferKludge= []
endif
fun! s:OneBuffer_RegisterCurrentBufferForSession()
" Skipping some buffer types
let l:AlwaysSkippedBufferTypes = [ "nofile" , "nowrite" ,
"quickfix" , "help" ]
if strlen( &buftype ) != 0
if index( l:AlwaysSkippedBufferTypes , &buftype ) != -1
call ColorMessageOutput( "Skipping buffer-type
for session : " . expand("%") . " type:" . &buftype )
return
endif
endif
" Skipping readonly buffers
if &readonly == 1
call ColorMessageOutput( "Skipping readonly buffer for
session : " . expand("%") )
return
endif
" Truncating the path so that it is only the filename
let l:FileNamePartOfBufferPath = GetBasenameOfPath( expand("%")
)
if strlen( l:FileNamePartOfBufferPath ) == 0
" can happen: new buffers do not have the type "nofile"
call ColorMessageOutput( "Empty Name bufnum:" .
bufnr("%") )
return
endif
" Registering
call add( g:RegisteredBuffersForSession ,
l:FileNamePartOfBufferPath )
endfun
fun! s:GetBufferListFileName()
if strlen( v:this_session ) == 0
throw "no session, need a session"
endif
return v:this_session . s:BufferListFilenameExtension
endfun
fun! s:WriteBufferListToFile()
let s:BufferListFileName = s:GetBufferListFileName()
call writefile( g:RegisteredBuffersForSession , s:BufferListFileName )
endfun
fun! s:ReadBufferListFromFile()
let s:BufferListFileName = s:GetBufferListFileName()
if ! filereadable( s:BufferListFileName )
throw "RegisteredBuffers filename not readable : " .
s:BufferListFileName
endif
let g:RegisteredBuffersForSession = readfile( s:BufferListFileName )
echo "Did restore these : "
echo g:RegisteredBuffersForSession
endfun
fun! s:InsertThisBufferIntoTheToBeDeletedListIfUnmodified()
if &modified != 0
throw "Buffer is modified"
endif
call add( g:ToBeDeletedBufferKludge , bufnr("%") )
endfun
fun! s:MarkCurrentBufferToBeDeletedIfNotRegisteredAndUnmodified()
let s:AlwaysKeptBufferTypes = [ "quickfix" ]
if strlen( &buftype ) != 0
if index( s:AlwaysKeptBufferTypes , &buftype ) != -1
call ColorMessageOutput( "Keeping buffer-type : " .
expand("%") . " type:" . &buftype )
return
endif
endif
let s:FileNamePartOfBufferPath = GetBasenameOfPath( expand("%") )
if strlen( s:FileNamePartOfBufferPath ) == 0
" can happen: new buffers do not have the type "nofile"
" bufnr("%") failed once,
call ColorMessageOutput( "Empty Name bufnum:" . expand("%") )
call s:InsertThisBufferIntoTheToBeDeletedListIfUnmodified()
return
" TODO does that work correctly together with exceptions?
" a return?
endif
if index( g:RegisteredBuffersForSession , s:FileNamePartOfBufferPath )
== -1
call ColorMessageOutput( "Unregisterd Buffer bufname:" .
s:FileNamePartOfBufferPath . " bufnr:" . bufnr("%") )
call s:InsertThisBufferIntoTheToBeDeletedListIfUnmodified()
endif
endfun
fun! RegisterAllValidBuffersForSession()
" Can not be part of ':bufdo' iteration, as they are inaccessible
call EmptyOutTheAggregationOfBuffersEditedWithinAnotherSession()
let g:RegisteredBuffersForSession = []
let l:CurrentBufNum = bufnr("%")
try
bufdo call s:OneBuffer_RegisterCurrentBufferForSession()
finally
if bufexists( l:CurrentBufNum )
execute "bu " . l:CurrentBufNum
else
call ColorMessageOutput( "Warning : buffer disappeared while
registering buffers"
endif
endtry
call s:WriteBufferListToFile()
echo "Registered these : "
echo g:RegisteredBuffersForSession
endfun
fun! MarkAllNonRegisteredBuffersToBeDeleted()
" Can not be part of ':bufdo' iteration, as they are inaccessible
call EmptyOutTheAggregationOfBuffersEditedWithinAnotherSession()
" I don't know why ':bufdo' closes quickfix-window if it is the current
buffer...
let l:QuickFixGetsClosedIfCurrentWorkaround = 0
if &buftype == "quickfix"
let l:QuickFixGetsClosedIfCurrentWorkaround = 1
execute 0 . "tabnext"
endif
let g:ToBeDeletedBufferKludge = []
call s:ReadBufferListFromFile()
let l:CurrentBufNum = bufnr("%")
try
bufdo call
s:MarkCurrentBufferToBeDeletedIfNotRegisteredAndUnmodified()
finally
if l:QuickFixGetsClosedIfCurrentWorkaround == 1
tabl
else
if bufexists( l:CurrentBufNum )
execute "bu " . l:CurrentBufNum
endif
endif
endtry
endfun
" This can be used to get something like ":bd 1 2 3 | mks" on the commandline
" and inspect it prior to executing
:fun! GetBufferDeletionCommandIfAppropriateAndConcatCommand( concatCommand )
" Causes " E523: Not allowed here: bu 4 " when executed through pulling the
" expression-register
"call MarkAllNonRegisteredBuffersToBeDeleted()
"ls
let l:concat = ""
if strlen( a:concatCommand ) > 0
let l:concat = "|" . a:concatCommand
endif
if empty( g:ToBeDeletedBufferKludge )
return a:concatCommand
else
let l:BufferNumbersToBeDeleted = join(g:ToBeDeletedBufferKludge," ")
let g:ToBeDeletedBufferKludge = []
return "bd " . l:BufferNumbersToBeDeleted . l:concat
endif
:endfun
"---------------------------------------------------------------------------------------------------------
"--------------- Remote Instance Interaction Impl
--------------------------------------------------------
"---------------------------------------------------------------------------------------------------------
"---------------
" Command Buffer
"---------------
" Note : 'v:servername' envvar that hold the current instance's servername
" The command's data isjust a list with predefined indizes:
let s:BufferIdx = 0
let s:CommandIdx = 1
let s:ContextIdx = 2
let s:RemoteAcceptorFunName = "AcceptRemoteCommandFromClient"
let s:UnhandledRetval = "unhandled"
let s:HandledRetval = "successfullyHandled"
if ! exists("s:RemoteInteractionCommand")
let s:RemoteInteractionCommand = []
endif
if ! exists("s:SourceServerOfLastRemoteCommand")
let s:SourceServerOfLastRemoteCommand = ""
endif
if ! exists("s:LastReceivedRemoteCommand")
let s:LastReceivedRemoteCommand = ""
endif
if ! exists("s:RemoteInteractionContextSpecifier")
let s:RemoteInteractionContextSpecifier = ""
endif
if ! exists('s:AggregationOfBuffersEditedWithinAnotherSession')
let s:AggregationOfBuffersEditedWithinAnotherSession = {}
endif
" Yet unused, but for future use, clients would set the context with this
" function prior to executing a command that could reveal buffers within
" another session (make, tags, ...)
fun! SetRemoteInteractionContext( contextSpecifier )
let s:RemoteInteractionContextSpecifier = a:contextSpecifier
endfun
"--------------------------------------------------------------------------------------
" Accept Commands from a client
"--------------------------------------------------------------------------------------
fun!
{s:RemoteAcceptorFunName}(SourceServer,WantedBuffer,WantedCommand,ContextOfCommand)
" Check if this session has the requested buffer.
let l:RelevantBuffer = bufnr( a:WantedBuffer )
if l:RelevantBuffer == -1
return s:UnhandledRetval
endif
" inform
call ColorMessageOutput( printf( "Accepting a remote command.
SourceServer(%s) WantedBuffer(%s) WantedCommand(%s) Context(%s)",
a:SourceServer , a:WantedBuffer , a:WantedCommand , a:Context )
" record the source server and the command
let s:SourceServerOfLastRemoteCommand = a:SourceServer
let s:LastReceivedRemoteCommand = printf( "SourceServer(%s) Buffer(%s)
Command(%s) Context(%s)" , a:SourceServer , a:WantedBuffer , strtrans(
a:WantedCommand ) , a:ContextOfCommand )
" this is an utility to jump to/from the last tab page which is kind of
a rummaging tab.
" MAILTODO : remove this.
call SetLastTabSwitchTabPageNumber()
" go to last tab, top left-window , visualize the buffer, and present it
tabl
wincmd t
execute ":buffer " . l:RelevantBuffer
call foreground()
" Assert we are in the right buffer
if bufnr("%") != bufnr( l:RelevantBuffer )
throw "Assertion of being in the right buffer immediately
before command execution failed"
endif
" do what must be done
call ColorMessageOutput( "executing '" . strtrans(a:WantedCommand). "'"
)
call ColorMessageOutput( "strlen is (want to know if special chars are
converted) : " . strlen(a:WantedCommand) )
let v:errormsg = ""
" As it appears ':normal xyz' executed in insert mode (from remote)
does not
" insert these chars but executes them in normal mode (hey, now I get
the name, I though it would mean 'unimpressed').
" It seems that you can't even leave insert mode by prepending
"\<C-\>\<C-N>"
" Anyway, the former renders the latter unnecessary.
" double escape would not hurt, would it? It ensures the command is
terminated.
" Necessary for example for the ':ta' swapcommands
execute ":normal " . expand(a:WantedCommand) . "\e"
if strlen( v:errormsg ) != 0
echo "(TODO: duplicated output of errormsg?)" . v:errormsg
endif
return s:HandledRetval
endfun
"--------------------------------------------------------------------------------------
" Server Focus
"--------------------------------------------------------------------------------------
fun! ReturnToTheSourceServerOfTheLastRemoteCommand()
if strlen(s:SourceServerOfLastRemoteCommand) == 0
call ColorMessageOutput( "Cannot return, no source server
recorded" )
return
endif
try
call remote_expr( s:SourceServerOfLastRemoteCommand ,
"foreground()" )
catch /E241/
call ColorMessageOutput( s:SourceServerOfLastRemoteCommand . ":
no such server. Giving focus to that server failed." )
endtry
endfun
"--------------------------------------------------------------------------------------
" Sending a Command Request
"--------------------------------------------------------------------------------------
fun! GimmeListOfTheServersWithoutCurrentServerAsAnActualList()
let l:WholeList = split( serverlist() )
let l:ThisServersIndex = index( l:WholeList , v:servername )
if ( l:ThisServersIndex == -1 )
throw "serverlist without v:servername - unexpected"
endif
call remove( l:WholeList , l:ThisServersIndex )
if index( l:WholeList , v:servername ) != -1
throw "Failed to remove v:servername from serverlist() -
unexpected, should really work"
endif
return l:WholeList
endfun
" TODO ELIGIBLE ELLIGIBLE?
fun! GetPendingRemoteCommandAsAnExpressionEligibleForSending()
" empty return-string means no command pending.
if empty( s:RemoteInteractionCommand ) == 1
return ""
endif
if len( s:RemoteInteractionCommand ) != 3
throw "Invalid len(s:RemoteInteractionCommand) : " . string(
s:RemoteInteractionCommand )
endif
let l:BufferBase = GetBasenameOfPath( s:RemoteInteractionCommand[
s:BufferIdx ] )
let l:RemCommand = s:RemoteInteractionCommand[ s:CommandIdx ]
let l:Context = s:RemoteInteractionCommand[ s:ContextIdx ]
let l:RequestedRemoteCommand = printf( "%s('%s','%s','%s','%s')" ,
s:RemoteAcceptorFunName , v:servername , l:BufferBase , l:RemCommand ,
l:Context )
return l:RequestedRemoteCommand
endfun
fun! SetPendingRemoteCommand(RemoteBuffer,RemoteCommand,Context)
let s:RemoteInteractionCommand = [ a:RemoteBuffer , a:RemoteCommand ,
a:Context]
endfun
fun! TryToDispatchPendingRemoteCommand()
let l:RemoteCommandToBeDispatched =
GetPendingRemoteCommandAsAnExpressionEligibleForSending()
if strlen( l:RemoteCommandToBeDispatched ) == 0
call ColorMessageOutput( "No remote command pending" )
return
endif
call ColorMessageOutput( "Trying to dispatch this : " . strtrans(
l:RemoteCommandToBeDispatched ) )
let l:HasCommandBeenHandled = ""
for l:CandidateServer in
GimmeListOfTheServersWithoutCurrentServerAsAnActualList()
try
let l:HasCommandBeenHandled = remote_expr(
l:CandidateServer , l:RemoteCommandToBeDispatched )
if l:HasCommandBeenHandled == s:HandledRetval
call ColorMessageOutput( printf( "server(%s)
accepted the command" , l:CandidateServer ) )
break
endif
catch /E241/
throw "Impossible? A server from the serverlist()
disappeared"
endtry
endfor
if l:HasCommandBeenHandled != s:HandledRetval
call ColorMessageOutput( "No server accepted the command" )
endif
endfun
"--------------------------------------------------------------------------------------
" SwapExistsEvent handling, and recording/deleting the buffers which are added
but not loaded
"--------------------------------------------------------------------------------------
fun! EmptyOutTheAggregationOfBuffersEditedWithinAnotherSession()
for l:BuffernameToDelete in
keys(s:AggregationOfBuffersEditedWithinAnotherSession)
let l:ToBeDeletedBufNumber = bufnr( l:BuffernameToDelete )
if l:ToBeDeletedBufNumber == -1
call ColorMessageOutput( "Buffer does not exist : " .
l:BuffernameToDelete )
continue
elseif bufloaded( l:ToBeDeletedBufNumber )
call ColorMessageOutput( "Buffer is now loaded. keeping
it : " . l:BuffernameToDelete )
elseif ! buflisted( l:ToBeDeletedBufNumber )
call ColorMessageOutput( "Buffer is now missing -
unexpected: " . l:BuffernameToDelete )
else
" is not loaded can't be modified, will produce error
otherwise (no '!' after 'bd')
call ColorMessageOutput( "Deleting foreign buffer : " .
l:BuffernameToDelete )
execute( "bd " . l:ToBeDeletedBufNumber )
endif
endfor
let s:AggregationOfBuffersEditedWithinAnotherSession = {}
endfun
fun! HandleSwapExistsEventAndRecordRemoteCommand( BufferBeingEditedAlready )
let l:BufferBeingEditedAlreadyBasename = GetBasenameOfPath(
a:BufferBeingEditedAlready )
" Don't know how to prevent the remote buffers from being recorded in
the
" local bufferlist Can't delete them right here (probably sandbox)
let s:AggregationOfBuffersEditedWithinAnotherSession[
BufferBeingEditedAlreadyBasename ] = 0
call SetPendingRemoteCommand( BufferBeingEditedAlreadyBasename ,
v:swapcommand , s:RemoteInteractionContextSpecifier )
" a-bort (alternative would be q-uit). Both add the buffer to the
bufferlist.
let v:swapchoice = 'a'
call ColorMessageOutputConfirm( printf("Buffer edited by another
session. Recorded remote command : buffer(%s) command(%s). Going to
dispatch..." , BufferBeingEditedAlreadyBasename , strtrans(v:swapcommand) ) )
call TryToDispatchPendingRemoteCommand()
endfun
fun! TurnOnForeignBufferAutoHandling()
au! SwapExists * call HandleSwapExistsEventAndRecordRemoteCommand(
expand('<afile>') )
endfun
"--------------------------------------------------------------------------------------
" Info
"--------------------------------------------------------------------------------------
fun! ShowRegisteredCommand()
echo "Remote Command is (strtrans converted): " . strtrans(
GetPendingRemoteCommandAsAnExpressionEligibleForSending() )
endfun
fun! ShowLastRemoteCommandSourceServer()
echo "Last Remote Command Source Server is : " .
s:SourceServerOfLastRemoteCommand
endfun
fun! ShowLastReceivedRemoteCommand()
echo "Last Received Remote Command is : " . s:LastReceivedRemoteCommand
endfun
fun! ShowBuffersEditedByAnotherSession()
echo "Buffers edited by another session : " .
string(keys(s:AggregationOfBuffersEditedWithinAnotherSession))
endfun
--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---