diff options
author | jluehrs2 <jluehrs2@uiuc.edu> | 2007-09-09 17:46:35 -0500 |
---|---|---|
committer | jluehrs2 <jluehrs2@uiuc.edu> | 2007-09-09 17:46:35 -0500 |
commit | 4f13ec9fef25ef0a958b74836e13b342f885feff (patch) | |
tree | 6c1d7c7a22c274969d15dbf60f0b81698082ddba /vim | |
parent | a15446b3fd41656efa0677b8f10a9be4ad8b74fd (diff) | |
download | conf-4f13ec9fef25ef0a958b74836e13b342f885feff.tar.gz conf-4f13ec9fef25ef0a958b74836e13b342f885feff.zip |
add enhancedcommentify vim script
Diffstat (limited to 'vim')
-rw-r--r-- | vim/doc/EnhancedCommentify.txt | 475 | ||||
-rw-r--r-- | vim/ftplugin/ocaml_enhcomm.vim | 1 | ||||
-rw-r--r-- | vim/ftplugin/php_enhcomm.vim | 6 | ||||
-rw-r--r-- | vim/plugin/EnhancedCommentify.vim | 1579 |
4 files changed, 2061 insertions, 0 deletions
diff --git a/vim/doc/EnhancedCommentify.txt b/vim/doc/EnhancedCommentify.txt new file mode 100644 index 0000000..e690ddb --- /dev/null +++ b/vim/doc/EnhancedCommentify.txt @@ -0,0 +1,475 @@ + + ENHANCED COMMENTIFY + + + *EnhancedCommentify* +This is the online help to EnhancedCommentify-script for Vim. It +provides a convenient way to comment/decomment lines of code in source +files. Currently several languages are supported (eg. Vim, C(++), Ada, +Perl, Python and many more), but it is really easy to add other languages. +(see 'Adding new languages') + +============================================================================== +CONTENTS + +1. The main function |EnhComm-EnhCommentify| +2. Changing behaviour (options) |EnhComm-Options| +3. Adding new languages |EnhComm-NewLanguages| +4. Changing the keybindings |EnhComm-Keybindings| +5. Adding fallbacks |EnhComm-Fallbacks| +6. Support |EnhComm-Support| +7. Credits |EnhComm-Credits| +8. Known problems |EnhComm-Bugs| +============================================================================== + +1. The main function *EnhComm-EnhCommentify* *EnhancedCommentify()* + +The function which does the main work is called EnhancedCommentify(). +It may also be called from outside the script. It takes two to four +arguments. + +EnhancedCommentify(overrideEL, mode [, startBlock, endBlock]) + + overrideEL -- may be 'yes', 'no' or ''. With UseSyntax option this + value is guessed for every invocation of the function. + However, if the passed value is not '', the given value + will ocerride any checks. + mode -- may be 'comment', 'decomment', 'guess' or 'first'. + Specifies in what mode the script is run. + comment => region gets commented + decomment => region gets decommented + guess => every line is checked wether it should + be commented or decommented. This is the + traditional mode. + first => blocks is commented or decommented based + on the very first line of the block. For + single lines, this is identical to 'guess' + startBlock -- number of line, where block starts (optional) + endBlock -- number of line, where block ends (optional) + +If startBlock and endBlock are omitted, the function operates on the +current line. Examples: > + EnhancedCommentify('yes', 'guess') + EnhancedCommentify('no', 'decomment', 55, 137) +< +The first call operates on the current line in the traditional way. Empty +lines are also processed. The second call ignores empty lines and decomments +all lines from line 55 up to line 137 inclusive. + +============================================================================== + +2. Changing behaviour (options) *EnhComm-Options* + +All options are boolean except those marked with a (*). Boolean variables +may hold 'yes' or 'no' in any case or abbreviation. So in some sense the +variables aren't really of boolean type: they are always strings. So don't +forget the quotation marks! Examples: > + let g:EnhCommentifyUseAltKeys = 'yes' + let g:EnhCommentifyTraditionalMode = 'N' +< + +'g:EnhCommentifyAltOpen' string (default '|+') (*) + Defines the alternative placeholder for the opening string of + multipart comments. + +'g:EnhCommentifyAltClose' string (default '+|') (*) + Same for closing string. Example: > + /* This is a problem in C. */ +< + If the above line would be commented again, it would produce + this wrong code: > + /*/* This is a problem in C. */*/ +< + The script recognises this problem and removes the inner comment + strings replacing them with the given alternative "escape" + strings. In the default configuration you get > + /*|+ This is a problem in C. +|*/ +< + which is fine. When decommenting the line, the alternative strings + are replaced with the original comment strings. These two options + have no meaning for languages, which have only singlepart comment + strings (like Perl or Scheme). + +'g:EnhCommentifyIdentString' string (default '') (*) + Add this identity string to every comment made by the script. This + looks ugly and introduces non-standard comments, but solves several + problems (see |EnhComm-Bugs|). Setting this option makes only sense + in combination with 'g:EnhCommentifyTraditionalMode'. Examples: + The default setting would comment lines like this one > + /*foo();*/ +< + Setting g:EnhCommentifyIdentString to '+' would result in > + /*+foo();+*/ +< + +'g:EnhCommentifyRespectIndent' string (default 'No') + Respect the indent of a line. The comment leader is inserted correctly + indented, not at the beginning of the line. + Examples: + g:EnhCommentifyRespectIndent = 'No' > + if (bar) { + /* foo();*/ + } +< + g:EnhCommentifyRespectIndent = 'Yes' > + if (bar) { + /*foo();*/ + } +< + +'g:EnhCommentifyIgnoreWS' string (default: 'Yes') + Ignore whitespace at the beginning of the line. This decomments + indented lines. This option has no effect when setting + g:EnhCommentifyRespectIndent to 'Yes'. + +'g:EnhCommentifyPretty' string (default: 'No') + Add a whitespace between comment strings and code. Mainly for + readability. Comments without this space may still be decommented. + Examples: + g:EnhCommentifyPretty = 'No' > + /*foo();*/ +< + g:EnhCommentifyPretty = 'Yes' > + /* foo(); */ +< + +'g:EnhCommentifyBindPerBuffer' string (default: 'No') + Make keybindings local to buffer. + +'g:EnhCommentifyBindUnknown' string (default: 'Yes') + If the filetype is not known, don't add keybindings. + This option has no meaning, when g:EnhCommentifyBindPerBuffer is + set to 'No'. + +'g:EnhCommentifyBindInNormal' string (default: 'Yes') +'g:EnhCommentifyBindInInsert' +'g:EnhCommentifyBindInVisual' + Add keybindings in normal/insert/visual mode. + +'g:EnhCommentifyUseAltKeys' string (default: 'No') + Use alternative keybindings. <Meta-X> instead of <Leader>X. This + may cause trouble on some terminals. Eg. aterm has to be used with + 'meta8: true' in the Xresources. This option has no meaning, when + g:EnhCommentifyUserBindings is set to 'Yes'. + +'g:EnhCommentifyTraditionalMode' string (default: 'Yes') + The traditional commentify mode. Check for every line what action + should be performed. This option has no meaning, when + g:EnhCommentifyUserBindings is set to 'Yes'. + +'g:EnhCommentifyFirstLineMode' string (default: 'No') + The decision, which action (commenting/decommenting) is performed + for a visual block, is based on the first line of this block. + This option has no meaning, when one of g:EnhCommentifyUserBindings + or g:EnhCommentifyTraditionalMode is set to 'Yes'. + +'g:EnhCommentifyUserMode' string (default: 'No') + If this option is set to yes, the scripts lets you decide what to + do. Then there are several two different keybindings active, one + for commenting, one for decommenting. (see |EnhComm-Keybindings|) + This option has no effect if any of these options is set to 'yes': + - g:EnhCommentifyTraditionalMode + - g:EnhCommentifyFirstLineMode + - g:EnhCommentifyUserBindings + +'g:EnhCommentifyUserBindings' string (default: 'No') + This option allows you to choose your own keybindings, without much + trouble. Please see below (|EnhComm-Keybindings|) for more details. + +'g:EnhCommentifyUseBlockIndent' string (default: 'No') + It's a bit difficult to explain, what this option does, so here are + some examples (The numbers are just line numbers!): > + 1if (foo) { + 2 bar(); + 3 baz(); + 4} else { + 5 bar(); + 6 baz(); + 7} +< + Commenting lines 1 to 7 in a visual block will give: > + /*if (foo) {*/ + /* bar();*/ + /* baz();*/ + /*} else {*/ + /* bar();*/ + /* baz();*/ + /*}*/ +< + Or commenting lines 3 to 5 will give: > + if (foo) { + bar(); + /* baz();*/ + /*} else {*/ + /* bar();*/ + baz(); + } +< + lines 2 to 3: > + if (foo) { + /*bar();*/ + /*baz();*/ + } else { + bar(); + baz(); + } +< + However you should think about activating g:EnhCommentifyIgnoreWS, + if you do not use g:EnhCommentifyRespectIndent or you won't be + able to decomment lines, which are commented with this method, + if they have leading whitespace! + +'g:EnhCommentifyMultiPartBlocks'string (default: 'No') + When using a language with multipart-comments commenting a visual + block will result in the whole block commented in unit, not line + by line. + let g:EnhCommentifyMultiPartBlocks = 'yes' > + /*if (foo) { + frobnicate(baz); + }*/ +< + +'g:EnhCommentifyCommentsOp' string (default: 'No') + !!! EXPERIMENTAL !!! + When set the comments option is parsed. This is currently only used + for the above option in order to set the middle part of the comment. + let g:EnhCommentifyCommentsOp = 'yes' > + /*if (foo) { + * frobnicate(baz); + *}*/ +< + +'g:EnhCommentifyAlignRight' string (default: 'No') + When commenting a visual block align the right hand side comment + strings. Examples: + let g:EnhCommentifyAlignRight = 'no' > + /*if (foo) {*/ + /* frobnicate(baz);*/ + /*}*/ +< + let g:EnhCommentifyAlignRight = 'yes' > + /*if (foo) { */ + /* frobnicate(baz);*/ + /*} */ +< +'g:EnhCommentifyUseSyntax' string (default: 'No') + With this option set, the script tries to figure out which filetype to + use for every block by using the synID of the block. This improves + handling of embedded languages eg. CSS in HTML, Perl in VimL... + But be aware, that this feature currently relies on a special form + of the names of the syntax items. So it might not work with every + syntax file (see |EnhComm-Bugs|). It also calls synID only once for + every block! So the first line is significant. Be aware, that "cross" + commenting might cause problems. Examples: > + 1<h1>a header</h1> + 2<style type="text/css"> + 3table { + 4 align: left; + 5} + 6</style> + 7<a href="some">link</a> +< + Commenting line 1 will give: > + <!--<h1>a header</h1>--> + <style type="text/css"> + table { + align: left; + } + </style> + <a href="some">link</a> +< + Commenting line 4 will give: > + <h1>a header</h1> + <style type="text/css"> + table { + /* align: left;*/ + } + </style> + <a href="some">link</a> +< + You don't have to change anything. The filetype is still 'html'. + However marking the whole paragraph in one visual block and commenting + it, will result in the following: > + <!--<h1>a header</h1>--> + <!--<style type="text/css">--> + <!--table {--> + <!-- align: left;--> + <!--}--> + <!--</style>--> + <!--<a href="some">link</a>--> +< + BTW: Don't expect any sense or correctness of code in these examples. + +============================================================================== + +3. Adding new languages *EnhComm-NewLanguages* + +Adding new languages is really easy. Just follow this step-by-step +process. Assume we want to add the language Foobar. It uses multipart +comments like C ("/*" <=> "bar", "*/" <=> "baz") and has the filetype +"foo". + + a) Open the script. + b) Go to the GetFileTypeSettings() function. + c) Now you should see the large "if"-tree. > + if fileType =~ '^\(c\|css\|...' + let b:EnhCommentifyCommentOpen = '/*' + let b:EnhCommentifyCommentClose = '*/' + elseif ... +< + d) There are two buffer-local variables, which hold the different comment + strings. With this in mind we add Foobar: > + if fileType =~ '^\(c\|css\|...' + let b:EnhCommentifyCommentOpen = '/*' + let b:EnhCommentifyCommentClose = '*/' + elseif fileType == 'foo' + let b:EnhCommentifyCommentOpen = 'bar' + let b:EnhCommentifyCommentClose = 'baz' + elseif ... +< + If the new language has only one comment string (like '#' in Perl), we + simply leave the CommentClose variable empty (''). + +That's it! Optionally you can also take step e) and f) in order to complete +the setup, but this is not necessary. + e) Go to the CommentEmptyLines() function and add the new language to + the apropriate "if"-clause. The first will cause empty lines to be + processed also. The second make the script ignore empty lines for + this filetype. (My rule of thumb: single part comment strings + => "yes", multi part comment strings => "no") The default is to + ignore empty lines. + f) Some syntax-files are "broken". "Broken" in the sense, that the syntax + items are not named with xxxFoo or xxxBar, where xxx is the filetype. + This scheme seems to be used by ninety percent of all syntax files I saw. + This is currently the only way to get the filetype from the synID. + If your language may have other languages embedded, you should add + the filetype to "if"-clause in CheckPossibleEmbedding(). + +============================================================================== + +4. Changing the keybindings *EnhComm-Keybindings* + +The script defines several <Plug>'s, which can be used to bind the different +actions to different keys: + - <Plug>Comment / <Plug>DeComment + - <Plug>Traditional + - <Plug>FirstLine +I don't think, that there's much, what needs to be explained. The <Plug>'s +names are descriptive enough. For every <Plug> there is also its visual +counterpart (eg. <Plug>VisualComment), which may be used to bind the actions +for visual mode. + +Here an example from the standard bindings: > + imap <M-c> <Esc><Plug>Traditionalji +< +Clearly this definition binds <Meta-c> in insert mode to the traditional +Commentify-functionality. Step-by-Step: + 1) <Esc>: leave insert mode + 2) <Plug>Traditional: execute the Commentify-function for this line + 3) j: go one line down + 4) i: go back to insert mode + +Another example, which adds a binding for visual mode with the new first- +line-mode: > + vmap <Leader>c <Plug>VisualFirstLine +< +If you absolutely don't like the standards you can specify your own. Search +for '***' in the script. Insert your bindings at this place and add > + let g:EnhCommentifyUserBindings = 'yes' +< +to your .vimrc. That should do the trick. + +4.1 Standard keybindings: + + Meta-Keys: <Leader>Keys: + +Traditional-mode: +Traditionial <M-x> <Leader>x +Traditionial + one line down <M-c> <Leader>c + +FirstLine-mode: +FirstLine <M-x> <Leader>x +FirstLine + one line down <M-c> <Leader>c + +User-mode: +Comment <M-x> <Leader>x +Comment + one line down <M-c> <Leader>c +DeComment <M-y> <Leader>X +DeComment + one line down <M-v> <Leader>C + +============================================================================== + +5. Fallbacks *EnhComm-Fallbacks* + +Problems showed up with the php syntax file. In general it worked, but +when there was simple text in a line it had an empty synID-name. Then +the default kicked in using '&ft', which caused the php comments to be +used, instead of the correct HTML comments. So it seemed necessary to +provide a possibility to override the standard fallback. The solution +looks like the following: + +1st step: + Create a .vim/ftplugin/foo_enhcomm.vim. + +2nd step: + Add the fallback-setting function call: + call EnhCommentifyFallback4Embedded('synFiletype == "bar"', "baz") + +3rd step: + Have fun! :) + +So the general idea is: You specify a test and a fallback filetype. +In the test 'synFiletype' can be used to reference the name tag of the +current synID. For PHP this looks like: > + call EnhCommentifyFallback4Embedded('synFiletype == ""', "html") +< + +============================================================================== + +6. Support *EnhComm-Support* + +Suggestions, feature requests and bugreports are always welcome! You can +contact me with <Brandels_Mikesh@web.de>. + +============================================================================== + +7. Credits *EnhComm-Credits* + +The following people contributed to EnhancedCommentify resp. reported +bugs: (in temporal order) + - Vincent Nijs (this script is based on his ToggleCommentify) + - Mary Ellen Foster + - Scott Stirling + - Zak Beck + - Xiangjiang Ma + - Steve Butts + - Preben Randhol + - John Orr + - Luc Hermite + - Brett Calcott + +Thanks to John Orr and Xiangjiang Ma for their rich feedback and to Luc +Hermite for some good improvement suggestions. + +============================================================================== + +8. Known Problems *EnhComm-Bugs* + +If g:EnhCommentifyFirstLineMode is used, the following block produces wrong +code: > + /* This is a comment and not programm code. */ + foo = bar; + baz(); +< +With indent string, the script recognises, that the comment is not created +by the script and correctly comments the block. + +Lines like the following will not be correctly decommented. > + /*|+ foo +| bar |+ baz +|*/ +< +The script currently relies on the assumption, that the names of syntax items +are of the form '<Filetype in lower case><Uppercase char><Rest>', eg. "phpXy" +or "htmlFooBar". This seems to be true except for some rare, esoteric cases. + +============================================================================== +vim: ft=help:norl:ts=8:tw=78 diff --git a/vim/ftplugin/ocaml_enhcomm.vim b/vim/ftplugin/ocaml_enhcomm.vim new file mode 100644 index 0000000..7609054 --- /dev/null +++ b/vim/ftplugin/ocaml_enhcomm.vim @@ -0,0 +1 @@ +set commentstring=(*%s*) diff --git a/vim/ftplugin/php_enhcomm.vim b/vim/ftplugin/php_enhcomm.vim new file mode 100644 index 0000000..b6a9c04 --- /dev/null +++ b/vim/ftplugin/php_enhcomm.vim @@ -0,0 +1,6 @@ +" +" Normal HTML text has no synID-name. So we have to specify this problem +" case here. Note that you should not try to comment lines starting +" with '<?'. +" +call EnhCommentifyFallback4Embedded('&ft == "php" && synFiletype == ""', "html") diff --git a/vim/plugin/EnhancedCommentify.vim b/vim/plugin/EnhancedCommentify.vim new file mode 100644 index 0000000..1451373 --- /dev/null +++ b/vim/plugin/EnhancedCommentify.vim @@ -0,0 +1,1579 @@ +" EnhancedCommentify.vim +" Maintainer: Meikel Brandmeyer <Brandels_Mikesh@web.de> +" Version: 2.2 +" Last Change: Monday, 27th September 2004 + +" License: +" Copyright (c) 2002,2003,2004 Meikel Brandmeyer, Kaiserslautern. +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" * Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" * Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" * Neither the name of the author nor the names of its contributors may be +" used to endorse or promote products derived from this software without +" specific prior written permission. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +" DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +" Description: +" This is a (well... more or less) simple script to comment lines in a program. +" Currently supported languages are C, C++, PHP, the vim scripting +" language, python, HTML, Perl, LISP, Tex, Shell, CAOS and others. + +" Bugfixes: +" 2.2 +" Fixed problem with UseSyntax (thanks to Pieter Naaijkens) +" Fixed typo in ParseCommentsOp (commstr -> commStr). +" Fixed support for ocaml (thanks to Zhang Le) +" 2.1 +" Fixed problems with alignement when a line contains tabs +" Fixed (resp. cleaned up) issues with overrideEL (thanks to Steve Hall) +" Fixed problems with javascript detection (thanks to Brian Neu) +" Changed Buffer init to BufWinEnter in order to use the modelines. +" 2.0 +" Fixed invalid expression '\'' -> "'" (thanks to Zak Beck) +" Setting AltOpen/AltClose to '' (ie. disabling it) would +" insert '/*' resp. '*/' for character in a line (thanks to Ben Kibbey) +" 1.8 +" Backslashes in comment symbols should not be escaped. +" typo (commensSymbol -> commentSymbol) (thanks to Steve Butts) +" typo (== -> =) +" Fixed hardwired '|+'-'+|' pair. +" 1.7 +" Lines were not correctly decommentified, when there was whitespace +" at the beginning of the line. (thanks to Xiangjiang Ma) +" Fixed error detecting '*sh' filetypes. +" 1.3 +" hlsearch was set unconditionally (thanks to Mary Ellen Foster) +" made function silent (thanks to Mare Ellen Foster) + +" Changelog: +" 2.2 +" Added possibility to override the modes, in which keybindings are +" defined. +" Keybindings may be defined local to every buffer now. +" If a filetype is unknown, one can turn off the keybindings now. +" 2.1 +" Removed any cursor movement. The script should now be free of +" side-effects. +" The script now uses &commentstring to determine the right +" comment strings. Fallback is still the ugly if-thingy. +" Script can now interpret &comments in order to add a middle +" string in blocks. +" Added EnhancedCommentifySet for use by other scripts. (Necessary?) +" Added MultiPartBlocks for languages with multipart-comments. +" Added parsing for comments option if using MultiPartBlocks. +" 2.0 +" IMPORTANT: EnhancedCommentify is now licensed under BSD license +" for distribution with Cream! However this shouldn't +" change anything... +" useBlockIndent does no longer depend on respectIndent. +" Added code to cope with 'C' in '&cpo'. (thanks to Luc Hermitte +" for pointing this out!) +" Added EnhCommentifyIdentFrontOnly option. +" All options are now handled on a per buffer basis. So options +" can be overriden for different buffers. +" 1.9 +" Filetype is now recognized via regular expressions. +" All known filetypes are (more or less) supported. +" Decomments multipart-block comments. +" Added RespectIndent, AlignRight and synID-guessing. +" Switched to buffer variables. +" 1.8 +" Added Ada support. (thanks to Preben Randhol) +" Added Latte support. +" Added blocksupport and possibility to specify action (comment or +" decomment). It's also possible to guess the action line by line or +" using the first line of a block. +" Thanks to Xiangjiang Ma and John Orr for the rich feedback on these +" issues. +" Decomments /*foo();*/, when PrettyComments is set. +" Added 'vhdl' and 'verilog'. (thanks to Steve Butts) +" 1.7 +" Added different options to control behaviour of the plugin. +" Changed default Keybindings to proper plugin settings. +" 1.6 +" Now supports 'm4', 'config', 'automake' +" 'vb', 'aspvbs', 'plsql' (thanks to Zak Beck) +" 1.5 +" Now supports 'java', 'xml', 'jproperties'. (thanks to Scott Stirling) +" 1.4 +" Lines containing only whitespace are now considered empty. +" Added Tcl support. +" Multipart comments are now escaped with configurable alternative +" strings. Prevents nesting errors (eg. /**/*/ in C) +" 1.3 +" Doesn't break lines like +" foo(); /* bar */ +" when doing commentify. + +" Install Details: +" Simply drop this file into your $HOME/.vim/plugin directory. + +if exists("DidEnhancedCommentify") + finish +endif +let DidEnhancedCommentify = 1 + +let s:savedCpo = &cpo +set cpo-=C + +" Note: These must be defined here, since they are used during +" initialisation. +" +" InitBooleanVariable(confVar, scriptVar, defaultVal) +" confVar -- name of the configuration variable +" scriptVar -- name of the variable to set +" defaultVal -- default value +" +" Tests on existence of configuration variable and sets scriptVar +" according to its contents. +" +function s:InitBooleanVariable(confVar, scriptVar, defaultVal) + let regex = a:defaultVal ? 'no*' : 'ye*s*' + + if exists(a:confVar) && {a:confVar} =~? regex + let {a:scriptVar} = !a:defaultVal + else + let {a:scriptVar} = a:defaultVal + endif +endfunction + +" +" InitStringVariable(confVar, scriptVar, defaultVal) +" confVar -- name of the configuration variable +" scriptVar -- name of the variable to set +" defaultVal -- default value +" +" Tests on existence of configuration variable and sets scriptVar +" to its contents. +" +function s:InitStringVariable(confVar, scriptVar, defaultVal) + if exists(a:confVar) + execute "let ". a:scriptVar ." = ". a:confVar + else + let {a:scriptVar} = a:defaultVal + endif +endfunction + +" +" InitScriptVariables(nameSpace) +" nameSpace -- may be "g" for global or "b" for local +" +" Initialises the script variables. +" +function s:InitScriptVariables(nameSpace) + let ns = a:nameSpace " just for abbreviation + let lns = (ns == "g") ? "s" : "b" " 'local namespace' + + " Comment escape strings... + call s:InitStringVariable(ns .":EnhCommentifyAltOpen", lns .":ECaltOpen", + \ s:ECaltOpen) + call s:InitStringVariable(ns .":EnhCommentifyAltClose", lns .":ECaltClose", + \ s:ECaltClose) + + call s:InitBooleanVariable(ns .":EnhCommentifyIgnoreWS", lns .":ECignoreWS", + \ s:ECignoreWS) + + " Adding a space between comment strings and code... + if exists(ns .":EnhCommentifyPretty") + if {ns}:EnhCommentifyPretty =~? 'ye*s*' + let {lns}:ECprettyComments = ' ' + let {lns}:ECprettyUnComments = ' \=' + else + let {lns}:ECprettyComments = '' + let {lns}:ECprettyUnComments = '' + endif + else + let {lns}:ECprettyComments = s:ECprettyComments + let {lns}:ECprettyUnComments = s:ECprettyUnComments + endif + + " Identification string settings... + call s:InitStringVariable(ns .":EnhCommentifyIdentString", + \ lns .":ECidentFront", s:ECidentFront) + let {lns}:ECidentBack = + \ (exists(ns .":EnhCommentifyIdentFrontOnly") + \ && {ns}:EnhCommentifyIdentFrontOnly =~? 'ye*s*') + \ ? '' + \ : {lns}:ECidentFront + + " Wether to use syntax items... + call s:InitBooleanVariable(ns .":EnhCommentifyUseSyntax", + \ lns .":ECuseSyntax", s:ECuseSyntax) + + " Should the script respect line indentation, when inserting strings? + call s:InitBooleanVariable(ns .":EnhCommentifyRespectIndent", + \ lns .":ECrespectIndent", s:ECrespectIndent) + + " Keybindings... + call s:InitBooleanVariable(ns .":EnhCommentifyUseAltKeys", + \ lns .":ECuseAltKeys", s:ECuseAltKeys) + call s:InitBooleanVariable(ns .":EnhCommentifyBindPerBuffer", + \ lns .":ECbindPerBuffer", s:ECbindPerBuffer) + call s:InitBooleanVariable(ns .":EnhCommentifyBindInNormal", + \ lns .":ECbindInNormal", s:ECbindInNormal) + call s:InitBooleanVariable(ns .":EnhCommentifyBindInInsert", + \ lns .":ECbindInInsert", s:ECbindInInsert) + call s:InitBooleanVariable(ns .":EnhCommentifyBindInVisual", + \ lns .":ECbindInVisual", s:ECbindInVisual) + call s:InitBooleanVariable(ns .":EnhCommentifyUserBindings", + \ lns .":ECuserBindings", s:ECuserBindings) + call s:InitBooleanVariable(ns .":EnhCommentifyTraditionalMode", + \ lns .":ECtraditionalMode", s:ECtraditionalMode) + call s:InitBooleanVariable(ns .":EnhCommentifyFirstLineMode", + \ lns .":ECfirstLineMode", s:ECfirstLineMode) + call s:InitBooleanVariable(ns .":EnhCommentifyUserMode", + \ lns .":ECuserMode", s:ECuserMode) + call s:InitBooleanVariable(ns .":EnhCommentifyBindUnknown", + \ lns .":ECbindUnknown", s:ECbindUnknown) + + " Block stuff... + call s:InitBooleanVariable(ns .":EnhCommentifyAlignRight", + \ lns .":ECalignRight", s:ECalignRight) + call s:InitBooleanVariable(ns .":EnhCommentifyUseBlockIndent", + \ lns .":ECuseBlockIndent", s:ECuseBlockIndent) + call s:InitBooleanVariable(ns .":EnhCommentifyMultiPartBlocks", + \ lns .":ECuseMPBlock", s:ECuseMPBlock) + call s:InitBooleanVariable(ns .":EnhCommentifyCommentsOp", + \ lns .":ECuseCommentsOp", s:ECuseCommentsOp) + + let {lns}:ECsaveWhite = ({lns}:ECrespectIndent + \ || {lns}:ECignoreWS || {lns}:ECuseBlockIndent) + \ ? '\(\s*\)' + \ : '' + + if !{lns}:ECrespectIndent + let {lns}:ECuseBlockIndent = 0 + endif + + if {lns}:ECrespectIndent + let {lns}:ECrespectWhite = '\1' + let {lns}:ECignoreWhite = '' + elseif {lns}:ECignoreWS + let {lns}:ECrespectWhite = '' + let {lns}:ECignoreWhite = '\1' + else + let {lns}:ECrespectWhite = '' + let {lns}:ECignoreWhite = '' + endif + + " Using comments option, doesn't make sense without useMPBlock + "if lns == 'b' && b:ECuseCommentsOp + " let b:ECuseMPBlock = 1 + "endif +endfunction + +" +" EnhancedCommentifySet(option, value, ...) +" option -- which option +" value -- value which will be asigned to the option +" +" The purpose of this function is mainly to act as an interface to the +" outer world. It hides the internally used variables. +" +function EnhancedCommentifySet(option, value) + if a:option == 'AltOpen' + let oldval = b:ECaltOpen + let b:ECaltOpen = a:value + elseif a:option == 'AltClose' + let oldval = b:ECaltClose + let b:ECaltClose = a:value + elseif a:option == 'IdentString' + let oldval = b:ECidentFront + let b:ECidentFront = a:value + elseif a:option == 'IdentFrontOnly' + let oldval = (b:ECidentBack == '') ? 'Yes' : 'No' + let b:ECidentBack = (a:value =~? 'ye*s*') ? '' : b:ECidentFront + elseif a:option == 'RespectIndent' + let oldval = b:ECrespectIndent + let b:ECrespectIndent = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'IgnoreWS' + let oldval = b:ECignoreWS + let b:ECignoreWS = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'Pretty' + let oldval = (b:ECprettyComments == ' ') ? 'Yes' : 'No' + if a:value =~? 'ye*s*' + let b:ECprettyComments = ' ' + let b:ECprettyUnComments = ' \=' + else + let b:ECprettyComments = '' + let b:ECprettyUnComments = '' + endif + elseif a:option == 'MultiPartBlocks' + let oldval = b:ECuseMPBlock + let b:ECuseMPBlock = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'CommentsOp' + let oldval = b:ECuseCommentsOp + let b:ECuseCommentsOp = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'UseBlockIndent' + let oldval = b:ECuseBlockIndent + let b:ECuseBlockIndent = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'AlignRight' + let oldval = b:ECalignRight + let b:ECalignRight = (a:value =~? 'ye*s*') ? 1 : 0 + elseif a:option == 'UseSyntax' + let oldval = b:ECuseSyntax + let b:ECuseSyntax = (a:value =~? 'ye*s*') ? 1 : 0 + else + if (has("dialog_gui") && has("gui_running")) + call confirm("EnhancedCommentifySet: Unknwon option '" + \ . option . "'") + else + echohl ErrorMsg + echo "EnhancedCommentifySet: Unknown option '". option ."'" + echohl None + endif + endif + + if oldval == 1 + let oldval = 'Yes' + elseif oldval == 0 + let oldval = 'No' + endif + + return oldval +endfunction + +" Initial settings. +" +" Setting the default options resp. taking user preferences. +if !exists("g:EnhCommentifyUserMode") + \ && !exists("g:EnhCommentifyFirstLineMode") + \ && !exists("g:EnhCommentifyTraditionalMode") + \ && !exists("g:EnhCommentifyUserBindings") + let g:EnhCommentifyTraditionalMode = 'Yes' +endif + +" These will be the default settings for the script: +let s:ECaltOpen = "|+" +let s:ECaltClose = "+|" +let s:ECignoreWS = 1 +let s:ECprettyComments = '' +let s:ECprettyUnComments = '' +let s:ECidentFront = '' +let s:ECuseSyntax = 0 +let s:ECrespectIndent = 0 +let s:ECalignRight = 0 +let s:ECuseBlockIndent = 0 +let s:ECuseMPBlock = 0 +let s:ECuseCommentsOp = 0 +let s:ECuseAltKeys = 0 +let s:ECbindPerBuffer = 0 +let s:ECbindInNormal = 1 +let s:ECbindInInsert = 1 +let s:ECbindInVisual = 1 +let s:ECuserBindings = 0 +let s:ECtraditionalMode = 0 +let s:ECfirstLineMode = 0 +let s:ECuserMode = 1 +let s:ECbindUnknown = 1 + +" Now initialise the global defaults with the preferences set +" by the user in his .vimrc. Settings local to a buffer will be +" done later on, when the script is first called in a buffer. +" +call s:InitScriptVariables("g") + +" Globally used variables with some initialisation. +" FIXME: explain what they are good for +" +let s:Action = 'guess' +let s:firstOfBlock = 1 +let s:blockAction = 'comment' +let s:blockIndentRegex = '' +let s:blockIndent = 0 +let s:inBlock = 0 +let s:tabConvert = '' +let s:overrideEmptyLines = 0 +let s:emptyLines = 'no' +let s:maxLen = 0 + +function EnhancedCommentifyInitBuffer() + if !exists("b:ECdidBufferInit") + call s:InitScriptVariables("b") + + if !exists("b:EnhCommentifyFallbackTest") + let b:EnhCommentifyFallbackTest = 0 + endif + + call s:GetFileTypeSettings(&ft) + call s:CheckPossibleEmbedding(&ft) + + " + " If the filetype is not supported and the user wants us to, we do not + " add keybindings. + " + if s:ECbindPerBuffer + if b:ECcommentOpen != "" || b:ECbindUnknown + call s:SetKeybindings("l") + endif + endif + + let b:ECdidBufferInit = 1 + let b:ECsyntax = &ft + endif +endfunction + +autocmd BufWinEnter,BufNewFile * call EnhancedCommentifyInitBuffer() + +" +" EnhancedCommentify(emptyLines, action, ...) +" overrideEL -- commentify empty lines +" may be 'yes', 'no' or '' for guessing +" action -- action which should be executed: +" * guess: +" toggle commetification (old behaviour) +" * comment: +" comment lines +" * decomment: +" decomment lines +" * first: +" use first line of block to determine action +" a:1, a:2 -- first and last line of block, which should be +" processed. +" +" Commentifies the current line. +" +function EnhancedCommentify(overrideEL, action, ...) + if a:overrideEL != '' + let s:overrideEmptyLines = 1 + endif + + " Now do the buffer initialisation. Every buffer will get + " it's pendant to a global variable (eg. s:ECalignRight -> b:ECalignRight). + " The local variable is actually used, whereas the global variable + " holds the defaults from the user's .vimrc. In this way the settings + " can be overriden for single buffers. + " + " NOTE: Buffer init is done by autocommands now. + " + + let b:ECemptyLines = a:overrideEL + + " The language is not supported. + if b:ECcommentOpen == '' + if (has("dialog_gui") && has("gui_running")) + call confirm("This filetype is currently _not_ supported!\n" + \ ."Please consider contacting the author in order" + \ ." to add this filetype.", "", 1, "Error") + else + echohl ErrorMsg + echo "This filetype is currently _not_ supported!" + echo "Please consider contacting the author in order to add" + echo "this filetype in future releases!" + echohl None + endif + return + endif + + let lnum = line(".") + + " Now some initialisations... + let s:Action = a:action + + " FIXME: Is there really _no_ function to simplify this??? + " (Maybe something like 'let foo = 8x" "'?) + if s:tabConvert == '' && strlen(s:tabConvert) != &tabstop + let s:tabConvert = '' + let i = 0 + while i < &tabstop + let s:tabConvert = s:tabConvert .' ' + let i = i + 1 + endwhile + endif + + if a:0 == 2 + let s:startBlock = a:1 + let s:i = a:1 + let s:endBlock = a:2 + + let s:inBlock = 1 + else + let s:startBlock = lnum + let s:i = lnum + let s:endBlock = lnum + + let s:inBlock = 0 + endif + + if b:ECuseSyntax && b:ECpossibleEmbedding + let column = indent(s:startBlock) + 1 + if !&expandtab + let rem = column % &tabstop + let column = ((column - rem) / &tabstop) + rem + endif + call s:CheckSyntax(s:startBlock, column) + endif + + " Get the indent of the less indented line of the block. + if s:inBlock && (b:ECuseBlockIndent || b:ECalignRight) + call s:DoBlockComputations(s:startBlock, s:endBlock) + endif + + while s:i <= s:endBlock + let lineString = getline(s:i) + let lineString = s:TabsToSpaces(lineString) + + " If we should comment "empty" lines, we have to add + " the correct indent, if we use blockIndent. + if b:ECemptyLines =~? 'ye*s*' + \ && b:ECuseBlockIndent + \ && lineString =~ "^\s*$" + let i = 0 + while i < s:blockIndent + let lineString = " " . lineString + let i = i + 1 + endwhile + endif + + " Don't comment empty lines. + if lineString !~ "^\s*$" + \ || b:ECemptyLines =~? 'ye*s*' + if b:ECcommentClose != '' + let lineString = s:CommentifyMultiPart(lineString, + \ b:ECcommentOpen, + \ b:ECcommentClose, + \ b:ECcommentMiddle) + else + let lineString = s:CommentifySinglePart(lineString, + \ b:ECcommentOpen) + endif + endif + + " Revert the above: If the line is "empty" and we + " used blockIndent, we remove the spaces. + " FIXME: Why does "^\s*$" not work? + if b:ECemptyLines =~? 'ye*s*' + \ && b:ECuseBlockIndent + \ && lineString =~ "^" . s:blockIndentRegex ."\s*$" + let lineString = + \ substitute(lineString, s:blockIndentRegex, + \ '', '') + endif + + let lineString = s:SpacesToTabs(lineString) + call setline(s:i, lineString) + + let s:i = s:i + 1 + let s:firstOfBlock = 0 + endwhile + + let s:firstOfBlock = 1 +endfunction + +" +" DoBlockComputations(start, end) +" start -- number of first line +" end -- number of last line +" +" This function does some computations which are necessary for useBlockIndent +" and alignRight. ie. find smallest indent and longest line. +" +function s:DoBlockComputations(start, end) + let i = a:start + let len = 0 + let amount = 100000 " this should be enough ... + + while i <= a:end + if b:ECuseBlockIndent && getline(i) !~ '^\s*$' + let cur = indent(i) + if cur < amount + let amount = cur + endif + endif + + if b:ECalignRight + let cur = s:GetLineLen(s:TabsToSpaces(getline(i)), + \ s:GetLineLen(b:ECcommentOpen, 0) + \ + strlen(b:ECprettyComments)) + if b:ECuseMPBlock + let cur = cur + s:GetLineLen(b:ECcommentOpen, 0) + \ + strlen(b:ECprettyComments) + endif + + if len < cur + let len = cur + endif + endif + + let i = i + 1 + endwhile + + if b:ECuseBlockIndent + if amount > 0 + let regex = '\( \{'. amount .'}\)' + else + let regex = '' + endif + let s:blockIndentRegex = regex + let s:blockIndent = amount + endif + + if b:ECalignRight + let s:maxLen = len + endif +endfunction + +" +" CheckSyntax(line, column) +" line -- line of line +" column -- column of line +" Check what syntax is active during call of main function. First hit +" wins. If the filetype changes during the block, we ignore that. +" Adjust the filetype if necessary. +" +function s:CheckSyntax(line, column) + let ft = "" + let synFiletype = synIDattr(synID(a:line, a:column, 1), "name") + + " FIXME: This feature currently relies on a certain format + " of the names of syntax items: the filetype must be prepended + " in lowwer case letters, followed by at least one upper case + " letter. + if match(synFiletype, '\l\+\u') == 0 + let ft = substitute(synFiletype, '^\(\l\+\)\u.*$', '\1', "") + endif + + if ft == "" + execute "let specialCase = ". b:EnhCommentifyFallbackTest + + if specialCase + let ft = b:EnhCommentifyFallbackValue + else + " Fallback: If nothing holds, use normal filetype! + let ft = &ft + endif + endif + + " Nothing changed! + if ft == b:ECsyntax + return + endif + + let b:ECsyntax = ft + call s:GetFileTypeSettings(ft) +endfunction + +" +" GetFileTypeSettings(ft) +" ft -- filetype +" +" This functions sets some buffer-variables, which control the comment +" strings and 'empty lines'-handling. +" +function s:GetFileTypeSettings(ft) + let fileType = a:ft + + " I learned about the commentstring option. Let's use it. + " For now we ignore it, if it is "/*%s*/". This is the + " default. We cannot check wether this is default or C or + " something other like CSS, etc. We have to wait, until the + " filetypes adopt this option. + if &commentstring != "/*%s*/" && !b:ECuseSyntax + let b:ECcommentOpen = + \ substitute(&commentstring, '%s.*', "", "") + let b:ECcommentClose = + \ substitute(&commentstring, '.*%s', "", "") + " Multipart comments: + elseif fileType =~ '^\(c\|b\|css\|csc\|cupl\|indent\|jam\|lex\|lifelines\|'. + \ 'lite\|nqc\|phtml\|progress\|rexx\|rpl\|sas\|sdl\|sl\|'. + \ 'strace\|xpm\|yacc\)$' + let b:ECcommentOpen = '/*' + let b:ECcommentClose = '*/' + elseif fileType =~ '^\(html\|xml\|dtd\|sgmllnx\)$' + let b:ECcommentOpen = '<!--' + let b:ECcommentClose = '-->' + elseif fileType =~ '^\(sgml\|smil\)$' + let b:ECcommentOpen = '<!' + let b:ECcommentClose = '>' + elseif fileType == 'atlas' + let b:ECcommentOpen = 'C' + let b:ECcommentClose = '$' + elseif fileType =~ '^\(catalog\|sgmldecl\)$' + let b:ECcommentOpen = '--' + let b:ECcommentClose = '--' + elseif fileType == 'dtml' + let b:ECcommentOpen = '<dtml-comment>' + let b:ECcommentClose = '</dtml-comment>' + elseif fileType == 'htmlos' + let b:ECcommentOpen = '#' + let b:ECcommentClose = '/#' + elseif fileType =~ '^\(jgraph\|lotos\|mma\|modula2\|modula3\|pascal\|'. + \ 'ocaml\|sml\)$' + let b:ECcommentOpen = '(*' + let b:ECcommentClose = '*)' + elseif fileType == 'jsp' + let b:ECcommentOpen = '<%--' + let b:ECcommentClose = '--%>' + elseif fileType == 'model' + let b:ECcommentOpen = '$' + let b:ECcommentClose = '$' + elseif fileType == 'st' + let b:ECcommentOpen = '"' + let b:ECcommentClose = '"' + elseif fileType =~ '^\(tssgm\|tssop\)$' + let b:ECcommentOpen = 'comment = "' + let b:ECcommentClose = '"' + " Singlepart comments: + elseif fileType =~ '^\(ox\|cpp\|php\|java\|verilog\|acedb\|ch\|clean\|'. + \ 'clipper\|cs\|dot\|dylan\|hercules\|idl\|ishd\|javascript\|'. + \ 'kscript\|mel\|named\|openroad\|pccts\|pfmain\|pike\|'. + \ 'pilrc\|plm\|pov\|rc\|scilab\|specman\|tads\|tsalt\|uc\|'. + \ 'xkb\)$' + let b:ECcommentOpen = '//' + let b:ECcommentClose = '' + elseif fileType =~ '^\(vim\|abel\)$' + let b:ECcommentOpen = '"' + let b:ECcommentClose = '' + elseif fileType =~ '^\(lisp\|scheme\|scsh\|amiga\|asm\|asm68k\|bindzone\|'. + \ 'def\|dns\|dosini\|dracula\|dsl\|idlang\|iss\|jess\|kix\|'. + \ 'masm\|monk\|nasm\|ncf\|omnimark\|pic\|povini\|rebol\|'. + \ 'registry\|samba\|skill\|smith\|tags\|tasm\|tf\|winbatch\|'. + \ 'wvdial\|z8a\)$' + let b:ECcommentOpen = ';' + let b:ECcommentClose = '' + elseif fileType =~ '^\(python\|perl\|[^w]*sh$\|tcl\|jproperties\|make\|'. + \ 'robots\|apacha\|apachestyle\|awk\|bc\|cfg\|cl\|conf\|'. + \ 'crontab\|diff\|ecd\|elmfilt\|eterm\|expect\|exports\|'. + \ 'fgl\|fvwm\|gdb\|gnuplot\|gtkrc\|hb\|hog\|ia64\|icon\|'. + \ 'inittab\|lftp\|lilo\|lout\|lss\|lynx\|maple\|mush\|'. + \ 'muttrc\|nsis\|ora\|pcap\|pine\|po\|procmail\|'. + \ 'psf\|ptcap\|r\|radiance\|ratpoison\|readline\remind\|'. + \ 'ruby\|screen\|sed\|sm\|snnsnet\|snnspat\|snnsres\|spec\|'. + \ 'squid\|terminfo\|tidy\|tli\|tsscl\|vgrindefs\|vrml\|'. + \ 'wget\|wml\|xf86conf\|xmath\)$' + let b:ECcommentOpen = '#' + let b:ECcommentClose = '' + elseif fileType == 'webmacro' + let b:ECcommentOpen = '##' + let b:ECcommentClose = '' + elseif fileType == 'ppwiz' + let b:ECcommentOpen = ';;' + let b:ECcommentClose = '' + elseif fileType == 'latte' + let b:ECcommentOpen = '\\;' + let b:ECcommentClose = '' + elseif fileType =~ '^\(tex\|abc\|erlang\|ist\|lprolog\|matlab\|mf\|'. + \ 'postscr\|ppd\|prolog\|simula\|slang\|slrnrc\|slrnsc\|'. + \ 'texmf\|virata\)$' + let b:ECcommentOpen = '%' + let b:ECcommentClose = '' + elseif fileType =~ '^\(caos\|cterm\|form\|foxpro\|sicad\|snobol4\)$' + let b:ECcommentOpen = '*' + let b:ECcommentClose = '' + elseif fileType =~ '^\(m4\|config\|automake\)$' + let b:ECcommentOpen = 'dnl ' + let b:ECcommentClose = '' + elseif fileType =~ '^\(vb\|aspvbs\|ave\|basic\|elf\|lscript\)$' + let b:ECcommentOpen = "'" + let b:ECcommentClose = '' + elseif fileType =~ '^\(plsql\|vhdl\|ahdl\|ada\|asn\|csp\|eiffel\|gdmo\|'. + \ 'haskell\|lace\|lua\|mib\|sather\|sql\|sqlforms\|sqlj\|'. + \ 'stp\)$' + let b:ECcommentOpen = '--' + let b:ECcommentClose = '' + elseif fileType == 'abaqus' + let b:ECcommentOpen = '**' + let b:ECcommentClose = '' + elseif fileType =~ '^\(aml\|natural\|vsejcl\)$' + let b:ECcommentOpen = '/*' + let b:ECcommentClose = '' + elseif fileType == 'ampl' + let b:ECcommentOpen = '\\#' + let b:ECcommentClose = '' + elseif fileType == 'bdf' + let b:ECcommentOpen = 'COMMENT ' + let b:ECcommentClose = '' + elseif fileType == 'btm' + let b:ECcommentOpen = '::' + let b:ECcommentClose = '' + elseif fileType == 'dcl' + let b:ECcommentOpen = '$!' + let b:ECcommentClose = '' + elseif fileType == 'dosbatch' + let b:ECcommentOpen = 'rem ' + let b:ECcommentClose = '' + elseif fileType == 'focexec' + let b:ECcommentOpen = '-*' + let b:ECcommentClose = '' + elseif fileType == 'forth' + let b:ECcommentOpen = '\\ ' + let b:ECcommentClose = '' + elseif fileType =~ '^\(fortran\|inform\|sqr\|uil\|xdefaults\|'. + \ 'xmodmap\|xpm2\)$' + let b:ECcommentOpen = '!' + let b:ECcommentClose = '' + elseif fileType == 'gp' + let b:ECcommentOpen = '\\\\' + let b:ECcommentClose = '' + elseif fileType =~ '^\(master\|nastran\|sinda\|spice\|tak\|trasys\)$' + let b:ECcommentOpen = '$' + let b:ECcommentClose = '' + elseif fileType == 'nroff' || fileType == 'groff' + let b:ECcommentOpen = ".\\\\\"" + let b:ECcommentClose = '' + elseif fileType == 'opl' + let b:ECcommentOpen = 'REM ' + let b:ECcommentClose = '' + elseif fileType == 'texinfo' + let b:ECcommentOpen = '@c ' + let b:ECcommentClose = '' + else + let b:ECcommentOpen = '' + let b:ECcommentClose = '' + endif + + if b:ECuseCommentsOp + let b:ECcommentMiddle = + \ s:ParseCommentsOp(b:ECcommentOpen, b:ECcommentClose) + if b:ECcommentMiddle == '' + let b:ECuseCommentsOp = 0 + endif + else + let b:ECcommentMiddle = '' + endif + + if !s:overrideEmptyLines + call s:CommentEmptyLines(fileType) + endif +endfunction + +" +" ParseCommentsOp(commentOpen, commentClose) +" commentOpen -- comment-open string +" commentClose-- comment-close string +" +" Try to extract the middle comment string from &comments. First hit wins. +" If nothing is found '' is returned. +" +function s:ParseCommentsOp(commentOpen, commentClose) + let commStr = &comments + let offset = 0 + let commentMiddle = '' + + while commStr != '' + " + " First decompose &omments into consecutive s-, m- and e-parts. + " + let s = stridx(commStr, 's') + if s == -1 + return '' + endif + + let commStr = strpart(commStr, s) + let comma = stridx(commStr, ',') + if comma == -1 + return '' + endif + let sPart = strpart(commStr, 0, comma) + + let commStr = strpart(commStr, comma) + let m = stridx(commStr, 'm') + if m == -1 + return '' + endif + + let commStr = strpart(commStr, m) + let comma = stridx(commStr, ',') + if comma == -1 + return '' + endif + let mPart = strpart(commStr, 0, comma) + + let commStr = strpart(commStr, comma) + let e = stridx(commStr, 'e') + if e == -1 + return '' + endif + + let commStr = strpart(commStr, e) + let comma = stridx(commStr, ',') + if comma == -1 + let comma = strlen(commStr) + endif + let ePart = strpart(commStr, 0, comma) + + let commStr = strpart(commStr, comma) + + " + " Now check wether this is what we want: + " Are the comment string the same? + " + let sColon = stridx(sPart, ':') + let eColon = stridx(ePart, ':') + if sColon == -1 || eColon == -1 + return '' + endif + if strpart(sPart, sColon + 1) != a:commentOpen + \ || strpart(ePart, eColon + 1) != a:commentClose + continue + endif + + let mColon = stridx(mPart, ':') + if mColon == -1 + return '' + endif + let commentMiddle = strpart(mPart, mColon + 1) + + " + " Check for any alignement. + " + let i = 1 + while sPart[i] != ':' + if sPart[i] == 'r' + let offset = strlen(a:commentOpen) - strlen(commentMiddle) + break + elseif sPart[i] == 'l' + let offset = 0 + break + elseif s:isDigit(sPart[i]) + let j = 1 + while s:isDigit(sPart[i + j]) + let j = j + 1 + endwhile + let offset = 1 * strpart(sPart, i, j) + break + endif + let i = i + 1 + endwhile + + if offset == 0 + let i = 1 + while ePart[i] != ':' + if ePart[i] == 'r' + let offset = strlen(a:commentClose) - strlen(commentMiddle) + break + elseif ePart[i] == 'l' + let offset = 0 + break + elseif s:isDigit(ePart[i]) + let j = 1 + while s:isDigit(ePart[i + j]) + let j = j + 1 + endwhile + let offset = 1 * strpart(ePart, i, j) + break + endif + + let i = i + 1 + endwhile + endif + + while offset > 0 + let commentMiddle = " " . commentMiddle + let offset = offset - 1 + endwhile + + break + endwhile + + return commentMiddle +endfunction + +" +" isDigit(char) +" +" Nomen est Omen. +" +function s:isDigit(char) + let r = 0 + + let charVal = char2nr(a:char) + + if charVal >= 48 && charVal <= 57 + let r = 1 + endif + + return r +endfunction + +" +" CommentEmptyLines(ft) +" ft -- filetype of current buffer +" +" Decides, if empty lines should be commentified or not. Add the filetype, +" you want to change, to the apropriate if-clause. +" +function s:CommentEmptyLines(ft) + " FIXME: Quick hack (tm)! + if 0 + " Add special filetypes here. + elseif b:ECcommentClose == '' + let b:ECemptyLines = 'yes' + else + let b:ECemptyLines = 'no' + endif +endfunction + +" +" CheckPossibleEmbedding(ft) +" ft -- the filetype of current buffer +" +" Check wether it makes sense to allow checking for the synIDs. +" Eg. C will never have embedded code... +" +function s:CheckPossibleEmbedding(ft) + if a:ft =~ '^\(php\|vim\|latte\|html\)$' + let b:ECpossibleEmbedding = 1 + else + " Since getting the synID is slow, we set the default to 'no'! + " There are also some 'broken' languages like the filetype for + " autoconf's configure.in's ('config'). + let b:ECpossibleEmbedding = 0 + endif +endfunction + +" +" CommentifyMultiPart(lineString, commentStart, commentEnd, action) +" lineString -- line to commentify +" commentStart -- comment-start string, eg '/*' +" commentEnd -- comment-end string, eg. '*/' +" commentMiddle -- comment-middle string, eg. ' *' +" +" This function commentifies code of languages, which have multipart +" comment strings, eg. '/*' - '*/' of C. +" +function s:CommentifyMultiPart(lineString, commentStart, + \ commentEnd, commentMiddle) + if s:Action == 'guess' || s:Action == 'first' || b:ECuseMPBlock + let todo = s:DecideWhatToDo(a:lineString, a:commentStart, a:commentEnd) + else + let todo = s:Action + endif + + if todo == 'decomment' + return s:UnCommentify(a:lineString, a:commentStart, + \ a:commentEnd, a:commentMiddle) + else + return s:Commentify(a:lineString, a:commentStart, + \ a:commentEnd, a:commentMiddle) + endif +endfunction + +" +" CommentifySinglePart(lineString, commentSymbol) +" lineString -- line to commentify +" commentSymbol -- comment string, eg '#' +" +" This function is used for all languages, whose comment strings +" consist only of one string at the beginning of a line. +" +function s:CommentifySinglePart(lineString, commentSymbol) + if s:Action == 'guess' || s:Action == 'first' + let todo = s:DecideWhatToDo(a:lineString, a:commentSymbol) + else + let todo = s:Action + endif + + if todo == 'decomment' + return s:UnCommentify(a:lineString, a:commentSymbol) + else + return s:Commentify(a:lineString, a:commentSymbol) + endif +endfunction + +" +" Escape(lineString, commentStart, commentEnd) +" +" Escape already present symbols. +" +function s:Escape(lineString, commentStart, commentEnd) + let line = a:lineString + + if b:ECaltOpen != '' + let line = substitute(line, s:EscapeString(a:commentStart), + \ b:ECaltOpen, "g") + endif + if b:ECaltClose != '' + let line = substitute(line, s:EscapeString(a:commentEnd), + \ b:ECaltClose, "g") + endif + + return line +endfunction + +" +" UnEscape(lineString, commentStart, commentEnd) +" +" Unescape already present escape symbols. +" +function s:UnEscape(lineString, commentStart, commentEnd) + let line = a:lineString + + if b:ECaltOpen != '' + let line = substitute(line, s:EscapeString(b:ECaltOpen), + \ a:commentStart, "g") + endif + if b:ECaltClose != '' + let line = substitute(line, s:EscapeString(b:ECaltClose), + \ a:commentEnd, "g") + endif + + return line +endfunction + +" +" Commentify(lineString, commentSymbol, [commentEnd]) +" lineString -- the line in work +" commentSymbol -- string to insert at the beginning of the line +" commentEnd -- string to insert at the end of the line +" may be omitted +" +" This function inserts the start- (and if given the end-) string of the +" comment in the current line. +" +function s:Commentify(lineString, commentSymbol, ...) + let line = a:lineString + let j = 0 + + " If a end string is present, insert it too. + if a:0 > 0 + " First we have to escape any comment already contained in the line, + " since (at least for C) comments are not allowed to nest. + let line = s:Escape(line, a:commentSymbol, a:1) + + if b:ECuseCommentsOp && b:ECuseMPBlock + \ && a:0 > 1 + \ && s:i > s:startBlock + let line = substitute(line, s:LookFor('commentmiddle'), + \ s:SubstituteWith('commentmiddle', a:2), "") + endif + + if !b:ECuseMPBlock || (b:ECuseMPBlock && s:i == s:endBlock) + " Align the closing part to the right. + if b:ECalignRight && s:inBlock + let len = s:GetLineLen(line, strlen(a:commentSymbol) + \ + strlen(b:ECprettyComments)) + while j < s:maxLen - len + let line = line .' ' + let j = j + 1 + endwhile + endif + + let line = substitute(line, s:LookFor('commentend'), + \ s:SubstituteWith('commentend', a:1), "") + endif + endif + + " insert the comment symbol + if !b:ECuseMPBlock || a:0 == 0 || (b:ECuseMPBlock && s:i == s:startBlock) + let line = substitute(line, s:LookFor('commentstart'), + \ s:SubstituteWith('commentstart', a:commentSymbol), "") + endif + + return line +endfunction + +" +" UnCommentify(lineString, commentSymbol, [commentEnd]) +" lineString -- the line in work +" commentSymbol -- string to remove at the beginning of the line +" commentEnd -- string to remove at the end of the line +" may be omitted +" +" This function removes the start- (and if given the end-) string of the +" comment in the current line. +" +function s:UnCommentify(lineString, commentSymbol, ...) + let line = a:lineString + + " remove the first comment symbol found on a line + if a:0 == 0 || !b:ECuseMPBlock || (b:ECuseMPBlock && s:i == s:startBlock) + let line = substitute(line, s:LookFor('decommentstart', + \ a:commentSymbol), + \ s:SubstituteWith('decommentstart'), "") + endif + + " If a end string is present, we have to remove it, too. + if a:0 > 0 + " First, we remove the trailing comment symbol. + if !b:ECuseMPBlock || (b:ECuseMPBlock && s:i == s:endBlock) + let line = substitute(line, s:LookFor('decommentend', a:1), + \ s:SubstituteWith('decommentend'), "") + + " Remove any trailing whitespace, if we used alignRight. + if b:ECalignRight + let line = substitute(line, ' *$', '', "") + endif + endif + + " Maybe we added a middle string. Remove it here. + if b:ECuseCommentsOp && b:ECuseMPBlock + \ && a:0 > 1 + \ && s:i > s:startBlock + let line = substitute(line, s:LookFor('decommentmiddle', a:2), + \ s:SubstituteWith('decommentmiddle'), "") + endif + + " Remove escaped inner comments. + let line = s:UnEscape(line, a:commentSymbol, a:1) + endif + + return line +endfunction + +" +" GetLineLen(line, offset) +" line -- line of which length should be computed +" offset -- maybe a shift of the line to the right +" +" Expands '\t' to it's tabstop value. +" +function s:GetLineLen(line, offset) + let len = a:offset + let i = 0 + + while a:line[i] != "" + if a:line[i] == "\t" + let len = (((len / &tabstop) + 1) * &tabstop) + else + let len = len + 1 + endif + let i = i + 1 + endwhile + + return len +endfunction + +" +" EscapeString(string) +" string -- string to process +" +" Escapes characters in 'string', which have some function in +" regular expressions, with a '\'. +" +" Returns the escaped string. +" +function s:EscapeString(string) + return escape(a:string, "*{}[]$^-") +endfunction + +" +" LookFor(what, ...) +" what -- what type of regular expression +" * checkstart: +" * checkend: +" check for comment at start/end of line +" * commentstart: +" * commentend: +" insert comment strings +" * decommentstart: +" * decommentend: +" remove comment strings +" a:1 -- comment string +" +function s:LookFor(what, ...) + if b:ECuseBlockIndent && s:inBlock + let handleWhitespace = s:blockIndentRegex + else + let handleWhitespace = b:ECsaveWhite + endif + + if a:what == 'checkstart' + let regex = '^'. b:ECsaveWhite . s:EscapeString(a:1) + \ . s:EscapeString(b:ECidentFront) + elseif a:what == 'checkend' + let regex = s:EscapeString(b:ECidentBack) + \ . s:EscapeString(a:1) . b:ECsaveWhite . '$' + elseif a:what == 'commentstart' + let regex = '^'. handleWhitespace + elseif a:what == 'commentmiddle' + let regex = '^'. handleWhitespace + elseif a:what == 'commentend' + let regex = '$' + elseif a:what == 'decommentstart' + let regex = '^'. b:ECsaveWhite . s:EscapeString(a:1) + \ . s:EscapeString(b:ECidentFront) . b:ECprettyUnComments + elseif a:what == 'decommentmiddle' + let regex = '^'. b:ECsaveWhite . s:EscapeString(a:1) + \ . s:EscapeString(b:ECidentFront) . b:ECprettyUnComments + elseif a:what == 'decommentend' + let regex = b:ECprettyUnComments . s:EscapeString(b:ECidentBack) + \ . s:EscapeString(a:1) . b:ECsaveWhite .'$' + endif + + return regex +endfunction + +" +" SubstituteWith(what, ...) +" what -- what type of regular expression +" * commentstart: +" * commentend: +" insert comment strings +" * decommentstart: +" * decommentend: +" remove comment strings +" a:1 -- comment string +" +function s:SubstituteWith(what, ...) + if a:what == 'commentstart' + \ || a:what == 'commentmiddle' + \ || a:what == 'commentend' + let commentSymbol = a:1 + else + let commentSymbol = '' + endif + + if b:ECuseBlockIndent && s:inBlock + let handleWhitespace = '\1' . commentSymbol + else + let handleWhitespace = b:ECrespectWhite . commentSymbol + \ . b:ECignoreWhite + endif + + if a:what == 'commentstart' + let regex = handleWhitespace . b:ECidentFront + \ . b:ECprettyComments + elseif a:what == 'commentmiddle' + let regex = handleWhitespace . b:ECidentFront + \ . b:ECprettyComments + elseif a:what == 'commentend' + let regex = b:ECprettyComments . b:ECidentBack . a:1 + elseif a:what == 'decommentstart' + \ || a:what == 'decommentmiddle' + \ || a:what == 'decommentend' + let regex = handleWhitespace + endif + + return regex +endfunction + +" +" +" DecideWhatToDo(lineString, commentStart, ...) +" lineString -- first line of block +" commentStart -- comment start symbol +" a:1 -- comment end symbol +" +function s:DecideWhatToDo(lineString, commentStart, ...) + " If we checked already, we return our previous result. + if !s:firstOfBlock + \ && (s:Action == 'first' + \ || (b:ECuseMPBlock && s:inBlock && a:0)) + return s:blockAction + endif + + let s:blockAction = 'comment' + + if s:inBlock && a:0 && b:ECuseMPBlock + let first = getline(s:startBlock) + let last = getline(s:endBlock) + + if first =~ s:LookFor('checkstart', a:commentStart) + \ && first !~ s:LookFor('checkend', a:1) + \ && last !~ s:LookFor('checkstart', a:commentStart) + \ && last =~ s:LookFor('checkend', a:1) + let s:blockAction = 'decomment' + endif + + return s:blockAction + endif + + if a:lineString =~ s:LookFor('checkstart', a:commentStart) + let s:blockAction = 'decomment' + endif + + if a:0 + if a:lineString !~ s:LookFor('checkend', a:1) + let s:blockAction = 'comment' + endif + endif + + let s:firstOfBlock = 0 + return s:blockAction +endfunction + +" +" TabsToSpaces(str) +" str -- string to convert +" +" Convert leading tabs of given string to spaces. +" +function s:TabsToSpaces(str) + let string = a:str + + " FIXME: Can we use something like retab? I don't think so, + " because retab changes every whitespace in the line, but we + " wan't to modify only the leading spaces. Is this a problem? + while string =~ '^\( *\)\t' + let string = substitute(string, '^\( *\)\t', '\1'. s:tabConvert, "") + endwhile + + return string +endfunction + +" +" SpacesToTabs(str) +" str -- string to convert +" +" Convert leading spaces of given string to tabs. +" +function s:SpacesToTabs(str) + let string = a:str + + if !&expandtab + while string =~ '^\(\t*\)'. s:tabConvert + let string = substitute(string, '^\(\t*\)'. s:tabConvert, + \ '\1\t', "") + endwhile + endif + + return string +endfunction + +" +" EnhCommentifyFallback4Embedded(test, fallback) +" test -- test for the special case +" fallback -- filetype instead of normal fallback +" +" This function is global. It should be called from filetype +" plugins like php, where the normal fallback behaviour may +" not work. One may use 'synFiletype' to reference the guessed +" filetype via synID. +" +function EnhCommentifyFallback4Embedded(test, fallback) + let b:EnhCommentifyFallbackTest = a:test + let b:EnhCommentifyFallbackValue = a:fallback +endfunction + +" +" Keyboard mappings. +" +noremap <Plug>Comment + \ :call EnhancedCommentify('', 'comment')<CR> +noremap <Plug>DeComment + \ :call EnhancedCommentify('', 'decomment')<CR> +noremap <Plug>Traditional + \ :call EnhancedCommentify('', 'guess')<CR> +noremap <Plug>FirstLine + \ :call EnhancedCommentify('', 'first')<CR> + +noremap <Plug>VisualComment + \ <Esc>:call EnhancedCommentify('', 'comment', + \ line("'<"), line("'>"))<CR> +noremap <Plug>VisualDeComment + \ <Esc>:call EnhancedCommentify('', 'decomment', + \ line("'<"), line("'>"))<CR> +noremap <Plug>VisualTraditional + \ <Esc>:call EnhancedCommentify('', 'guess', + \ line("'<"), line("'>"))<CR> +noremap <Plug>VisualFirstLine + \ <Esc>:call EnhancedCommentify('', 'first', + \ line("'<"), line("'>"))<CR> +" +" Finally set keybindings. +" +" SetKeybindings(where) +" where -- "l" for local to the buffer, "g" for global +" +function s:SetKeybindings(where) + if a:where == "l" + let where = "<buffer>" + let ns = "b" + else + let where = "" + let ns = "s" + endif + + execute "let userBindings = ". ns .":ECuserBindings" + execute "let useAltKeys = ". ns .":ECuseAltKeys" + execute "let traditionalMode = ". ns .":ECtraditionalMode" + execute "let firstLineMode = ". ns .":ECfirstLineMode" + execute "let bindInNormal = ". ns .":ECbindInNormal" + execute "let bindInInsert = ". ns .":ECbindInInsert" + execute "let bindInVisual = ". ns .":ECbindInVisual" + + if userBindings + " + " *** Put your personal bindings here! *** + " + else + if useAltKeys + let s:c = '<M-c>' + let s:x = '<M-x>' + let s:C = '<M-v>' + let s:X = '<M-y>' + else + let s:c = '<Leader>c' + let s:x = '<Leader>x' + let s:C = '<Leader>C' + let s:X = '<Leader>X' + endif + + if traditionalMode + let s:Method = 'Traditional' + elseif firstLineMode + let s:Method = 'FirstLine' + else + let s:Method = 'Comment' + + " Decomment must be defined here. Everything else is mapped below. + if bindInNormal + execute 'nmap '. where .' <silent> <unique> '. s:C + \ .' <Plug>DeCommentj' + execute 'nmap '. where .' <silent> <unique> '. s:X + \ .' <Plug>DeComment' + endif + + if bindInInsert + execute 'imap '. where .' <silent> <unique> '. s:C + \ .' <Esc><Plug>DeCommentji' + execute 'imap '. where .' <silent> <unique> '. s:X + \ .' <Esc><Plug>DeCommenti' + endif + + if bindInVisual + execute 'vmap '. where .' <silent> <unique> '. s:C + \ .' <Plug>VisualDeCommentj' + execute 'vmap '. where .' <silent> <unique> '. s:X + \ .' <Plug>VisualDeComment' + endif + endif + + if bindInNormal + execute 'nmap '. where .' <silent> <unique> '. s:c + \ .' <Plug>'. s:Method .'j' + execute 'nmap '. where .' <silent> <unique> '. s:x + \ .' <Plug>'. s:Method + endif + + if bindInInsert + execute 'imap '. where .' <silent> <unique> '. s:c + \ .' <Esc><Plug>'. s:Method .'ji' + execute 'imap '. where .' <silent> <unique> '. s:x + \ .' <Esc><Plug>'. s:Method + endif + + if bindInVisual + execute 'vmap <silent> <unique> '. s:c + \ .' <Plug>Visual'. s:Method .'j' + execute 'vmap '. where .' <silent> <unique> '. s:x + \ .' <Plug>Visual'. s:Method + endif + endif +endfunction + +if !s:ECbindPerBuffer + call s:SetKeybindings("g") +endif + +let &cpo = s:savedCpo + +" vim: set sts=4 sw=4 ts=8 : |