PageRenderTime 57ms CodeModel.GetById 16ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/vim/plugin/vcscommand.vim

https://bitbucket.org/vertespain/config
Vim Script | 1344 lines | 718 code | 149 blank | 477 comment | 142 complexity | bb6bb1fbde0b62f5634cb355d5294f0c MD5 | raw file
   1" vim600: set foldmethod=marker:
   2"
   3" Vim plugin to assist in working with files under control of various Version
   4" Control Systems, such as CVS, SVN, SVK, and git.
   5"
   6" Version:       1.99.31
   7" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
   8" License:
   9" Copyright (c) 2008 Bob Hiestand
  10"
  11" Permission is hereby granted, free of charge, to any person obtaining a copy
  12" of this software and associated documentation files (the "Software"), to
  13" deal in the Software without restriction, including without limitation the
  14" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  15" sell copies of the Software, and to permit persons to whom the Software is
  16" furnished to do so, subject to the following conditions:
  17"
  18" The above copyright notice and this permission notice shall be included in
  19" all copies or substantial portions of the Software.
  20"
  21" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  27" IN THE SOFTWARE.
  28"
  29" Section: Documentation {{{1
  30"
  31" Provides functions to invoke various source control commands on the current
  32" file (either the current buffer, or, in the case of an directory buffer, the
  33" directory and all subdirectories associated with the current buffer).  The
  34" output of the commands is captured in a new scratch window.
  35"
  36" This plugin needs additional extension plugins, each  specific to a source
  37" control system, to function.  Those plugins should be placed in a
  38" subdirectory of the standard plugin directory named 'vcscommand'.  Several
  39" options include the name of the version control system in the option name.
  40" Such options use the placeholder text '{VCSType}', which would be replaced
  41" in actual usage with 'CVS' or 'SVN', for instance.
  42"
  43" Command documentation {{{2
  44"
  45" VCSAdd           Adds the current file to source control.
  46"
  47" VCSAnnotate[!]   Displays the current file with each line annotated with the
  48"                  version in which it was most recently changed.  If an
  49"                  argument is given, the argument is used as a revision
  50"                  number to display.  If not given an argument, it uses the
  51"                  most recent version of the file on the current branch.
  52"                  Additionally, if the current buffer is a VCSAnnotate buffer
  53"                  already, the version number on the current line is used.
  54"
  55"                  If '!' is used, the view of the annotated buffer is split
  56"                  so that the annotation is in a separate window from the
  57"                  content, and each is highlighted separately.
  58"
  59" VCSBlame         Alias for 'VCSAnnotate'.
  60"
  61" VCSCommit[!]     Commits changes to the current file to source control.
  62"
  63"                  If called with arguments, the arguments are the log message.
  64"
  65"                  If '!' is used, an empty log message is committed.
  66"
  67"                  If called with no arguments, this is a two-step command.
  68"                  The first step opens a buffer to accept a log message.
  69"                  When that buffer is written, it is automatically closed and
  70"                  the file is committed using the information from that log
  71"                  message.  The commit can be abandoned if the log message
  72"                  buffer is deleted or wiped before being written.
  73"
  74" VCSDelete        Deletes the current file and removes it from source control.
  75"
  76" VCSDiff          With no arguments, this displays the differences between
  77"                  the current file and its parent version under source
  78"                  control in a new scratch buffer.
  79"
  80"                  With one argument, the diff is performed on the
  81"                  current file against the specified revision.
  82"
  83"                  With two arguments, the diff is performed between the
  84"                  specified revisions of the current file.
  85"
  86"                  This command uses the 'VCSCommand{VCSType}DiffOpt' variable
  87"                  to specify diff options.  If that variable does not exist,
  88"                  a plugin-specific default is used.  If you wish to have no
  89"                  options, then set it to the empty string.
  90"
  91" VCSGotoOriginal  Jumps to the source buffer if the current buffer is a VCS
  92"                  scratch buffer.  If VCSGotoOriginal[!] is used, remove all
  93"                  VCS scratch buffers associated with the original file.
  94"
  95" VCSInfo          Displays extended information about the current file in a
  96"                  new scratch buffer. 
  97"
  98" VCSLock          Locks the current file in order to prevent other users from
  99"                  concurrently modifying it.  The exact semantics of this
 100"                  command depend on the underlying VCS.
 101"
 102" VCSLog           Displays the version history of the current file in a new
 103"                  scratch buffer.
 104"
 105" VCSRemove        Alias for 'VCSDelete'.
 106"
 107" VCSRevert        Replaces the modified version of the current file with the
 108"                  most recent version from the repository.
 109"
 110" VCSReview        Displays a particular version of the current file in a new
 111"                  scratch buffer.  If no argument is given, the most recent
 112"                  version of the file on the current branch is retrieved.
 113"
 114" VCSStatus        Displays versioning information about the current file in a
 115"                  new scratch buffer.
 116"
 117" VCSUnlock        Unlocks the current file in order to allow other users from
 118"                  concurrently modifying it.  The exact semantics of this
 119"                  command depend on the underlying VCS.
 120"
 121" VCSUpdate        Updates the current file with any relevant changes from the
 122"                  repository.
 123"
 124" VCSVimDiff       Uses vimdiff to display differences between versions of the
 125"                  current file.
 126"
 127"                  If no revision is specified, the most recent version of the
 128"                  file on the current branch is used.  With one argument,
 129"                  that argument is used as the revision as above.  With two
 130"                  arguments, the differences between the two revisions is
 131"                  displayed using vimdiff.
 132"
 133"                  With either zero or one argument, the original buffer is
 134"                  used to perform the vimdiff.  When the scratch buffer is
 135"                  closed, the original buffer will be returned to normal
 136"                  mode.
 137"
 138"                  Once vimdiff mode is started using the above methods,
 139"                  additional vimdiff buffers may be added by passing a single
 140"                  version argument to the command.  There may be up to 4
 141"                  vimdiff buffers total.
 142"
 143"                  Using the 2-argument form of the command resets the vimdiff
 144"                  to only those 2 versions.  Additionally, invoking the
 145"                  command on a different file will close the previous vimdiff
 146"                  buffers.
 147"
 148" Mapping documentation: {{{2
 149"
 150" By default, a mapping is defined for each command.  User-provided mappings
 151" can be used instead by mapping to <Plug>CommandName, for instance:
 152"
 153" nmap ,ca <Plug>VCSAdd
 154"
 155" The default mappings are as follow:
 156"
 157"   <Leader>ca VCSAdd
 158"   <Leader>cn VCSAnnotate
 159"   <Leader>cN VCSAnnotate!
 160"   <Leader>cc VCSCommit
 161"   <Leader>cD VCSDelete
 162"   <Leader>cd VCSDiff
 163"   <Leader>cg VCSGotoOriginal
 164"   <Leader>cG VCSGotoOriginal!
 165"   <Leader>ci VCSInfo
 166"   <Leader>cl VCSLog
 167"   <Leader>cL VCSLock
 168"   <Leader>cr VCSReview
 169"   <Leader>cs VCSStatus
 170"   <Leader>cu VCSUpdate
 171"   <Leader>cU VCSUnlock
 172"   <Leader>cv VCSVimDiff
 173"
 174" Options documentation: {{{2
 175"
 176" Several variables are checked by the script to determine behavior as follow:
 177"
 178" VCSCommandCommitOnWrite
 179"   This variable, if set to a non-zero value, causes the pending commit to
 180"   take place immediately as soon as the log message buffer is written.  If
 181"   set to zero, only the VCSCommit mapping will cause the pending commit to
 182"   occur.  If not set, it defaults to 1.
 183"
 184" VCSCommandDeleteOnHide
 185"   This variable, if set to a non-zero value, causes the temporary VCS result
 186"   buffers to automatically delete themselves when hidden.
 187"
 188" VCSCommand{VCSType}DiffOpt
 189"   This variable, if set, determines the options passed to the diff command
 190"   of the underlying VCS.  Each VCS plugin defines a default value.
 191"
 192" VCSCommandDiffSplit
 193"   This variable overrides the VCSCommandSplit variable, but only for buffers
 194"   created with VCSVimDiff.
 195"
 196" VCSCommandDisableAll
 197"   This variable, if set, prevents the plugin or any extensions from loading
 198"   at all.  This is useful when a single runtime distribution is used on
 199"   multiple systems with varying versions.
 200"
 201" VCSCommandDisableMappings
 202"   This variable, if set to a non-zero value, prevents the default command
 203"   mappings from being set.
 204"
 205" VCSCommandDisableExtensionMappings
 206"   This variable, if set to a non-zero value, prevents the default command
 207"   mappings from being set for commands specific to an individual VCS.
 208"
 209" VCSCommandEdit
 210"   This variable controls whether to split the current window to display a
 211"   scratch buffer ('split'), or to display it in the current buffer ('edit').
 212"   If not set, it defaults to 'split'.
 213"
 214" VCSCommandEnableBufferSetup
 215"   This variable, if set to a non-zero value, activates VCS buffer management
 216"   mode.  This mode means that the buffer variable 'VCSRevision' is set if
 217"   the file is VCS-controlled.  This is useful for displaying version
 218"   information in the status bar.  Additional options may be set by
 219"   individual VCS plugins.
 220"
 221" VCSCommandMappings
 222"   This variable, if set, overrides the default mappings used for shortcuts.
 223"   It should be a List of 2-element Lists, each containing a shortcut and
 224"   function name pair.
 225"
 226" VCSCommandMapPrefix
 227"   This variable, if set, overrides the default mapping prefix ('<Leader>c').
 228"   This allows customization of the mapping space used by the vcscommand
 229"   shortcuts.
 230"
 231" VCSCommandResultBufferNameExtension
 232"   This variable, if set to a non-blank value, is appended to the name of the
 233"   VCS command output buffers.  For example, '.vcs'.  Using this option may
 234"   help avoid problems caused by autocommands dependent on file extension.
 235"
 236" VCSCommandResultBufferNameFunction
 237"   This variable, if set, specifies a custom function for naming VCS command
 238"   output buffers.  This function will be passed the following arguments:
 239"
 240"   command - name of the VCS command being executed (such as 'Log' or
 241"   'Diff').
 242"
 243"   originalBuffer - buffer number of the source file.
 244"
 245"   vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN').
 246"
 247"   statusText - extra text associated with the VCS action (such as version
 248"   numbers).
 249"
 250" VCSCommandSplit
 251"   This variable controls the orientation of the various window splits that
 252"   may occur (such as with VCSVimDiff, when using a VCS command on a VCS
 253"   command buffer, or when the 'VCSCommandEdit' variable is set to 'split'.
 254"   If set to 'horizontal', the resulting windows will be on stacked on top of
 255"   one another.  If set to 'vertical', the resulting windows will be
 256"   side-by-side.  If not set, it defaults to 'horizontal' for all but
 257"   VCSVimDiff windows.
 258"
 259" VCSCommandVCSTypeOverride
 260"   This variable allows the VCS type detection to be overridden on a
 261"   path-by-path basis.  The value of this variable is expected to be a List
 262"   of Lists.  Each high-level List item is a List containing two elements.
 263"   The first element is a regular expression that will be matched against the
 264"   full file name of a given buffer.  If it matches, the second element will
 265"   be used as the VCS type.
 266"
 267" Event documentation {{{2
 268"   For additional customization, VCSCommand.vim uses User event autocommand
 269"   hooks.  Each event is in the VCSCommand group, and different patterns
 270"   match the various hooks.
 271"
 272"   For instance, the following could be added to the vimrc to provide a 'q'
 273"   mapping to quit a VCS scratch buffer:
 274"
 275"   augroup VCSCommand
 276"     au VCSCommand User VCSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> 
 277"   augroup END
 278"
 279"   The following hooks are available:
 280"
 281"   VCSBufferCreated           This event is fired just after a VCS command
 282"                              output buffer is created.  It is executed
 283"                              within the context of the new buffer.
 284"
 285"   VCSBufferSetup             This event is fired just after VCS buffer setup
 286"                              occurs, if enabled.
 287"
 288"   VCSPluginInit              This event is fired when the VCSCommand plugin
 289"                              first loads.
 290"
 291"   VCSPluginFinish            This event is fired just after the VCSCommand
 292"                              plugin loads.
 293"
 294"   VCSVimDiffFinish           This event is fired just after the VCSVimDiff
 295"                              command executes to allow customization of,
 296"                              for instance, window placement and focus.
 297"
 298" Section: Plugin header {{{1
 299
 300" loaded_VCSCommand is set to 1 when the initialization begins, and 2 when it
 301" completes.  This allows various actions to only be taken by functions after
 302" system initialization.
 303
 304if exists('VCSCommandDisableAll')
 305	finish
 306endif
 307
 308if exists('loaded_VCSCommand')
 309	finish
 310endif
 311let loaded_VCSCommand = 1
 312
 313if v:version < 700
 314	echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
 315	finish
 316endif
 317
 318let s:save_cpo=&cpo
 319set cpo&vim
 320
 321" Section: Event group setup {{{1
 322
 323augroup VCSCommand
 324augroup END
 325
 326augroup VCSCommandCommit
 327augroup END
 328
 329" Section: Plugin initialization {{{1
 330silent do VCSCommand User VCSPluginInit
 331
 332" Section: Constants declaration {{{1
 333
 334let g:VCSCOMMAND_IDENTIFY_EXACT = 1
 335let g:VCSCOMMAND_IDENTIFY_INEXACT = -1
 336
 337" Section: Script variable initialization {{{1
 338
 339" plugin-specific information:  {vcs -> [script, {command -> function}, {key -> mapping}]}
 340let s:plugins = {}
 341
 342" temporary values of overridden configuration variables
 343let s:optionOverrides = {}
 344
 345" state flag used to vary behavior of certain automated actions
 346let s:isEditFileRunning = 0
 347
 348" commands needed to restore diff buffers to their original state
 349unlet! s:vimDiffRestoreCmd
 350
 351" original buffer currently reflected in vimdiff windows
 352unlet! s:vimDiffSourceBuffer
 353
 354" 
 355unlet! s:vimDiffScratchList
 356
 357" Section: Utility functions {{{1
 358
 359" Function: s:ReportError(mapping) {{{2
 360" Displays the given error in a consistent faction.  This is intended to be
 361" invoked from a catch statement.
 362
 363function! s:ReportError(error)
 364	echohl WarningMsg|echomsg 'VCSCommand:  ' . a:error|echohl None
 365endfunction
 366
 367
 368" Function: s:CreateMapping(shortcut, expansion, display) {{{2
 369" Creates the given mapping by prepending the contents of
 370" 'VCSCommandMapPrefix' (by default '<Leader>c') to the given shortcut and
 371" mapping it to the given plugin function.  If a mapping exists for the
 372" specified shortcut + prefix, emit an error but continue.  If a mapping
 373" exists for the specified function, do nothing.
 374
 375function! s:CreateMapping(shortcut, expansion, display)
 376	let lhs = VCSCommandGetOption('VCSCommandMapPrefix', '<Leader>c') . a:shortcut
 377	if !hasmapto(a:expansion)
 378		try
 379			execute 'nmap <silent> <unique>' lhs a:expansion
 380		catch /^Vim(.*):E227:/
 381			if(&verbose != 0)
 382				echohl WarningMsg|echomsg 'VCSCommand:  mapping ''' . lhs . ''' already exists, refusing to overwrite.  The mapping for ' . a:display . ' will not be available.'|echohl None
 383			endif
 384		endtry
 385	endif
 386endfunction
 387
 388" Function: s:ExecuteExtensionMapping(mapping) {{{2
 389" Invokes the appropriate extension mapping depending on the type of the
 390" current buffer.
 391
 392function! s:ExecuteExtensionMapping(mapping)
 393	let buffer = bufnr('%')
 394	let vcsType = VCSCommandGetVCSType(buffer)
 395	if !has_key(s:plugins, vcsType)
 396		throw 'Unknown VCS type:  ' . vcsType
 397	endif
 398	if !has_key(s:plugins[vcsType][2], a:mapping)
 399		throw 'This extended mapping is not defined for ' . vcsType
 400	endif
 401	silent execute 'normal' ':' .  s:plugins[vcsType][2][a:mapping] . "\<CR>"
 402endfunction
 403
 404" Function: s:ExecuteVCSCommand(command, argList) {{{2
 405" Calls the indicated plugin-specific VCS command on the current buffer.
 406" Returns: buffer number of resulting output scratch buffer, or -1 if an error
 407" occurs.
 408
 409function! s:ExecuteVCSCommand(command, argList)
 410	try
 411		let buffer = bufnr('%')
 412
 413		let vcsType = VCSCommandGetVCSType(buffer)
 414		if !has_key(s:plugins, vcsType)
 415			throw 'Unknown VCS type:  ' . vcsType
 416		endif
 417
 418		let originalBuffer = VCSCommandGetOriginalBuffer(buffer)
 419		let bufferName = bufname(originalBuffer)
 420
 421		" It is already known that the directory is under VCS control.  No further
 422		" checks are needed.  Otherwise, perform some basic sanity checks to avoid
 423		" VCS-specific error messages from confusing things.
 424		if !isdirectory(bufferName)
 425			if !filereadable(bufferName)
 426				throw 'No such file ' . bufferName
 427			endif
 428		endif
 429
 430		let functionMap = s:plugins[vcsType][1]
 431		if !has_key(functionMap, a:command)
 432			throw 'Command ''' . a:command . ''' not implemented for ' . vcsType
 433		endif
 434		return functionMap[a:command](a:argList)
 435	catch
 436		call s:ReportError(v:exception)
 437		return -1
 438	endtry
 439endfunction
 440
 441" Function: s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) {{{2
 442" Default method of generating the name for VCS result buffers.  This can be
 443" overridden with the VCSResultBufferNameFunction variable.
 444
 445function! s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText)
 446	let fileName = bufname(a:originalBuffer)
 447	let bufferName = a:vcsType . ' ' . a:command
 448	if strlen(a:statusText) > 0
 449		let bufferName .= ' ' . a:statusText
 450	endif
 451	let bufferName .= ' ' . fileName
 452	let counter = 0
 453	let versionedBufferName = bufferName
 454	while buflisted(versionedBufferName)
 455		let counter += 1
 456		let versionedBufferName = bufferName . ' (' . counter . ')'
 457	endwhile
 458	return versionedBufferName
 459endfunction
 460
 461" Function: s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) {{{2
 462" Method of generating the name for VCS result buffers that uses the original
 463" file name with the VCS type and command appended as extensions.
 464
 465function! s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText)
 466	let fileName = bufname(a:originalBuffer)
 467	let bufferName = a:vcsType . ' ' . a:command
 468	if strlen(a:statusText) > 0
 469		let bufferName .= ' ' . a:statusText
 470	endif
 471	let bufferName .= ' ' . fileName . VCSCommandGetOption('VCSCommandResultBufferNameExtension', '.vcs')
 472	let counter = 0
 473	let versionedBufferName = bufferName
 474	while buflisted(versionedBufferName)
 475		let counter += 1
 476		let versionedBufferName = '(' . counter . ') ' . bufferName
 477	endwhile
 478	return versionedBufferName
 479endfunction
 480
 481" Function: s:EditFile(command, originalBuffer, statusText) {{{2
 482" Creates a new buffer of the given name and associates it with the given
 483" original buffer.
 484
 485function! s:EditFile(command, originalBuffer, statusText)
 486	let vcsType = getbufvar(a:originalBuffer, 'VCSCommandVCSType')
 487
 488	" Protect against useless buffer set-up
 489	let s:isEditFileRunning += 1
 490	try
 491		let editCommand = VCSCommandGetOption('VCSCommandEdit', 'split')
 492		if editCommand == 'split'
 493			if VCSCommandGetOption('VCSCommandSplit', 'horizontal') == 'horizontal'
 494				rightbelow split
 495			else
 496				vert rightbelow split
 497			endif
 498		endif
 499
 500		enew
 501
 502		call s:SetupScratchBuffer(a:command, vcsType, a:originalBuffer, a:statusText)
 503
 504	finally
 505		let s:isEditFileRunning -= 1
 506	endtry
 507endfunction
 508
 509" Function: s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText) {{{2
 510" Creates convenience buffer variables and the name of a vcscommand result
 511" buffer.
 512
 513function! s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText)
 514	let nameExtension = VCSCommandGetOption('VCSCommandResultBufferNameExtension', '')
 515	if nameExtension == ''
 516		let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferName')
 517	else
 518		let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferNameWithExtension')
 519	endif
 520
 521	let name = call(nameFunction, [a:command, a:originalBuffer, a:vcsType, a:statusText])
 522
 523	let b:VCSCommandCommand = a:command
 524	let b:VCSCommandOriginalBuffer = a:originalBuffer
 525	let b:VCSCommandSourceFile = bufname(a:originalBuffer)
 526	let b:VCSCommandVCSType = a:vcsType
 527	if a:statusText != ''
 528		let b:VCSCommandStatusText = a:statusText
 529	endif
 530
 531	setlocal buftype=nofile
 532	setlocal noswapfile
 533	let &filetype = a:vcsType . a:command
 534
 535	if VCSCommandGetOption('VCSCommandDeleteOnHide', 0)
 536		setlocal bufhidden=delete
 537	endif
 538	silent noautocmd file `=name`
 539endfunction
 540
 541" Function: s:SetupBuffer() {{{2
 542" Attempts to set the b:VCSCommandBufferInfo variable
 543
 544function! s:SetupBuffer()
 545	if (exists('b:VCSCommandBufferSetup') && b:VCSCommandBufferSetup)
 546		" This buffer is already set up.
 547		return
 548	endif
 549
 550	if !isdirectory(@%) && (strlen(&buftype) > 0 || !filereadable(@%))
 551		" No special status for special buffers other than directory buffers.
 552		return
 553	endif
 554
 555	if !VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) || s:isEditFileRunning > 0
 556		unlet! b:VCSCommandBufferSetup
 557		return
 558	endif
 559
 560	try
 561		let vcsType = VCSCommandGetVCSType(bufnr('%'))
 562		let b:VCSCommandBufferInfo = s:plugins[vcsType][1].GetBufferInfo()
 563		silent do VCSCommand User VCSBufferSetup
 564	catch /No suitable plugin/
 565		" This is not a VCS-controlled file.
 566		let b:VCSCommandBufferInfo = []
 567	endtry
 568
 569	let b:VCSCommandBufferSetup = 1
 570endfunction
 571
 572" Function: s:MarkOrigBufferForSetup(buffer) {{{2
 573" Resets the buffer setup state of the original buffer for a given VCS scratch
 574" buffer.
 575" Returns:  The VCS buffer number in a passthrough mode.
 576
 577function! s:MarkOrigBufferForSetup(buffer)
 578	checktime
 579	if a:buffer > 0 
 580		let origBuffer = VCSCommandGetOriginalBuffer(a:buffer)
 581		" This should never not work, but I'm paranoid
 582		if origBuffer != a:buffer
 583			call setbufvar(origBuffer, 'VCSCommandBufferSetup', 0)
 584		endif
 585	endif
 586	return a:buffer
 587endfunction
 588
 589" Function: s:OverrideOption(option, [value]) {{{2
 590" Provides a temporary override for the given VCS option.  If no value is
 591" passed, the override is disabled.
 592
 593function! s:OverrideOption(option, ...)
 594	if a:0 == 0
 595		call remove(s:optionOverrides[a:option], -1)
 596	else
 597		if !has_key(s:optionOverrides, a:option)
 598			let s:optionOverrides[a:option] = []
 599		endif
 600		call add(s:optionOverrides[a:option], a:1)
 601	endif
 602endfunction
 603
 604" Function: s:WipeoutCommandBuffers() {{{2
 605" Clears all current VCS output buffers of the specified type for a given source.
 606
 607function! s:WipeoutCommandBuffers(originalBuffer, VCSCommand)
 608	let buffer = 1
 609	while buffer <= bufnr('$')
 610		if getbufvar(buffer, 'VCSCommandOriginalBuffer') == a:originalBuffer
 611			if getbufvar(buffer, 'VCSCommandCommand') == a:VCSCommand
 612				execute 'bw' buffer
 613			endif
 614		endif
 615		let buffer = buffer + 1
 616	endwhile
 617endfunction
 618
 619" Function: s:VimDiffRestore(vimDiffBuff) {{{2
 620" Checks whether the given buffer is one whose deletion should trigger
 621" restoration of an original buffer after it was diffed.  If so, it executes
 622" the appropriate setting command stored with that original buffer.
 623
 624function! s:VimDiffRestore(vimDiffBuff)
 625	let s:isEditFileRunning += 1
 626	try
 627		if exists('s:vimDiffSourceBuffer')
 628			if a:vimDiffBuff == s:vimDiffSourceBuffer
 629				" Original file is being removed.
 630				unlet! s:vimDiffSourceBuffer
 631				unlet! s:vimDiffRestoreCmd
 632				unlet! s:vimDiffScratchList
 633			else
 634				let index = index(s:vimDiffScratchList, a:vimDiffBuff)
 635				if index >= 0
 636					call remove(s:vimDiffScratchList, index)
 637					if len(s:vimDiffScratchList) == 0
 638						if exists('s:vimDiffRestoreCmd')
 639							" All scratch buffers are gone, reset the original.
 640							" Only restore if the source buffer is still in Diff mode
 641
 642							let sourceWinNR = bufwinnr(s:vimDiffSourceBuffer)
 643							if sourceWinNR != -1
 644								" The buffer is visible in at least one window
 645								let currentWinNR = winnr()
 646								while winbufnr(sourceWinNR) != -1
 647									if winbufnr(sourceWinNR) == s:vimDiffSourceBuffer
 648										execute sourceWinNR . 'wincmd w'
 649										if getwinvar(0, '&diff')
 650											execute s:vimDiffRestoreCmd
 651										endif
 652									endif
 653									let sourceWinNR = sourceWinNR + 1
 654								endwhile
 655								execute currentWinNR . 'wincmd w'
 656							else
 657								" The buffer is hidden.  It must be visible in order to set the
 658								" diff option.
 659								let currentBufNR = bufnr('')
 660								execute 'hide buffer' s:vimDiffSourceBuffer
 661								if getwinvar(0, '&diff')
 662									execute s:vimDiffRestoreCmd
 663								endif
 664								execute 'hide buffer' currentBufNR
 665							endif
 666
 667							unlet s:vimDiffRestoreCmd
 668						endif 
 669						" All buffers are gone.
 670						unlet s:vimDiffSourceBuffer
 671						unlet s:vimDiffScratchList
 672					endif
 673				endif
 674			endif
 675		endif
 676	finally
 677		let s:isEditFileRunning -= 1
 678	endtry
 679endfunction
 680
 681" Section: Generic VCS command functions {{{1
 682
 683" Function: s:VCSAnnotate(...) {{{2
 684function! s:VCSAnnotate(bang, ...)
 685	try
 686		let annotateBuffer = s:ExecuteVCSCommand('Annotate', a:000)
 687		if annotateBuffer == -1
 688			return -1
 689		endif
 690		if a:bang == '!' && VCSCommandGetOption('VCSCommandDisableSplitAnnotate', 0) == 0
 691			let vcsType = VCSCommandGetVCSType(annotateBuffer)
 692			let functionMap = s:plugins[vcsType][1]
 693			let splitRegex = ''
 694			if has_key(s:plugins[vcsType][1], 'AnnotateSplitRegex')
 695				let splitRegex = s:plugins[vcsType][1]['AnnotateSplitRegex']
 696			endif
 697			let splitRegex = VCSCommandGetOption('VCSCommand' . vcsType . 'AnnotateSplitRegex', splitRegex)
 698			if splitRegex == ''
 699				return annotateBuffer
 700			endif
 701			let originalBuffer = VCSCommandGetOriginalBuffer(annotateBuffer)
 702			let originalFileType = getbufvar(originalBuffer, '&ft')
 703			let annotateFileType = getbufvar(annotateBuffer, '&ft')
 704			execute "normal 0zR\<c-v>G/" . splitRegex . "/e\<cr>d"
 705			call setbufvar('%', '&filetype', getbufvar(originalBuffer, '&filetype'))
 706			set scrollbind
 707			leftabove vert new
 708			normal 0P
 709			execute "normal" . col('$') . "\<c-w>|"
 710			call s:SetupScratchBuffer('annotate', vcsType, originalBuffer, 'header')
 711			wincmd l
 712		endif
 713		return annotateBuffer
 714	catch
 715		call s:ReportError(v:exception)
 716		return -1
 717	endtry
 718endfunction
 719
 720" Function: s:VCSCommit() {{{2
 721function! s:VCSCommit(bang, message)
 722	try
 723		let vcsType = VCSCommandGetVCSType(bufnr('%'))
 724		if !has_key(s:plugins, vcsType)
 725			throw 'Unknown VCS type:  ' . vcsType
 726		endif
 727
 728		let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
 729
 730		" Handle the commit message being specified.  If a message is supplied, it
 731		" is used; if bang is supplied, an empty message is used; otherwise, the
 732		" user is provided a buffer from which to edit the commit message.
 733
 734		if strlen(a:message) > 0 || a:bang == '!'
 735			return s:VCSFinishCommit([a:message], originalBuffer)
 736		endif
 737
 738		call s:EditFile('commitlog', originalBuffer, '')
 739		setlocal ft=vcscommit
 740
 741		" Create a commit mapping.
 742
 743		nnoremap <silent> <buffer> <Plug>VCSCommit :call <SID>VCSFinishCommitWithBuffer()<CR>
 744
 745		silent 0put ='VCS: ----------------------------------------------------------------------'
 746		silent put ='VCS: Please enter log message.  Lines beginning with ''VCS:'' are removed automatically.'
 747		silent put ='VCS: To finish the commit, Type <leader>cc (or your own <Plug>VCSCommit mapping)'
 748
 749		if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1
 750			setlocal buftype=acwrite
 751			au VCSCommandCommit BufWriteCmd <buffer> call s:VCSFinishCommitWithBuffer()
 752			silent put ='VCS: or write this buffer'
 753		endif
 754
 755		silent put ='VCS: ----------------------------------------------------------------------'
 756		$
 757		setlocal nomodified
 758	catch
 759		call s:ReportError(v:exception)
 760		return -1
 761	endtry
 762endfunction
 763
 764" Function: s:VCSFinishCommitWithBuffer() {{{2
 765" Wrapper for s:VCSFinishCommit which is called only from a commit log buffer
 766" which removes all lines starting with 'VCS:'.
 767
 768function! s:VCSFinishCommitWithBuffer()
 769	setlocal nomodified
 770	let currentBuffer = bufnr('%') 
 771	let logMessageList = getbufline('%', 1, '$')
 772	call filter(logMessageList, 'v:val !~ ''^\s*VCS:''')
 773	let resultBuffer = s:VCSFinishCommit(logMessageList, b:VCSCommandOriginalBuffer)
 774	if resultBuffer >= 0
 775		execute 'bw' currentBuffer
 776	endif
 777	return resultBuffer
 778endfunction
 779
 780" Function: s:VCSFinishCommit(logMessageList, originalBuffer) {{{2
 781function! s:VCSFinishCommit(logMessageList, originalBuffer)
 782	let shellSlashBak = &shellslash
 783	try
 784		set shellslash
 785		let messageFileName = tempname()
 786		call writefile(a:logMessageList, messageFileName)
 787		try
 788			let resultBuffer = s:ExecuteVCSCommand('Commit', [messageFileName])
 789			if resultBuffer < 0
 790				return resultBuffer
 791			endif
 792			return s:MarkOrigBufferForSetup(resultBuffer)
 793		finally
 794			call delete(messageFileName)
 795		endtry
 796	finally
 797		let &shellslash = shellSlashBak
 798	endtry
 799endfunction
 800
 801" Function: s:VCSGotoOriginal(bang) {{{2
 802function! s:VCSGotoOriginal(bang)
 803	let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
 804	if originalBuffer > 0
 805		let origWinNR = bufwinnr(originalBuffer)
 806		if origWinNR == -1
 807			execute 'buffer' originalBuffer
 808		else
 809			execute origWinNR . 'wincmd w'
 810		endif
 811		if a:bang == '!'
 812			let buffnr = 1
 813			let buffmaxnr = bufnr('$')
 814			while buffnr <= buffmaxnr
 815				if getbufvar(buffnr, 'VCSCommandOriginalBuffer') == originalBuffer
 816					execute 'bw' buffnr
 817				endif
 818				let buffnr = buffnr + 1
 819			endwhile
 820		endif
 821	endif
 822endfunction
 823
 824" Function: s:VCSVimDiff(...) {{{2
 825function! s:VCSVimDiff(...)
 826	try
 827		let vcsType = VCSCommandGetVCSType(bufnr('%'))
 828		if !has_key(s:plugins, vcsType)
 829			throw 'Unknown VCS type:  ' . vcsType
 830		endif
 831		let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
 832		let s:isEditFileRunning = s:isEditFileRunning + 1
 833		try
 834			" If there's already a VimDiff'ed window, restore it.
 835			" There may only be one VCSVimDiff original window at a time.
 836
 837			if exists('s:vimDiffSourceBuffer') && s:vimDiffSourceBuffer != originalBuffer
 838				" Clear the existing vimdiff setup by removing the result buffers.
 839				call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
 840			endif
 841
 842			let orientation = &diffopt =~ 'horizontal' ? 'horizontal' : 'vertical'
 843			let orientation = VCSCommandGetOption('VCSCommandSplit', orientation)
 844			let orientation = VCSCommandGetOption('VCSCommandDiffSplit', orientation)
 845
 846			" Split and diff
 847			if(a:0 == 2)
 848				" Reset the vimdiff system, as 2 explicit versions were provided.
 849				if exists('s:vimDiffSourceBuffer')
 850					call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
 851				endif
 852				let resultBuffer = s:plugins[vcsType][1].Review([a:1])
 853				if resultBuffer < 0
 854					echomsg 'Can''t open revision ' . a:1
 855					return resultBuffer
 856				endif
 857				let b:VCSCommandCommand = 'vimdiff'
 858				diffthis
 859				let s:vimDiffScratchList = [resultBuffer]
 860				" If no split method is defined, cheat, and set it to vertical.
 861				try
 862					call s:OverrideOption('VCSCommandSplit', orientation)
 863					let resultBuffer = s:plugins[vcsType][1].Review([a:2])
 864				finally
 865					call s:OverrideOption('VCSCommandSplit')
 866				endtry
 867				if resultBuffer < 0
 868					echomsg 'Can''t open revision ' . a:1
 869					return resultBuffer
 870				endif
 871				let b:VCSCommandCommand = 'vimdiff'
 872				diffthis
 873				let s:vimDiffScratchList += [resultBuffer]
 874			else
 875				" Add new buffer
 876				call s:OverrideOption('VCSCommandEdit', 'split')
 877				try
 878					" Force splitting behavior, otherwise why use vimdiff?
 879					call s:OverrideOption('VCSCommandSplit', orientation)
 880					try
 881						if(a:0 == 0)
 882							let resultBuffer = s:plugins[vcsType][1].Review([])
 883						else
 884							let resultBuffer = s:plugins[vcsType][1].Review([a:1])
 885						endif
 886					finally
 887						call s:OverrideOption('VCSCommandSplit')
 888					endtry
 889				finally
 890					call s:OverrideOption('VCSCommandEdit')
 891				endtry
 892				if resultBuffer < 0
 893					echomsg 'Can''t open current revision'
 894					return resultBuffer
 895				endif
 896				let b:VCSCommandCommand = 'vimdiff'
 897				diffthis
 898
 899				if !exists('s:vimDiffSourceBuffer')
 900					" New instance of vimdiff.
 901					let s:vimDiffScratchList = [resultBuffer]
 902
 903					" This could have been invoked on a VCS result buffer, not the
 904					" original buffer.
 905					wincmd W
 906					execute 'buffer' originalBuffer
 907					" Store info for later original buffer restore
 908					let s:vimDiffRestoreCmd = 
 909								\    'call setbufvar('.originalBuffer.', ''&diff'', '.getbufvar(originalBuffer, '&diff').')'
 910								\ . '|call setbufvar('.originalBuffer.', ''&foldcolumn'', '.getbufvar(originalBuffer, '&foldcolumn').')'
 911								\ . '|call setbufvar('.originalBuffer.', ''&foldenable'', '.getbufvar(originalBuffer, '&foldenable').')'
 912								\ . '|call setbufvar('.originalBuffer.', ''&foldmethod'', '''.getbufvar(originalBuffer, '&foldmethod').''')'
 913								\ . '|call setbufvar('.originalBuffer.', ''&foldlevel'', '''.getbufvar(originalBuffer, '&foldlevel').''')'
 914								\ . '|call setbufvar('.originalBuffer.', ''&scrollbind'', '.getbufvar(originalBuffer, '&scrollbind').')'
 915								\ . '|call setbufvar('.originalBuffer.', ''&wrap'', '.getbufvar(originalBuffer, '&wrap').')'
 916								\ . '|if &foldmethod==''manual''|execute ''normal zE''|endif'
 917					diffthis
 918					wincmd w
 919				else
 920					" Adding a window to an existing vimdiff
 921					let s:vimDiffScratchList += [resultBuffer]
 922				endif
 923			endif
 924
 925			let s:vimDiffSourceBuffer = originalBuffer
 926
 927			" Avoid executing the modeline in the current buffer after the autocommand.
 928
 929			let currentBuffer = bufnr('%')
 930			let saveModeline = getbufvar(currentBuffer, '&modeline')
 931			try
 932				call setbufvar(currentBuffer, '&modeline', 0)
 933				silent do VCSCommand User VCSVimDiffFinish
 934			finally
 935				call setbufvar(currentBuffer, '&modeline', saveModeline)
 936			endtry
 937			return resultBuffer
 938		finally
 939			let s:isEditFileRunning = s:isEditFileRunning - 1
 940		endtry
 941	catch
 942		call s:ReportError(v:exception)
 943		return -1
 944	endtry
 945endfunction
 946
 947" Section: Public functions {{{1
 948
 949" Function: VCSCommandGetVCSType() {{{2
 950" Sets the b:VCSCommandVCSType variable in the given buffer to the
 951" appropriate source control system name.
 952"
 953" This uses the Identify extension function to test the buffer.  If the
 954" Identify function returns VCSCOMMAND_IDENTIFY_EXACT, the match is considered
 955" exact.  If the Identify function returns VCSCOMMAND_IDENTIFY_INEXACT, the
 956" match is considered inexact, and is only applied if no exact match is found.
 957" Multiple inexact matches is currently considered an error.
 958
 959function! VCSCommandGetVCSType(buffer)
 960	let vcsType = getbufvar(a:buffer, 'VCSCommandVCSType')
 961	if strlen(vcsType) > 0
 962		return vcsType
 963	endif
 964	if exists("g:VCSCommandVCSTypeOverride")
 965		let fullpath = fnamemodify(bufname(a:buffer), ':p')
 966		for [path, vcsType] in g:VCSCommandVCSTypeOverride
 967			if match(fullpath, path) > -1
 968				call setbufvar(a:buffer, 'VCSCommandVCSType', vcsType)
 969				return vcsType
 970			endif
 971		endfor
 972	endif
 973	let matches = []
 974	for vcsType in keys(s:plugins)
 975		let identified = s:plugins[vcsType][1].Identify(a:buffer)
 976		if identified
 977			if identified == g:VCSCOMMAND_IDENTIFY_EXACT
 978				let matches = [vcsType]
 979				break
 980			else
 981				let matches += [vcsType]
 982			endif
 983		endif
 984	endfor
 985	if len(matches) == 1
 986		call setbufvar(a:buffer, 'VCSCommandVCSType', matches[0])
 987		return matches[0]
 988	elseif len(matches) == 0
 989		throw 'No suitable plugin'
 990	else
 991		throw 'Too many matching VCS:  ' . join(matches)
 992	endif
 993endfunction
 994
 995" Function: VCSCommandChdir(directory) {{{2
 996" Changes the current directory, respecting :lcd changes.
 997
 998function! VCSCommandChdir(directory)
 999	let command = 'cd'
1000	if exists("*haslocaldir") && haslocaldir()
1001		let command = 'lcd'
1002	endif
1003	execute command escape(a:directory, ' ')
1004endfunction
1005
1006" Function: VCSCommandChangeToCurrentFileDir() {{{2
1007" Go to the directory in which the given file is located.
1008
1009function! VCSCommandChangeToCurrentFileDir(fileName)
1010	let oldCwd = getcwd()
1011	let newCwd = fnamemodify(resolve(a:fileName), ':p:h')
1012	if strlen(newCwd) > 0
1013		call VCSCommandChdir(newCwd)
1014	endif
1015	return oldCwd
1016endfunction
1017
1018" Function: VCSCommandGetOriginalBuffer(vcsBuffer) {{{2
1019" Attempts to locate the original file to which VCS operations were applied
1020" for a given buffer.
1021
1022function! VCSCommandGetOriginalBuffer(vcsBuffer)
1023	let origBuffer = getbufvar(a:vcsBuffer, 'VCSCommandOriginalBuffer')
1024	if origBuffer
1025		if bufexists(origBuffer)
1026			return origBuffer
1027		else
1028			" Original buffer no longer exists.
1029			throw 'Original buffer for this VCS buffer no longer exists.'
1030		endif
1031	else
1032		" No original buffer
1033		return a:vcsBuffer
1034	endif
1035endfunction
1036
1037" Function: VCSCommandRegisterModule(name, file, commandMap) {{{2
1038" Allows VCS modules to register themselves.
1039
1040function! VCSCommandRegisterModule(name, path, commandMap, mappingMap)
1041	let s:plugins[a:name] = [a:path, a:commandMap, a:mappingMap]
1042	if !empty(a:mappingMap)
1043				\ && !VCSCommandGetOption('VCSCommandDisableMappings', 0)
1044				\ && !VCSCommandGetOption('VCSCommandDisableExtensionMappings', 0)
1045		for shortcut in keys(a:mappingMap)
1046			let expansion = ":call <SID>ExecuteExtensionMapping('" . shortcut . "')<CR>"
1047			call s:CreateMapping(shortcut, expansion, a:name . " extension mapping " . shortcut)
1048		endfor
1049	endif
1050endfunction
1051
1052" Function: VCSCommandDoCommand(cmd, cmdName, statusText, [options]) {{{2
1053" General skeleton for VCS function execution.  The given command is executed
1054" after appending the current buffer name (or substituting it for
1055" <VCSCOMMANDFILE>, if such a token is present).  The output is captured in a
1056" new buffer.
1057"
1058" The optional 'options' Dictionary may contain the following options:
1059" 	allowNonZeroExit:  if non-zero, if the underlying VCS command has a
1060"		non-zero exit status, the command is still considered
1061"		successfuly.  This defaults to zero.
1062" Returns: name of the new command buffer containing the command results
1063
1064function! VCSCommandDoCommand(cmd, cmdName, statusText, options)
1065	let allowNonZeroExit = 0
1066	if has_key(a:options, 'allowNonZeroExit')
1067		let allowNonZeroExit = a:options.allowNonZeroExit
1068	endif
1069
1070	let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
1071	if originalBuffer == -1 
1072		throw 'Original buffer no longer exists, aborting.'
1073	endif
1074
1075	let path = resolve(bufname(originalBuffer))
1076
1077	" Work with netrw or other systems where a directory listing is displayed in
1078	" a buffer.
1079
1080	if isdirectory(path)
1081		let fileName = '.'
1082	else
1083		let fileName = fnamemodify(path, ':t')
1084	endif
1085
1086	if match(a:cmd, '<VCSCOMMANDFILE>') > 0
1087		let fullCmd = substitute(a:cmd, '<VCSCOMMANDFILE>', fileName, 'g')
1088	else
1089		let fullCmd = a:cmd . ' "' . fileName . '"'
1090	endif
1091
1092	" Change to the directory of the current buffer.  This is done for CVS, but
1093	" is left in for other systems as it does not affect them negatively.
1094
1095	let oldCwd = VCSCommandChangeToCurrentFileDir(path)
1096	try
1097		let output = system(fullCmd)
1098	finally
1099		call VCSCommandChdir(oldCwd)
1100	endtry
1101
1102	" HACK:  if line endings in the repository have been corrupted, the output
1103	" of the command will be confused.
1104	let output = substitute(output, "\r", '', 'g')
1105
1106	if v:shell_error && !allowNonZeroExit
1107		if strlen(output) == 0
1108			throw 'Version control command failed'
1109		else
1110			let output = substitute(output, '\n', '  ', 'g')
1111			throw 'Version control command failed:  ' . output
1112		endif
1113	endif
1114
1115	if strlen(output) == 0
1116		" Handle case of no output.  In this case, it is important to check the
1117		" file status, especially since cvs edit/unedit may change the attributes
1118		" of the file with no visible output.
1119
1120		checktime
1121		return 0
1122	endif
1123
1124	call s:EditFile(a:cmdName, originalBuffer, a:statusText)
1125
1126	silent 0put=output
1127
1128	" The last command left a blank line at the end of the buffer.  If the
1129	" last line is folded (a side effect of the 'put') then the attempt to
1130	" remove the blank line will kill the last fold.
1131	"
1132	" This could be fixed by explicitly detecting whether the last line is
1133	" within a fold, but I prefer to simply unfold the result buffer altogether.
1134
1135	if has('folding')
1136		normal zR
1137	endif
1138
1139	$d
1140	1
1141
1142	" Define the environment and execute user-defined hooks.
1143
1144	silent do VCSCommand User VCSBufferCreated
1145	return bufnr('%')
1146endfunction
1147
1148" Function: VCSCommandGetOption(name, default) {{{2
1149" Grab a user-specified option to override the default provided.  Options are
1150" searched in the window, buffer, then global spaces.
1151
1152function! VCSCommandGetOption(name, default)
1153	if has_key(s:optionOverrides, a:name) && len(s:optionOverrides[a:name]) > 0
1154		return s:optionOverrides[a:name][-1]
1155	elseif exists('w:' . a:name)
1156		return w:{a:name}
1157	elseif exists('b:' . a:name)
1158		return b:{a:name}
1159	elseif exists('g:' . a:name)
1160		return g:{a:name}
1161	else
1162		return a:default
1163	endif
1164endfunction
1165
1166" Function: VCSCommandDisableBufferSetup() {{{2
1167" Global function for deactivating the buffer autovariables.
1168
1169function! VCSCommandDisableBufferSetup()
1170	let g:VCSCommandEnableBufferSetup = 0
1171	silent! augroup! VCSCommandPlugin
1172endfunction
1173
1174" Function: VCSCommandEnableBufferSetup() {{{2
1175" Global function for activating the buffer autovariables.
1176
1177function! VCSCommandEnableBufferSetup()
1178	let g:VCSCommandEnableBufferSetup = 1
1179	augroup VCSCommandPlugin
1180		au!
1181		au BufEnter * call s:SetupBuffer()
1182	augroup END
1183
1184	" Only auto-load if the plugin is fully loaded.  This gives other plugins a
1185	" chance to run.
1186	if g:loaded_VCSCommand == 2
1187		call s:SetupBuffer()
1188	endif
1189endfunction
1190
1191" Function: VCSCommandGetStatusLine() {{{2
1192" Default (sample) status line entry for VCS-controlled files.  This is only
1193" useful if VCS-managed buffer mode is on (see the VCSCommandEnableBufferSetup
1194" variable for how to do this).
1195
1196function! VCSCommandGetStatusLine()
1197	if exists('b:VCSCommandCommand')
1198		" This is a result buffer.  Return nothing because the buffer name
1199		" contains information already.
1200		return ''
1201	endif
1202
1203	if exists('b:VCSCommandVCSType')
1204				\ && exists('g:VCSCommandEnableBufferSetup')
1205				\ && g:VCSCommandEnableBufferSetup
1206				\ && exists('b:VCSCommandBufferInfo')
1207		return '[' . join(extend([b:VCSCommandVCSType], b:VCSCommandBufferInfo), ' ') . ']'
1208	else
1209		return ''
1210	endif
1211endfunction
1212
1213" Section: Command definitions {{{1
1214" Section: Primary commands {{{2
1215com! -nargs=* VCSAdd call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Add', [<f-args>]))
1216com! -nargs=* -bang VCSAnnotate call s:VCSAnnotate(<q-bang>, <f-args>)
1217com! -nargs=* -bang VCSBlame call s:VCSAnnotate(<q-bang>, <f-args>)
1218com! -nargs=? -bang VCSCommit call s:VCSCommit(<q-bang>, <q-args>)
1219com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', [<f-args>])
1220com! -nargs=* VCSDiff call s:ExecuteVCSCommand('Diff', [<f-args>])
1221com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal(<q-bang>)
1222com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', [<f-args>])
1223com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [<f-args>]))
1224com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', [<f-args>])
1225com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', [<f-args>])
1226com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', []))
1227com! -nargs=? VCSReview call s:ExecuteVCSCommand('Review', [<f-args>])
1228com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', [<f-args>])
1229com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [<f-args>]))
1230com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', []))
1231com! -nargs=* VCSVimDiff call s:VCSVimDiff(<f-args>)
1232
1233" Section: VCS buffer management commands {{{2
1234com! VCSCommandDisableBufferSetup call VCSCommandDisableBufferSetup()
1235com! VCSCommandEnableBufferSetup call VCSCommandEnableBufferSetup()
1236
1237" Allow reloading VCSCommand.vim
1238com! VCSReload let savedPlugins = s:plugins|let s:plugins = {}|aunmenu Plugin.VCS|unlet! g:loaded_VCSCommand|runtime plugin/vcscommand.vim|for plugin in values(savedPlugins)|execute 'source' plugin[0]|endfor|unlet savedPlugins
1239
1240" Section: Plugin command mappings {{{1
1241nnoremap <silent> <Plug>VCSAdd :VCSAdd<CR>
1242nnoremap <silent> <Plug>VCSAnnotate :VCSAnnotate<CR>
1243nnoremap <silent> <Plug>VCSCommit :VCSCommit<CR>
1244nnoremap <silent> <Plug>VCSDelete :VCSDelete<CR>
1245nnoremap <silent> <Plug>VCSDiff :VCSDiff<CR>
1246nnoremap <silent> <Plug>VCSGotoOriginal :VCSGotoOriginal<CR>
1247nnoremap <silent> <Plug>VCSClearAndGotoOriginal :VCSGotoOriginal!<CR>
1248nnoremap <silent> <Plug>VCSInfo :VCSInfo<CR>
1249nnoremap <silent> <Plug>VCSLock :VCSLock<CR>
1250nnoremap <silent> <Plug>VCSLog :VCSLog<CR>
1251nnoremap <silent> <Plug>VCSRevert :VCSRevert<CR>
1252nnoremap <silent> <Plug>VCSReview :VCSReview<CR>
1253nnoremap <silent> <Plug>VCSSplitAnnotate :VCSAnnotate!<CR>
1254nnoremap <silent> <Plug>VCSStatus :VCSStatus<CR>
1255nnoremap <silent> <Plug>VCSUnlock :VCSUnlock<CR>
1256nnoremap <silent> <Plug>VCSUpdate :VCSUpdate<CR>
1257nnoremap <silent> <Plug>VCSVimDiff :VCSVimDiff<CR>
1258
1259" Section: Default mappings {{{1
1260
1261let s:defaultMappings = [
1262			\['a', 'VCSAdd'],
1263			\['c', 'VCSCommit'],
1264			\['D', 'VCSDelete'],
1265			\['d', 'VCSDiff'],
1266			\['G', 'VCSClearAndGotoOriginal'],
1267			\['g', 'VCSGotoOriginal'],
1268			\['i', 'VCSInfo'],
1269			\['L', 'VCSLock'],
1270			\['l', 'VCSLog'],
1271			\['N', 'VCSSplitAnnotate'],
1272			\['n', 'VCSAnnotate'],
1273			\['q', 'VCSRevert'],
1274			\['r', 'VCSReview'],
1275			\['s', 'VCSStatus'],
1276			\['U', 'VCSUnlock'],
1277			\['u', 'VCSUpdate'],
1278			\['v', 'VCSVimDiff'],
1279			\]
1280
1281if !VCSCommandGetOption('VCSCommandDisableMappings', 0)
1282	for [shortcut, vcsFunction] in VCSCommandGetOption('VCSCommandMappings', s:defaultMappings)
1283		call s:CreateMapping(shortcut, '<Plug>' . vcsFunction, '''' . vcsFunction . '''')
1284	endfor
1285endif
1286
1287" Section: Menu items {{{1
1288amenu <silent> &Plugin.VCS.&Add        <Plug>VCSAdd
1289amenu <silent> &Plugin.VCS.A&nnotate   <Plug>VCSAnnotate
1290amenu <silent> &Plugin.VCS.&Commit     <Plug>VCSCommit
1291amenu <silent> &Plugin.VCS.Delete      <Plug>VCSDelete
1292amenu <silent> &Plugin.VCS.&Diff       <Plug>VCSDiff
1293amenu <silent> &Plugin.VCS.&Info       <Plug>VCSInfo
1294amenu <silent> &Plugin.VCS.&Log        <Plug>VCSLog
1295amenu <silent> &Plugin.VCS.Revert      <Plug>VCSRevert
1296amenu <silent> &Plugin.VCS.&Review     <Plug>VCSReview
1297amenu <silent> &Plugin.VCS.&Status     <Plug>VCSStatus
1298amenu <silent> &Plugin.VCS.&Update     <Plug>VCSUpdate
1299amenu <silent> &Plugin.VCS.&VimDiff    <Plug>VCSVimDiff
1300
1301" Section: Autocommands to restore vimdiff state {{{1
1302augroup VimDiffRestore
1303	au!
1304	au BufUnload * call s:VimDiffRestore(str2nr(expand('<abuf>')))
1305augroup END
1306
1307" Section: Optional activation of buffer management {{{1
1308
1309if VCSCommandGetOption('VCSCommandEnableBufferSetup', 0)
1310	call VCSCommandEnableBufferSetup()
1311endif
1312
1313" Section: VIM shutdown hook {{{1
1314
1315" Close all result buffers when VIM exits, to prevent them from being restored
1316" via viminfo.
1317
1318" Function: s:CloseAllResultBuffers() {{{2
1319" Closes all vcscommand result buffers.
1320function! s:CloseAllResultBuffers()
1321	" This avoids using bufdo as that may load buffers already loaded in another
1322	" vim process, resulting in an error.
1323	let buffnr = 1
1324	let buffmaxnr = bufnr('$')
1325	while buffnr <= buffmaxnr
1326		if getbufvar(buffnr, 'VCSCommandOriginalBuffer') != "" 
1327			execute 'bw' buffnr
1328		endif
1329		let buffnr = buffnr + 1
1330	endwhile
1331endfunction
1332
1333augroup VCSCommandVIMShutdown
1334	au!
1335	au VimLeavePre * call s:CloseAllResultBuffers()
1336augroup END
1337
1338" Section: Plugin completion {{{1
1339
1340let loaded_VCSCommand = 2
1341
1342silent do VCSCommand User VCSPluginFinish
1343
1344let &cpo = s:save_cpo