Mercurial > hg
changeset 2591:61f2008cd6bf
Addition of CVScommand vim script as a base for HGcommand
author | "Mathieu Clabaut <mathieu.clabaut@gmail.com>" |
---|---|
date | Mon, 10 Jul 2006 23:39:08 +0200 |
parents | 6a961a54f953 |
children | 457846f400e8 |
files | contrib/vim/HGAnnotate.vim contrib/vim/hgcommand.txt contrib/vim/hgcommand.vim |
diffstat | 3 files changed, 2031 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/vim/HGAnnotate.vim Mon Jul 10 23:39:08 2006 +0200 @@ -0,0 +1,27 @@ +" $Id: CVSAnnotate.vim,v 1.5 2002/10/01 21:34:02 rhiestan Exp $ +" Vim syntax file +" Language: CVS annotate output +" Maintainer: Bob Hiestand <bob@hiestandfamily.org> +" Last Change: $Date: 2002/10/01 21:34:02 $ +" Remark: Used by the cvscommand plugin. Originally written by Mathieu +" Clabaut +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn match cvsDate /\S\S\S \S\+ \d\+ \d\+:\d\+:\d\+ \d\+ [+-]\?\d\+/ contained +syn match cvsName /^\s*\S\+ / contained nextgroup=cvsVer +syn match cvsVer /\d\+ / contained nextgroup=cvsDate +syn region cvsHead start="^" end=":" contains=cvsVer,cvsName,cvsDate + +if !exists("did_cvsannotate_syntax_inits") +let did_cvsannotate_syntax_inits = 1 +hi link cvsText String +hi link cvsDate Comment +hi link cvsName Type +hi link cvsVer Statement +endif + +let b:current_syntax="CVSAnnotate"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/vim/hgcommand.txt Mon Jul 10 23:39:08 2006 +0200 @@ -0,0 +1,617 @@ +*cvscommand.txt* CVSCommand + +For instructions on installing this file, type + :help add-local-help +inside Vim. + +Author: Bob Hiestand <bob.hiestand@gmail.com> +Credits: Benji Fisher's excellent MatchIt documentation + +============================================================================== +1. Contents *cvscommand-contents* + + Installation : |cvscommand-install| + CVSCommand Intro : |cvscommand| + CVSCommand Manual : |cvscommand-manual| + Customization : |cvscommand-customize| + SSH "integration" : |cvscommand-ssh| + Bugs : |cvscommand-bugs| + +============================================================================== + +2. CVSCommand Installation *cvscommand-install* + +The CVSCommand plugin comprises two files, cvscommand.vim and cvscommand.txt +(this file). In order to install the plugin, place the cvscommand.vim file +into a plugin' directory in your runtime path (please see |add-global-plugin| +and |'runtimepath'|. + +CVSCommand may be customized by setting variables, creating maps, and +specifying event handlers. Please see |cvscommand-customize| for more +details. + +This help file can be included in the VIM help system by copying it into a +'doc' directory in your runtime path and then executing the |:helptags| +command, specifying the full path of the 'doc' directory. Please see +|add-local-help| for more details. + +============================================================================== + +3. CVSCommand Intro *cvscommand* + *cvscommand-intro* + +The CVSCommand plugin provides global ex commands for manipulating +CVS-controlled source files. In general, each command operates on the current +buffer and accomplishes a separate cvs function, such as update, commit, log, +and others (please see |cvscommand-commands| for a list of all available +commands). The results of each operation are displayed in a scratch buffer. +Several buffer variables are defined for those scratch buffers (please see +|cvscommand-buffer-variables|). + +The notion of "current file" means either the current buffer, or, in the case +of a directory buffer, the file on the current line within the buffer. + +For convenience, any CVSCommand invoked on a CVSCommand scratch buffer acts as +though it was invoked on the original file and splits the screen so that the +output appears in a new window. + +Many of the commands accept revisions as arguments. By default, most operate +on the most recent revision on the current branch if no revision is specified +(though see |CVSCommandInteractive| to prompt instead). + +Each CVSCommand is mapped to a key sequence starting with the <Leader> +keystroke. The default mappings may be overridden by supplying different +mappings before the plugin is loaded, such as in the vimrc, in the standard +fashion for plugin mappings. For examples, please see +|cvscommand-mappings-override|. + +The CVSCommand plugin may be configured in several ways. For more details, +please see |cvscommand-customize|. + +============================================================================== + +4. CVSCommand Manual *cvscommand-manual* + +4.1 CVSCommand commands *cvscommand-commands* + +CVSCommand defines the following commands: + +|:CVSAdd| +|:CVSAnnotate| +|:CVSCommit| +|:CVSDiff| +|:CVSEdit| +|:CVSEditors| +|:CVSGotoOriginal| +|:CVSLog| +|:CVSRevert| +|:CVSReview| +|:CVSStatus| +|:CVSUnedit| +|:CVSUpdate| +|:CVSVimDiff| +|:CVSWatch| +|:CVSWatchAdd| +|:CVSWatchOn| +|:CVSWatchOff| +|:CVSWatchRemove| +|:CVSWatchers| + +:CVSAdd *:CVSAdd* + +This command performs "cvs add" on the current file. Please note, this does +not commit the newly-added file. + +:CVSAnnotate *:CVSAnnotate* + +This command performs "cvs annotate" on the current file. 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 CVSAnnotate buffer already, the +version number on the current line is used. + +If the |CVSCommandAnnotateParent| variable is set to a non-zero value, the +version previous to the one on the current line is used instead. This allows +one to navigate back to examine the previous version of a line. + +The filetype of the CVSCommand scratch buffer is set to 'CVSAnnotate', to take +advantage of the bundled syntax file. + + +:CVSCommit[!] *:CVSCommit* + +If called with arguments, this performs "cvs commit" using the arguments as +the log message. + +If '!' is used with no arguments, 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. + +Alternatively, the mapping that is used to invoke :CVSCommit (by default +<Leader>cc) can be used in the log message buffer to immediately commit. This +is useful if the |CVSCommandCommitOnWrite| variable is set to 0 to disable the +normal commit-on-write behavior. + +:CVSDiff *:CVSDiff* + +With no arguments, this performs "cvs diff" on the current file against the +current repository version. + +With one argument, "cvs diff" is performed on the current file against the +specified revision. + +With two arguments, cvs diff is performed between the specified +revisions of the current file. + +This command uses the 'CVSCommandDiffOpt' variable to specify diff options. +If that variable does not exist, then 'wbBc' is assumed. If you wish to have +no options, then set it to the empty string. + +:CVSEdit *:CVSEdit* + +This command performs "cvs edit" on the current file. Yes, the output buffer +in this case is almost completely useless. + +:CVSEditors *:CVSEditors* + +This command performs "cvs edit" on the current file. + +:CVSGotoOriginal *:CVSGotoOriginal* + +This command returns the current window to the source buffer, if the current +buffer is a CVS command output buffer. + +:CVSGotoOriginal! + +Like ":CVSGotoOriginal" but also executes :bufwipeout on all CVS command +output buffers for the source buffer. + +:CVSLog *:CVSLog* + +Performs "cvs log" on the current file. + +If an argument is given, it is passed as an argument to the "-r" option of +"cvs log". + +:CVSRevert *:CVSRevert* + +Replaces the current file with the most recent version from the repository in +order to wipe out any undesired changes. + +:CVSReview *:CVSReview* + +Retrieves a particular version of the current file. If no argument is given, +the most recent version of the file on the current branch is retrieved. +Otherwise, the specified version is retrieved. + +:CVSStatus *:CVSStatus* + +Performs "cvs status" on the current file. + +:CVSUnedit *:CVSUnedit* + +Performs "cvs unedit" on the current file. Again, yes, the output buffer here +is basically useless. + +:CVSUpdate *:CVSUpdate* + +Performs "cvs update" on the current file. This intentionally does not +automatically reload the current buffer, though vim should prompt the user to +do so if the underlying file is altered by this command. + +:CVSVimDiff *:CVSVimDiff* + +With no arguments, this prompts the user for a revision and then uses vimdiff +to display the differences between the current file and the specified +revision. 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 other 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. + +:CVSWatch *:CVSWatch* + +This command takes an argument which must be one of [on|off|add|remove]. The +command performs "cvs watch" with the given argument on the current file. + +:CVSWatchAdd *:CVSWatchAdd* + +This command is an alias for ":CVSWatch add" + +:CVSWatchOn *:CVSWatchOn* + +This command is an alias for ":CVSWatch on" + +:CVSWatchOff *:CVSWatchOff* + +This command is an alias for ":CVSWatch off" + +:CVSWatchRemove *:CVSWatchRemove* + +This command is an alias for ":CVSWatch remove" + +:CVSWatchers *:CVSWatchers* + +This command performs "cvs watchers" on the current file. + +4.2 Mappings *cvscommand-mappings* + +By default, a mapping is defined for each command. These mappings execute the +default (no-argument) form of each command. + +<Leader>ca CVSAdd +<Leader>cn CVSAnnotate +<Leader>cc CVSCommit +<Leader>cd CVSDiff +<Leader>ce CVSEdit +<Leader>ci CVSEditors +<Leader>cg CVSGotoOriginal +<Leader>cG CVSGotoOriginal! +<Leader>cl CVSLog +<Leader>cr CVSReview +<Leader>cs CVSStatus +<Leader>ct CVSUnedit +<Leader>cu CVSUpdate +<Leader>cv CVSVimDiff +<Leader>cwv CVSWatchers +<Leader>cwa CVSWatchAdd +<Leader>cwn CVSWatchOn +<Leader>cwf CVSWatchOff +<Leader>cwf CVSWatchRemove + + *cvscommand-mappings-override* + +The default mappings can be overriden by user-provided instead by mapping to +<Plug>CommandName. This is especially useful when these mappings collide with +other existing mappings (vim will warn of this during plugin initialization, +but will not clobber the existing mappings). + +For instance, to override the default mapping for :CVSAdd to set it to '\add', +add the following to the vimrc: + +nmap \add <Plug>CVSAdd + +4.3 Automatic buffer variables *cvscommand-buffer-variables* + +Several buffer variables are defined in each CVSCommand result buffer. These +may be useful for additional customization in callbacks defined in the event +handlers (please see |cvscommand-events|). + +The following variables are automatically defined: + +b:cvsOrigBuffNR *b:cvsOrigBuffNR* + +This variable is set to the buffer number of the source file. + +b:cvscmd *b:cvscmd* + +This variable is set to the name of the cvs command that created the result +buffer. +============================================================================== + +5. Configuration and customization *cvscommand-customize* + *cvscommand-config* + +The CVSCommand plugin can be configured in two ways: by setting configuration +variables (see |cvscommand-options|) or by defining CVSCommand event handlers +(see |cvscommand-events|). Additionally, the CVSCommand plugin provides +several option for naming the CVS result buffers (see |cvscommand-naming|) and +supported a customized status line (see |cvscommand-statusline| and +|cvscommand-buffer-management|). + +5.1 CVSCommand configuration variables *cvscommand-options* + +Several variables affect the plugin's behavior. These variables are checked +at time of execution, and may be defined at the window, buffer, or global +level and are checked in that order of precedence. + + +The following variables are available: + +|CVSCommandAnnotateParent| +|CVSCommandCommitOnWrite| +|CVSCommandCVSExec| +|CVSCommandDeleteOnHide| +|CVSCommandDiffOpt| +|CVSCommandDiffSplit| +|CVSCommandEdit| +|CVSCommandEnableBufferSetup| +|CVSCommandInteractive| +|CVSCommandNameMarker| +|CVSCommandNameResultBuffers| +|CVSCommandSplit| + +CVSCommandAnnotateParent *CVSCommandAnnotateParent* + +This variable, if set to a non-zero value, causes the zero-argument form of +CVSAnnotate when invoked on a CVSAnnotate buffer to go to the version previous +to that displayed on the current line. If not set, it defaults to 0. + +CVSCommandCommitOnWrite *CVSCommandCommitOnWrite* + +This variable, if set to a non-zero value, causes the pending cvs commit +to take place immediately as soon as the log message buffer is written. +If set to zero, only the CVSCommit mapping will cause the pending commit to +occur. If not set, it defaults to 1. + +CVSCommandCVSExec *CVSCommandCVSExec* + +This variable controls the executable used for all CVS commands If not set, +it defaults to "cvs". + +CVSCommandDeleteOnHide *CVSCommandDeleteOnHide* + +This variable, if set to a non-zero value, causes the temporary CVS result +buffers to automatically delete themselves when hidden. + +CVSCommandDiffOpt *CVSCommandDiffOpt* + +This variable, if set, determines the options passed to the diff command of +CVS. If not set, it defaults to 'wbBc'. + +CVSCommandDiffSplit *CVSCommandDiffSplit* + +This variable overrides the |CVSCommandSplit| variable, but only for buffers +created with |:CVSVimDiff|. + +CVSCommandEdit *CVSCommandEdit* + +This variable controls whether the original buffer is replaced ('edit') or +split ('split'). If not set, it defaults to 'edit'. + +CVSCommandEnableBufferSetup *CVSCommandEnableBufferSetup* + +This variable, if set to a non-zero value, activates CVS buffer management +mode see (|cvscommand-buffer-management|). This mode means that two buffer +variables, 'CVSRevision' and 'CVSBranch', are set if the file is +CVS-controlled. This is useful for displaying version information in the +status bar. + +CVSCommandInteractive *CVSCommandInteractive* + +This variable, if set to a non-zero value, causes appropriate commands (for +the moment, only |:CVSReview|) to query the user for a revision to use instead +of the current revision if none is specified. + +CVSCommandNameMarker *CVSCommandNameMarker* + +This variable, if set, configures the special attention-getting characters +that appear on either side of the cvs buffer type in the buffer name. This +has no effect unless |CVSCommandNameResultBuffers| is set to a true value. If +not set, it defaults to '_'. + +CVSCommandNameResultBuffers *CVSCommandNameResultBuffers* + +This variable, if set to a true value, causes the cvs result buffers to be +named in the old way ('<source file name> _<cvs command>_'). If not set +or set to a false value, the result buffer is nameless. + +CVSCommandSplit *CVSCommandSplit* + +This variable controls the orientation of the various window splits that +may occur (such as with CVSVimDiff, when using a CVS command on a CVS +command buffer, or when the |CVSCommandEdit| 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 +CVSVimDiff windows. + +5.2 CVSCommand events *cvscommand-events* + +For additional customization, CVSCommand can trigger user-defined events. +Event handlers are provided by defining User event autocommands (see +|autocommand|, |User|) in the CVSCommand group with patterns matching the +event name. + +For instance, the following could be added to the vimrc to provide a 'q' +mapping to quit a CVSCommand scratch buffer: + +augroup CVSCommand + au CVSCommand User CVSBufferCreated silent! nmap <unique> <buffer> q: bwipeout<cr> +augroup END + +The following hooks are available: + +CVSBufferCreated This event is fired just after a cvs command + result buffer is created and filled with the + result of a cvs command. It is executed within + the context of the CVS command buffer. The + CVSCommand buffer variables may be useful for + handlers of this event (please see + |cvscommand-buffer-variables|). + +CVSBufferSetup This event is fired just after CVS buffer setup + occurs, if enabled. + +CVSPluginInit This event is fired when the CVSCommand plugin + first loads. + +CVSPluginFinish This event is fired just after the CVSCommand + plugin loads. + +CVSVimDiffFinish This event is fired just after the CVSVimDiff + command executes to allow customization of, + for instance, window placement and focus. + +5.3 CVSCommand buffer naming *cvscommand-naming* + +By default, the buffers containing the result of CVS commands are nameless +scratch buffers. It is intended that buffer variables of those buffers be +used to customize the statusline option so that the user may fully control the +display of result buffers. + +If the old-style naming is desired, please enable the +|CVSCommandNameResultBuffers| variable. Then, each result buffer will receive +a unique name that includes the source file name, the CVS command, and any +extra data (such as revision numbers) that were part of the command. + +5.4 CVSCommand status line support *cvscommand-statusline* + +It is intended that the user will customize the |'statusline'| option to +include CVS result buffer attributes. A sample function that may be used in +the |'statusline'| option is provided by the plugin, CVSGetStatusLine(). In +order to use that function in the status line, do something like the +following: + +set statusline=%<%f\ %{CVSGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P + +of which %{CVSGetStatusLine()} is the relevant portion. + +The sample CVSGetStatusLine() function handles both CVS result buffers and +CVS-managed files if CVSCommand buffer management is enabled (please see +|cvscommand-buffer-management|). + +5.5 CVSCommand buffer management *cvscommand-buffer-management* + +The CVSCommand plugin can operate in buffer management mode, which means that +it attempts to set two buffer variables ('CVSRevision' and 'CVSBranch') upon +entry into a buffer. This is rather slow because it means that 'cvs status' +will be invoked at each entry into a buffer (during the |BufEnter| +autocommand). + +This mode is disabled by default. In order to enable it, set the +|CVSCommandEnableBufferSetup| variable to a true (non-zero) value. Enabling +this mode simply provides the buffer variables mentioned above. The user must +explicitly include those in the |'statusline'| option if they are to appear in +the status line (but see |cvscommand-statusline| for a simple way to do that). + +============================================================================== + +6. SSH "integration" *cvscommand-ssh* + +The following instructions are intended for use in integrating the +cvscommand.vim plugin with an SSH-based CVS environment. + +Familiarity with SSH and CVS are assumed. + +These instructions assume that the intent is to have a message box pop up in +order to allow the user to enter a passphrase. If, instead, the user is +comfortable using certificate-based authentication, then only instructions +6.1.1 and 6.1.2 (and optionally 6.1.4) need to be followed; ssh should then +work transparently. + +6.1 Environment settings *cvscommand-ssh-env* + +6.1.1 CVSROOT should be set to something like: + + :ext:user@host:/path_to_repository + +6.1.2 CVS_RSH should be set to: + + ssh + + Together, those settings tell CVS to use ssh as the transport when + performing CVS calls. + +6.1.3 SSH_ASKPASS should be set to the password-dialog program. In my case, + running gnome, it's set to: + + /usr/libexec/openssh/gnome-ssh-askpass + + This tells SSH how to get passwords if no input is available. + +6.1.4 OPTIONAL. You may need to set SSH_SERVER to the location of the cvs + executable on the remote (server) machine. + +6.2 CVS wrapper program *cvscommand-ssh-wrapper* + +Now you need to convince SSH to use the password-dialog program. This means +you need to execute SSH (and therefore CVS) without standard input. The +following script is a simple perl wrapper that dissasociates the CVS command +from the current terminal. Specific steps to do this may vary from system to +system; the following example works for me on linux. + +#!/usr/bin/perl -w +use strict; +use POSIX qw(setsid); +open STDIN, '/dev/null'; +fork and do {wait; exit;}; +setsid; +exec('cvs', @ARGV); + +6.3 Configuring cvscommand.vim *cvscommand-ssh-config* + +At this point, you should be able to use your wrapper script to invoke CVS with +various commands, and get the password dialog. All that's left is to make CVS +use your newly-created wrapper script. + +6.3.1 Tell cvscommand.vim what CVS executable to use. The easiest way to do this + is globally, by putting the following in your .vimrc: + + let CVSCommandCVSExec=/path/to/cvs/wrapper/script + +6.4 Where to go from here *cvscommand-ssh-other* + +The script given above works even when non-SSH CVS connections are used, +except possibly when interactively entering the message for CVS commit log +(depending on the editor you use... VIM works fine). Since the cvscommand.vim +plugin handles that message without a terminal, the wrapper script can be used +all the time. + +This allows mixed-mode operation, where some work is done with SSH-based CVS +repositories, and others with pserver or local access. + +It is possible, though beyond the scope of the plugin, to dynamically set the +CVS executable based on the CVSROOT for the file being edited. The user +events provided (such as CVSBufferCreated and CVSBufferSetup) can be used to +set a buffer-local value (b:CVSCommandCVSExec) to override the CVS executable +on a file-by-file basis. Alternatively, much the same can be done (less +automatically) by the various project-oriented plugins out there. + +It is highly recommended for ease-of-use that certificates with no passphrase +or ssh-agent are employed so that the user is not given the password prompt +too often. + +============================================================================== +9. Tips *cvscommand-tips* + +9.1 Split window annotation, by Michael Anderson + +:nmap <Leader>cN :vs<CR><C-w>h<Leader>cn:vertical res 40<CR> + \ggdddd:set scb<CR>:set nowrap<CR><C-w>lgg:set scb<CR> + \:set nowrap<CR> + +This splits the buffer vertically, puts an annotation on the left (minus the +header) with the width set to 40. An editable/normal copy is placed on the +right. The two versions are scroll locked so they move as one. and wrapping +is turned off so that the lines line up correctly. The advantages are... + +1) You get a versioning on the right. +2) You can still edit your own code. +3) Your own code still has syntax highlighting. + +============================================================================== + +8. Known bugs *cvscommand-bugs* + +Please let me know if you run across any. + +CVSUnedit may, if a file is changed from the repository, provide prompt text +to determine whether the changes should be thrown away. Currently, that text +shows up in the CVS result buffer as information; there is no way for the user +to actually respond to the prompt and the CVS unedit command does nothing. If +this really bothers anyone, please let me know. + +CVSVimDiff, when using the original (real) source buffer as one of the diff +buffers, uses some hacks to try to restore the state of the original buffer +when the scratch buffer containing the other version is destroyed. There may +still be bugs in here, depending on many configuration details. + +vim:tw=78:ts=8:ft=help
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/vim/hgcommand.vim Mon Jul 10 23:39:08 2006 +0200 @@ -0,0 +1,1387 @@ +" vim600: set foldmethod=marker: +" +" Vim plugin to assist in working with CVS-controlled files. +" +" Last Change: 2006/02/22 +" Version: 1.76 +" Maintainer: Bob Hiestand <bob.hiestand@gmail.com> +" License: This file is placed in the public domain. +" Credits: {{{1 +" Mathieu Clabaut for many suggestions and improvements. +" +" Suresh Govindachar and Jeeva Chelladhurai for finding waaaay +" too many bugs. +" +" Suresh Govindachar (again!) for finding the +" fully-folded-last-line-delete bug. +" +" Albrecht Gass for the Delete-on-Hide behavior suggestion. +" +" Joe MacDonald for finding the CVS log message header bug and +" pointing out that buffer refreshes are needed after CVS +" \%(un\)\?edit. +" +" Srinath Avadhanula for the suggestion and original patch for +" the CVSCommitOnWrite option and mapping hot key. +" +" John Sivak for helping to debug Windows issues and suggesting +" the CVSEditors and CVSWatchers commands. +" +" Igor Levko for the patch to recognize numerical sticky tags. +" +" Domink Strasser for the patch to correct the status line for +" CVSAdd'd files. +" +" Weerapong Sirikanya for finding a bug with CVSCommit and +" autochdir. +" +" David Gotz for finding a bug with CVSVimDiff buffer splitting +" and original buffer restoration. +" +" CJ van den Berg for the patch to not change working directory +" when editing a non-CVS file. +" +" Luca Gerli for noticing bad behavior for keywords in files +" after commit if split windows are used. + +" Section: Documentation {{{1 +" +" Provides functions to invoke various CVS commands on the current file +" (either the current buffer, or, in the case of an directory buffer, the file +" on the current line). The output of the commands is captured in a new +" scratch window. For convenience, if the functions are invoked on a CVS +" output window, the original file is used for the cvs operation instead after +" the window is split. This is primarily useful when running CVSCommit and +" you need to see the changes made, so that CVSDiff is usable and shows up in +" another window. +" +" Command documentation {{{2 +" +" CVSAdd Performs "cvs add" on the current file. +" +" CVSAnnotate Performs "cvs annotate" on the current file. 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 CVSAnnotate buffer +" already, the version number on the current line is used. +" +" If the 'CVSCommandAnnotateParent' variable is set to a +" non-zero value, the version previous to the one on the +" current line is used instead. This allows one to navigate +" back to examine the previous version of a line. +" +" CVSCommit[!] If called with arguments, this performs "cvs commit" using +" the arguments as 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. +" +" CVSDiff With no arguments, this performs "cvs diff" on the current +" file. With one argument, "cvs diff" is performed on the +" current file against the specified revision. With two +" arguments, cvs diff is performed between the specified +" revisions of the current file. This command uses the +" 'CVSCommandDiffOpt' variable to specify diff options. If +" that variable does not exist, then 'wbBc' is assumed. If +" you wish to have no options, then set it to the empty +" string. +" +" CVSEdit Performs "cvs edit" on the current file. +" +" CVSEditors Performs "cvs editors" on the current file. +" +" CVSGotoOriginal Returns the current window to the source buffer if the +" current buffer is a CVS output buffer. +" +" CVSLog Performs "cvs log" on the current file. +" +" CVSRevert Replaces the modified version of the current file with the +" most recent version from the repository. +" +" CVSReview Retrieves a particular version of the current file. If no +" argument is given, the most recent version of the file on +" the current branch is retrieved. The specified revision is +" retrieved into a new buffer. +" +" CVSStatus Performs "cvs status" on the current file. +" +" CVSUnedit Performs "cvs unedit" on the current file. +" +" CVSUpdate Performs "cvs update" on the current file. +" +" CVSVimDiff With no arguments, this prompts the user for a revision and +" then uses vimdiff to display the differences between the +" current file and the specified revision. 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 other 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. +" +" 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 <Plug>CommandName, for instance: +" +" nnoremap ,ca <Plug>CVSAdd +" +" The default mappings are as follow: +" +" <Leader>ca CVSAdd +" <Leader>cn CVSAnnotate +" <Leader>cc CVSCommit +" <Leader>cd CVSDiff +" <Leader>ce CVSEdit +" <Leader>ci CVSEditors +" <Leader>cg CVSGotoOriginal +" <Leader>cG CVSGotoOriginal! +" <Leader>cl CVSLog +" <Leader>cr CVSReview +" <Leader>cs CVSStatus +" <Leader>ct CVSUnedit +" <Leader>cu CVSUpdate +" <Leader>cv CVSVimDiff +" <Leader>cwv CVSWatchers +" <Leader>cwa CVSWatchAdd +" <Leader>cwn CVSWatchOn +" <Leader>cwa CVSWatchOff +" <Leader>cwr CVSWatchRemove +" +" Options documentation: {{{2 +" +" Several variables are checked by the script to determine behavior as follow: +" +" CVSCommandAnnotateParent +" This variable, if set to a non-zero value, causes the zero-argument form +" of CVSAnnotate when invoked on a CVSAnnotate buffer to go to the version +" previous to that displayed on the current line. If not set, it defaults +" to 0. +" +" CVSCommandCommitOnWrite +" This variable, if set to a non-zero value, causes the pending cvs commit +" to take place immediately as soon as the log message buffer is written. +" If set to zero, only the CVSCommit mapping will cause the pending commit +" to occur. If not set, it defaults to 1. +" +" CVSCommandDeleteOnHide +" This variable, if set to a non-zero value, causes the temporary CVS result +" buffers to automatically delete themselves when hidden. +" +" CVSCommandDiffOpt +" This variable, if set, determines the options passed to the diff command +" of CVS. If not set, it defaults to 'wbBc'. +" +" CVSCommandDiffSplit +" This variable overrides the CVSCommandSplit variable, but only for buffers +" created with CVSVimDiff. +" +" CVSCommandEdit +" This variable controls whether the original buffer is replaced ('edit') or +" split ('split'). If not set, it defaults to 'edit'. +" +" CVSCommandEnableBufferSetup +" This variable, if set to a non-zero value, activates CVS buffer management +" mode. This mode means that two buffer variables, 'CVSRevision' and +" 'CVSBranch', are set if the file is CVS-controlled. This is useful for +" displaying version information in the status bar. +" +" CVSCommandInteractive +" This variable, if set to a non-zero value, causes appropriate functions (for +" the moment, only CVSReview) to query the user for a revision to use +" instead of the current revision if none is specified. +" +" CVSCommandNameMarker +" This variable, if set, configures the special attention-getting characters +" that appear on either side of the cvs buffer type in the buffer name. +" This has no effect unless 'CVSCommandNameResultBuffers' is set to a true +" value. If not set, it defaults to '_'. +" +" CVSCommandNameResultBuffers +" This variable, if set to a true value, causes the cvs result buffers to be +" named in the old way ('<source file name> _<cvs command>_'). If not set +" or set to a false value, the result buffer is nameless. +" +" CVSCommandSplit +" This variable controls the orientation of the various window splits that +" may occur (such as with CVSVimDiff, when using a CVS command on a CVS +" command buffer, or when the 'CVSCommandEdit' 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 +" CVSVimDiff windows. +" +" Event documentation {{{2 +" For additional customization, cvscommand.vim uses User event autocommand +" hooks. Each event is in the CVSCommand 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 CVS buffer: +" +" augroup CVSCommand +" au CVSCommand User CVSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr> +" augroup END +" +" The following hooks are available: +" +" CVSBufferCreated This event is fired just after a cvs command +" result buffer is created and filled with the +" result of a cvs command. It is executed within +" the context of the new buffer. +" +" CVSBufferSetup This event is fired just after CVS buffer setup +" occurs, if enabled. +" +" CVSPluginInit This event is fired when the CVSCommand plugin +" first loads. +" +" CVSPluginFinish This event is fired just after the CVSCommand +" plugin loads. +" +" CVSVimDiffFinish This event is fired just after the CVSVimDiff +" command executes to allow customization of, +" for instance, window placement and focus. +" +" Section: Plugin header {{{1 + +" loaded_cvscommand 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_cvscommand") + finish +endif +let loaded_cvscommand = 1 + +if v:version < 602 + echohl WarningMsg|echomsg "CVSCommand 1.69 or later requires VIM 6.2 or later"|echohl None + finish +endif + +" Section: Event group setup {{{1 + +augroup CVSCommand +augroup END + +" Section: Plugin initialization {{{1 +silent do CVSCommand User CVSPluginInit + +" Section: Script variable initialization {{{1 + +let s:CVSCommandEditFileRunning = 0 +unlet! s:vimDiffRestoreCmd +unlet! s:vimDiffSourceBuffer +unlet! s:vimDiffBufferCount +unlet! s:vimDiffScratchList + +" Section: Utility functions {{{1 + +" Function: s:CVSResolveLink() {{{2 +" Fully resolve the given file name to remove shortcuts or symbolic links. + +function! s:CVSResolveLink(fileName) + let resolved = resolve(a:fileName) + if resolved != a:fileName + let resolved = s:CVSResolveLink(resolved) + endif + return resolved +endfunction + +" Function: s:CVSChangeToCurrentFileDir() {{{2 +" Go to the directory in which the current CVS-controlled file is located. +" If this is a CVS command buffer, first switch to the original file. + +function! s:CVSChangeToCurrentFileDir(fileName) + let oldCwd=getcwd() + let fileName=s:CVSResolveLink(a:fileName) + let newCwd=fnamemodify(fileName, ':h') + if strlen(newCwd) > 0 + execute 'cd' escape(newCwd, ' ') + endif + return oldCwd +endfunction + +" Function: s:CVSGetOption(name, default) {{{2 +" Grab a user-specified option to override the default provided. Options are +" searched in the window, buffer, then global spaces. + +function! s:CVSGetOption(name, default) + if exists("s:" . a:name . "Override") + execute "return s:".a:name."Override" + elseif exists("w:" . a:name) + execute "return w:".a:name + elseif exists("b:" . a:name) + execute "return b:".a:name + elseif exists("g:" . a:name) + execute "return g:".a:name + else + return a:default + endif +endfunction + +" Function: s:CVSEditFile(name, origBuffNR) {{{2 +" Wrapper around the 'edit' command to provide some helpful error text if the +" current buffer can't be abandoned. If name is provided, it is used; +" otherwise, a nameless scratch buffer is used. +" Returns: 0 if successful, -1 if an error occurs. + +function! s:CVSEditFile(name, origBuffNR) + "Name parameter will be pasted into expression. + let name = escape(a:name, ' *?\') + + let editCommand = s:CVSGetOption('CVSCommandEdit', 'edit') + if editCommand != 'edit' + if s:CVSGetOption('CVSCommandSplit', 'horizontal') == 'horizontal' + if name == "" + let editCommand = 'rightbelow new' + else + let editCommand = 'rightbelow split ' . name + endif + else + if name == "" + let editCommand = 'vert rightbelow new' + else + let editCommand = 'vert rightbelow split ' . name + endif + endif + else + if name == "" + let editCommand = 'enew' + else + let editCommand = 'edit ' . name + endif + endif + + " Protect against useless buffer set-up + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1 + try + execute editCommand + finally + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1 + endtry + + let b:CVSOrigBuffNR=a:origBuffNR + let b:CVSCommandEdit='split' +endfunction + +" Function: s:CVSCreateCommandBuffer(cmd, cmdName, statusText, filename) {{{2 +" Creates a new scratch buffer and captures the output from execution of the +" given command. The name of the scratch buffer is returned. + +function! s:CVSCreateCommandBuffer(cmd, cmdName, statusText, origBuffNR) + let fileName=bufname(a:origBuffNR) + + let resultBufferName='' + + if s:CVSGetOption("CVSCommandNameResultBuffers", 0) + let nameMarker = s:CVSGetOption("CVSCommandNameMarker", '_') + if strlen(a:statusText) > 0 + let bufName=a:cmdName . ' -- ' . a:statusText + else + let bufName=a:cmdName + endif + let bufName=fileName . ' ' . nameMarker . bufName . nameMarker + let counter=0 + let resultBufferName = bufName + while buflisted(resultBufferName) + let counter=counter + 1 + let resultBufferName=bufName . ' (' . counter . ')' + endwhile + endif + + let cvsCommand = s:CVSGetOption("CVSCommandCVSExec", "cvs") . " " . a:cmd + let cvsOut = system(cvsCommand) + " HACK: diff command does not return proper error codes + if v:shell_error && a:cmdName != 'cvsdiff' + if strlen(cvsOut) == 0 + echoerr "CVS command failed" + else + echoerr "CVS command failed: " . cvsOut + endif + return -1 + endif + if strlen(cvsOut) == 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. + + echomsg "No output from CVS command" + checktime + return -1 + endif + + if s:CVSEditFile(resultBufferName, a:origBuffNR) == -1 + return -1 + endif + + set buftype=nofile + set noswapfile + set filetype= + + if s:CVSGetOption("CVSCommandDeleteOnHide", 0) + set bufhidden=delete + endif + + silent 0put=cvsOut + + " 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. + + let b:CVSSourceFile=fileName + let b:CVSCommand=a:cmdName + if a:statusText != "" + let b:CVSStatusText=a:statusText + endif + + silent do CVSCommand User CVSBufferCreated + return bufnr("%") +endfunction + +" Function: s:CVSBufferCheck(cvsBuffer) {{{2 +" Attempts to locate the original file to which CVS operations were applied +" for a given buffer. + +function! s:CVSBufferCheck(cvsBuffer) + let origBuffer = getbufvar(a:cvsBuffer, "CVSOrigBuffNR") + if origBuffer + if bufexists(origBuffer) + return origBuffer + else + " Original buffer no longer exists. + return -1 + endif + else + " No original buffer + return a:cvsBuffer + endif +endfunction + +" Function: s:CVSCurrentBufferCheck() {{{2 +" Attempts to locate the original file to which CVS operations were applied +" for the current buffer. + +function! s:CVSCurrentBufferCheck() + return s:CVSBufferCheck(bufnr("%")) +endfunction + +" Function: s:CVSToggleDeleteOnHide() {{{2 +" Toggles on and off the delete-on-hide behavior of CVS buffers + +function! s:CVSToggleDeleteOnHide() + if exists("g:CVSCommandDeleteOnHide") + unlet g:CVSCommandDeleteOnHide + else + let g:CVSCommandDeleteOnHide=1 + endif +endfunction + +" Function: s:CVSDoCommand(cvscmd, cmdName, statusText) {{{2 +" General skeleton for CVS function execution. +" Returns: name of the new command buffer containing the command results + +function! s:CVSDoCommand(cmd, cmdName, statusText) + let cvsBufferCheck=s:CVSCurrentBufferCheck() + if cvsBufferCheck == -1 + echo "Original buffer no longer exists, aborting." + return -1 + endif + + let fileName=bufname(cvsBufferCheck) + if isdirectory(fileName) + let fileName=fileName . "/" . getline(".") + endif + let realFileName = fnamemodify(s:CVSResolveLink(fileName), ':t') + let oldCwd=s:CVSChangeToCurrentFileDir(fileName) + try + if !filereadable('CVS/Root') + throw fileName . ' is not a CVS-controlled file.' + endif + let fullCmd = a:cmd . ' "' . realFileName . '"' + let resultBuffer=s:CVSCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, cvsBufferCheck) + return resultBuffer + catch + echoerr v:exception + return -1 + finally + execute 'cd' escape(oldCwd, ' ') + endtry +endfunction + + +" Function: s:CVSGetStatusVars(revision, branch, repository) {{{2 +" +" Obtains a CVS revision number and branch name. The 'revisionVar', +" 'branchVar'and 'repositoryVar' arguments, if non-empty, contain the names of variables to hold +" the corresponding results. +" +" Returns: string to be exec'd that sets the multiple return values. + +function! s:CVSGetStatusVars(revisionVar, branchVar, repositoryVar) + let cvsBufferCheck=s:CVSCurrentBufferCheck() + if cvsBufferCheck == -1 + return "" + endif + let fileName=bufname(cvsBufferCheck) + let realFileName = fnamemodify(s:CVSResolveLink(fileName), ':t') + let oldCwd=s:CVSChangeToCurrentFileDir(fileName) + try + if !filereadable('CVS/Root') + return "" + endif + let cvsCommand = s:CVSGetOption("CVSCommandCVSExec", "cvs") . " status " . escape(realFileName, ' *?\') + let statustext=system(cvsCommand) + 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 + else + return "" + endif + + let returnExpression = "let " . a:revisionVar . "='" . revision . "'" + + if a:branchVar != "" + let branch=substitute(statustext, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', "") + let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'" + endif + + if a:repositoryVar != "" + 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', "") + let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . repository . "'" + endif + + return returnExpression + finally + execute 'cd' escape(oldCwd, ' ') + endtry +endfunction + +" Function: s:CVSSetupBuffer() {{{2 +" Attempts to set the b:CVSBranch, b:CVSRevision and b:CVSRepository variables. + +function! s:CVSSetupBuffer() + if (exists("b:CVSBufferSetup") && b:CVSBufferSetup) + " This buffer is already set up. + return + endif + + if !s:CVSGetOption("CVSCommandEnableBufferSetup", 0) + \ || @% == "" + \ || s:CVSCommandEditFileRunning > 0 + \ || exists("b:CVSOrigBuffNR") + unlet! b:CVSRevision + unlet! b:CVSBranch + unlet! b:CVSRepository + return + endif + + if !filereadable(expand("%")) + return -1 + endif + + let revision="" + let branch="" + let repository="" + + exec s:CVSGetStatusVars('revision', 'branch', 'repository') + if revision != "" + let b:CVSRevision=revision + else + unlet! b:CVSRevision + endif + if branch != "" + let b:CVSBranch=branch + else + unlet! b:CVSBranch + endif + if repository != "" + let b:CVSRepository=repository + else + unlet! b:CVSRepository + endif + silent do CVSCommand User CVSBufferSetup + let b:CVSBufferSetup=1 +endfunction + +" Function: s:CVSMarkOrigBufferForSetup(cvsbuffer) {{{2 +" Resets the buffer setup state of the original buffer for a given CVS buffer. +" Returns: The CVS buffer number in a passthrough mode. + +function! s:CVSMarkOrigBufferForSetup(cvsBuffer) + checktime + if a:cvsBuffer != -1 + let origBuffer = s:CVSBufferCheck(a:cvsBuffer) + "This should never not work, but I'm paranoid + if origBuffer != a:cvsBuffer + call setbufvar(origBuffer, "CVSBufferSetup", 0) + endif + endif + return a:cvsBuffer +endfunction + +" Function: s:CVSOverrideOption(option, [value]) {{{2 +" Provides a temporary override for the given CVS option. If no value is +" passed, the override is disabled. + +function! s:CVSOverrideOption(option, ...) + if a:0 == 0 + unlet! s:{a:option}Override + else + let s:{a:option}Override = a:1 + endif +endfunction + +" Function: s:CVSWipeoutCommandBuffers() {{{2 +" Clears all current CVS buffers of the specified type for a given source. + +function! s:CVSWipeoutCommandBuffers(originalBuffer, cvsCommand) + let buffer = 1 + while buffer <= bufnr('$') + if getbufvar(buffer, 'CVSOrigBuffNR') == a:originalBuffer + if getbufvar(buffer, 'CVSCommand') == a:cvsCommand + execute 'bw' buffer + endif + endif + let buffer = buffer + 1 + endwhile +endfunction + +" Section: Public functions {{{1 + +" Function: CVSGetRevision() {{{2 +" Global function for retrieving the current buffer's CVS revision number. +" Returns: Revision number or an empty string if an error occurs. + +function! CVSGetRevision() + let revision="" + exec s:CVSGetStatusVars('revision', '', '') + return revision +endfunction + +" Function: CVSDisableBufferSetup() {{{2 +" Global function for deactivating the buffer autovariables. + +function! CVSDisableBufferSetup() + let g:CVSCommandEnableBufferSetup=0 + silent! augroup! CVSCommandPlugin +endfunction + +" Function: CVSEnableBufferSetup() {{{2 +" Global function for activating the buffer autovariables. + +function! CVSEnableBufferSetup() + let g:CVSCommandEnableBufferSetup=1 + augroup CVSCommandPlugin + au! + au BufEnter * call s:CVSSetupBuffer() + augroup END + + " Only auto-load if the plugin is fully loaded. This gives other plugins a + " chance to run. + if g:loaded_cvscommand == 2 + call s:CVSSetupBuffer() + endif +endfunction + +" Function: CVSGetStatusLine() {{{2 +" Default (sample) status line entry for CVS files. This is only useful if +" CVS-managed buffer mode is on (see the CVSCommandEnableBufferSetup variable +" for how to do this). + +function! CVSGetStatusLine() + if exists('b:CVSSourceFile') + " This is a result buffer + let value='[' . b:CVSCommand . ' ' . b:CVSSourceFile + if exists('b:CVSStatusText') + let value=value . ' ' . b:CVSStatusText + endif + let value = value . ']' + return value + endif + + if exists('b:CVSRevision') + \ && b:CVSRevision != '' + \ && exists('b:CVSBranch') + \ && b:CVSBranch != '' + \ && exists('b:CVSRepository') + \ && b:CVSRepository != '' + \ && exists('g:CVSCommandEnableBufferSetup') + \ && g:CVSCommandEnableBufferSetup + if b:CVSRevision == b:CVSRepository + return '[CVS ' . b:CVSBranch . '/' . b:CVSRevision . ']' + else + return '[CVS ' . b:CVSBranch . '/' . b:CVSRevision . '/' . b:CVSRepository . ']' + endif + else + return '' + endif +endfunction + +" Section: CVS command functions {{{1 + +" Function: s:CVSAdd() {{{2 +function! s:CVSAdd() + return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('add', 'cvsadd', '')) +endfunction + +" Function: s:CVSAnnotate(...) {{{2 +function! s:CVSAnnotate(...) + if a:0 == 0 + if &filetype == "CVSAnnotate" + " This is a CVSAnnotate buffer. Perform annotation of the version + " indicated by the current line. + let revision = substitute(getline("."),'\(^[0-9.]*\).*','\1','') + let revmin = substitute(revision,'^[0-9.]*\.\([0-9]\+\)','\1','') + let revmaj = substitute(revision,'^\([0-9.]*\)\.[0-9]\+','\1','') + if s:CVSGetOption('CVSCommandAnnotateParent', 0) != 0 + let revmin = revmin - 1 + endif + if revmin == 0 + " Jump to ancestor branch + let revision = substitute(revmaj,'^\([0-9.]*\)\.[0-9]\+','\1','') + else + let revision=revmaj . "." . revmin + endif + else + let revision=CVSGetRevision() + if revision == "" + echoerr "Unable to obtain CVS version information." + return -1 + endif + endif + else + let revision=a:1 + endif + + if revision == "NEW" + echo "No annotatation available for new file." + return -1 + endif + + let resultBuffer=s:CVSDoCommand('-q annotate -r ' . revision, 'cvsannotate', revision) + if resultBuffer != -1 + set filetype=CVSAnnotate + " Remove header lines from standard error + silent v/^\d\+\%(\.\d\+\)\+/d + endif + + return resultBuffer +endfunction + +" Function: s:CVSCommit() {{{2 +function! s:CVSCommit(...) + " 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 a:2 != "" || a:1 == "!" + return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('commit -m "' . a:2 . '"', 'cvscommit', '')) + endif + + let cvsBufferCheck=s:CVSCurrentBufferCheck() + if cvsBufferCheck == -1 + echo "Original buffer no longer exists, aborting." + return -1 + endif + + " Protect against windows' backslashes in paths. They confuse exec'd + " commands. + + let shellSlashBak = &shellslash + try + set shellslash + + let messageFileName = tempname() + + let fileName=bufname(cvsBufferCheck) + let realFilePath=s:CVSResolveLink(fileName) + let newCwd=fnamemodify(realFilePath, ':h') + if strlen(newCwd) == 0 + " Account for autochdir being in effect, which will make this blank, but + " we know we'll be in the current directory for the original file. + let newCwd = getcwd() + endif + + let realFileName=fnamemodify(realFilePath, ':t') + + if s:CVSEditFile(messageFileName, cvsBufferCheck) == -1 + return + endif + + " Protect against case and backslash issues in Windows. + let autoPattern = '\c' . messageFileName + + " Ensure existance of group + augroup CVSCommit + augroup END + + execute 'au CVSCommit BufDelete' autoPattern 'call delete("' . messageFileName . '")' + execute 'au CVSCommit BufDelete' autoPattern 'au! CVSCommit * ' autoPattern + + " Create a commit mapping. The mapping must clear all autocommands in case + " it is invoked when CVSCommandCommitOnWrite is active, as well as to not + " invoke the buffer deletion autocommand. + + execute 'nnoremap <silent> <buffer> <Plug>CVSCommit '. + \ ':au! CVSCommit * ' . autoPattern . '<CR>'. + \ ':g/^CVS:/d<CR>'. + \ ':update<CR>'. + \ ':call <SID>CVSFinishCommit("' . messageFileName . '",' . + \ '"' . newCwd . '",' . + \ '"' . realFileName . '",' . + \ cvsBufferCheck . ')<CR>' + + silent 0put ='CVS: ----------------------------------------------------------------------' + silent put =\"CVS: Enter Log. Lines beginning with `CVS:' are removed automatically\" + silent put ='CVS: Type <leader>cc (or your own <Plug>CVSCommit mapping)' + + if s:CVSGetOption('CVSCommandCommitOnWrite', 1) == 1 + execute 'au CVSCommit BufWritePre' autoPattern 'g/^CVS:/d' + execute 'au CVSCommit BufWritePost' autoPattern 'call s:CVSFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . cvsBufferCheck . ') | au! * ' autoPattern + silent put ='CVS: or write this buffer' + endif + + silent put ='CVS: to finish this commit operation' + silent put ='CVS: ----------------------------------------------------------------------' + $ + let b:CVSSourceFile=fileName + let b:CVSCommand='CVSCommit' + set filetype=cvs + finally + let &shellslash = shellSlashBak + endtry + +endfunction + +" Function: s:CVSDiff(...) {{{2 +function! s:CVSDiff(...) + if a:0 == 1 + let revOptions = '-r' . a:1 + let caption = a:1 . ' -> current' + elseif a:0 == 2 + let revOptions = '-r' . a:1 . ' -r' . a:2 + let caption = a:1 . ' -> ' . a:2 + else + let revOptions = '' + let caption = '' + endif + + let cvsdiffopt=s:CVSGetOption('CVSCommandDiffOpt', 'wbBc') + + if cvsdiffopt == "" + let diffoptionstring="" + else + let diffoptionstring=" -" . cvsdiffopt . " " + endif + + let resultBuffer = s:CVSDoCommand('diff ' . diffoptionstring . revOptions , 'cvsdiff', caption) + if resultBuffer != -1 + set filetype=diff + endif + return resultBuffer +endfunction + +" Function: s:CVSEdit() {{{2 +function! s:CVSEdit() + return s:CVSDoCommand('edit', 'cvsedit', '') +endfunction + +" Function: s:CVSEditors() {{{2 +function! s:CVSEditors() + return s:CVSDoCommand('editors', 'cvseditors', '') +endfunction + +" Function: s:CVSGotoOriginal(["!]) {{{2 +function! s:CVSGotoOriginal(...) + let origBuffNR = s:CVSCurrentBufferCheck() + if origBuffNR > 0 + let origWinNR = bufwinnr(origBuffNR) + if origWinNR == -1 + execute 'buffer' origBuffNR + else + execute origWinNR . 'wincmd w' + endif + if a:0 == 1 + if a:1 == "!" + let buffnr = 1 + let buffmaxnr = bufnr("$") + while buffnr <= buffmaxnr + if getbufvar(buffnr, "CVSOrigBuffNR") == origBuffNR + execute "bw" buffnr + endif + let buffnr = buffnr + 1 + endwhile + endif + endif + endif +endfunction + +" Function: s:CVSFinishCommit(messageFile, targetDir, targetFile) {{{2 +function! s:CVSFinishCommit(messageFile, targetDir, targetFile, origBuffNR) + if filereadable(a:messageFile) + let oldCwd=getcwd() + if strlen(a:targetDir) > 0 + execute 'cd' escape(a:targetDir, ' ') + endif + let resultBuffer=s:CVSCreateCommandBuffer('commit -F "' . a:messageFile . '" "'. a:targetFile . '"', 'cvscommit', '', a:origBuffNR) + execute 'cd' escape(oldCwd, ' ') + execute 'bw' escape(a:messageFile, ' *?\') + silent execute 'call delete("' . a:messageFile . '")' + return s:CVSMarkOrigBufferForSetup(resultBuffer) + else + echoerr "Can't read message file; no commit is possible." + return -1 + endif +endfunction + +" Function: s:CVSLog() {{{2 +function! s:CVSLog(...) + if a:0 == 0 + let versionOption = "" + let caption = '' + else + let versionOption=" -r" . a:1 + let caption = a:1 + endif + + let resultBuffer=s:CVSDoCommand('log' . versionOption, 'cvslog', caption) + if resultBuffer != "" + set filetype=rcslog + endif + return resultBuffer +endfunction + +" Function: s:CVSRevert() {{{2 +function! s:CVSRevert() + return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('update -C', 'cvsrevert', '')) +endfunction + +" Function: s:CVSReview(...) {{{2 +function! s:CVSReview(...) + if a:0 == 0 + let versiontag="" + if s:CVSGetOption('CVSCommandInteractive', 0) + let versiontag=input('Revision: ') + endif + if versiontag == "" + let versiontag="(current)" + let versionOption="" + else + let versionOption=" -r " . versiontag . " " + endif + else + let versiontag=a:1 + let versionOption=" -r " . versiontag . " " + endif + + let resultBuffer = s:CVSDoCommand('-q update -p' . versionOption, 'cvsreview', versiontag) + if resultBuffer > 0 + let &filetype=getbufvar(b:CVSOrigBuffNR, '&filetype') + endif + + return resultBuffer +endfunction + +" Function: s:CVSStatus() {{{2 +function! s:CVSStatus() + return s:CVSDoCommand('status', 'cvsstatus', '') +endfunction + +" Function: s:CVSUnedit() {{{2 +function! s:CVSUnedit() + return s:CVSDoCommand('unedit', 'cvsunedit', '') +endfunction + +" Function: s:CVSUpdate() {{{2 +function! s:CVSUpdate() + return s:CVSMarkOrigBufferForSetup(s:CVSDoCommand('update', 'update', '')) +endfunction + +" Function: s:CVSVimDiff(...) {{{2 +function! s:CVSVimDiff(...) + let originalBuffer = s:CVSCurrentBufferCheck() + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1 + try + " If there's already a VimDiff'ed window, restore it. + " There may only be one CVSVimDiff original window at a time. + + if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer + " Clear the existing vimdiff setup by removing the result buffers. + call s:CVSWipeoutCommandBuffers(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:CVSWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff') + endif + let resultBuffer = s:CVSReview(a:1) + if resultBuffer < 0 + echomsg "Can't open CVS revision " . a:1 + return resultBuffer + endif + let b:CVSCommand = 'vimdiff' + diffthis + let s:vimDiffBufferCount = 1 + let s:vimDiffScratchList = '{'. resultBuffer . '}' + " If no split method is defined, cheat, and set it to vertical. + try + call s:CVSOverrideOption('CVSCommandSplit', s:CVSGetOption('CVSCommandDiffSplit', s:CVSGetOption('CVSCommandSplit', 'vertical'))) + let resultBuffer=s:CVSReview(a:2) + finally + call s:CVSOverrideOption('CVSCommandSplit') + endtry + if resultBuffer < 0 + echomsg "Can't open CVS revision " . a:1 + return resultBuffer + endif + let b:CVSCommand = 'vimdiff' + diffthis + let s:vimDiffBufferCount = 2 + let s:vimDiffScratchList = s:vimDiffScratchList . '{'. resultBuffer . '}' + else + " Add new buffer + try + " Force splitting behavior, otherwise why use vimdiff? + call s:CVSOverrideOption("CVSCommandEdit", "split") + call s:CVSOverrideOption("CVSCommandSplit", s:CVSGetOption('CVSCommandDiffSplit', s:CVSGetOption('CVSCommandSplit', 'vertical'))) + if(a:0 == 0) + let resultBuffer=s:CVSReview() + else + let resultBuffer=s:CVSReview(a:1) + endif + finally + call s:CVSOverrideOption("CVSCommandEdit") + call s:CVSOverrideOption("CVSCommandSplit") + endtry + if resultBuffer < 0 + echomsg "Can't open current CVS revision" + return resultBuffer + endif + let b:CVSCommand = 'vimdiff' + diffthis + + if !exists('s:vimDiffBufferCount') + " New instance of vimdiff. + let s:vimDiffBufferCount = 2 + let s:vimDiffScratchList = '{' . resultBuffer . '}' + + " This could have been invoked on a CVS 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.", \"&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:vimDiffBufferCount = s:vimDiffBufferCount + 1 + let s:vimDiffScratchList = 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 CVSCommand User CVSVimDiffFinish + finally + call setbufvar(currentBuffer, '&modeline', saveModeline) + endtry + return resultBuffer + finally + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1 + endtry +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:CVSDoCommand('watch ' . tolower(a:onoff), 'cvswatch', '') +endfunction + +" Function: s:CVSWatchers() {{{2 +function! s:CVSWatchers() + return s:CVSDoCommand('watchers', 'cvswatchers', '') +endfunction + +" Section: Command definitions {{{1 +" Section: Primary commands {{{2 +com! CVSAdd call s:CVSAdd() +com! -nargs=? CVSAnnotate call s:CVSAnnotate(<f-args>) +com! -bang -nargs=? CVSCommit call s:CVSCommit(<q-bang>, <q-args>) +com! -nargs=* CVSDiff call s:CVSDiff(<f-args>) +com! CVSEdit call s:CVSEdit() +com! CVSEditors call s:CVSEditors() +com! -bang CVSGotoOriginal call s:CVSGotoOriginal(<q-bang>) +com! -nargs=? CVSLog call s:CVSLog(<f-args>) +com! CVSRevert call s:CVSRevert() +com! -nargs=? CVSReview call s:CVSReview(<f-args>) +com! CVSStatus call s:CVSStatus() +com! CVSUnedit call s:CVSUnedit() +com! CVSUpdate call s:CVSUpdate() +com! -nargs=* CVSVimDiff call s:CVSVimDiff(<f-args>) +com! -nargs=1 CVSWatch call s:CVSWatch(<f-args>) +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: CVS buffer management commands {{{2 +com! CVSDisableBufferSetup call CVSDisableBufferSetup() +com! CVSEnableBufferSetup call CVSEnableBufferSetup() + +" Allow reloading cvscommand.vim +com! CVSReload unlet! loaded_cvscommand | runtime plugin/cvscommand.vim + +" Section: Plugin command mappings {{{1 +nnoremap <silent> <Plug>CVSAdd :CVSAdd<CR> +nnoremap <silent> <Plug>CVSAnnotate :CVSAnnotate<CR> +nnoremap <silent> <Plug>CVSCommit :CVSCommit<CR> +nnoremap <silent> <Plug>CVSDiff :CVSDiff<CR> +nnoremap <silent> <Plug>CVSEdit :CVSEdit<CR> +nnoremap <silent> <Plug>CVSEditors :CVSEditors<CR> +nnoremap <silent> <Plug>CVSGotoOriginal :CVSGotoOriginal<CR> +nnoremap <silent> <Plug>CVSClearAndGotoOriginal :CVSGotoOriginal!<CR> +nnoremap <silent> <Plug>CVSLog :CVSLog<CR> +nnoremap <silent> <Plug>CVSRevert :CVSRevert<CR> +nnoremap <silent> <Plug>CVSReview :CVSReview<CR> +nnoremap <silent> <Plug>CVSStatus :CVSStatus<CR> +nnoremap <silent> <Plug>CVSUnedit :CVSUnedit<CR> +nnoremap <silent> <Plug>CVSUpdate :CVSUpdate<CR> +nnoremap <silent> <Plug>CVSVimDiff :CVSVimDiff<CR> +nnoremap <silent> <Plug>CVSWatchers :CVSWatchers<CR> +nnoremap <silent> <Plug>CVSWatchAdd :CVSWatchAdd<CR> +nnoremap <silent> <Plug>CVSWatchOn :CVSWatchOn<CR> +nnoremap <silent> <Plug>CVSWatchOff :CVSWatchOff<CR> +nnoremap <silent> <Plug>CVSWatchRemove :CVSWatchRemove<CR> + +" Section: Default mappings {{{1 +if !hasmapto('<Plug>CVSAdd') + nmap <unique> <Leader>ca <Plug>CVSAdd +endif +if !hasmapto('<Plug>CVSAnnotate') + nmap <unique> <Leader>cn <Plug>CVSAnnotate +endif +if !hasmapto('<Plug>CVSClearAndGotoOriginal') + nmap <unique> <Leader>cG <Plug>CVSClearAndGotoOriginal +endif +if !hasmapto('<Plug>CVSCommit') + nmap <unique> <Leader>cc <Plug>CVSCommit +endif +if !hasmapto('<Plug>CVSDiff') + nmap <unique> <Leader>cd <Plug>CVSDiff +endif +if !hasmapto('<Plug>CVSEdit') + nmap <unique> <Leader>ce <Plug>CVSEdit +endif +if !hasmapto('<Plug>CVSEditors') + nmap <unique> <Leader>ci <Plug>CVSEditors +endif +if !hasmapto('<Plug>CVSGotoOriginal') + nmap <unique> <Leader>cg <Plug>CVSGotoOriginal +endif +if !hasmapto('<Plug>CVSLog') + nmap <unique> <Leader>cl <Plug>CVSLog +endif +if !hasmapto('<Plug>CVSRevert') + nmap <unique> <Leader>cq <Plug>CVSRevert +endif +if !hasmapto('<Plug>CVSReview') + nmap <unique> <Leader>cr <Plug>CVSReview +endif +if !hasmapto('<Plug>CVSStatus') + nmap <unique> <Leader>cs <Plug>CVSStatus +endif +if !hasmapto('<Plug>CVSUnedit') + nmap <unique> <Leader>ct <Plug>CVSUnedit +endif +if !hasmapto('<Plug>CVSUpdate') + nmap <unique> <Leader>cu <Plug>CVSUpdate +endif +if !hasmapto('<Plug>CVSVimDiff') + nmap <unique> <Leader>cv <Plug>CVSVimDiff +endif +if !hasmapto('<Plug>CVSWatchers') + nmap <unique> <Leader>cwv <Plug>CVSWatchers +endif +if !hasmapto('<Plug>CVSWatchAdd') + nmap <unique> <Leader>cwa <Plug>CVSWatchAdd +endif +if !hasmapto('<Plug>CVSWatchOn') + nmap <unique> <Leader>cwn <Plug>CVSWatchOn +endif +if !hasmapto('<Plug>CVSWatchOff') + nmap <unique> <Leader>cwf <Plug>CVSWatchOff +endif +if !hasmapto('<Plug>CVSWatchRemove') + nmap <unique> <Leader>cwr <Plug>CVSWatchRemove +endif + +" Section: Menu items {{{1 +silent! aunmenu Plugin.CVS +amenu <silent> &Plugin.CVS.&Add <Plug>CVSAdd +amenu <silent> &Plugin.CVS.A&nnotate <Plug>CVSAnnotate +amenu <silent> &Plugin.CVS.&Commit <Plug>CVSCommit +amenu <silent> &Plugin.CVS.&Diff <Plug>CVSDiff +amenu <silent> &Plugin.CVS.&Edit <Plug>CVSEdit +amenu <silent> &Plugin.CVS.Ed&itors <Plug>CVSEditors +amenu <silent> &Plugin.CVS.&Log <Plug>CVSLog +amenu <silent> &Plugin.CVS.Revert <Plug>CVSRevert +amenu <silent> &Plugin.CVS.&Review <Plug>CVSReview +amenu <silent> &Plugin.CVS.&Status <Plug>CVSStatus +amenu <silent> &Plugin.CVS.Unedi&t <Plug>CVSUnedit +amenu <silent> &Plugin.CVS.&Update <Plug>CVSUpdate +amenu <silent> &Plugin.CVS.&VimDiff <Plug>CVSVimDiff +amenu <silent> &Plugin.CVS.&Watchers <Plug>CVSWatchers +amenu <silent> &Plugin.CVS.WatchAdd <Plug>CVSWatchAdd +amenu <silent> &Plugin.CVS.WatchOn <Plug>CVSWatchOn +amenu <silent> &Plugin.CVS.WatchOff <Plug>CVSWatchOff +amenu <silent> &Plugin.CVS.WatchRemove <Plug>CVSWatchRemove + +" Section: Autocommands to restore vimdiff state {{{1 +function! s:CVSVimDiffRestore(vimDiffBuff) + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning + 1 + try + if exists("s:vimDiffSourceBuffer") + if a:vimDiffBuff == s:vimDiffSourceBuffer + " Original file is being removed. + unlet! s:vimDiffSourceBuffer + unlet! s:vimDiffBufferCount + unlet! s:vimDiffRestoreCmd + unlet! s:vimDiffScratchList + elseif match(s:vimDiffScratchList, '{' . a:vimDiffBuff . '}') >= 0 + let s:vimDiffScratchList = substitute(s:vimDiffScratchList, '{' . a:vimDiffBuff . '}', '', '') + let s:vimDiffBufferCount = s:vimDiffBufferCount - 1 + if s:vimDiffBufferCount == 1 && 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('', "&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('', "&diff") + execute s:vimDiffRestoreCmd + endif + execute "hide buffer" currentBufNR + endif + + unlet s:vimDiffRestoreCmd + unlet s:vimDiffSourceBuffer + unlet s:vimDiffBufferCount + unlet s:vimDiffScratchList + elseif s:vimDiffBufferCount == 0 + " All buffers are gone. + unlet s:vimDiffSourceBuffer + unlet s:vimDiffBufferCount + unlet s:vimDiffScratchList + endif + endif + endif + finally + let s:CVSCommandEditFileRunning = s:CVSCommandEditFileRunning - 1 + endtry +endfunction + +augroup CVSVimDiffRestore + au! + au BufUnload * call s:CVSVimDiffRestore(expand("<abuf>")) +augroup END + +" Section: Optional activation of buffer management {{{1 + +if s:CVSGetOption('CVSCommandEnableBufferSetup', 0) + call CVSEnableBufferSetup() +endif + +" Section: Plugin completion {{{1 + +let loaded_cvscommand=2 +silent do CVSCommand User CVSPluginFinish