1607 lines
46 KiB
VimL
1607 lines
46 KiB
VimL
" EnhancedCommentify.vim
|
|
" Maintainer: Meikel Brandmeyer <Brandels_Mikesh@web.de>
|
|
" 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 <Tab>.
|
|
" (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 = '<!--'
|
|
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\|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 <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 :
|