contrib/vim/hgcommand.vim
changeset 2591 61f2008cd6bf
child 2592 457846f400e8
equal deleted inserted replaced
2576:6a961a54f953 2591:61f2008cd6bf
       
     1 " vim600: set foldmethod=marker:
       
     2 "
       
     3 " Vim plugin to assist in working with CVS-controlled files.
       
     4 "
       
     5 " Last Change:   2006/02/22
       
     6 " Version:       1.76
       
     7 " Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
       
     8 " License:       This file is placed in the public domain.
       
     9 " Credits: {{{1
       
    10 "                Mathieu Clabaut for many suggestions and improvements.
       
    11 "
       
    12 "                Suresh Govindachar and Jeeva Chelladhurai for finding waaaay
       
    13 "                too many bugs.
       
    14 "
       
    15 "                Suresh Govindachar (again!) for finding the
       
    16 "                fully-folded-last-line-delete bug.
       
    17 "
       
    18 "                Albrecht Gass for the Delete-on-Hide behavior suggestion.
       
    19 "
       
    20 "                Joe MacDonald for finding the CVS log message header bug and
       
    21 "                pointing out that buffer refreshes are needed after CVS
       
    22 "                \%(un\)\?edit.
       
    23 "
       
    24 "                Srinath Avadhanula for the suggestion and original patch for
       
    25 "                the CVSCommitOnWrite option and mapping hot key.
       
    26 "
       
    27 "                John Sivak for helping to debug Windows issues and suggesting
       
    28 "                the CVSEditors and CVSWatchers commands.
       
    29 "
       
    30 "                Igor Levko for the patch to recognize numerical sticky tags.
       
    31 "
       
    32 "                Domink Strasser for the patch to correct the status line for
       
    33 "                CVSAdd'd files.
       
    34 "
       
    35 "                Weerapong Sirikanya for finding a bug with CVSCommit and
       
    36 "                autochdir.
       
    37 "
       
    38 "                David Gotz for finding a bug with CVSVimDiff buffer splitting
       
    39 "                and original buffer restoration.
       
    40 "
       
    41 "                CJ van den Berg for the patch to not change working directory
       
    42 "                when editing a non-CVS file.
       
    43 "
       
    44 "                Luca Gerli for noticing bad behavior for keywords in files
       
    45 "                after commit if split windows are used.
       
    46 
       
    47 " Section: Documentation {{{1
       
    48 "
       
    49 " Provides functions to invoke various CVS commands on the current file
       
    50 " (either the current buffer, or, in the case of an directory buffer, the file
       
    51 " on the current line).  The output of the commands is captured in a new
       
    52 " scratch window.  For convenience, if the functions are invoked on a CVS
       
    53 " output window, the original file is used for the cvs operation instead after
       
    54 " the window is split.  This is primarily useful when running CVSCommit and
       
    55 " you need to see the changes made, so that CVSDiff is usable and shows up in
       
    56 " another window.
       
    57 "
       
    58 " Command documentation {{{2
       
    59 "
       
    60 " CVSAdd           Performs "cvs add" on the current file.
       
    61 "
       
    62 " CVSAnnotate      Performs "cvs annotate" on the current file.  If an
       
    63 "                  argument is given, the argument is used as a revision
       
    64 "                  number to display.  If not given an argument, it uses the
       
    65 "                  most recent version of the file on the current branch.
       
    66 "                  Additionally, if the current buffer is a CVSAnnotate buffer
       
    67 "                  already, the version number on the current line is used.
       
    68 "
       
    69 "                  If the 'CVSCommandAnnotateParent' variable is set to a
       
    70 "                  non-zero value, the version previous to the one on the
       
    71 "                  current line is used instead.  This allows one to navigate
       
    72 "                  back to examine the previous version of a line.
       
    73 "
       
    74 " CVSCommit[!]     If called with arguments, this performs "cvs commit" using
       
    75 "                  the arguments as the log message.
       
    76 "
       
    77 "                  If '!' is used, an empty log message is committed.
       
    78 "
       
    79 "                  If called with no arguments, this is a two-step command.
       
    80 "                  The first step opens a buffer to accept a log message.
       
    81 "                  When that buffer is written, it is automatically closed and
       
    82 "                  the file is committed using the information from that log
       
    83 "                  message.  The commit can be abandoned if the log message
       
    84 "                  buffer is deleted or wiped before being written.
       
    85 "
       
    86 " CVSDiff          With no arguments, this performs "cvs diff" on the current
       
    87 "                  file.  With one argument, "cvs diff" is performed on the
       
    88 "                  current file against the specified revision.  With two
       
    89 "                  arguments, cvs diff is performed between the specified
       
    90 "                  revisions of the current file.  This command uses the
       
    91 "                  'CVSCommandDiffOpt' variable to specify diff options.  If
       
    92 "                  that variable does not exist, then 'wbBc' is assumed.  If
       
    93 "                  you wish to have no options, then set it to the empty
       
    94 "                  string.
       
    95 "
       
    96 " CVSEdit          Performs "cvs edit" on the current file.
       
    97 "
       
    98 " CVSEditors       Performs "cvs editors" on the current file.
       
    99 "
       
   100 " CVSGotoOriginal  Returns the current window to the source buffer if the
       
   101 "                  current buffer is a CVS output buffer.
       
   102 "
       
   103 " CVSLog           Performs "cvs log" on the current file.
       
   104 "
       
   105 " CVSRevert        Replaces the modified version of the current file with the
       
   106 "                  most recent version from the repository.
       
   107 "
       
   108 " CVSReview        Retrieves a particular version of the current file.  If no
       
   109 "                  argument is given, the most recent version of the file on
       
   110 "                  the current branch is retrieved.  The specified revision is
       
   111 "                  retrieved into a new buffer.
       
   112 "
       
   113 " CVSStatus        Performs "cvs status" on the current file.
       
   114 "
       
   115 " CVSUnedit        Performs "cvs unedit" on the current file.
       
   116 "
       
   117 " CVSUpdate        Performs "cvs update" on the current file.
       
   118 "
       
   119 " CVSVimDiff       With no arguments, this prompts the user for a revision and
       
   120 "                  then uses vimdiff to display the differences between the
       
   121 "                  current file and the specified revision.  If no revision is
       
   122 "                  specified, the most recent version of the file on the
       
   123 "                  current branch is used.  With one argument, that argument
       
   124 "                  is used as the revision as above.  With two arguments, the
       
   125 "                  differences between the two revisions is displayed using
       
   126 "                  vimdiff.
       
   127 "
       
   128 "                  With either zero or one argument, the original buffer is used
       
   129 "                  to perform the vimdiff.  When the other buffer is closed, the
       
   130 "                  original buffer will be returned to normal mode.
       
   131 "
       
   132 "                  Once vimdiff mode is started using the above methods,
       
   133 "                  additional vimdiff buffers may be added by passing a single
       
   134 "                  version argument to the command.  There may be up to 4
       
   135 "                  vimdiff buffers total.
       
   136 "
       
   137 "                  Using the 2-argument form of the command resets the vimdiff
       
   138 "                  to only those 2 versions.  Additionally, invoking the
       
   139 "                  command on a different file will close the previous vimdiff
       
   140 "                  buffers.
       
   141 "
       
   142 " CVSWatch         Takes an argument which must be one of [on|off|add|remove].
       
   143 "                  Performs "cvs watch" with the given argument on the current
       
   144 "                  file.
       
   145 "
       
   146 " CVSWatchers      Performs "cvs watchers" on the current file.
       
   147 "
       
   148 " CVSWatchAdd      Alias for "CVSWatch add"
       
   149 "
       
   150 " CVSWatchOn       Alias for "CVSWatch on"
       
   151 "
       
   152 " CVSWatchOff      Alias for "CVSWatch off"
       
   153 "
       
   154 " CVSWatchRemove   Alias for "CVSWatch remove"
       
   155 "
       
   156 " Mapping documentation: {{{2
       
   157 "
       
   158 " By default, a mapping is defined for each command.  User-provided mappings
       
   159 " can be used instead by mapping to <Plug>CommandName, for instance:
       
   160 "
       
   161 " nnoremap ,ca <Plug>CVSAdd
       
   162 "
       
   163 " The default mappings are as follow:
       
   164 "
       
   165 "   <Leader>ca CVSAdd
       
   166 "   <Leader>cn CVSAnnotate
       
   167 "   <Leader>cc CVSCommit
       
   168 "   <Leader>cd CVSDiff
       
   169 "   <Leader>ce CVSEdit
       
   170 "   <Leader>ci CVSEditors
       
   171 "   <Leader>cg CVSGotoOriginal
       
   172 "   <Leader>cG CVSGotoOriginal!
       
   173 "   <Leader>cl CVSLog
       
   174 "   <Leader>cr CVSReview
       
   175 "   <Leader>cs CVSStatus
       
   176 "   <Leader>ct CVSUnedit
       
   177 "   <Leader>cu CVSUpdate
       
   178 "   <Leader>cv CVSVimDiff
       
   179 "   <Leader>cwv CVSWatchers
       
   180 "   <Leader>cwa CVSWatchAdd
       
   181 "   <Leader>cwn CVSWatchOn
       
   182 "   <Leader>cwa CVSWatchOff
       
   183 "   <Leader>cwr CVSWatchRemove
       
   184 "
       
   185 " Options documentation: {{{2
       
   186 "
       
   187 " Several variables are checked by the script to determine behavior as follow:
       
   188 "
       
   189 " CVSCommandAnnotateParent
       
   190 "   This variable, if set to a non-zero value, causes the zero-argument form
       
   191 "   of CVSAnnotate when invoked on a CVSAnnotate buffer to go to the version
       
   192 "   previous to that displayed on the current line.  If not set, it defaults
       
   193 "   to 0.
       
   194 "
       
   195 " CVSCommandCommitOnWrite
       
   196 "   This variable, if set to a non-zero value, causes the pending cvs commit
       
   197 "   to take place immediately as soon as the log message buffer is written.
       
   198 "   If set to zero, only the CVSCommit mapping will cause the pending commit
       
   199 "   to occur.  If not set, it defaults to 1.
       
   200 "
       
   201 " CVSCommandDeleteOnHide
       
   202 "   This variable, if set to a non-zero value, causes the temporary CVS result
       
   203 "   buffers to automatically delete themselves when hidden.
       
   204 "
       
   205 " CVSCommandDiffOpt
       
   206 "   This variable, if set, determines the options passed to the diff command
       
   207 "   of CVS.  If not set, it defaults to 'wbBc'.
       
   208 "
       
   209 " CVSCommandDiffSplit
       
   210 "   This variable overrides the CVSCommandSplit variable, but only for buffers
       
   211 "   created with CVSVimDiff.
       
   212 "
       
   213 " CVSCommandEdit
       
   214 "   This variable controls whether the original buffer is replaced ('edit') or
       
   215 "   split ('split').  If not set, it defaults to 'edit'.
       
   216 "
       
   217 " CVSCommandEnableBufferSetup
       
   218 "   This variable, if set to a non-zero value, activates CVS buffer management
       
   219 "   mode.  This mode means that two buffer variables, 'CVSRevision' and
       
   220 "   'CVSBranch', are set if the file is CVS-controlled.  This is useful for
       
   221 "   displaying version information in the status bar.
       
   222 "
       
   223 " CVSCommandInteractive
       
   224 "   This variable, if set to a non-zero value, causes appropriate functions (for
       
   225 "   the moment, only CVSReview) to query the user for a revision to use
       
   226 "   instead of the current revision if none is specified.
       
   227 "
       
   228 " CVSCommandNameMarker
       
   229 "   This variable, if set, configures the special attention-getting characters
       
   230 "   that appear on either side of the cvs buffer type in the buffer name.
       
   231 "   This has no effect unless 'CVSCommandNameResultBuffers' is set to a true
       
   232 "   value.  If not set, it defaults to '_'.  
       
   233 "
       
   234 " CVSCommandNameResultBuffers
       
   235 "   This variable, if set to a true value, causes the cvs result buffers to be
       
   236 "   named in the old way ('<source file name> _<cvs command>_').  If not set
       
   237 "   or set to a false value, the result buffer is nameless.
       
   238 "
       
   239 " CVSCommandSplit
       
   240 "   This variable controls the orientation of the various window splits that
       
   241 "   may occur (such as with CVSVimDiff, when using a CVS command on a CVS
       
   242 "   command buffer, or when the 'CVSCommandEdit' variable is set to 'split'.
       
   243 "   If set to 'horizontal', the resulting windows will be on stacked on top of
       
   244 "   one another.  If set to 'vertical', the resulting windows will be
       
   245 "   side-by-side.  If not set, it defaults to 'horizontal' for all but
       
   246 "   CVSVimDiff windows.
       
   247 "
       
   248 " Event documentation {{{2
       
   249 "   For additional customization, cvscommand.vim uses User event autocommand
       
   250 "   hooks.  Each event is in the CVSCommand group, and different patterns
       
   251 "   match the various hooks.
       
   252 "
       
   253 "   For instance, the following could be added to the vimrc to provide a 'q'
       
   254 "   mapping to quit a CVS buffer:
       
   255 "
       
   256 "   augroup CVSCommand
       
   257 "     au CVSCommand User CVSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> 
       
   258 "   augroup END
       
   259 "
       
   260 "   The following hooks are available:
       
   261 "
       
   262 "   CVSBufferCreated           This event is fired just after a cvs command
       
   263 "                              result buffer is created and filled with the
       
   264 "                              result of a cvs command.  It is executed within
       
   265 "                              the context of the new buffer.
       
   266 "
       
   267 "   CVSBufferSetup             This event is fired just after CVS buffer setup
       
   268 "                              occurs, if enabled.
       
   269 "
       
   270 "   CVSPluginInit              This event is fired when the CVSCommand plugin
       
   271 "                              first loads.
       
   272 "
       
   273 "   CVSPluginFinish            This event is fired just after the CVSCommand
       
   274 "                              plugin loads.
       
   275 "
       
   276 "   CVSVimDiffFinish           This event is fired just after the CVSVimDiff
       
   277 "                              command executes to allow customization of,
       
   278 "                              for instance, window placement and focus.
       
   279 "
       
   280 " Section: Plugin header {{{1
       
   281 
       
   282 " loaded_cvscommand is set to 1 when the initialization begins, and 2 when it
       
   283 " completes.  This allows various actions to only be taken by functions after
       
   284 " system initialization.
       
   285 
       
   286 if exists("loaded_cvscommand")
       
   287    finish
       
   288 endif
       
   289 let loaded_cvscommand = 1
       
   290 
       
   291 if v:version < 602
       
   292   echohl WarningMsg|echomsg "CVSCommand 1.69 or later requires VIM 6.2 or later"|echohl None
       
   293   finish
       
   294 endif
       
   295 
       
   296 " Section: Event group setup {{{1
       
   297 
       
   298 augroup CVSCommand
       
   299 augroup END
       
   300 
       
   301 " Section: Plugin initialization {{{1
       
   302 silent do CVSCommand User CVSPluginInit
       
   303 
       
   304 " Section: Script variable initialization {{{1
       
   305 
       
   306 let s:CVSCommandEditFileRunning = 0
       
   307 unlet! s:vimDiffRestoreCmd
       
   308 unlet! s:vimDiffSourceBuffer
       
   309 unlet! s:vimDiffBufferCount
       
   310 unlet! s:vimDiffScratchList
       
   311 
       
   312 " Section: Utility functions {{{1
       
   313 
       
   314 " Function: s:CVSResolveLink() {{{2
       
   315 " Fully resolve the given file name to remove shortcuts or symbolic links.
       
   316 
       
   317 function! s:CVSResolveLink(fileName)
       
   318   let resolved = resolve(a:fileName)
       
   319   if resolved != a:fileName
       
   320     let resolved = s:CVSResolveLink(resolved)
       
   321   endif
       
   322   return resolved
       
   323 endfunction
       
   324 
       
   325 " Function: s:CVSChangeToCurrentFileDir() {{{2
       
   326 " Go to the directory in which the current CVS-controlled file is located.
       
   327 " If this is a CVS command buffer, first switch to the original file.
       
   328 
       
   329 function! s:CVSChangeToCurrentFileDir(fileName)
       
   330   let oldCwd=getcwd()
       
   331   let fileName=s:CVSResolveLink(a:fileName)
       
   332   let newCwd=fnamemodify(fileName, ':h')
       
   333   if strlen(newCwd) > 0
       
   334     execute 'cd' escape(newCwd, ' ')
       
   335   endif
       
   336   return oldCwd
       
   337 endfunction
       
   338 
       
   339 " Function: s:CVSGetOption(name, default) {{{2
       
   340 " Grab a user-specified option to override the default provided.  Options are
       
   341 " searched in the window, buffer, then global spaces.
       
   342 
       
   343 function! s:CVSGetOption(name, default)
       
   344   if exists("s:" . a:name . "Override")
       
   345     execute "return s:".a:name."Override"
       
   346   elseif exists("w:" . a:name)
       
   347     execute "return w:".a:name
       
   348   elseif exists("b:" . a:name)
       
   349     execute "return b:".a:name
       
   350   elseif exists("g:" . a:name)
       
   351     execute "return g:".a:name
       
   352   else
       
   353     return a:default
       
   354   endif
       
   355 endfunction
       
   356 
       
   357 " Function: s:CVSEditFile(name, origBuffNR) {{{2
       
   358 " Wrapper around the 'edit' command to provide some helpful error text if the
       
   359 " current buffer can't be abandoned.  If name is provided, it is used;
       
   360 " otherwise, a nameless scratch buffer is used.
       
   361 " Returns: 0 if successful, -1 if an error occurs.
       
   362 
       
   363 function! s:CVSEditFile(name, origBuffNR)
       
   364   "Name parameter will be pasted into expression.
       
   365   let name = escape(a:name, ' *?\')
       
   366 
       
   367   let editCommand = s:CVSGetOption('CVSCommandEdit', 'edit')
       
   368   if editCommand != 'edit'
       
   369     if s:CVSGetOption('CVSCommandSplit', 'horizontal') == 'horizontal'
       
   370       if name == ""
       
   371         let editCommand = 'rightbelow new'
       
   372       else
       
   373         let editCommand = 'rightbelow split ' . name
       
   374       endif
       
   375     else
       
   376       if name == ""
       
   377         let editCommand = 'vert rightbelow new'
       
   378       else
       
   379         let editCommand = 'vert rightbelow split ' . name
       
   380       endif
       
   381     endif
       
   382   else
       
   383     if name == ""
       
   384       let editCommand = 'enew'
       
   385     else
       
   386       let editCommand = 'edit ' . name
       
   387     endif
       
   388   endif
       
   389 
       
   390   " Protect against useless buffer set-up
       
   391   let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1
       
   392   try
       
   393     execute editCommand
       
   394   finally
       
   395     let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1
       
   396   endtry
       
   397 
       
   398   let b:CVSOrigBuffNR=a:origBuffNR
       
   399   let b:CVSCommandEdit='split'
       
   400 endfunction
       
   401 
       
   402 " Function: s:CVSCreateCommandBuffer(cmd, cmdName, statusText, filename) {{{2
       
   403 " Creates a new scratch buffer and captures the output from execution of the
       
   404 " given command.  The name of the scratch buffer is returned.
       
   405 
       
   406 function! s:CVSCreateCommandBuffer(cmd, cmdName, statusText, origBuffNR)
       
   407   let fileName=bufname(a:origBuffNR)
       
   408 
       
   409   let resultBufferName=''
       
   410 
       
   411   if s:CVSGetOption("CVSCommandNameResultBuffers", 0)
       
   412     let nameMarker = s:CVSGetOption("CVSCommandNameMarker", '_')
       
   413     if strlen(a:statusText) > 0
       
   414       let bufName=a:cmdName . ' -- ' . a:statusText
       
   415     else
       
   416       let bufName=a:cmdName
       
   417     endif
       
   418     let bufName=fileName . ' ' . nameMarker . bufName . nameMarker
       
   419     let counter=0
       
   420     let resultBufferName = bufName
       
   421     while buflisted(resultBufferName)
       
   422       let counter=counter + 1
       
   423       let resultBufferName=bufName . ' (' . counter . ')'
       
   424     endwhile
       
   425   endif
       
   426 
       
   427   let cvsCommand = s:CVSGetOption("CVSCommandCVSExec", "cvs") . " " . a:cmd
       
   428   let cvsOut = system(cvsCommand)
       
   429   " HACK:  diff command does not return proper error codes
       
   430   if v:shell_error && a:cmdName != 'cvsdiff'
       
   431     if strlen(cvsOut) == 0
       
   432       echoerr "CVS command failed"
       
   433     else
       
   434       echoerr "CVS command failed:  " . cvsOut
       
   435     endif
       
   436     return -1
       
   437   endif
       
   438   if strlen(cvsOut) == 0
       
   439     " Handle case of no output.  In this case, it is important to check the
       
   440     " file status, especially since cvs edit/unedit may change the attributes
       
   441     " of the file with no visible output.
       
   442 
       
   443     echomsg "No output from CVS command"
       
   444     checktime
       
   445     return -1
       
   446   endif
       
   447 
       
   448   if s:CVSEditFile(resultBufferName, a:origBuffNR) == -1
       
   449     return -1
       
   450   endif
       
   451 
       
   452   set buftype=nofile
       
   453   set noswapfile
       
   454   set filetype=
       
   455 
       
   456   if s:CVSGetOption("CVSCommandDeleteOnHide", 0)
       
   457     set bufhidden=delete
       
   458   endif
       
   459 
       
   460   silent 0put=cvsOut
       
   461 
       
   462   " The last command left a blank line at the end of the buffer.  If the
       
   463   " last line is folded (a side effect of the 'put') then the attempt to
       
   464   " remove the blank line will kill the last fold.
       
   465   "
       
   466   " This could be fixed by explicitly detecting whether the last line is
       
   467   " within a fold, but I prefer to simply unfold the result buffer altogether.
       
   468 
       
   469   if has('folding')
       
   470     normal zR
       
   471   endif
       
   472 
       
   473   $d
       
   474   1
       
   475 
       
   476   " Define the environment and execute user-defined hooks.
       
   477 
       
   478   let b:CVSSourceFile=fileName
       
   479   let b:CVSCommand=a:cmdName
       
   480   if a:statusText != ""
       
   481     let b:CVSStatusText=a:statusText
       
   482   endif
       
   483 
       
   484   silent do CVSCommand User CVSBufferCreated
       
   485   return bufnr("%")
       
   486 endfunction
       
   487 
       
   488 " Function: s:CVSBufferCheck(cvsBuffer) {{{2
       
   489 " Attempts to locate the original file to which CVS operations were applied
       
   490 " for a given buffer.
       
   491 
       
   492 function! s:CVSBufferCheck(cvsBuffer)
       
   493   let origBuffer = getbufvar(a:cvsBuffer, "CVSOrigBuffNR")
       
   494   if origBuffer
       
   495     if bufexists(origBuffer)
       
   496       return origBuffer
       
   497     else
       
   498       " Original buffer no longer exists.
       
   499       return -1 
       
   500     endif
       
   501   else
       
   502     " No original buffer
       
   503     return a:cvsBuffer
       
   504   endif
       
   505 endfunction
       
   506 
       
   507 " Function: s:CVSCurrentBufferCheck() {{{2
       
   508 " Attempts to locate the original file to which CVS operations were applied
       
   509 " for the current buffer.
       
   510 
       
   511 function! s:CVSCurrentBufferCheck()
       
   512   return s:CVSBufferCheck(bufnr("%"))
       
   513 endfunction
       
   514 
       
   515 " Function: s:CVSToggleDeleteOnHide() {{{2
       
   516 " Toggles on and off the delete-on-hide behavior of CVS buffers
       
   517 
       
   518 function! s:CVSToggleDeleteOnHide()
       
   519   if exists("g:CVSCommandDeleteOnHide")
       
   520     unlet g:CVSCommandDeleteOnHide
       
   521   else
       
   522     let g:CVSCommandDeleteOnHide=1
       
   523   endif
       
   524 endfunction
       
   525 
       
   526 " Function: s:CVSDoCommand(cvscmd, cmdName, statusText) {{{2
       
   527 " General skeleton for CVS function execution.
       
   528 " Returns: name of the new command buffer containing the command results
       
   529 
       
   530 function! s:CVSDoCommand(cmd, cmdName, statusText)
       
   531   let cvsBufferCheck=s:CVSCurrentBufferCheck()
       
   532   if cvsBufferCheck == -1 
       
   533     echo "Original buffer no longer exists, aborting."
       
   534     return -1
       
   535   endif
       
   536 
       
   537   let fileName=bufname(cvsBufferCheck)
       
   538   if isdirectory(fileName)
       
   539     let fileName=fileName . "/" . getline(".")
       
   540   endif
       
   541   let realFileName = fnamemodify(s:CVSResolveLink(fileName), ':t')
       
   542   let oldCwd=s:CVSChangeToCurrentFileDir(fileName)
       
   543   try
       
   544     if !filereadable('CVS/Root')
       
   545       throw fileName . ' is not a CVS-controlled file.'
       
   546     endif
       
   547     let fullCmd = a:cmd . ' "' . realFileName . '"'
       
   548     let resultBuffer=s:CVSCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, cvsBufferCheck)
       
   549     return resultBuffer
       
   550   catch
       
   551     echoerr v:exception
       
   552     return -1
       
   553   finally
       
   554     execute 'cd' escape(oldCwd, ' ')
       
   555   endtry
       
   556 endfunction
       
   557 
       
   558 
       
   559 " Function: s:CVSGetStatusVars(revision, branch, repository) {{{2
       
   560 "
       
   561 " Obtains a CVS revision number and branch name.  The 'revisionVar',
       
   562 " 'branchVar'and 'repositoryVar' arguments, if non-empty, contain the names of variables to hold
       
   563 " the corresponding results.
       
   564 "
       
   565 " Returns: string to be exec'd that sets the multiple return values.
       
   566 
       
   567 function! s:CVSGetStatusVars(revisionVar, branchVar, repositoryVar)
       
   568   let cvsBufferCheck=s:CVSCurrentBufferCheck()
       
   569   if cvsBufferCheck == -1 
       
   570     return ""
       
   571   endif
       
   572   let fileName=bufname(cvsBufferCheck)
       
   573   let realFileName = fnamemodify(s:CVSResolveLink(fileName), ':t')
       
   574   let oldCwd=s:CVSChangeToCurrentFileDir(fileName)
       
   575   try
       
   576     if !filereadable('CVS/Root')
       
   577       return ""
       
   578     endif
       
   579     let cvsCommand = s:CVSGetOption("CVSCommandCVSExec", "cvs") . " status " . escape(realFileName, ' *?\')
       
   580     let statustext=system(cvsCommand)
       
   581     if(v:shell_error)
       
   582       return ""
       
   583     endif
       
   584     let revision=substitute(statustext, '^\_.*Working revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\)\_.*$', '\1', "")
       
   585 
       
   586     " We can still be in a CVS-controlled directory without this being a CVS
       
   587     " file
       
   588     if match(revision, '^New file!$') >= 0 
       
   589       let revision="NEW"
       
   590     elseif match(revision, '^\d\+\.\d\+\%(\.\d\+\.\d\+\)*$') >=0
       
   591     else
       
   592       return ""
       
   593     endif
       
   594 
       
   595     let returnExpression = "let " . a:revisionVar . "='" . revision . "'"
       
   596 
       
   597     if a:branchVar != ""
       
   598       let branch=substitute(statustext, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', "")
       
   599       let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
       
   600     endif
       
   601 
       
   602     if a:repositoryVar != ""
       
   603       let repository=substitute(statustext, '^\_.*Repository revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\|No revision control file\)\_.*$', '\1', "")
       
   604       let repository=substitute(repository, '^New file!\|No revision control file$', 'NEW', "")
       
   605       let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . repository . "'"
       
   606     endif
       
   607 
       
   608     return returnExpression
       
   609   finally
       
   610     execute 'cd' escape(oldCwd, ' ')
       
   611   endtry
       
   612 endfunction
       
   613 
       
   614 " Function: s:CVSSetupBuffer() {{{2
       
   615 " Attempts to set the b:CVSBranch, b:CVSRevision and b:CVSRepository variables.
       
   616 
       
   617 function! s:CVSSetupBuffer()
       
   618   if (exists("b:CVSBufferSetup") && b:CVSBufferSetup)
       
   619     " This buffer is already set up.
       
   620     return
       
   621   endif
       
   622 
       
   623   if !s:CVSGetOption("CVSCommandEnableBufferSetup", 0)
       
   624         \ || @% == ""
       
   625         \ || s:CVSCommandEditFileRunning > 0
       
   626         \ || exists("b:CVSOrigBuffNR")
       
   627     unlet! b:CVSRevision
       
   628     unlet! b:CVSBranch
       
   629     unlet! b:CVSRepository
       
   630     return
       
   631   endif
       
   632 
       
   633   if !filereadable(expand("%"))
       
   634     return -1
       
   635   endif
       
   636 
       
   637   let revision=""
       
   638   let branch=""
       
   639   let repository=""
       
   640 
       
   641   exec s:CVSGetStatusVars('revision', 'branch', 'repository')
       
   642   if revision != ""
       
   643     let b:CVSRevision=revision
       
   644   else
       
   645     unlet! b:CVSRevision
       
   646   endif
       
   647   if branch != ""
       
   648     let b:CVSBranch=branch
       
   649   else
       
   650     unlet! b:CVSBranch
       
   651   endif
       
   652   if repository != ""
       
   653      let b:CVSRepository=repository
       
   654   else
       
   655      unlet! b:CVSRepository
       
   656   endif
       
   657   silent do CVSCommand User CVSBufferSetup
       
   658   let b:CVSBufferSetup=1
       
   659 endfunction
       
   660 
       
   661 " Function: s:CVSMarkOrigBufferForSetup(cvsbuffer) {{{2
       
   662 " Resets the buffer setup state of the original buffer for a given CVS buffer.
       
   663 " Returns:  The CVS buffer number in a passthrough mode.
       
   664 
       
   665 function! s:CVSMarkOrigBufferForSetup(cvsBuffer)
       
   666   checktime
       
   667   if a:cvsBuffer != -1
       
   668     let origBuffer = s:CVSBufferCheck(a:cvsBuffer)
       
   669     "This should never not work, but I'm paranoid
       
   670     if origBuffer != a:cvsBuffer
       
   671       call setbufvar(origBuffer, "CVSBufferSetup", 0)
       
   672     endif
       
   673   endif
       
   674   return a:cvsBuffer
       
   675 endfunction
       
   676 
       
   677 " Function: s:CVSOverrideOption(option, [value]) {{{2
       
   678 " Provides a temporary override for the given CVS option.  If no value is
       
   679 " passed, the override is disabled.
       
   680 
       
   681 function! s:CVSOverrideOption(option, ...)
       
   682   if a:0 == 0
       
   683     unlet! s:{a:option}Override
       
   684   else
       
   685     let s:{a:option}Override = a:1
       
   686   endif
       
   687 endfunction
       
   688 
       
   689 " Function: s:CVSWipeoutCommandBuffers() {{{2
       
   690 " Clears all current CVS buffers of the specified type for a given source.
       
   691 
       
   692 function! s:CVSWipeoutCommandBuffers(originalBuffer, cvsCommand)
       
   693   let buffer = 1
       
   694   while buffer <= bufnr('$')
       
   695     if getbufvar(buffer, 'CVSOrigBuffNR') == a:originalBuffer
       
   696       if getbufvar(buffer, 'CVSCommand') == a:cvsCommand
       
   697         execute 'bw' buffer
       
   698       endif
       
   699     endif
       
   700     let buffer = buffer + 1
       
   701   endwhile
       
   702 endfunction
       
   703 
       
   704 " Section: Public functions {{{1
       
   705 
       
   706 " Function: CVSGetRevision() {{{2
       
   707 " Global function for retrieving the current buffer's CVS revision number.
       
   708 " Returns: Revision number or an empty string if an error occurs.
       
   709 
       
   710 function! CVSGetRevision()
       
   711   let revision=""
       
   712   exec s:CVSGetStatusVars('revision', '', '')
       
   713   return revision
       
   714 endfunction
       
   715 
       
   716 " Function: CVSDisableBufferSetup() {{{2
       
   717 " Global function for deactivating the buffer autovariables.
       
   718 
       
   719 function! CVSDisableBufferSetup()
       
   720   let g:CVSCommandEnableBufferSetup=0
       
   721   silent! augroup! CVSCommandPlugin
       
   722 endfunction
       
   723 
       
   724 " Function: CVSEnableBufferSetup() {{{2
       
   725 " Global function for activating the buffer autovariables.
       
   726 
       
   727 function! CVSEnableBufferSetup()
       
   728   let g:CVSCommandEnableBufferSetup=1
       
   729   augroup CVSCommandPlugin
       
   730     au!
       
   731     au BufEnter * call s:CVSSetupBuffer()
       
   732   augroup END
       
   733 
       
   734   " Only auto-load if the plugin is fully loaded.  This gives other plugins a
       
   735   " chance to run.
       
   736   if g:loaded_cvscommand == 2
       
   737     call s:CVSSetupBuffer()
       
   738   endif
       
   739 endfunction
       
   740 
       
   741 " Function: CVSGetStatusLine() {{{2
       
   742 " Default (sample) status line entry for CVS files.  This is only useful if
       
   743 " CVS-managed buffer mode is on (see the CVSCommandEnableBufferSetup variable
       
   744 " for how to do this).
       
   745 
       
   746 function! CVSGetStatusLine()
       
   747   if exists('b:CVSSourceFile')
       
   748     " This is a result buffer
       
   749     let value='[' . b:CVSCommand . ' ' . b:CVSSourceFile
       
   750     if exists('b:CVSStatusText')
       
   751       let value=value . ' ' . b:CVSStatusText
       
   752     endif
       
   753     let value = value . ']'
       
   754     return value
       
   755   endif
       
   756 
       
   757   if exists('b:CVSRevision')
       
   758         \ && b:CVSRevision != ''
       
   759         \ && exists('b:CVSBranch')
       
   760         \ && b:CVSBranch != ''
       
   761         \ && exists('b:CVSRepository')
       
   762         \ && b:CVSRepository != ''
       
   763         \ && exists('g:CVSCommandEnableBufferSetup')
       
   764         \ && g:CVSCommandEnableBufferSetup
       
   765     if b:CVSRevision == b:CVSRepository
       
   766       return '[CVS ' . b:CVSBranch . '/' . b:CVSRevision . ']'
       
   767     else
       
   768       return '[CVS ' . b:CVSBranch . '/' . b:CVSRevision . '/' . b:CVSRepository . ']'
       
   769     endif
       
   770   else
       
   771     return ''
       
   772   endif
       
   773 endfunction
       
   774 
       
   775 " Section: CVS command functions {{{1
       
   776 
       
   777 " Function: s:CVSAdd() {{{2
       
   778 function! s:CVSAdd()
       
   779   return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('add', 'cvsadd', ''))
       
   780 endfunction
       
   781 
       
   782 " Function: s:CVSAnnotate(...) {{{2
       
   783 function! s:CVSAnnotate(...)
       
   784   if a:0 == 0
       
   785     if &filetype == "CVSAnnotate"
       
   786       " This is a CVSAnnotate buffer.  Perform annotation of the version
       
   787       " indicated by the current line.
       
   788       let revision = substitute(getline("."),'\(^[0-9.]*\).*','\1','')
       
   789       let revmin = substitute(revision,'^[0-9.]*\.\([0-9]\+\)','\1','')
       
   790       let revmaj = substitute(revision,'^\([0-9.]*\)\.[0-9]\+','\1','')
       
   791       if s:CVSGetOption('CVSCommandAnnotateParent', 0) != 0
       
   792         let revmin = revmin - 1
       
   793       endif
       
   794       if revmin == 0
       
   795         " Jump to ancestor branch
       
   796         let revision = substitute(revmaj,'^\([0-9.]*\)\.[0-9]\+','\1','')
       
   797       else
       
   798         let revision=revmaj . "." .  revmin
       
   799       endif
       
   800     else
       
   801       let revision=CVSGetRevision()
       
   802       if revision == ""
       
   803         echoerr "Unable to obtain CVS version information."
       
   804         return -1
       
   805       endif
       
   806     endif
       
   807   else
       
   808     let revision=a:1
       
   809   endif
       
   810 
       
   811   if revision == "NEW"
       
   812     echo "No annotatation available for new file."
       
   813     return -1
       
   814   endif
       
   815 
       
   816   let resultBuffer=s:CVSDoCommand('-q annotate -r ' . revision, 'cvsannotate', revision) 
       
   817   if resultBuffer !=  -1
       
   818     set filetype=CVSAnnotate
       
   819     " Remove header lines from standard error
       
   820     silent v/^\d\+\%(\.\d\+\)\+/d
       
   821   endif
       
   822 
       
   823   return resultBuffer
       
   824 endfunction
       
   825 
       
   826 " Function: s:CVSCommit() {{{2
       
   827 function! s:CVSCommit(...)
       
   828   " Handle the commit message being specified.  If a message is supplied, it
       
   829   " is used; if bang is supplied, an empty message is used; otherwise, the
       
   830   " user is provided a buffer from which to edit the commit message.
       
   831   if a:2 != "" || a:1 == "!"
       
   832     return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('commit -m "' . a:2 . '"', 'cvscommit', ''))
       
   833   endif
       
   834 
       
   835   let cvsBufferCheck=s:CVSCurrentBufferCheck()
       
   836   if cvsBufferCheck ==  -1
       
   837     echo "Original buffer no longer exists, aborting."
       
   838     return -1
       
   839   endif
       
   840 
       
   841   " Protect against windows' backslashes in paths.  They confuse exec'd
       
   842   " commands.
       
   843 
       
   844   let shellSlashBak = &shellslash
       
   845   try
       
   846     set shellslash
       
   847 
       
   848     let messageFileName = tempname()
       
   849 
       
   850     let fileName=bufname(cvsBufferCheck)
       
   851     let realFilePath=s:CVSResolveLink(fileName)
       
   852     let newCwd=fnamemodify(realFilePath, ':h')
       
   853     if strlen(newCwd) == 0
       
   854       " Account for autochdir being in effect, which will make this blank, but
       
   855       " we know we'll be in the current directory for the original file.
       
   856       let newCwd = getcwd()
       
   857     endif
       
   858 
       
   859     let realFileName=fnamemodify(realFilePath, ':t')
       
   860 
       
   861     if s:CVSEditFile(messageFileName, cvsBufferCheck) == -1
       
   862       return
       
   863     endif
       
   864 
       
   865     " Protect against case and backslash issues in Windows.
       
   866     let autoPattern = '\c' . messageFileName
       
   867 
       
   868     " Ensure existance of group
       
   869     augroup CVSCommit
       
   870     augroup END
       
   871 
       
   872     execute 'au CVSCommit BufDelete' autoPattern 'call delete("' . messageFileName . '")'
       
   873     execute 'au CVSCommit BufDelete' autoPattern 'au! CVSCommit * ' autoPattern
       
   874 
       
   875     " Create a commit mapping.  The mapping must clear all autocommands in case
       
   876     " it is invoked when CVSCommandCommitOnWrite is active, as well as to not
       
   877     " invoke the buffer deletion autocommand.
       
   878 
       
   879     execute 'nnoremap <silent> <buffer> <Plug>CVSCommit '.
       
   880           \ ':au! CVSCommit * ' . autoPattern . '<CR>'.
       
   881           \ ':g/^CVS:/d<CR>'.
       
   882           \ ':update<CR>'.
       
   883           \ ':call <SID>CVSFinishCommit("' . messageFileName . '",' .
       
   884           \                             '"' . newCwd . '",' .
       
   885           \                             '"' . realFileName . '",' .
       
   886           \                             cvsBufferCheck . ')<CR>'
       
   887 
       
   888     silent 0put ='CVS: ----------------------------------------------------------------------'
       
   889     silent put =\"CVS: Enter Log.  Lines beginning with `CVS:' are removed automatically\"
       
   890     silent put ='CVS: Type <leader>cc (or your own <Plug>CVSCommit mapping)'
       
   891 
       
   892     if s:CVSGetOption('CVSCommandCommitOnWrite', 1) == 1
       
   893       execute 'au CVSCommit BufWritePre' autoPattern 'g/^CVS:/d'
       
   894       execute 'au CVSCommit BufWritePost' autoPattern 'call s:CVSFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . cvsBufferCheck . ') | au! * ' autoPattern
       
   895       silent put ='CVS: or write this buffer'
       
   896     endif
       
   897 
       
   898     silent put ='CVS: to finish this commit operation'
       
   899     silent put ='CVS: ----------------------------------------------------------------------'
       
   900     $
       
   901     let b:CVSSourceFile=fileName
       
   902     let b:CVSCommand='CVSCommit'
       
   903     set filetype=cvs
       
   904   finally
       
   905     let &shellslash = shellSlashBak
       
   906   endtry
       
   907 
       
   908 endfunction
       
   909 
       
   910 " Function: s:CVSDiff(...) {{{2
       
   911 function! s:CVSDiff(...)
       
   912   if a:0 == 1
       
   913     let revOptions = '-r' . a:1
       
   914     let caption = a:1 . ' -> current'
       
   915   elseif a:0 == 2
       
   916     let revOptions = '-r' . a:1 . ' -r' . a:2
       
   917     let caption = a:1 . ' -> ' . a:2
       
   918   else
       
   919     let revOptions = ''
       
   920     let caption = ''
       
   921   endif
       
   922 
       
   923   let cvsdiffopt=s:CVSGetOption('CVSCommandDiffOpt', 'wbBc')
       
   924 
       
   925   if cvsdiffopt == ""
       
   926     let diffoptionstring=""
       
   927   else
       
   928     let diffoptionstring=" -" . cvsdiffopt . " "
       
   929   endif
       
   930 
       
   931   let resultBuffer = s:CVSDoCommand('diff ' . diffoptionstring . revOptions , 'cvsdiff', caption)
       
   932   if resultBuffer != -1 
       
   933     set filetype=diff
       
   934   endif
       
   935   return resultBuffer
       
   936 endfunction
       
   937 
       
   938 " Function: s:CVSEdit() {{{2
       
   939 function! s:CVSEdit()
       
   940   return s:CVSDoCommand('edit', 'cvsedit', '')
       
   941 endfunction
       
   942 
       
   943 " Function: s:CVSEditors() {{{2
       
   944 function! s:CVSEditors()
       
   945   return s:CVSDoCommand('editors', 'cvseditors', '')
       
   946 endfunction
       
   947 
       
   948 " Function: s:CVSGotoOriginal(["!]) {{{2
       
   949 function! s:CVSGotoOriginal(...)
       
   950   let origBuffNR = s:CVSCurrentBufferCheck()
       
   951   if origBuffNR > 0
       
   952     let origWinNR = bufwinnr(origBuffNR)
       
   953     if origWinNR == -1
       
   954       execute 'buffer' origBuffNR
       
   955     else
       
   956       execute origWinNR . 'wincmd w'
       
   957     endif
       
   958     if a:0 == 1
       
   959       if a:1 == "!"
       
   960         let buffnr = 1
       
   961         let buffmaxnr = bufnr("$")
       
   962         while buffnr <= buffmaxnr
       
   963           if getbufvar(buffnr, "CVSOrigBuffNR") == origBuffNR
       
   964             execute "bw" buffnr
       
   965           endif
       
   966           let buffnr = buffnr + 1
       
   967         endwhile
       
   968       endif
       
   969     endif
       
   970   endif
       
   971 endfunction
       
   972 
       
   973 " Function: s:CVSFinishCommit(messageFile, targetDir, targetFile) {{{2
       
   974 function! s:CVSFinishCommit(messageFile, targetDir, targetFile, origBuffNR)
       
   975   if filereadable(a:messageFile)
       
   976     let oldCwd=getcwd()
       
   977     if strlen(a:targetDir) > 0
       
   978       execute 'cd' escape(a:targetDir, ' ')
       
   979     endif
       
   980     let resultBuffer=s:CVSCreateCommandBuffer('commit -F "' . a:messageFile . '" "'. a:targetFile . '"', 'cvscommit', '', a:origBuffNR)
       
   981     execute 'cd' escape(oldCwd, ' ')
       
   982     execute 'bw' escape(a:messageFile, ' *?\')
       
   983     silent execute 'call delete("' . a:messageFile . '")'
       
   984     return s:CVSMarkOrigBufferForSetup(resultBuffer)
       
   985   else
       
   986     echoerr "Can't read message file; no commit is possible."
       
   987     return -1
       
   988   endif
       
   989 endfunction
       
   990 
       
   991 " Function: s:CVSLog() {{{2
       
   992 function! s:CVSLog(...)
       
   993   if a:0 == 0
       
   994     let versionOption = ""
       
   995     let caption = ''
       
   996   else
       
   997     let versionOption=" -r" . a:1
       
   998     let caption = a:1
       
   999   endif
       
  1000 
       
  1001   let resultBuffer=s:CVSDoCommand('log' . versionOption, 'cvslog', caption)
       
  1002   if resultBuffer != ""
       
  1003     set filetype=rcslog
       
  1004   endif
       
  1005   return resultBuffer
       
  1006 endfunction
       
  1007 
       
  1008 " Function: s:CVSRevert() {{{2
       
  1009 function! s:CVSRevert()
       
  1010   return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('update -C', 'cvsrevert', ''))
       
  1011 endfunction
       
  1012 
       
  1013 " Function: s:CVSReview(...) {{{2
       
  1014 function! s:CVSReview(...)
       
  1015   if a:0 == 0
       
  1016     let versiontag=""
       
  1017     if s:CVSGetOption('CVSCommandInteractive', 0)
       
  1018       let versiontag=input('Revision:  ')
       
  1019     endif
       
  1020     if versiontag == ""
       
  1021       let versiontag="(current)"
       
  1022       let versionOption=""
       
  1023     else
       
  1024       let versionOption=" -r " . versiontag . " "
       
  1025     endif
       
  1026   else
       
  1027     let versiontag=a:1
       
  1028     let versionOption=" -r " . versiontag . " "
       
  1029   endif
       
  1030 
       
  1031   let resultBuffer = s:CVSDoCommand('-q update -p' . versionOption, 'cvsreview', versiontag)
       
  1032   if resultBuffer > 0
       
  1033     let &filetype=getbufvar(b:CVSOrigBuffNR, '&filetype')
       
  1034   endif
       
  1035 
       
  1036   return resultBuffer
       
  1037 endfunction
       
  1038 
       
  1039 " Function: s:CVSStatus() {{{2
       
  1040 function! s:CVSStatus()
       
  1041   return s:CVSDoCommand('status', 'cvsstatus', '')
       
  1042 endfunction
       
  1043 
       
  1044 " Function: s:CVSUnedit() {{{2
       
  1045 function! s:CVSUnedit()
       
  1046   return s:CVSDoCommand('unedit', 'cvsunedit', '')
       
  1047 endfunction
       
  1048 
       
  1049 " Function: s:CVSUpdate() {{{2
       
  1050 function! s:CVSUpdate()
       
  1051   return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('update', 'update', ''))
       
  1052 endfunction
       
  1053 
       
  1054 " Function: s:CVSVimDiff(...) {{{2
       
  1055 function! s:CVSVimDiff(...)
       
  1056   let originalBuffer = s:CVSCurrentBufferCheck()
       
  1057   let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1
       
  1058   try
       
  1059     " If there's already a VimDiff'ed window, restore it.
       
  1060     " There may only be one CVSVimDiff original window at a time.
       
  1061 
       
  1062     if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer
       
  1063       " Clear the existing vimdiff setup by removing the result buffers.
       
  1064       call s:CVSWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
       
  1065     endif
       
  1066 
       
  1067     " Split and diff
       
  1068     if(a:0 == 2)
       
  1069       " Reset the vimdiff system, as 2 explicit versions were provided.
       
  1070       if exists('s:vimDiffSourceBuffer')
       
  1071         call s:CVSWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
       
  1072       endif
       
  1073       let resultBuffer = s:CVSReview(a:1)
       
  1074       if resultBuffer < 0
       
  1075         echomsg "Can't open CVS revision " . a:1
       
  1076         return resultBuffer
       
  1077       endif
       
  1078       let b:CVSCommand = 'vimdiff'
       
  1079       diffthis
       
  1080       let s:vimDiffBufferCount = 1
       
  1081       let s:vimDiffScratchList = '{'. resultBuffer . '}'
       
  1082       " If no split method is defined, cheat, and set it to vertical.
       
  1083       try
       
  1084         call s:CVSOverrideOption('CVSCommandSplit', s:CVSGetOption('CVSCommandDiffSplit', s:CVSGetOption('CVSCommandSplit', 'vertical')))
       
  1085         let resultBuffer=s:CVSReview(a:2)
       
  1086       finally
       
  1087         call s:CVSOverrideOption('CVSCommandSplit')
       
  1088       endtry
       
  1089       if resultBuffer < 0
       
  1090         echomsg "Can't open CVS revision " . a:1
       
  1091         return resultBuffer
       
  1092       endif
       
  1093       let b:CVSCommand = 'vimdiff'
       
  1094       diffthis
       
  1095       let s:vimDiffBufferCount = 2
       
  1096       let s:vimDiffScratchList = s:vimDiffScratchList . '{'. resultBuffer . '}'
       
  1097     else
       
  1098       " Add new buffer
       
  1099       try
       
  1100         " Force splitting behavior, otherwise why use vimdiff?
       
  1101         call s:CVSOverrideOption("CVSCommandEdit", "split")
       
  1102         call s:CVSOverrideOption("CVSCommandSplit", s:CVSGetOption('CVSCommandDiffSplit', s:CVSGetOption('CVSCommandSplit', 'vertical')))
       
  1103         if(a:0 == 0)
       
  1104           let resultBuffer=s:CVSReview()
       
  1105         else
       
  1106           let resultBuffer=s:CVSReview(a:1)
       
  1107         endif
       
  1108       finally
       
  1109         call s:CVSOverrideOption("CVSCommandEdit")
       
  1110         call s:CVSOverrideOption("CVSCommandSplit")
       
  1111       endtry
       
  1112       if resultBuffer < 0
       
  1113         echomsg "Can't open current CVS revision"
       
  1114         return resultBuffer
       
  1115       endif
       
  1116       let b:CVSCommand = 'vimdiff'
       
  1117       diffthis
       
  1118 
       
  1119       if !exists('s:vimDiffBufferCount')
       
  1120         " New instance of vimdiff.
       
  1121         let s:vimDiffBufferCount = 2
       
  1122         let s:vimDiffScratchList = '{' . resultBuffer . '}'
       
  1123 
       
  1124         " This could have been invoked on a CVS result buffer, not the
       
  1125         " original buffer.
       
  1126         wincmd W
       
  1127         execute 'buffer' originalBuffer
       
  1128         " Store info for later original buffer restore
       
  1129         let s:vimDiffRestoreCmd = 
       
  1130               \    "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")"
       
  1131               \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")"
       
  1132               \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")"
       
  1133               \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')"
       
  1134               \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")"
       
  1135               \ . "|call setbufvar(".originalBuffer.", \"&wrap\", ".getbufvar(originalBuffer, '&wrap').")"
       
  1136               \ . "|if &foldmethod=='manual'|execute 'normal zE'|endif"
       
  1137         diffthis
       
  1138         wincmd w
       
  1139       else
       
  1140         " Adding a window to an existing vimdiff
       
  1141         let s:vimDiffBufferCount = s:vimDiffBufferCount + 1
       
  1142         let s:vimDiffScratchList = s:vimDiffScratchList . '{' . resultBuffer . '}'
       
  1143       endif
       
  1144     endif
       
  1145 
       
  1146     let s:vimDiffSourceBuffer = originalBuffer
       
  1147 
       
  1148     " Avoid executing the modeline in the current buffer after the autocommand.
       
  1149 
       
  1150     let currentBuffer = bufnr('%')
       
  1151     let saveModeline = getbufvar(currentBuffer, '&modeline')
       
  1152     try
       
  1153       call setbufvar(currentBuffer, '&modeline', 0)
       
  1154       silent do CVSCommand User CVSVimDiffFinish
       
  1155     finally
       
  1156       call setbufvar(currentBuffer, '&modeline', saveModeline)
       
  1157     endtry
       
  1158     return resultBuffer
       
  1159   finally
       
  1160     let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1
       
  1161   endtry
       
  1162 endfunction
       
  1163 
       
  1164 " Function: s:CVSWatch(onoff) {{{2
       
  1165 function! s:CVSWatch(onoff)
       
  1166   if a:onoff !~ '^\c\%(on\|off\|add\|remove\)$'
       
  1167     echoerr "Argument to CVSWatch must be one of [on|off|add|remove]"
       
  1168     return -1
       
  1169   end
       
  1170   return s:CVSDoCommand('watch ' . tolower(a:onoff), 'cvswatch', '')
       
  1171 endfunction
       
  1172 
       
  1173 " Function: s:CVSWatchers() {{{2
       
  1174 function! s:CVSWatchers()
       
  1175   return s:CVSDoCommand('watchers', 'cvswatchers', '')
       
  1176 endfunction
       
  1177 
       
  1178 " Section: Command definitions {{{1
       
  1179 " Section: Primary commands {{{2
       
  1180 com! CVSAdd call s:CVSAdd()
       
  1181 com! -nargs=? CVSAnnotate call s:CVSAnnotate(<f-args>)
       
  1182 com! -bang -nargs=? CVSCommit call s:CVSCommit(<q-bang>, <q-args>)
       
  1183 com! -nargs=* CVSDiff call s:CVSDiff(<f-args>)
       
  1184 com! CVSEdit call s:CVSEdit()
       
  1185 com! CVSEditors call s:CVSEditors()
       
  1186 com! -bang CVSGotoOriginal call s:CVSGotoOriginal(<q-bang>)
       
  1187 com! -nargs=? CVSLog call s:CVSLog(<f-args>)
       
  1188 com! CVSRevert call s:CVSRevert()
       
  1189 com! -nargs=? CVSReview call s:CVSReview(<f-args>)
       
  1190 com! CVSStatus call s:CVSStatus()
       
  1191 com! CVSUnedit call s:CVSUnedit()
       
  1192 com! CVSUpdate call s:CVSUpdate()
       
  1193 com! -nargs=* CVSVimDiff call s:CVSVimDiff(<f-args>)
       
  1194 com! -nargs=1 CVSWatch call s:CVSWatch(<f-args>)
       
  1195 com! CVSWatchAdd call s:CVSWatch('add')
       
  1196 com! CVSWatchOn call s:CVSWatch('on')
       
  1197 com! CVSWatchOff call s:CVSWatch('off')
       
  1198 com! CVSWatchRemove call s:CVSWatch('remove')
       
  1199 com! CVSWatchers call s:CVSWatchers()
       
  1200 
       
  1201 " Section: CVS buffer management commands {{{2
       
  1202 com! CVSDisableBufferSetup call CVSDisableBufferSetup()
       
  1203 com! CVSEnableBufferSetup call CVSEnableBufferSetup()
       
  1204 
       
  1205 " Allow reloading cvscommand.vim
       
  1206 com! CVSReload unlet! loaded_cvscommand | runtime plugin/cvscommand.vim
       
  1207 
       
  1208 " Section: Plugin command mappings {{{1
       
  1209 nnoremap <silent> <Plug>CVSAdd :CVSAdd<CR>
       
  1210 nnoremap <silent> <Plug>CVSAnnotate :CVSAnnotate<CR>
       
  1211 nnoremap <silent> <Plug>CVSCommit :CVSCommit<CR>
       
  1212 nnoremap <silent> <Plug>CVSDiff :CVSDiff<CR>
       
  1213 nnoremap <silent> <Plug>CVSEdit :CVSEdit<CR>
       
  1214 nnoremap <silent> <Plug>CVSEditors :CVSEditors<CR>
       
  1215 nnoremap <silent> <Plug>CVSGotoOriginal :CVSGotoOriginal<CR>
       
  1216 nnoremap <silent> <Plug>CVSClearAndGotoOriginal :CVSGotoOriginal!<CR>
       
  1217 nnoremap <silent> <Plug>CVSLog :CVSLog<CR>
       
  1218 nnoremap <silent> <Plug>CVSRevert :CVSRevert<CR>
       
  1219 nnoremap <silent> <Plug>CVSReview :CVSReview<CR>
       
  1220 nnoremap <silent> <Plug>CVSStatus :CVSStatus<CR>
       
  1221 nnoremap <silent> <Plug>CVSUnedit :CVSUnedit<CR>
       
  1222 nnoremap <silent> <Plug>CVSUpdate :CVSUpdate<CR>
       
  1223 nnoremap <silent> <Plug>CVSVimDiff :CVSVimDiff<CR>
       
  1224 nnoremap <silent> <Plug>CVSWatchers :CVSWatchers<CR>
       
  1225 nnoremap <silent> <Plug>CVSWatchAdd :CVSWatchAdd<CR>
       
  1226 nnoremap <silent> <Plug>CVSWatchOn :CVSWatchOn<CR>
       
  1227 nnoremap <silent> <Plug>CVSWatchOff :CVSWatchOff<CR>
       
  1228 nnoremap <silent> <Plug>CVSWatchRemove :CVSWatchRemove<CR>
       
  1229 
       
  1230 " Section: Default mappings {{{1
       
  1231 if !hasmapto('<Plug>CVSAdd')
       
  1232   nmap <unique> <Leader>ca <Plug>CVSAdd
       
  1233 endif
       
  1234 if !hasmapto('<Plug>CVSAnnotate')
       
  1235   nmap <unique> <Leader>cn <Plug>CVSAnnotate
       
  1236 endif
       
  1237 if !hasmapto('<Plug>CVSClearAndGotoOriginal')
       
  1238   nmap <unique> <Leader>cG <Plug>CVSClearAndGotoOriginal
       
  1239 endif
       
  1240 if !hasmapto('<Plug>CVSCommit')
       
  1241   nmap <unique> <Leader>cc <Plug>CVSCommit
       
  1242 endif
       
  1243 if !hasmapto('<Plug>CVSDiff')
       
  1244   nmap <unique> <Leader>cd <Plug>CVSDiff
       
  1245 endif
       
  1246 if !hasmapto('<Plug>CVSEdit')
       
  1247   nmap <unique> <Leader>ce <Plug>CVSEdit
       
  1248 endif
       
  1249 if !hasmapto('<Plug>CVSEditors')
       
  1250   nmap <unique> <Leader>ci <Plug>CVSEditors
       
  1251 endif
       
  1252 if !hasmapto('<Plug>CVSGotoOriginal')
       
  1253   nmap <unique> <Leader>cg <Plug>CVSGotoOriginal
       
  1254 endif
       
  1255 if !hasmapto('<Plug>CVSLog')
       
  1256   nmap <unique> <Leader>cl <Plug>CVSLog
       
  1257 endif
       
  1258 if !hasmapto('<Plug>CVSRevert')
       
  1259   nmap <unique> <Leader>cq <Plug>CVSRevert
       
  1260 endif
       
  1261 if !hasmapto('<Plug>CVSReview')
       
  1262   nmap <unique> <Leader>cr <Plug>CVSReview
       
  1263 endif
       
  1264 if !hasmapto('<Plug>CVSStatus')
       
  1265   nmap <unique> <Leader>cs <Plug>CVSStatus
       
  1266 endif
       
  1267 if !hasmapto('<Plug>CVSUnedit')
       
  1268   nmap <unique> <Leader>ct <Plug>CVSUnedit
       
  1269 endif
       
  1270 if !hasmapto('<Plug>CVSUpdate')
       
  1271   nmap <unique> <Leader>cu <Plug>CVSUpdate
       
  1272 endif
       
  1273 if !hasmapto('<Plug>CVSVimDiff')
       
  1274   nmap <unique> <Leader>cv <Plug>CVSVimDiff
       
  1275 endif
       
  1276 if !hasmapto('<Plug>CVSWatchers')
       
  1277   nmap <unique> <Leader>cwv <Plug>CVSWatchers
       
  1278 endif
       
  1279 if !hasmapto('<Plug>CVSWatchAdd')
       
  1280   nmap <unique> <Leader>cwa <Plug>CVSWatchAdd
       
  1281 endif
       
  1282 if !hasmapto('<Plug>CVSWatchOn')
       
  1283   nmap <unique> <Leader>cwn <Plug>CVSWatchOn
       
  1284 endif
       
  1285 if !hasmapto('<Plug>CVSWatchOff')
       
  1286   nmap <unique> <Leader>cwf <Plug>CVSWatchOff
       
  1287 endif
       
  1288 if !hasmapto('<Plug>CVSWatchRemove')
       
  1289   nmap <unique> <Leader>cwr <Plug>CVSWatchRemove
       
  1290 endif
       
  1291 
       
  1292 " Section: Menu items {{{1
       
  1293 silent! aunmenu Plugin.CVS
       
  1294 amenu <silent> &Plugin.CVS.&Add        <Plug>CVSAdd
       
  1295 amenu <silent> &Plugin.CVS.A&nnotate   <Plug>CVSAnnotate
       
  1296 amenu <silent> &Plugin.CVS.&Commit     <Plug>CVSCommit
       
  1297 amenu <silent> &Plugin.CVS.&Diff       <Plug>CVSDiff
       
  1298 amenu <silent> &Plugin.CVS.&Edit       <Plug>CVSEdit
       
  1299 amenu <silent> &Plugin.CVS.Ed&itors    <Plug>CVSEditors
       
  1300 amenu <silent> &Plugin.CVS.&Log        <Plug>CVSLog
       
  1301 amenu <silent> &Plugin.CVS.Revert      <Plug>CVSRevert
       
  1302 amenu <silent> &Plugin.CVS.&Review     <Plug>CVSReview
       
  1303 amenu <silent> &Plugin.CVS.&Status     <Plug>CVSStatus
       
  1304 amenu <silent> &Plugin.CVS.Unedi&t     <Plug>CVSUnedit
       
  1305 amenu <silent> &Plugin.CVS.&Update     <Plug>CVSUpdate
       
  1306 amenu <silent> &Plugin.CVS.&VimDiff    <Plug>CVSVimDiff
       
  1307 amenu <silent> &Plugin.CVS.&Watchers   <Plug>CVSWatchers
       
  1308 amenu <silent> &Plugin.CVS.WatchAdd    <Plug>CVSWatchAdd
       
  1309 amenu <silent> &Plugin.CVS.WatchOn     <Plug>CVSWatchOn
       
  1310 amenu <silent> &Plugin.CVS.WatchOff    <Plug>CVSWatchOff
       
  1311 amenu <silent> &Plugin.CVS.WatchRemove <Plug>CVSWatchRemove
       
  1312 
       
  1313 " Section: Autocommands to restore vimdiff state {{{1
       
  1314 function! s:CVSVimDiffRestore(vimDiffBuff)
       
  1315   let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1
       
  1316   try
       
  1317     if exists("s:vimDiffSourceBuffer")
       
  1318       if a:vimDiffBuff == s:vimDiffSourceBuffer
       
  1319         " Original file is being removed.
       
  1320         unlet! s:vimDiffSourceBuffer
       
  1321         unlet! s:vimDiffBufferCount
       
  1322         unlet! s:vimDiffRestoreCmd
       
  1323         unlet! s:vimDiffScratchList
       
  1324       elseif match(s:vimDiffScratchList, '{' . a:vimDiffBuff . '}') >= 0
       
  1325         let s:vimDiffScratchList = substitute(s:vimDiffScratchList, '{' . a:vimDiffBuff . '}', '', '')
       
  1326         let s:vimDiffBufferCount = s:vimDiffBufferCount - 1
       
  1327         if s:vimDiffBufferCount == 1 && exists('s:vimDiffRestoreCmd')
       
  1328           " All scratch buffers are gone, reset the original.
       
  1329           " Only restore if the source buffer is still in Diff mode
       
  1330 
       
  1331           let sourceWinNR=bufwinnr(s:vimDiffSourceBuffer)
       
  1332           if sourceWinNR != -1
       
  1333             " The buffer is visible in at least one window
       
  1334             let currentWinNR = winnr()
       
  1335             while winbufnr(sourceWinNR) != -1
       
  1336               if winbufnr(sourceWinNR) == s:vimDiffSourceBuffer
       
  1337                 execute sourceWinNR . 'wincmd w'
       
  1338                 if getwinvar('', "&diff")
       
  1339                   execute s:vimDiffRestoreCmd
       
  1340                 endif
       
  1341               endif
       
  1342               let sourceWinNR = sourceWinNR + 1
       
  1343             endwhile
       
  1344             execute currentWinNR . 'wincmd w'
       
  1345           else
       
  1346             " The buffer is hidden.  It must be visible in order to set the
       
  1347             " diff option.
       
  1348             let currentBufNR = bufnr('')
       
  1349             execute "hide buffer" s:vimDiffSourceBuffer
       
  1350             if getwinvar('', "&diff")
       
  1351               execute s:vimDiffRestoreCmd
       
  1352             endif
       
  1353             execute "hide buffer" currentBufNR
       
  1354           endif
       
  1355 
       
  1356           unlet s:vimDiffRestoreCmd
       
  1357           unlet s:vimDiffSourceBuffer
       
  1358           unlet s:vimDiffBufferCount
       
  1359           unlet s:vimDiffScratchList
       
  1360         elseif s:vimDiffBufferCount == 0
       
  1361           " All buffers are gone.
       
  1362           unlet s:vimDiffSourceBuffer
       
  1363           unlet s:vimDiffBufferCount
       
  1364           unlet s:vimDiffScratchList
       
  1365         endif
       
  1366       endif
       
  1367     endif
       
  1368   finally
       
  1369     let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1
       
  1370   endtry
       
  1371 endfunction
       
  1372 
       
  1373 augroup CVSVimDiffRestore
       
  1374   au!
       
  1375   au BufUnload * call s:CVSVimDiffRestore(expand("<abuf>"))
       
  1376 augroup END
       
  1377 
       
  1378 " Section: Optional activation of buffer management {{{1
       
  1379 
       
  1380 if s:CVSGetOption('CVSCommandEnableBufferSetup', 0)
       
  1381   call CVSEnableBufferSetup()
       
  1382 endif
       
  1383 
       
  1384 " Section: Plugin completion {{{1
       
  1385 
       
  1386 let loaded_cvscommand=2
       
  1387 silent do CVSCommand User CVSPluginFinish