" EnhancedCommentify.vim " Maintainer: Meikel Brandmeyer " Version: 2.3 " Last Change: Wednesday, February 20th, 2008 " License: " Copyright (c) 2008 Meikel Brandmeyer, Frankfurt am Main " All rights reserved. " " Redistribution and use in source and binary form are permitted provided " that the following conditions are met: " " 1. Redistribition of source code must retain the above copyright " notice, this list of conditions and the following disclaimer. " " 2. 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. " " THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS "AS IS" AND " ANY EXPRESSED 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 AUTHORS 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.3 " Fixed type 'apacha' -> 'apache' (thanks to Brian Neu) " Fixed nested comment escapes strings. (thanks to Samuel Ferencik) " Fixed broken keyboard mappings when wcm was set to . " (thanks to xpatriotx) " 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.3 " Added support for viki/deplate (thanks to Thomas Link) " Added support for xslt/xsd/mail (thanks to Stefano Zacchiroli) " Added callback-functionality to enable extensions without the " need of modification directly in the script. " 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 " If we find nothing appropriate this is the default. let b:ECcommentOpen = '' let b:ECcommentClose = '' if exists("g:EnhCommentifyCallbackExists") call EnhCommentifyCallback(fileType) " Check whether the callback did yield a result. " If so we use it. The user nows, what he's doing. if b:ECcommentOpen != '' return endif endif " 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\|xhtml\|xml\|xslt\|xsd\|dtd\|sgmllnx\)$' let b:ECcommentOpen = '' elseif fileType =~ '^\(sgml\|smil\)$' let b:ECcommentOpen = '' 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 = '' let b:ECcommentClose = '' 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\|apache\|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\|viki\|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 = '' elseif fileType == 'mail' 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 " We translate only the first and the last occurrence " of this resp. escape string. Commenting a line several " times and decommenting it again breaks things. if b:ECaltOpen != '' let line = substitute(line, s:EscapeString(b:ECaltOpen), \ a:commentStart, "") endif if b:ECaltClose != '' let esAltClose = s:EscapeString(b:ECaltClose) let line = substitute(line, esAltClose \ . "\\(.*" . esAltClose . "\\)\\@!", \ a:commentEnd, "") 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 Comment \ :call EnhancedCommentify('', 'comment') noremap DeComment \ :call EnhancedCommentify('', 'decomment') noremap Traditional \ :call EnhancedCommentify('', 'guess') noremap FirstLine \ :call EnhancedCommentify('', 'first') noremap VisualComment \ :call EnhancedCommentify('', 'comment', \ line("'<"), line("'>")) noremap VisualDeComment \ :call EnhancedCommentify('', 'decomment', \ line("'<"), line("'>")) noremap VisualTraditional \ :call EnhancedCommentify('', 'guess', \ line("'<"), line("'>")) noremap VisualFirstLine \ :call EnhancedCommentify('', 'first', \ line("'<"), line("'>")) " " 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 = "" 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 = '' let s:x = '' let s:C = '' let s:X = '' else let s:c = 'c' let s:x = 'x' let s:C = 'C' let s:X = '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 .' '. s:C \ .' DeCommentj' execute 'nmap '. where .' '. s:X \ .' DeComment' endif if bindInInsert execute 'imap '. where .' '. s:C \ .' DeCommentji' execute 'imap '. where .' '. s:X \ .' DeCommenti' endif if bindInVisual execute 'vmap '. where .' '. s:C \ .' VisualDeCommentj' execute 'vmap '. where .' '. s:X \ .' VisualDeComment' endif endif if bindInNormal execute 'nmap '. where .' '. s:c \ .' '. s:Method .'j' execute 'nmap '. where .' '. s:x \ .' '. s:Method endif if bindInInsert execute 'imap '. where .' '. s:c \ .' '. s:Method .'ji' execute 'imap '. where .' '. s:x \ .' '. s:Method endif if bindInVisual execute 'vmap '. s:c \ .' Visual'. s:Method .'j' execute 'vmap '. where .' '. s:x \ .' 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 :