From 7f6642486ab606a86e3eb6ec59130de86b835330 Mon Sep 17 00:00:00 2001 From: Hugues Hiegel Date: Fri, 4 Jul 2008 10:39:16 +0200 Subject: Vim files with source code colors and VCS commands --- plugin/vcscommand.vim | 1253 +++++++++++++++++++++++++++++++++++++++++++++++++ plugin/vcscvs.vim | 437 +++++++++++++++++ plugin/vcsgit.vim | 254 ++++++++++ plugin/vcssvk.vim | 258 ++++++++++ plugin/vcssvn.vim | 284 +++++++++++ 5 files changed, 2486 insertions(+) create mode 100644 plugin/vcscommand.vim create mode 100644 plugin/vcscvs.vim create mode 100644 plugin/vcsgit.vim create mode 100644 plugin/vcssvk.vim create mode 100644 plugin/vcssvn.vim (limited to 'plugin') diff --git a/plugin/vcscommand.vim b/plugin/vcscommand.vim new file mode 100644 index 0000000..4a52a03 --- /dev/null +++ b/plugin/vcscommand.vim @@ -0,0 +1,1253 @@ +" vim600: set foldmethod=marker: +" +" Vim plugin to assist in working with files under control of CVS or SVN. +" +" Version: Beta 22 +" Maintainer: Bob Hiestand +" License: +" Copyright (c) 2007 Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Provides functions to invoke various source control commands on the current +" file (either the current buffer, or, in the case of an directory buffer, the +" directory and all subdirectories associated with the current buffer). The +" output of the commands is captured in a new scratch window. +" +" This plugin needs additional extension plugins, each specific to a source +" control system, to function. Those plugins should be placed in a +" subdirectory of the standard plugin directory named 'vcscommand'. Several +" options include the name of the version control system in the option name. +" Such options use the placeholder text '{VCSType}', which would be replaced +" in actual usage with 'CVS' or 'SVN', for instance. +" +" Command documentation {{{2 +" +" VCSAdd Adds the current file to source control. +" +" VCSAnnotate Displays the current file with each line annotated with the +" version in which it was most recently changed. If an +" argument is given, the argument is used as a revision +" number to display. If not given an argument, it uses the +" most recent version of the file on the current branch. +" Additionally, if the current buffer is a VCSAnnotate buffer +" already, the version number on the current line is used. +" +" VCSBlame Alias for 'VCSAnnotate'. +" +" VCSCommit[!] Commits changes to the current file to source control. +" +" If called with arguments, the arguments are the log message. +" +" If '!' is used, an empty log message is committed. +" +" If called with no arguments, this is a two-step command. +" The first step opens a buffer to accept a log message. +" When that buffer is written, it is automatically closed and +" the file is committed using the information from that log +" message. The commit can be abandoned if the log message +" buffer is deleted or wiped before being written. +" +" VCSDelete Deletes the current file and removes it from source control. +" +" VCSDiff With no arguments, this displays the differences between +" the current file and its parent version under source +" control in a new scratch buffer. +" +" With one argument, the diff is performed on the +" current file against the specified revision. +" +" With two arguments, the diff is performed between the +" specified revisions of the current file. +" +" This command uses the 'VCSCommand{VCSType}DiffOpt' variable +" to specify diff options. If that variable does not exist, +" a plugin-specific default is used. If you wish to have no +" options, then set it to the empty string. +" +" VCSGotoOriginal Jumps to the source buffer if the current buffer is a VCS +" scratch buffer. If VCSGotoOriginal[!] is used, remove all +" VCS scratch buffers associated with the original file. +" +" VCSInfo Displays extended information about the current file in a +" new scratch buffer. +" +" VCSLock Locks the current file in order to prevent other users from +" concurrently modifying it. The exact semantics of this +" command depend on the underlying VCS. +" +" VCSLog Displays the version history of the current file in a new +" scratch buffer. +" +" VCSRemove Alias for 'VCSDelete'. +" +" VCSRevert Replaces the modified version of the current file with the +" most recent version from the repository. +" +" VCSReview Displays a particular version of the current file in a new +" scratch buffer. If no argument is given, the most recent +" version of the file on the current branch is retrieved. +" +" VCSStatus Displays versioning information about the current file in a +" new scratch buffer. +" +" VCSUnlock Unlocks the current file in order to allow other users from +" concurrently modifying it. The exact semantics of this +" command depend on the underlying VCS. +" +" VCSUpdate Updates the current file with any relevant changes from the +" repository. +" +" VCSVimDiff Uses vimdiff to display differences between versions of the +" current file. +" +" If no revision is specified, the most recent version of the +" file on the current branch is used. With one argument, +" that argument is used as the revision as above. With two +" arguments, the differences between the two revisions is +" displayed using vimdiff. +" +" With either zero or one argument, the original buffer is +" used to perform the vimdiff. When the scratch buffer is +" closed, the original buffer will be returned to normal +" mode. +" +" Once vimdiff mode is started using the above methods, +" additional vimdiff buffers may be added by passing a single +" version argument to the command. There may be up to 4 +" vimdiff buffers total. +" +" Using the 2-argument form of the command resets the vimdiff +" to only those 2 versions. Additionally, invoking the +" command on a different file will close the previous vimdiff +" buffers. +" +" Mapping documentation: {{{2 +" +" By default, a mapping is defined for each command. User-provided mappings +" can be used instead by mapping to CommandName, for instance: +" +" nmap ,ca VCSAdd +" +" The default mappings are as follow: +" +" ca VCSAdd +" cn VCSAnnotate +" cc VCSCommit +" cD VCSDelete +" cd VCSDiff +" cg VCSGotoOriginal +" cG VCSGotoOriginal! +" ci VCSInfo +" cl VCSLog +" cL VCSLock +" cr VCSReview +" cs VCSStatus +" cu VCSUpdate +" cU VCSUnlock +" cv VCSVimDiff +" +" Options documentation: {{{2 +" +" Several variables are checked by the script to determine behavior as follow: +" +" VCSCommandCommitOnWrite +" This variable, if set to a non-zero value, causes the pending commit to +" take place immediately as soon as the log message buffer is written. If +" set to zero, only the VCSCommit mapping will cause the pending commit to +" occur. If not set, it defaults to 1. +" +" VCSCommandDeleteOnHide +" This variable, if set to a non-zero value, causes the temporary VCS result +" buffers to automatically delete themselves when hidden. +" +" VCSCommand{VCSType}DiffOpt +" This variable, if set, determines the options passed to the diff command +" of the underlying VCS. Each VCS plugin defines a default value. +" +" VCSCommandDiffSplit +" This variable overrides the VCSCommandSplit variable, but only for buffers +" created with VCSVimDiff. +" +" VCSCommandDisableMappings +" This variable, if set to a non-zero value, prevents the default command +" mappings from being set. +" +" VCSCommandDisableExtensionMappings +" This variable, if set to a non-zero value, prevents the default command +" mappings from being set for commands specific to an individual VCS. +" +" VCSCommandEdit +" This variable controls whether to split the current window to display a +" scratch buffer ('split'), or to display it in the current buffer ('edit'). +" If not set, it defaults to 'split'. +" +" VCSCommandEnableBufferSetup +" This variable, if set to a non-zero value, activates VCS buffer management +" mode. This mode means that the buffer variable 'VCSRevision' is set if +" the file is VCS-controlled. This is useful for displaying version +" information in the status bar. Additional options may be set by +" individual VCS plugins. +" +" VCSCommandResultBufferNameExtension +" This variable, if set to a non-blank value, is appended to the name of the +" VCS command output buffers. For example, '.vcs'. Using this option may +" help avoid problems caused by autocommands dependent on file extension. +" +" VCSCommandResultBufferNameFunction +" This variable, if set, specifies a custom function for naming VCS command +" output buffers. This function will be passed the following arguments: +" +" command - name of the VCS command being executed (such as 'Log' or +" 'Diff'). +" +" originalBuffer - buffer number of the source file. +" +" vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN'). +" +" statusText - extra text associated with the VCS action (such as version +" numbers). +" +" VCSCommandSplit +" This variable controls the orientation of the various window splits that +" may occur (such as with VCSVimDiff, when using a VCS command on a VCS +" command buffer, or when the 'VCSCommandEdit' variable is set to 'split'. +" If set to 'horizontal', the resulting windows will be on stacked on top of +" one another. If set to 'vertical', the resulting windows will be +" side-by-side. If not set, it defaults to 'horizontal' for all but +" VCSVimDiff windows. +" +" Event documentation {{{2 +" For additional customization, VCSCommand.vim uses User event autocommand +" hooks. Each event is in the VCSCommand group, and different patterns +" match the various hooks. +" +" For instance, the following could be added to the vimrc to provide a 'q' +" mapping to quit a VCS scratch buffer: +" +" augroup VCSCommand +" au VCSCommand User VCSBufferCreated silent! nmap q :bwipeout +" augroup END +" +" The following hooks are available: +" +" VCSBufferCreated This event is fired just after a VCS command +" output buffer is created. It is executed +" within the context of the new buffer. +" +" VCSBufferSetup This event is fired just after VCS buffer setup +" occurs, if enabled. +" +" VCSPluginInit This event is fired when the VCSCommand plugin +" first loads. +" +" VCSPluginFinish This event is fired just after the VCSCommand +" plugin loads. +" +" VCSVimDiffFinish This event is fired just after the VCSVimDiff +" command executes to allow customization of, +" for instance, window placement and focus. +" +" Section: Plugin header {{{1 + +" loaded_VCSCommand is set to 1 when the initialization begins, and 2 when it +" completes. This allows various actions to only be taken by functions after +" system initialization. + +if exists('loaded_VCSCommand') + finish +endif +let loaded_VCSCommand = 1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Event group setup {{{1 + +augroup VCSCommand +augroup END + +augroup VCSCommandCommit +augroup END + +" Section: Plugin initialization {{{1 +silent do VCSCommand User VCSPluginInit + +" Section: Constants declaration {{{1 + +let g:VCSCOMMAND_IDENTIFY_EXACT = 1 +let g:VCSCOMMAND_IDENTIFY_INEXACT = -1 + +" Section: Script variable initialization {{{1 + +" plugin-specific information: {vcs -> [script, {command -> function}, {key -> mapping}]} +let s:plugins = {} + +" temporary values of overridden configuration variables +let s:optionOverrides = {} + +" state flag used to vary behavior of certain automated actions +let s:isEditFileRunning = 0 + +" commands needed to restore diff buffers to their original state +unlet! s:vimDiffRestoreCmd + +" original buffer currently reflected in vimdiff windows +unlet! s:vimDiffSourceBuffer + +" +unlet! s:vimDiffScratchList + +" Section: Utility functions {{{1 + +" Function: s:ReportError(mapping) {{{2 +" Displays the given error in a consistent faction. This is intended to be +" invoked from a catch statement. + +function! s:ReportError(error) + echohl WarningMsg|echomsg 'VCSCommand: ' . a:error|echohl None +endfunction + +" Function: s:ExecuteExtensionMapping(mapping) {{{2 +" Invokes the appropriate extension mapping depending on the type of the +" current buffer. + +function! s:ExecuteExtensionMapping(mapping) + let buffer = bufnr('%') + let vcsType = VCSCommandGetVCSType(buffer) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + if !has_key(s:plugins[vcsType][2], a:mapping) + throw 'This extended mapping is not defined for ' . vcsType + endif + silent execute 'normal' ':' . s:plugins[vcsType][2][a:mapping] . "\" +endfunction + +" Function: s:ExecuteVCSCommand(command, argList) {{{2 +" Calls the indicated plugin-specific VCS command on the current buffer. +" Returns: buffer number of resulting output scratch buffer, or -1 if an error +" occurs. + +function! s:ExecuteVCSCommand(command, argList) + try + let buffer = bufnr('%') + + let vcsType = VCSCommandGetVCSType(buffer) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(buffer) + let bufferName = bufname(originalBuffer) + + " It is already known that the directory is under VCS control. No further + " checks are needed. Otherwise, perform some basic sanity checks to avoid + " VCS-specific error messages from confusing things. + if !isdirectory(bufferName) + if !filereadable(bufferName) + throw 'No such file ' . bufferName + endif + endif + + let functionMap = s:plugins[vcsType][1] + if !has_key(functionMap, a:command) + throw 'Command ''' . a:command . ''' not implemented for ' . vcsType + endif + return functionMap[a:command](a:argList) + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Function: s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) {{{2 +" Default method of generating the name for VCS result buffers. This can be +" overridden with the VCSResultBufferNameFunction variable. + +function! s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) + let fileName = bufname(a:originalBuffer) + let bufferName = a:vcsType . ' ' . a:command + if strlen(a:statusText) > 0 + let bufferName .= ' ' . a:statusText + endif + let bufferName .= ' ' . fileName + let counter = 0 + let versionedBufferName = bufferName + while buflisted(versionedBufferName) + let counter += 1 + let versionedBufferName = bufferName . ' (' . counter . ')' + endwhile + return versionedBufferName +endfunction + +" Function: s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) {{{2 +" Method of generating the name for VCS result buffers that uses the original +" file name with the VCS type and command appended as extensions. + +function! s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) + let fileName = bufname(a:originalBuffer) + let bufferName = a:vcsType . ' ' . a:command + if strlen(a:statusText) > 0 + let bufferName .= ' ' . a:statusText + endif + let bufferName .= ' ' . fileName . VCSCommandGetOption('VCSCommandResultBufferNameExtension', '.vcs') + let counter = 0 + let versionedBufferName = bufferName + while buflisted(versionedBufferName) + let counter += 1 + let versionedBufferName = '(' . counter . ') ' . bufferName + endwhile + return versionedBufferName +endfunction + +" Function: s:EditFile(command, originalBuffer, statusText) {{{2 +" Creates a new buffer of the given name and associates it with the given +" original buffer. + +function! s:EditFile(command, originalBuffer, statusText) + let vcsType = getbufvar(a:originalBuffer, 'VCSCommandVCSType') + + let nameExtension = VCSCommandGetOption('VCSCommandResultBufferNameExtension', '') + if nameExtension == '' + let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferName') + else + let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferNameWithExtension') + endif + + let resultBufferName = call(nameFunction, [a:command, a:originalBuffer, vcsType, a:statusText]) + + " Protect against useless buffer set-up + let s:isEditFileRunning += 1 + try + let editCommand = VCSCommandGetOption('VCSCommandEdit', 'split') + if editCommand == 'split' + if VCSCommandGetOption('VCSCommandSplit', 'horizontal') == 'horizontal' + rightbelow split + else + vert rightbelow split + endif + endif + edit `=resultBufferName` + let b:VCSCommandCommand = a:command + let b:VCSCommandOriginalBuffer = a:originalBuffer + let b:VCSCommandSourceFile = bufname(a:originalBuffer) + let b:VCSCommandVCSType = vcsType + + set buftype=nofile + set noswapfile + let &filetype = vcsType . a:command + + if a:statusText != '' + let b:VCSCommandStatusText = a:statusText + endif + + if VCSCommandGetOption('VCSCommandDeleteOnHide', 0) + set bufhidden=delete + endif + finally + let s:isEditFileRunning -= 1 + endtry + +endfunction + +" Function: s:SetupBuffer() {{{2 +" Attempts to set the b:VCSCommandBufferInfo variable + +function! s:SetupBuffer() + if (exists('b:VCSCommandBufferSetup') && b:VCSCommandBufferSetup) + " This buffer is already set up. + return + endif + + if !isdirectory(@%) && (strlen(&buftype) > 0 || !filereadable(@%)) + " No special status for special buffers other than directory buffers. + return + endif + + if !VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) || s:isEditFileRunning > 0 + unlet! b:VCSCommandBufferSetup + return + endif + + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + let b:VCSCommandBufferInfo = s:plugins[vcsType][1].GetBufferInfo() + silent do VCSCommand User VCSBufferSetup + catch /No suitable plugin/ + " This is not a VCS-controlled file. + let b:VCSCommandBufferInfo = [] + endtry + + let b:VCSCommandBufferSetup = 1 +endfunction + +" Function: s:MarkOrigBufferForSetup(buffer) {{{2 +" Resets the buffer setup state of the original buffer for a given VCS scratch +" buffer. +" Returns: The VCS buffer number in a passthrough mode. + +function! s:MarkOrigBufferForSetup(buffer) + checktime + if a:buffer > 0 + let origBuffer = VCSCommandGetOriginalBuffer(a:buffer) + " This should never not work, but I'm paranoid + if origBuffer != a:buffer + call setbufvar(origBuffer, 'VCSCommandBufferSetup', 0) + endif + endif + return a:buffer +endfunction + +" Function: s:OverrideOption(option, [value]) {{{2 +" Provides a temporary override for the given VCS option. If no value is +" passed, the override is disabled. + +function! s:OverrideOption(option, ...) + if a:0 == 0 + call remove(s:optionOverrides[a:option], -1) + else + if !has_key(s:optionOverrides, a:option) + let s:optionOverrides[a:option] = [] + endif + call add(s:optionOverrides[a:option], a:1) + endif +endfunction + +" Function: s:WipeoutCommandBuffers() {{{2 +" Clears all current VCS output buffers of the specified type for a given source. + +function! s:WipeoutCommandBuffers(originalBuffer, VCSCommand) + let buffer = 1 + while buffer <= bufnr('$') + if getbufvar(buffer, 'VCSCommandOriginalBuffer') == a:originalBuffer + if getbufvar(buffer, 'VCSCommandCommand') == a:VCSCommand + execute 'bw' buffer + endif + endif + let buffer = buffer + 1 + endwhile +endfunction + +" Function: s:VimDiffRestore(vimDiffBuff) {{{2 +" Checks whether the given buffer is one whose deletion should trigger +" restoration of an original buffer after it was diffed. If so, it executes +" the appropriate setting command stored with that original buffer. + +function! s:VimDiffRestore(vimDiffBuff) + let s:isEditFileRunning += 1 + try + if exists('s:vimDiffSourceBuffer') + if a:vimDiffBuff == s:vimDiffSourceBuffer + " Original file is being removed. + unlet! s:vimDiffSourceBuffer + unlet! s:vimDiffRestoreCmd + unlet! s:vimDiffScratchList + else + let index = index(s:vimDiffScratchList, a:vimDiffBuff) + if index >= 0 + call remove(s:vimDiffScratchList, index) + if len(s:vimDiffScratchList) == 0 + if exists('s:vimDiffRestoreCmd') + " All scratch buffers are gone, reset the original. + " Only restore if the source buffer is still in Diff mode + + let sourceWinNR = bufwinnr(s:vimDiffSourceBuffer) + if sourceWinNR != -1 + " The buffer is visible in at least one window + let currentWinNR = winnr() + while winbufnr(sourceWinNR) != -1 + if winbufnr(sourceWinNR) == s:vimDiffSourceBuffer + execute sourceWinNR . 'wincmd w' + if getwinvar(0, '&diff') + execute s:vimDiffRestoreCmd + endif + endif + let sourceWinNR = sourceWinNR + 1 + endwhile + execute currentWinNR . 'wincmd w' + else + " The buffer is hidden. It must be visible in order to set the + " diff option. + let currentBufNR = bufnr('') + execute 'hide buffer' s:vimDiffSourceBuffer + if getwinvar(0, '&diff') + execute s:vimDiffRestoreCmd + endif + execute 'hide buffer' currentBufNR + endif + + unlet s:vimDiffRestoreCmd + endif + " All buffers are gone. + unlet s:vimDiffSourceBuffer + unlet s:vimDiffScratchList + endif + endif + endif + endif + finally + let s:isEditFileRunning -= 1 + endtry +endfunction + +" Section: Generic VCS command functions {{{1 + +" Function: s:VCSCommit() {{{2 +function! s:VCSCommit(bang, message) + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + + " Handle the commit message being specified. If a message is supplied, it + " is used; if bang is supplied, an empty message is used; otherwise, the + " user is provided a buffer from which to edit the commit message. + + if strlen(a:message) > 0 || a:bang == '!' + return s:VCSFinishCommit([a:message], originalBuffer) + endif + + call s:EditFile('commitlog', originalBuffer, '') + set ft=vcscommit + + " Create a commit mapping. + + nnoremap VCSCommit :call VCSFinishCommitWithBuffer() + + silent 0put ='VCS: ----------------------------------------------------------------------' + silent put ='VCS: Please enter log message. Lines beginning with ''VCS:'' are removed automatically.' + silent put ='VCS: To finish the commit, Type cc (or your own VCSCommit mapping)' + + if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1 + set buftype=acwrite + au VCSCommandCommit BufWriteCmd call s:VCSFinishCommitWithBuffer() + silent put ='VCS: or write this buffer' + endif + + silent put ='VCS: ----------------------------------------------------------------------' + $ + set nomodified + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Function: s:VCSFinishCommitWithBuffer() {{{2 +" Wrapper for s:VCSFinishCommit which is called only from a commit log buffer +" which removes all lines starting with 'VCS:'. + +function! s:VCSFinishCommitWithBuffer() + set nomodified + let currentBuffer = bufnr('%') + let logMessageList = getbufline('%', 1, '$') + call filter(logMessageList, 'v:val !~ ''^\s*VCS:''') + let resultBuffer = s:VCSFinishCommit(logMessageList, b:VCSCommandOriginalBuffer) + if resultBuffer >= 0 + execute 'bw' currentBuffer + endif + return resultBuffer +endfunction + +" Function: s:VCSFinishCommit(logMessageList, originalBuffer) {{{2 +function! s:VCSFinishCommit(logMessageList, originalBuffer) + let shellSlashBak = &shellslash + try + set shellslash + let messageFileName = tempname() + call writefile(a:logMessageList, messageFileName) + try + let resultBuffer = s:ExecuteVCSCommand('Commit', [messageFileName]) + if resultBuffer < 0 + return resultBuffer + endif + return s:MarkOrigBufferForSetup(resultBuffer) + finally + call delete(messageFileName) + endtry + finally + let &shellslash = shellSlashBak + endtry +endfunction + +" Function: s:VCSGotoOriginal(bang) {{{2 +function! s:VCSGotoOriginal(bang) + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + if originalBuffer > 0 + let origWinNR = bufwinnr(originalBuffer) + if origWinNR == -1 + execute 'buffer' originalBuffer + else + execute origWinNR . 'wincmd w' + endif + if a:bang == '!' + let buffnr = 1 + let buffmaxnr = bufnr('$') + while buffnr <= buffmaxnr + if getbufvar(buffnr, 'VCSCommandOriginalBuffer') == originalBuffer + execute 'bw' buffnr + endif + let buffnr = buffnr + 1 + endwhile + endif + endif +endfunction + +" Function: s:VCSVimDiff(...) {{{2 +function! s:VCSVimDiff(...) + try + let vcsType = VCSCommandGetVCSType(bufnr('%')) + if !has_key(s:plugins, vcsType) + throw 'Unknown VCS type: ' . vcsType + endif + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let s:isEditFileRunning = s:isEditFileRunning + 1 + try + " If there's already a VimDiff'ed window, restore it. + " There may only be one VCSVimDiff original window at a time. + + if exists('s:vimDiffSourceBuffer') && s:vimDiffSourceBuffer != originalBuffer + " Clear the existing vimdiff setup by removing the result buffers. + call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') + endif + + " Split and diff + if(a:0 == 2) + " Reset the vimdiff system, as 2 explicit versions were provided. + if exists('s:vimDiffSourceBuffer') + call s:WipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') + endif + let resultBuffer = s:plugins[vcsType][1].Review([a:1]) + if resultBuffer < 0 + echomsg 'Can''t open revision ' . a:1 + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + let s:vimDiffScratchList = [resultBuffer] + " If no split method is defined, cheat, and set it to vertical. + try + call s:OverrideOption('VCSCommandSplit', VCSCommandGetOption('VCSCommandDiffSplit', VCSCommandGetOption('VCSCommandSplit', 'vertical'))) + let resultBuffer = s:plugins[vcsType][1].Review([a:2]) + finally + call s:OverrideOption('VCSCommandSplit') + endtry + if resultBuffer < 0 + echomsg 'Can''t open revision ' . a:1 + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + let s:vimDiffScratchList += [resultBuffer] + else + " Add new buffer + call s:OverrideOption('VCSCommandEdit', 'split') + try + " Force splitting behavior, otherwise why use vimdiff? + call s:OverrideOption('VCSCommandSplit', VCSCommandGetOption('VCSCommandDiffSplit', VCSCommandGetOption('VCSCommandSplit', 'vertical'))) + try + if(a:0 == 0) + let resultBuffer = s:plugins[vcsType][1].Review([]) + else + let resultBuffer = s:plugins[vcsType][1].Review([a:1]) + endif + finally + call s:OverrideOption('VCSCommandSplit') + endtry + finally + call s:OverrideOption('VCSCommandEdit') + endtry + if resultBuffer < 0 + echomsg 'Can''t open current revision' + return resultBuffer + endif + let b:VCSCommandCommand = 'vimdiff' + diffthis + + if !exists('s:vimDiffSourceBuffer') + " New instance of vimdiff. + let s:vimDiffScratchList = [resultBuffer] + + " This could have been invoked on a VCS result buffer, not the + " original buffer. + wincmd W + execute 'buffer' originalBuffer + " Store info for later original buffer restore + let s:vimDiffRestoreCmd = + \ 'call setbufvar('.originalBuffer.', ''&diff'', '.getbufvar(originalBuffer, '&diff').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldcolumn'', '.getbufvar(originalBuffer, '&foldcolumn').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldenable'', '.getbufvar(originalBuffer, '&foldenable').')' + \ . '|call setbufvar('.originalBuffer.', ''&foldmethod'', '''.getbufvar(originalBuffer, '&foldmethod').''')' + \ . '|call setbufvar('.originalBuffer.', ''&foldlevel'', '''.getbufvar(originalBuffer, '&foldlevel').''')' + \ . '|call setbufvar('.originalBuffer.', ''&scrollbind'', '.getbufvar(originalBuffer, '&scrollbind').')' + \ . '|call setbufvar('.originalBuffer.', ''&wrap'', '.getbufvar(originalBuffer, '&wrap').')' + \ . '|if &foldmethod==''manual''|execute ''normal zE''|endif' + diffthis + wincmd w + else + " Adding a window to an existing vimdiff + let s:vimDiffScratchList += [resultBuffer] + endif + endif + + let s:vimDiffSourceBuffer = originalBuffer + + " Avoid executing the modeline in the current buffer after the autocommand. + + let currentBuffer = bufnr('%') + let saveModeline = getbufvar(currentBuffer, '&modeline') + try + call setbufvar(currentBuffer, '&modeline', 0) + silent do VCSCommand User VCSVimDiffFinish + finally + call setbufvar(currentBuffer, '&modeline', saveModeline) + endtry + return resultBuffer + finally + let s:isEditFileRunning = s:isEditFileRunning - 1 + endtry + catch + call s:ReportError(v:exception) + return -1 + endtry +endfunction + +" Section: Public functions {{{1 + +" Function: VCSCommandGetVCSType() {{{2 +" Sets the b:VCSCommandVCSType variable in the given buffer to the +" appropriate source control system name. +" +" This uses the Identify extension function to test the buffer. If the +" Identify function returns VCSCOMMAND_IDENTIFY_EXACT, the match is considered +" exact. If the Identify function returns VCSCOMMAND_IDENTIFY_INEXACT, the +" match is considered inexact, and is only applied if no exact match is found. +" Multiple inexact matches is currently considered an error. + +function! VCSCommandGetVCSType(buffer) + let vcsType = getbufvar(a:buffer, 'VCSCommandVCSType') + if strlen(vcsType) > 0 + return vcsType + endif + let matches = [] + for vcsType in keys(s:plugins) + let identified = s:plugins[vcsType][1].Identify(a:buffer) + if identified + if identified == g:VCSCOMMAND_IDENTIFY_EXACT + let matches = [vcsType] + break + else + let matches += [vcsType] + endif + endif + endfor + if len(matches) == 1 + call setbufvar(a:buffer, 'VCSCommandVCSType', matches[0]) + return matches[0] + elseif len(matches) == 0 + throw 'No suitable plugin' + else + throw 'Too many matching VCS: ' . join(matches) + endif +endfunction + +" Function: VCSCommandChdir(directory) {{{2 +" Changes the current directory, respecting :lcd changes. + +function! VCSCommandChdir(directory) + let command = 'cd' + if exists("*haslocaldir") && haslocaldir() + let command = 'lcd' + endif + execute command escape(a:directory, ' ') +endfunction + +" Function: VCSCommandChangeToCurrentFileDir() {{{2 +" Go to the directory in which the given file is located. + +function! VCSCommandChangeToCurrentFileDir(fileName) + let oldCwd = getcwd() + let newCwd = fnamemodify(resolve(a:fileName), ':p:h') + if strlen(newCwd) > 0 + call VCSCommandChdir(newCwd) + endif + return oldCwd +endfunction + +" Function: VCSCommandGetOriginalBuffer(vcsBuffer) {{{2 +" Attempts to locate the original file to which VCS operations were applied +" for a given buffer. + +function! VCSCommandGetOriginalBuffer(vcsBuffer) + let origBuffer = getbufvar(a:vcsBuffer, 'VCSCommandOriginalBuffer') + if origBuffer + if bufexists(origBuffer) + return origBuffer + else + " Original buffer no longer exists. + throw 'Original buffer for this VCS buffer no longer exists.' + endif + else + " No original buffer + return a:vcsBuffer + endif +endfunction + +" Function: VCSCommandRegisterModule(name, file, commandMap) {{{2 +" Allows VCS modules to register themselves. + +function! VCSCommandRegisterModule(name, path, commandMap, mappingMap) + let s:plugins[a:name] = [a:path, a:commandMap, a:mappingMap] + if !empty(a:mappingMap) + \ && !VCSCommandGetOption('VCSCommandDisableMappings', 0) + \ && !VCSCommandGetOption('VCSCommandDisableExtensionMappings', 0) + for mapname in keys(a:mappingMap) + execute 'noremap ' . mapname ':call ExecuteExtensionMapping(''' . mapname . ''')' + endfor + endif +endfunction + +" Function: VCSCommandDoCommand(cmd, cmdName, statusText, [options]) {{{2 +" General skeleton for VCS function execution. The given command is executed +" after appending the current buffer name (or substituting it for +" , if such a token is present). The output is captured in a +" new buffer. +" +" The optional 'options' Dictionary may contain the following options: +" allowNonZeroExit: if non-zero, if the underlying VCS command has a +" non-zero exit status, the command is still considered +" successfuly. This defaults to zero. +" Returns: name of the new command buffer containing the command results + +function! VCSCommandDoCommand(cmd, cmdName, statusText, options) + let allowNonZeroExit = 0 + if has_key(a:options, 'allowNonZeroExit') + let allowNonZeroExit = a:options.allowNonZeroExit + endif + + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + if originalBuffer == -1 + throw 'Original buffer no longer exists, aborting.' + endif + + let path = resolve(bufname(originalBuffer)) + + " Work with netrw or other systems where a directory listing is displayed in + " a buffer. + + if isdirectory(path) + let fileName = '.' + else + let fileName = fnamemodify(path, ':t') + endif + + if match(a:cmd, '') > 0 + let fullCmd = substitute(a:cmd, '', fileName, 'g') + else + let fullCmd = a:cmd . ' "' . fileName . '"' + endif + + " Change to the directory of the current buffer. This is done for CVS, but + " is left in for other systems as it does not affect them negatively. + + let oldCwd = VCSCommandChangeToCurrentFileDir(path) + try + let output = system(fullCmd) + finally + call VCSCommandChdir(oldCwd) + endtry + + " HACK: if line endings in the repository have been corrupted, the output + " of the command will be confused. + let output = substitute(output, "\r", '', 'g') + + if v:shell_error && !allowNonZeroExit + if strlen(output) == 0 + throw 'Version control command failed' + else + let output = substitute(output, '\n', ' ', 'g') + throw 'Version control command failed: ' . output + endif + endif + + if strlen(output) == 0 + " Handle case of no output. In this case, it is important to check the + " file status, especially since cvs edit/unedit may change the attributes + " of the file with no visible output. + + checktime + return 0 + endif + + call s:EditFile(a:cmdName, originalBuffer, a:statusText) + + silent 0put=output + + " The last command left a blank line at the end of the buffer. If the + " last line is folded (a side effect of the 'put') then the attempt to + " remove the blank line will kill the last fold. + " + " This could be fixed by explicitly detecting whether the last line is + " within a fold, but I prefer to simply unfold the result buffer altogether. + + if has('folding') + normal zR + endif + + $d + 1 + + " Define the environment and execute user-defined hooks. + + silent do VCSCommand User VCSBufferCreated + return bufnr('%') +endfunction + +" Function: VCSCommandGetOption(name, default) {{{2 +" Grab a user-specified option to override the default provided. Options are +" searched in the window, buffer, then global spaces. + +function! VCSCommandGetOption(name, default) + if has_key(s:optionOverrides, a:name) && len(s:optionOverrides[a:name]) > 0 + return s:optionOverrides[a:name][-1] + elseif exists('w:' . a:name) + return w:{a:name} + elseif exists('b:' . a:name) + return b:{a:name} + elseif exists('g:' . a:name) + return g:{a:name} + else + return a:default + endif +endfunction + +" Function: VCSCommandDisableBufferSetup() {{{2 +" Global function for deactivating the buffer autovariables. + +function! VCSCommandDisableBufferSetup() + let g:VCSCommandEnableBufferSetup = 0 + silent! augroup! VCSCommandPlugin +endfunction + +" Function: VCSCommandEnableBufferSetup() {{{2 +" Global function for activating the buffer autovariables. + +function! VCSCommandEnableBufferSetup() + let g:VCSCommandEnableBufferSetup = 1 + augroup VCSCommandPlugin + au! + au BufEnter * call s:SetupBuffer() + augroup END + + " Only auto-load if the plugin is fully loaded. This gives other plugins a + " chance to run. + if g:loaded_VCSCommand == 2 + call s:SetupBuffer() + endif +endfunction + +" Function: VCSCommandGetStatusLine() {{{2 +" Default (sample) status line entry for VCS-controlled files. This is only +" useful if VCS-managed buffer mode is on (see the VCSCommandEnableBufferSetup +" variable for how to do this). + +function! VCSCommandGetStatusLine() + if exists('b:VCSCommandCommand') + " This is a result buffer. Return nothing because the buffer name + " contains information already. + return '' + endif + + if exists('b:VCSCommandVCSType') + \ && exists('g:VCSCommandEnableBufferSetup') + \ && g:VCSCommandEnableBufferSetup + \ && exists('b:VCSCommandBufferInfo') + return '[' . join(extend([b:VCSCommandVCSType], b:VCSCommandBufferInfo), ' ') . ']' + else + return '' + endif +endfunction + +" Section: Command definitions {{{1 +" Section: Primary commands {{{2 +com! -nargs=* VCSAdd call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Add', [])) +com! -nargs=* VCSAnnotate call s:ExecuteVCSCommand('Annotate', []) +com! -nargs=* VCSBlame call s:ExecuteVCSCommand('Annotate', []) +com! -nargs=? -bang VCSCommit call s:VCSCommit(, ) +com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', []) +com! -nargs=* VCSDiff call s:ExecuteVCSCommand('Diff', []) +com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal() +com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', []) +com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [])) +com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', []) +com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', []) +com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', [])) +com! -nargs=? VCSReview call s:ExecuteVCSCommand('Review', []) +com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', []) +com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [])) +com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', [])) +com! -nargs=* VCSVimDiff call s:VCSVimDiff() + +" Section: VCS buffer management commands {{{2 +com! VCSCommandDisableBufferSetup call VCSCommandDisableBufferSetup() +com! VCSCommandEnableBufferSetup call VCSCommandEnableBufferSetup() + +" Allow reloading VCSCommand.vim +com! 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 + +" Section: Plugin command mappings {{{1 +nnoremap VCSAdd :VCSAdd +nnoremap VCSAnnotate :VCSAnnotate +nnoremap VCSCommit :VCSCommit +nnoremap VCSDelete :VCSDelete +nnoremap VCSDiff :VCSDiff +nnoremap VCSGotoOriginal :VCSGotoOriginal +nnoremap VCSClearAndGotoOriginal :VCSGotoOriginal! +nnoremap VCSInfo :VCSInfo +nnoremap VCSLock :VCSLock +nnoremap VCSLog :VCSLog +nnoremap VCSRevert :VCSRevert +nnoremap VCSReview :VCSReview +nnoremap VCSStatus :VCSStatus +nnoremap VCSUnlock :VCSUnlock +nnoremap VCSUpdate :VCSUpdate +nnoremap VCSVimDiff :VCSVimDiff + +" Section: Default mappings {{{1 + +if !VCSCommandGetOption('VCSCommandDisableMappings', 0) + if !hasmapto('VCSAdd') + nmap ca VCSAdd + endif + if !hasmapto('VCSAnnotate') + nmap cn VCSAnnotate + endif + if !hasmapto('VCSClearAndGotoOriginal') + nmap cG VCSClearAndGotoOriginal + endif + if !hasmapto('VCSCommit') + nmap cc VCSCommit + endif + if !hasmapto('VCSDelete') + nmap cD VCSDelete + endif + if !hasmapto('VCSDiff') + nmap cd VCSDiff + endif + if !hasmapto('VCSGotoOriginal') + nmap cg VCSGotoOriginal + endif + if !hasmapto('VCSInfo') + nmap ci VCSInfo + endif + if !hasmapto('VCSLock') + nmap cL VCSLock + endif + if !hasmapto('VCSLog') + nmap cl VCSLog + endif + if !hasmapto('VCSRevert') + nmap cq VCSRevert + endif + if !hasmapto('VCSReview') + nmap cr VCSReview + endif + if !hasmapto('VCSStatus') + nmap cs VCSStatus + endif + if !hasmapto('VCSUnlock') + nmap cU VCSUnlock + endif + if !hasmapto('VCSUpdate') + nmap cu VCSUpdate + endif + if !hasmapto('VCSVimDiff') + nmap cv VCSVimDiff + endif +endif + +" Section: Menu items {{{1 +amenu &Plugin.VCS.&Add VCSAdd +amenu &Plugin.VCS.A&nnotate VCSAnnotate +amenu &Plugin.VCS.&Commit VCSCommit +amenu &Plugin.VCS.Delete VCSDelete +amenu &Plugin.VCS.&Diff VCSDiff +amenu &Plugin.VCS.&Info VCSInfo +amenu &Plugin.VCS.&Log VCSLog +amenu &Plugin.VCS.Revert VCSRevert +amenu &Plugin.VCS.&Review VCSReview +amenu &Plugin.VCS.&Status VCSStatus +amenu &Plugin.VCS.&Update VCSUpdate +amenu &Plugin.VCS.&VimDiff VCSVimDiff + +" Section: Autocommands to restore vimdiff state {{{1 +augroup VimDiffRestore + au! + au BufUnload * call s:VimDiffRestore(str2nr(expand(''))) +augroup END + +" Section: Optional activation of buffer management {{{1 + +if VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) + call VCSCommandEnableBufferSetup() +endif + +" Section: VIM shutdown hook {{{1 + +" Close all result buffers when VIM exits, to prevent them from being restored +" via viminfo. + +" Function: s:CloseAllResultBuffers() {{{2 +" Closes all vcscommand result buffers. +function! s:CloseAllResultBuffers() + " This avoids using bufdo as that may load buffers already loaded in another + " vim process, resulting in an error. + let buffnr = 1 + let buffmaxnr = bufnr('$') + while buffnr <= buffmaxnr + if getbufvar(buffnr, 'VCSCommandOriginalBuffer') != "" + execute 'bw' buffnr + endif + let buffnr = buffnr + 1 + endwhile +endfunction + +augroup VCSCommandVIMShutdown + au! + au VimLeavePre * call s:CloseAllResultBuffers() +augroup END + +" Section: Plugin completion {{{1 + +let loaded_VCSCommand = 2 + +silent do VCSCommand User VCSPluginFinish + +let &cpo = s:save_cpo diff --git a/plugin/vcscvs.vim b/plugin/vcscvs.vim new file mode 100644 index 0000000..e2930e5 --- /dev/null +++ b/plugin/vcscvs.vim @@ -0,0 +1,437 @@ +" vim600: set foldmethod=marker: +" +" CVS extension for VCSCommand. +" +" Version: VCS development +" Maintainer: Bob Hiestand +" License: +" Copyright (c) 2007 Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Command documentation {{{2 +" +" The following commands only apply to files under CVS source control. +" +" CVSEdit Performs "cvs edit" on the current file. +" +" CVSEditors Performs "cvs editors" on the current file. +" +" CVSUnedit Performs "cvs unedit" on the current file. +" +" CVSWatch Takes an argument which must be one of [on|off|add|remove]. +" Performs "cvs watch" with the given argument on the current +" file. +" +" CVSWatchers Performs "cvs watchers" on the current file. +" +" CVSWatchAdd Alias for "CVSWatch add" +" +" CVSWatchOn Alias for "CVSWatch on" +" +" CVSWatchOff Alias for "CVSWatch off" +" +" CVSWatchRemove Alias for "CVSWatch remove" +" +" Mapping documentation: {{{2 +" +" By default, a mapping is defined for each command. User-provided mappings +" can be used instead by mapping to CommandName, for instance: +" +" nnoremap ,ce CVSEdit +" +" The default mappings are as follow: +" +" ce CVSEdit +" cE CVSEditors +" ct CVSUnedit +" cwv CVSWatchers +" cwa CVSWatchAdd +" cwn CVSWatchOn +" cwf CVSWatchOff +" cwr CVSWatchRemove +" +" Options documentation: {{{2 +" +" VCSCommandCVSExec +" This variable specifies the CVS executable. If not set, it defaults to +" 'cvs' executed from the user's executable path. +" +" VCSCommandCVSDiffOpt +" This variable, if set, determines the options passed to the cvs diff +" command. If not set, it defaults to 'u'. + +" Section: Plugin header {{{1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandCVSExec', 'cvs')) + " CVS is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:cvsFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the CVS executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'CVS' + let fullCmd = VCSCommandGetOption('VCSCommandCVSExec', 'cvs') . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'CVS VCSCommand plugin called on non-CVS item.' + endif +endfunction + +" Function: GetRevision() {{{2 +" Function for retrieving the current buffer's revision number. +" Returns: Revision number or an empty string if an error occurs. + +function! GetRevision() + if !exists('b:VCSCommandBufferInfo') + let b:VCSCommandBufferInfo = s:cvsFunctions.GetBufferInfo() + endif + + if len(b:VCSCommandBufferInfo) > 0 + return b:VCSCommandBufferInfo[0] + else + return '' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:cvsFunctions.Identify(buffer) {{{2 +function! s:cvsFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':h') + endif + if strlen(directoryName) > 0 + let CVSRoot = directoryName . '/CVS/Root' + else + let CVSRoot = 'CVS/Root' + endif + if filereadable(CVSRoot) + return 1 + else + return 0 + endif +endfunction + +" Function: s:cvsFunctions.Add(argList) {{{2 +function! s:cvsFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:cvsFunctions.Annotate(argList) {{{2 +function! s:cvsFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'CVSAnnotate' + " This is a CVSAnnotate buffer. Perform annotation of the version + " indicated by the current line. + let caption = matchstr(getline('.'),'\v^[0-9.]+') + + if VCSCommandGetOption('VCSCommandCVSAnnotateParent', 0) != 0 + if caption != '1.1' + let revmaj = matchstr(caption,'\v[0-9.]+\ze\.[0-9]+') + let revmin = matchstr(caption,'\v[0-9.]+\.\zs[0-9]+') - 1 + if revmin == 0 + " Jump to ancestor branch + let caption = matchstr(revmaj,'\v[0-9.]+\ze\.[0-9]+') + else + let caption = revmaj . "." . revmin + endif + endif + endif + + let options = ['-r' . caption] + else + " CVS defaults to pulling HEAD, regardless of current branch. + " Therefore, always pass desired revision. + let caption = '' + let options = ['-r' . GetRevision()] + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ['-r' . caption] + else + let caption = join(a:argList) + let options = a:argList + endif + + let resultBuffer = s:DoCommand(join(['-q', 'annotate'] + options), 'annotate', caption, {}) + if resultBuffer > 0 + set filetype=CVSAnnotate + " Remove header lines from standard error + silent v/^\d\+\%(\.\d\+\)\+/d + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Commit(argList) {{{2 +function! s:cvsFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Delete() {{{2 +" By default, use the -f option to remove the file first. If options are +" passed in, use those instead. +function! s:cvsFunctions.Delete(argList) + let options = ['-f'] + let caption = '' + if len(a:argList) > 0 + let options = a:argList + let caption = join(a:argList, ' ') + endif + return s:DoCommand(join(['remove'] + options, ' '), 'delete', caption, {}) +endfunction + +" Function: s:cvsFunctions.Diff(argList) {{{2 +function! s:cvsFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ' -r')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let cvsDiffOpt = VCSCommandGetOption('VCSCommandCVSDiffOpt', 'u') + if cvsDiffOpt == '' + let diffOptions = [] + else + let diffOptions = ['-' . cvsDiffOpt] + endif + + let resultBuffer = s:DoCommand(join(['diff'] + diffOptions + revOptions), 'diff', caption, {'allowNonZeroExit': 1}) + if resultBuffer > 0 + set filetype=diff + else + echomsg 'No differences found' + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. This CVS extension adds branch name to the return +" list as well. +" Returns: List of results: [revision, repository, branch] + +function! s:cvsFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = bufname(originalBuffer) + if isdirectory(fileName) + let tag = '' + if filereadable(fileName . '/CVS/Tag') + let tagFile = readfile(fileName . '/CVS/Tag') + if len(tagFile) == 1 + let tag = substitute(tagFile[0], '^T', '', '') + endif + endif + return [tag] + endif + let realFileName = fnamemodify(resolve(fileName), ':t') + if !filereadable(fileName) + return ['Unknown'] + endif + let oldCwd = VCSCommandChangeToCurrentFileDir(fileName) + try + let statusText=system(VCSCommandGetOption('VCSCommandCVSExec', 'cvs') . ' status "' . realFileName . '"') + if(v:shell_error) + return [] + endif + let revision=substitute(statusText, '^\_.*Working revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\)\_.*$', '\1', '') + + " We can still be in a CVS-controlled directory without this being a CVS + " file + if match(revision, '^New file!$') >= 0 + let revision='New' + elseif match(revision, '^\d\+\.\d\+\%(\.\d\+\.\d\+\)*$') <0 + return ['Unknown'] + endif + + let branch=substitute(statusText, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', '') + let repository=substitute(statusText, '^\_.*Repository revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\|No revision control file\)\_.*$', '\1', '') + let repository=substitute(repository, '^New file!\|No revision control file$', 'New', '') + return [revision, repository, branch] + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:cvsFunctions.Log() {{{2 +function! s:cvsFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer=s:DoCommand(join(['log'] + options), 'log', caption, {}) + if resultBuffer > 0 + set filetype=rcslog + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Revert(argList) {{{2 +function! s:cvsFunctions.Revert(argList) + return s:DoCommand('update -C', 'revert', '', {}) +endfunction + +" Function: s:cvsFunctions.Review(argList) {{{2 +function! s:cvsFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + let resultBuffer = s:DoCommand('-q update -p' . versionOption, 'review', versiontag, {}) + if resultBuffer > 0 + let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype') + endif + return resultBuffer +endfunction + +" Function: s:cvsFunctions.Status(argList) {{{2 +function! s:cvsFunctions.Status(argList) + return s:DoCommand(join(['status'] + a:argList, ' '), 'status', join(a:argList, ' '), {}) +endfunction + +" Function: s:cvsFunctions.Update(argList) {{{2 +function! s:cvsFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Section: CVS-specific functions {{{1 + +" Function: s:CVSEdit() {{{2 +function! s:CVSEdit() + return s:DoCommand('edit', 'cvsedit', '', {}) +endfunction + +" Function: s:CVSEditors() {{{2 +function! s:CVSEditors() + return s:DoCommand('editors', 'cvseditors', '', {}) +endfunction + +" Function: s:CVSUnedit() {{{2 +function! s:CVSUnedit() + return s:DoCommand('unedit', 'cvsunedit', '', {}) +endfunction + +" Function: s:CVSWatch(onoff) {{{2 +function! s:CVSWatch(onoff) + if a:onoff !~ '^\c\%(on\|off\|add\|remove\)$' + echoerr 'Argument to CVSWatch must be one of [on|off|add|remove]' + return -1 + end + return s:DoCommand('watch ' . tolower(a:onoff), 'cvswatch', '', {}) +endfunction + +" Function: s:CVSWatchers() {{{2 +function! s:CVSWatchers() + return s:DoCommand('watchers', 'cvswatchers', '', {}) +endfunction + +" Section: Command definitions {{{1 +" Section: Primary commands {{{2 +com! CVSEdit call s:CVSEdit() +com! CVSEditors call s:CVSEditors() +com! CVSUnedit call s:CVSUnedit() +com! -nargs=1 CVSWatch call s:CVSWatch() +com! CVSWatchAdd call s:CVSWatch('add') +com! CVSWatchOn call s:CVSWatch('on') +com! CVSWatchOff call s:CVSWatch('off') +com! CVSWatchRemove call s:CVSWatch('remove') +com! CVSWatchers call s:CVSWatchers() + +" Section: Plugin command mappings {{{1 + +let s:cvsExtensionMappings = {} +let mappingInfo = [ + \['CVSEdit', 'CVSEdit', 'ce'], + \['CVSEditors', 'CVSEditors', 'cE'], + \['CVSUnedit', 'CVSUnedit', 'ct'], + \['CVSWatchers', 'CVSWatchers', 'cwv'], + \['CVSWatchAdd', 'CVSWatch add', 'cwa'], + \['CVSWatchOff', 'CVSWatch off', 'cwf'], + \['CVSWatchOn', 'CVSWatch on', 'cwn'], + \['CVSWatchRemove', 'CVSWatch remove', 'cwr'] + \] + +for [pluginName, commandText, shortCut] in mappingInfo + execute 'nnoremap ' . pluginName . ' :' . commandText . '' + if !hasmapto('' . pluginName) + let s:cvsExtensionMappings[shortCut] = commandText + endif +endfor + +" Section: Menu items {{{1 +silent! aunmenu Plugin.VCS.CVS +amenu &Plugin.VCS.CVS.&Edit CVSEdit +amenu &Plugin.VCS.CVS.Ed&itors CVSEditors +amenu &Plugin.VCS.CVS.Unedi&t CVSUnedit +amenu &Plugin.VCS.CVS.&Watchers CVSWatchers +amenu &Plugin.VCS.CVS.WatchAdd CVSWatchAdd +amenu &Plugin.VCS.CVS.WatchOn CVSWatchOn +amenu &Plugin.VCS.CVS.WatchOff CVSWatchOff +amenu &Plugin.VCS.CVS.WatchRemove CVSWatchRemove + +" Section: Plugin Registration {{{1 +call VCSCommandRegisterModule('CVS', expand(''), s:cvsFunctions, s:cvsExtensionMappings) + +let &cpo = s:save_cpo diff --git a/plugin/vcsgit.vim b/plugin/vcsgit.vim new file mode 100644 index 0000000..19979e0 --- /dev/null +++ b/plugin/vcsgit.vim @@ -0,0 +1,254 @@ +" vim600: set foldmethod=marker: +" +" git extension for VCSCommand. +" +" Version: VCS development +" Maintainer: Bob Hiestand +" License: +" Copyright (c) 2007 Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandGitExec +" This variable specifies the git executable. If not set, it defaults to +" 'git' executed from the user's executable path. +" +" VCSCommandGitDiffOpt +" This variable, if set, determines the default options passed to the +" VCSDiff command. If any options (starting with '-') are passed to the +" command, this variable is not used. + +" Section: Plugin header {{{1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandGitExec', 'git')) + " git is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:gitFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the git executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'git' + let fullCmd = VCSCommandGetOption('VCSCommandGitExec', 'git',) . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'git VCSCommand plugin called on non-git item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:gitFunctions.Identify(buffer) {{{2 +" This function only returns an inexact match due to the detection method used +" by git, which simply traverses the directory structure upward. +function! s:gitFunctions.Identify(buffer) + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer))) + try + call system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' rev-parse --is-inside-work-tree') + if(v:shell_error) + return 0 + else + return g:VCSCOMMAND_IDENTIFY_INEXACT + endif + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:gitFunctions.Add(argList) {{{2 +function! s:gitFunctions.Add(argList) + return s:DoCommand(join(['add'] + ['-v'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:gitFunctions.Annotate(argList) {{{2 +function! s:gitFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'gitAnnotate' + " Perform annotation of the version indicated by the current line. + let options = matchstr(getline('.'),'^\x\+') + else + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let options = a:argList[0] + else + let options = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand('blame ' . options . ' -- ', 'annotate', options, {}) + if resultBuffer > 0 + normal 1G + set filetype=gitAnnotate + endif + return resultBuffer +endfunction + +" Function: s:gitFunctions.Commit(argList) {{{2 +function! s:gitFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif + return resultBuffer +endfunction + +" Function: s:gitFunctions.Delete() {{{2 +" All options are passed through. +function! s:gitFunctions.Delete(argList) + let options = a:argList + let caption = join(a:argList, ' ') + return s:DoCommand(join(['rm'] + options, ' '), 'delete', caption, {}) +endfunction + +" Function: s:gitFunctions.Diff(argList) {{{2 +" Pass-through call to git-diff. If no options (starting with '-') are found, +" then the options in the 'VCSCommandGitDiffOpt' variable are added. +function! s:gitFunctions.Diff(argList) + let gitDiffOpt = VCSCommandGetOption('VCSCommandGitDiffOpt', '') + if gitDiffOpt == '' + let diffOptions = [] + else + let diffOptions = [gitDiffOpt] + for arg in a:argList + if arg =~ '^-' + let diffOptions = [] + break + endif + endfor + endif + + let resultBuffer = s:DoCommand(join(['diff'] + diffOptions + a:argList), 'diff', join(a:argList), {}) + if resultBuffer > 0 + set filetype=diff + else + echomsg 'No differences found' + endif + return resultBuffer +endfunction + +" Function: s:gitFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. This CVS extension adds branch name to the return +" list as well. +" Returns: List of results: [revision, repository, branch] + +function! s:gitFunctions.GetBufferInfo() + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname('%'))) + try + let branch = substitute(system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' symbolic-ref -q HEAD'), '\n$', '', '') + if v:shell_error + let branch = 'DETACHED' + else + let branch = substitute(branch, '^refs/heads/', '', '') + endif + + let info = [branch] + + for method in split(VCSCommandGetOption('VCSCommandGitDescribeArgList', (',tags,all,always')), ',', 1) + if method != '' + let method = ' --' . method + endif + let tag = substitute(system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' describe' . method), '\n$', '', '') + if !v:shell_error + call add(info, tag) + break + endif + endfor + + return info + finally + call VCSCommandChdir(oldCwd) + endtry +endfunction + +" Function: s:gitFunctions.Log() {{{2 +function! s:gitFunctions.Log(argList) + let resultBuffer=s:DoCommand(join(['log'] + a:argList), 'log', join(a:argList, ' '), {}) + if resultBuffer > 0 + set filetype=gitlog + endif + return resultBuffer +endfunction + +" Function: s:gitFunctions.Revert(argList) {{{2 +function! s:gitFunctions.Revert(argList) + return s:DoCommand('checkout', 'revert', '', {}) +endfunction + +" Function: s:gitFunctions.Review(argList) {{{2 +function! s:gitFunctions.Review(argList) + if len(a:argList) == 0 + let revision = 'HEAD' + else + let revision = a:argList[0] + endif + + let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname('%'))) + try + let prefix = system(VCSCommandGetOption('VCSCommandGitExec', 'git') . ' rev-parse --show-prefix') + finally + call VCSCommandChdir(oldCwd) + endtry + + let prefix = substitute(prefix, '\n$', '', '') + let blob = revision . ':' . prefix . '' + let resultBuffer = s:DoCommand('show ' . blob, 'review', revision, {}) + if resultBuffer > 0 + let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype') + endif + return resultBuffer +endfunction + +" Function: s:gitFunctions.Status(argList) {{{2 +function! s:gitFunctions.Status(argList) + return s:DoCommand(join(['status'] + a:argList), 'log', join(a:argList), {'allowNonZeroExit': 1}) +endfunction + +" Function: s:gitFunctions.Update(argList) {{{2 +function! s:gitFunctions.Update(argList) + throw "This command is not implemented for git because file-by-file update doesn't make much sense in that context. If you have an idea for what it should do, please let me know." +endfunction + + +" Section: Plugin Registration {{{1 +call VCSCommandRegisterModule('git', expand(''), s:gitFunctions, []) + +let &cpo = s:save_cpo diff --git a/plugin/vcssvk.vim b/plugin/vcssvk.vim new file mode 100644 index 0000000..116f953 --- /dev/null +++ b/plugin/vcssvk.vim @@ -0,0 +1,258 @@ +" vim600: set foldmethod=marker: +" +" SVK extension for VCSCommand. +" +" Version: VCS development +" Maintainer: Bob Hiestand +" License: +" Copyright (c) 2007 Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandSVKExec +" This variable specifies the SVK executable. If not set, it defaults to +" 'svk' executed from the user's executable path. + +" Section: Plugin header {{{1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandSVKExec', 'svk')) + " SVK is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:svkFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the SVK executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'SVK' + let fullCmd = VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'SVK VCSCommand plugin called on non-SVK item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:svkFunctions.Identify(buffer) {{{2 +function! s:svkFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':p:h') + endif + let statusText = system(VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' info "' . directoryName . '"') + if(v:shell_error) + return 0 + else + return 1 + endif +endfunction + +" Function: s:svkFunctions.Add() {{{2 +function! s:svkFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Annotate(argList) {{{2 +function! s:svkFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'SVKAnnotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {}) + if resultBuffer > 0 + normal 1G2dd + set filetype=SVKAnnotate + endif + return resultBuffer +endfunction + +" Function: s:svkFunctions.Commit(argList) {{{2 +function! s:svkFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif +endfunction + +" Function: s:svkFunctions.Delete() {{{2 +function! s:svkFunctions.Delete(argList) + return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Diff(argList) {{{2 +function! s:svkFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ':')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let resultBuffer = s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {}) + if resultBuffer > 0 + set filetype=diff + else + echomsg 'No differences found' + endif + return resultBuffer +endfunction + +" Function: s:svkFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository] + +function! s:svkFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = resolve(bufname(originalBuffer)) + let statusText = system(VCSCommandGetOption('VCSCommandSVKExec', 'svk') . ' status -v "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under SVK control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let [flags, revision, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3] + if revision == '' + " Error + return ['Unknown'] + elseif flags =~ '^A' + return ['New', 'New'] + else + return [revision, repository] + endif +endfunction + +" Function: s:svkFunctions.Info(argList) {{{2 +function! s:svkFunctions.Info(argList) + return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Lock(argList) {{{2 +function! s:svkFunctions.Lock(argList) + return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {}) +endfunction + +" Function: s:svkFunctions.Log() {{{2 +function! s:svkFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:svkFunctions.Revert(argList) {{{2 +function! s:svkFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:svkFunctions.Review(argList) {{{2 +function! s:svkFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + let resultBuffer = s:DoCommand('cat' . versionOption, 'review', versiontag, {}) + if resultBuffer > 0 + let &filetype=getbufvar(b:VCSCommandOriginalBuffer, '&filetype') + endif + return resultBuffer +endfunction + +" Function: s:svkFunctions.Status(argList) {{{2 +function! s:svkFunctions.Status(argList) + let options = ['-v'] + if len(a:argList) == 0 + let options = a:argList + endif + return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:svkFunctions.Unlock(argList) {{{2 +function! s:svkFunctions.Unlock(argList) + return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {}) +endfunction +" Function: s:svkFunctions.Update(argList) {{{2 +function! s:svkFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Section: Plugin Registration {{{1 +call VCSCommandRegisterModule('SVK', expand(''), s:svkFunctions, []) + +let &cpo = s:save_cpo diff --git a/plugin/vcssvn.vim b/plugin/vcssvn.vim new file mode 100644 index 0000000..97bcbd5 --- /dev/null +++ b/plugin/vcssvn.vim @@ -0,0 +1,284 @@ +" vim600: set foldmethod=marker: +" +" SVN extension for VCSCommand. +" +" Version: VCS development +" Maintainer: Bob Hiestand +" License: +" Copyright (c) 2007 Bob Hiestand +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to +" deal in the Software without restriction, including without limitation the +" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +" sell copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in +" all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +" IN THE SOFTWARE. +" +" Section: Documentation {{{1 +" +" Options documentation: {{{2 +" +" VCSCommandSVNExec +" This variable specifies the SVN executable. If not set, it defaults to +" 'svn' executed from the user's executable path. +" +" VCSCommandSVNDiffExt +" This variable, if set, sets the external diff program used by Subversion. +" +" VCSCommandSVNDiffOpt +" This variable, if set, determines the options passed to the svn diff +" command (such as 'u', 'w', or 'b'). + +" Section: Plugin header {{{1 + +if v:version < 700 + echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None + finish +endif + +runtime plugin/vcscommand.vim + +if !executable(VCSCommandGetOption('VCSCommandSVNExec', 'svn')) + " SVN is not installed + finish +endif + +let s:save_cpo=&cpo +set cpo&vim + +" Section: Variable initialization {{{1 + +let s:svnFunctions = {} + +" Section: Utility functions {{{1 + +" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2 +" Wrapper to VCSCommandDoCommand to add the name of the SVN executable to the +" command argument. +function! s:DoCommand(cmd, cmdName, statusText, options) + if VCSCommandGetVCSType(expand('%')) == 'SVN' + let fullCmd = VCSCommandGetOption('VCSCommandSVNExec', 'svn') . ' ' . a:cmd + return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options) + else + throw 'SVN VCSCommand plugin called on non-SVN item.' + endif +endfunction + +" Section: VCS function implementations {{{1 + +" Function: s:svnFunctions.Identify(buffer) {{{2 +function! s:svnFunctions.Identify(buffer) + let fileName = resolve(bufname(a:buffer)) + if isdirectory(fileName) + let directoryName = fileName + else + let directoryName = fnamemodify(fileName, ':h') + endif + if strlen(directoryName) > 0 + let svnDir = directoryName . '/.svn' + else + let svnDir = '.svn' + endif + if isdirectory(svnDir) + return 1 + else + return 0 + endif +endfunction + +" Function: s:svnFunctions.Add() {{{2 +function! s:svnFunctions.Add(argList) + return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Annotate(argList) {{{2 +function! s:svnFunctions.Annotate(argList) + if len(a:argList) == 0 + if &filetype == 'SVNAnnotate' + " Perform annotation of the version indicated by the current line. + let caption = matchstr(getline('.'),'\v^\s+\zs\d+') + let options = ' -r' . caption + else + let caption = '' + let options = '' + endif + elseif len(a:argList) == 1 && a:argList[0] !~ '^-' + let caption = a:argList[0] + let options = ' -r' . caption + else + let caption = join(a:argList, ' ') + let options = ' ' . caption + endif + + let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {}) + if resultBuffer > 0 + set filetype=SVNAnnotate + endif + return resultBuffer +endfunction + +" Function: s:svnFunctions.Commit(argList) {{{2 +function! s:svnFunctions.Commit(argList) + let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {}) + if resultBuffer == 0 + echomsg 'No commit needed.' + endif +endfunction + +" Function: s:svnFunctions.Delete() {{{2 +function! s:svnFunctions.Delete(argList) + return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Diff(argList) {{{2 +function! s:svnFunctions.Diff(argList) + if len(a:argList) == 0 + let revOptions = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let revOptions = ['-r' . join(a:argList, ':')] + let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')' + else + " Pass-through + let caption = join(a:argList, ' ') + let revOptions = a:argList + endif + + let svnDiffExt = VCSCommandGetOption('VCSCommandSVNDiffExt', '') + if svnDiffExt == '' + let diffExt = [] + else + let diffExt = ['--diff-cmd ' . svnDiffExt] + endif + + let svnDiffOpt = VCSCommandGetOption('VCSCommandSVNDiffOpt', '') + if svnDiffOpt == '' + let diffOptions = [] + else + let diffOptions = ['-x -' . svnDiffOpt] + endif + + let resultBuffer = s:DoCommand(join(['diff'] + diffExt + diffOptions + revOptions), 'diff', caption, {}) + if resultBuffer > 0 + set filetype=diff + else + if svnDiffExt == '' + echomsg 'No differences found' + endif + endif + return resultBuffer +endfunction + +" Function: s:svnFunctions.GetBufferInfo() {{{2 +" Provides version control details for the current file. Current version +" number and current repository version number are required to be returned by +" the vcscommand plugin. +" Returns: List of results: [revision, repository, branch] + +function! s:svnFunctions.GetBufferInfo() + let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%')) + let fileName = bufname(originalBuffer) + let statusText = system(VCSCommandGetOption('VCSCommandSVNExec', 'svn') . ' status -vu "' . fileName . '"') + if(v:shell_error) + return [] + endif + + " File not under SVN control. + if statusText =~ '^?' + return ['Unknown'] + endif + + let [flags, revision, repository] = matchlist(statusText, '^\(.\{8}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3] + if revision == '' + " Error + return ['Unknown'] + elseif flags =~ '^A' + return ['New', 'New'] + else + return [revision, repository] + endif +endfunction + +" Function: s:svnFunctions.Info(argList) {{{2 +function! s:svnFunctions.Info(argList) + return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Lock(argList) {{{2 +function! s:svnFunctions.Lock(argList) + return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {}) +endfunction + +" Function: s:svnFunctions.Log(argList) {{{2 +function! s:svnFunctions.Log(argList) + if len(a:argList) == 0 + let options = [] + let caption = '' + elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1 + let options = ['-r' . join(a:argList, ':')] + let caption = options[0] + else + " Pass-through + let options = a:argList + let caption = join(a:argList, ' ') + endif + + let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {}) + return resultBuffer +endfunction + +" Function: s:svnFunctions.Revert(argList) {{{2 +function! s:svnFunctions.Revert(argList) + return s:DoCommand('revert', 'revert', '', {}) +endfunction + +" Function: s:svnFunctions.Review(argList) {{{2 +function! s:svnFunctions.Review(argList) + if len(a:argList) == 0 + let versiontag = '(current)' + let versionOption = '' + else + let versiontag = a:argList[0] + let versionOption = ' -r ' . versiontag . ' ' + endif + + let resultBuffer = s:DoCommand('cat' . versionOption, 'review', versiontag, {}) + if resultBuffer > 0 + let &filetype = getbufvar(b:VCSCommandOriginalBuffer, '&filetype') + endif + return resultBuffer +endfunction + +" Function: s:svnFunctions.Status(argList) {{{2 +function! s:svnFunctions.Status(argList) + let options = ['-u', '-v'] + if len(a:argList) == 0 + let options = a:argList + endif + return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {}) +endfunction + +" Function: s:svnFunctions.Unlock(argList) {{{2 +function! s:svnFunctions.Unlock(argList) + return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {}) +endfunction +" Function: s:svnFunctions.Update(argList) {{{2 +function! s:svnFunctions.Update(argList) + return s:DoCommand('update', 'update', '', {}) +endfunction + +" Section: Plugin Registration {{{1 +call VCSCommandRegisterModule('SVN', expand(''), s:svnFunctions, []) + +let &cpo = s:save_cpo -- cgit v1.2.3