PYENV prompt.
[profile.git] / .vim / plugin / NERD_commenter.vim
1 " ============================================================================
2 " File:        NERD_commenter.vim
3 " Description: vim global plugin that provides easy code commenting
4 " Maintainer:  Martin Grenfell <martin_grenfell at msn dot com>
5 " Version:     2.2.2
6 " Last Change: 30th March, 2008
7 " License:     This program is free software. It comes without any warranty,
8 "              to the extent permitted by applicable law. You can redistribute
9 "              it and/or modify it under the terms of the Do What The Fuck You
10 "              Want To Public License, Version 2, as published by Sam Hocevar.
11 "              See http://sam.zoy.org/wtfpl/COPYING for more details.
12 "
13 " ============================================================================
14
15 " Section: script init stuff {{{1
16 if exists("loaded_nerd_comments")
17     finish
18 endif
19 if v:version < 700
20     finish
21 endif
22 let loaded_nerd_comments = 1
23
24 " Function: s:InitVariable() function {{{2
25 " This function is used to initialise a given variable to a given value. The
26 " variable is only initialised if it does not exist prior
27 "
28 " Args:
29 "   -var: the name of the var to be initialised
30 "   -value: the value to initialise var to
31 "
32 " Returns:
33 "   1 if the var is set, 0 otherwise
34 function s:InitVariable(var, value)
35     if !exists(a:var)
36         exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
37         return 1
38     endif
39     return 0
40 endfunction
41
42 " Section: space string init{{{2
43 " When putting spaces after the left delim and before the right we use
44 " s:spaceStr for the space char. This way we can make it add anything after
45 " the left and before the right by modifying this variable
46 let s:spaceStr = ' '
47 let s:lenSpaceStr = strlen(s:spaceStr)
48
49 " Section: variable init calls {{{2
50 call s:InitVariable("g:NERDAllowAnyVisualDelims", 1)
51 call s:InitVariable("g:NERDBlockComIgnoreEmpty", 0)
52 call s:InitVariable("g:NERDCommentWholeLinesInVMode", 0)
53 call s:InitVariable("g:NERDCompactSexyComs", 0)
54 call s:InitVariable("g:NERDCreateDefaultMappings", 1)
55 call s:InitVariable("g:NERDDefaultNesting", 1)
56 call s:InitVariable("g:NERDMenuMode", 3)
57 call s:InitVariable("g:NERDLPlace", "[>")
58 call s:InitVariable("g:NERDUsePlaceHolders", 1)
59 call s:InitVariable("g:NERDRemoveAltComs", 1)
60 call s:InitVariable("g:NERDRemoveExtraSpaces", 1)
61 call s:InitVariable("g:NERDRPlace", "<]")
62 call s:InitVariable("g:NERDSpaceDelims", 0)
63 call s:InitVariable("g:NERDDelimiterRequests", 1)
64
65
66
67 let s:NERDFileNameEscape="[]#*$%'\" ?`!&();<>\\"
68
69 " Section: Comment mapping functions, autocommands and commands {{{1
70 " ============================================================================
71 " Section: Comment enabler autocommands {{{2
72 " ============================================================================
73
74 augroup commentEnablers
75
76     "if the user enters a buffer or reads a buffer then we gotta set up
77     "the comment delimiters for that new filetype
78     autocmd BufEnter,BufRead * :call s:SetUpForNewFiletype(&filetype, 0)
79
80     "if the filetype of a buffer changes, force the script to reset the
81     "delims for the buffer
82     autocmd Filetype * :call s:SetUpForNewFiletype(&filetype, 1)
83 augroup END
84
85
86 " Function: s:SetUpForNewFiletype(filetype) function {{{2
87 " This function is responsible for setting up buffer scoped variables for the
88 " given filetype.
89 "
90 " These variables include the comment delimiters for the given filetype and calls
91 " MapDelimiters or MapDelimitersWithAlternative passing in these delimiters.
92 "
93 " Args:
94 "   -filetype: the filetype to set delimiters for
95 "   -forceReset: 1 if the delimiters should be reset if they have already be
96 "    set for this buffer.
97 "
98 function s:SetUpForNewFiletype(filetype, forceReset)
99     "if we have already set the delimiters for this buffer then dont go thru
100     "it again
101     if !a:forceReset && exists("b:NERDLeft") && b:NERDLeft != ''
102         return
103     endif
104
105     let b:NERDSexyComMarker = ''
106
107     "check the filetype against all known filetypes to see if we have
108     "hardcoded the comment delimiters to use
109     if a:filetype ==? ""
110         call s:MapDelimiters('', '')
111     elseif a:filetype ==? "aap"
112         call s:MapDelimiters('#', '')
113     elseif a:filetype ==? "abc"
114         call s:MapDelimiters('%', '')
115     elseif a:filetype ==? "acedb"
116         call s:MapDelimitersWithAlternative('//','', '/*','*/')
117     elseif a:filetype ==? "actionscript"
118         call s:MapDelimitersWithAlternative('//','', '/*','*/')
119     elseif a:filetype ==? "ada"
120         call s:MapDelimitersWithAlternative('--','', '--  ', '')
121     elseif a:filetype ==? "ahdl"
122         call s:MapDelimiters('--', '')
123     elseif a:filetype ==? "ahk"
124         call s:MapDelimitersWithAlternative(';', '', '/*', '*/')
125     elseif a:filetype ==? "amiga"
126         call s:MapDelimiters(';', '')
127     elseif a:filetype ==? "aml"
128         call s:MapDelimiters('/*', '')
129     elseif a:filetype ==? "ampl"
130         call s:MapDelimiters('#', '')
131     elseif a:filetype ==? "apache"
132         call s:MapDelimiters('#', '')
133     elseif a:filetype ==? "apachestyle"
134         call s:MapDelimiters('#', '')
135     elseif a:filetype ==? "asciidoc"
136         call s:MapDelimiters('//', '')
137     elseif a:filetype ==? "applescript"
138         call s:MapDelimitersWithAlternative('--', '', '(*', '*)')
139     elseif a:filetype ==? "asm68k"
140         call s:MapDelimiters(';', '')
141     elseif a:filetype ==? "asm"
142         call s:MapDelimitersWithAlternative(';', '', '#', '')
143     elseif a:filetype ==? "asn"
144         call s:MapDelimiters('--', '')
145     elseif a:filetype ==? "aspvbs"
146         call s:MapDelimiters('''', '')
147     elseif a:filetype ==? "asterisk"
148         call s:MapDelimiters(';', '')
149     elseif a:filetype ==? "asy"
150         call s:MapDelimiters('//', '')
151     elseif a:filetype ==? "atlas"
152         call s:MapDelimiters('C','$')
153     elseif a:filetype ==? "autohotkey"
154         call s:MapDelimiters(';','')
155     elseif a:filetype ==? "autoit"
156         call s:MapDelimiters(';','')
157     elseif a:filetype ==? "ave"
158         call s:MapDelimiters("'",'')
159     elseif a:filetype ==? "awk"
160         call s:MapDelimiters('#','')
161     elseif a:filetype ==? "basic"
162         call s:MapDelimitersWithAlternative("'",'', 'REM ', '')
163     elseif a:filetype ==? "bbx"
164         call s:MapDelimiters('%', '')
165     elseif a:filetype ==? "bc"
166         call s:MapDelimiters('#', '')
167     elseif a:filetype ==? "bib"
168         call s:MapDelimiters('%','')
169     elseif a:filetype ==? "bindzone"
170         call s:MapDelimiters(';', '')
171     elseif a:filetype ==? "bst"
172         call s:MapDelimiters('%', '')
173     elseif a:filetype ==? "btm"
174         call s:MapDelimiters('::', '')
175     elseif a:filetype ==? "caos"
176         call s:MapDelimiters('*', '')
177     elseif a:filetype ==? "calibre"
178         call s:MapDelimiters('//','')
179     elseif a:filetype ==? "catalog"
180         call s:MapDelimiters('--','--')
181     elseif a:filetype ==? "c"
182         call s:MapDelimitersWithAlternative('/*','*/', '//', '')
183     elseif a:filetype ==? "cfg"
184         call s:MapDelimiters('#', '')
185     elseif a:filetype ==? "cg"
186         call s:MapDelimitersWithAlternative('//','', '/*','*/')
187     elseif a:filetype ==? "ch"
188         call s:MapDelimitersWithAlternative('//','', '/*','*/')
189     elseif a:filetype ==? "cl"
190         call s:MapDelimiters('#', '')
191     elseif a:filetype ==? "clean"
192         call s:MapDelimitersWithAlternative('//','', '/*','*/')
193     elseif a:filetype ==? "clipper"
194         call s:MapDelimitersWithAlternative('//','', '/*','*/')
195     elseif a:filetype ==? "clojure"
196         call s:MapDelimiters(';', '')
197     elseif a:filetype ==? "cmake"
198         call s:MapDelimiters('#','')
199     elseif a:filetype ==? "conkyrc"
200         call s:MapDelimiters('#', '')
201     elseif a:filetype ==? "cpp"
202         call s:MapDelimitersWithAlternative('//','', '/*','*/')
203     elseif a:filetype ==? "crontab"
204         call s:MapDelimiters('#', '')
205     elseif a:filetype ==? "cs"
206         call s:MapDelimitersWithAlternative('//','', '/*','*/')
207     elseif a:filetype ==? "csp"
208         call s:MapDelimiters('--', '')
209     elseif a:filetype ==? "cterm"
210         call s:MapDelimiters('*', '')
211     elseif a:filetype ==? "cucumber"
212         call s:MapDelimiters('#','')
213     elseif a:filetype ==? "cvs"
214         call s:MapDelimiters('CVS:','')
215     elseif a:filetype ==? "d"
216         call s:MapDelimitersWithAlternative('//','', '/*','*/')
217     elseif a:filetype ==? "dcl"
218         call s:MapDelimiters('$!', '')
219     elseif a:filetype ==? "dakota"
220         call s:MapDelimiters('#', '')
221     elseif a:filetype ==? "debcontrol"
222         call s:MapDelimiters('#', '')
223     elseif a:filetype ==? "debsources"
224         call s:MapDelimiters('#', '')
225     elseif a:filetype ==? "def"
226         call s:MapDelimiters(';', '')
227     elseif a:filetype ==? "desktop"
228         call s:MapDelimiters('#', '')
229     elseif a:filetype ==? "dhcpd"
230         call s:MapDelimiters('#', '')
231     elseif a:filetype ==? "diff"
232         call s:MapDelimiters('#', '')
233     elseif a:filetype ==? "django"
234         call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
235     elseif a:filetype ==? "docbk"
236         call s:MapDelimiters('<!--', '-->')
237     elseif a:filetype ==? "dns"
238         call s:MapDelimiters(';', '')
239     elseif a:filetype ==? "dosbatch"
240         call s:MapDelimitersWithAlternative('REM ','', '::', '')
241     elseif a:filetype ==? "dosini"
242         call s:MapDelimiters(';', '')
243     elseif a:filetype ==? "dot"
244         call s:MapDelimitersWithAlternative('//','', '/*','*/')
245     elseif a:filetype ==? "dracula"
246         call s:MapDelimiters(';', '')
247     elseif a:filetype ==? "dsl"
248         call s:MapDelimiters(';', '')
249     elseif a:filetype ==? "dtml"
250         call s:MapDelimiters('<dtml-comment>','</dtml-comment>')
251     elseif a:filetype ==? "dylan"
252         call s:MapDelimitersWithAlternative('//','', '/*','*/')
253     elseif a:filetype ==? 'ebuild'
254         call s:MapDelimiters('#', '')
255     elseif a:filetype ==? "ecd"
256         call s:MapDelimiters('#', '')
257     elseif a:filetype ==? 'eclass'
258         call s:MapDelimiters('#', '')
259     elseif a:filetype ==? "eiffel"
260         call s:MapDelimiters('--', '')
261     elseif a:filetype ==? "elf"
262         call s:MapDelimiters("'", '')
263     elseif a:filetype ==? "elmfilt"
264         call s:MapDelimiters('#', '')
265     elseif a:filetype ==? "erlang"
266         call s:MapDelimiters('%', '')
267     elseif a:filetype ==? "eruby"
268         call s:MapDelimitersWithAlternative('<%#', '%>', '<!--', '-->')
269     elseif a:filetype ==? "expect"
270         call s:MapDelimiters('#', '')
271     elseif a:filetype ==? "exports"
272         call s:MapDelimiters('#', '')
273     elseif a:filetype ==? "factor"
274         call s:MapDelimitersWithAlternative('! ', '', '!# ', '')
275     elseif a:filetype ==? "fgl"
276         call s:MapDelimiters('#', '')
277     elseif a:filetype ==? "focexec"
278         call s:MapDelimiters('-*', '')
279     elseif a:filetype ==? "form"
280         call s:MapDelimiters('*', '')
281     elseif a:filetype ==? "foxpro"
282         call s:MapDelimiters('*', '')
283     elseif a:filetype ==? "fstab"
284         call s:MapDelimiters('#', '')
285     elseif a:filetype ==? "fvwm"
286         call s:MapDelimiters('#', '')
287     elseif a:filetype ==? "fx"
288         call s:MapDelimitersWithAlternative('//','', '/*','*/')
289     elseif a:filetype ==? "gams"
290         call s:MapDelimiters('*', '')
291     elseif a:filetype ==? "gdb"
292         call s:MapDelimiters('#', '')
293     elseif a:filetype ==? "gdmo"
294         call s:MapDelimiters('--', '')
295     elseif a:filetype ==? "geek"
296         call s:MapDelimiters('GEEK_COMMENT:', '')
297     elseif a:filetype ==? "genshi"
298         call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
299     elseif a:filetype ==? "gentoo-conf-d"
300         call s:MapDelimiters('#', '')
301     elseif a:filetype ==? "gentoo-env-d"
302         call s:MapDelimiters('#', '')
303     elseif a:filetype ==? "gentoo-init-d"
304         call s:MapDelimiters('#', '')
305     elseif a:filetype ==? "gentoo-make-conf"
306         call s:MapDelimiters('#', '')
307     elseif a:filetype ==? 'gentoo-package-keywords'
308         call s:MapDelimiters('#', '')
309     elseif a:filetype ==? 'gentoo-package-mask'
310         call s:MapDelimiters('#', '')
311     elseif a:filetype ==? 'gentoo-package-use'
312         call s:MapDelimiters('#', '')
313     elseif a:filetype ==? 'gitcommit'
314         call s:MapDelimiters('#', '')
315     elseif a:filetype ==? 'gitconfig'
316         call s:MapDelimiters(';', '')
317     elseif a:filetype ==? 'gitrebase'
318         call s:MapDelimiters('#', '')
319     elseif a:filetype ==? "gnuplot"
320         call s:MapDelimiters('#','')
321     elseif a:filetype ==? "groovy"
322         call s:MapDelimitersWithAlternative('//','', '/*','*/')
323     elseif a:filetype ==? "gtkrc"
324         call s:MapDelimiters('#', '')
325     elseif a:filetype ==? "haskell"
326         call s:MapDelimitersWithAlternative('{-','-}', '--', '')
327     elseif a:filetype ==? "hb"
328         call s:MapDelimiters('#', '')
329     elseif a:filetype ==? "h"
330         call s:MapDelimitersWithAlternative('//','', '/*','*/')
331     elseif a:filetype ==? "haml"
332         call s:MapDelimitersWithAlternative('-#', '', '/', '')
333     elseif a:filetype ==? "hercules"
334         call s:MapDelimitersWithAlternative('//','', '/*','*/')
335     elseif a:filetype ==? "hog"
336         call s:MapDelimiters('#', '')
337     elseif a:filetype ==? "hostsaccess"
338         call s:MapDelimiters('#', '')
339     elseif a:filetype ==? "htmlcheetah"
340         call s:MapDelimiters('##','')
341     elseif a:filetype ==? "htmldjango"
342         call s:MapDelimitersWithAlternative('<!--','-->', '{#', '#}')
343     elseif a:filetype ==? "htmlos"
344         call s:MapDelimiters('#','/#')
345     elseif a:filetype ==? "ia64"
346         call s:MapDelimiters('#', '')
347     elseif a:filetype ==? "icon"
348         call s:MapDelimiters('#', '')
349     elseif a:filetype ==? "idlang"
350         call s:MapDelimiters(';', '')
351     elseif a:filetype ==? "idl"
352         call s:MapDelimitersWithAlternative('//','', '/*','*/')
353     elseif a:filetype ==? "inform"
354         call s:MapDelimiters('!', '')
355     elseif a:filetype ==? "inittab"
356         call s:MapDelimiters('#', '')
357     elseif a:filetype ==? "ishd"
358         call s:MapDelimitersWithAlternative('//','', '/*','*/')
359     elseif a:filetype ==? "iss"
360         call s:MapDelimiters(';', '')
361     elseif a:filetype ==? "ist"
362         call s:MapDelimiters('%', '')
363     elseif a:filetype ==? "java"
364         call s:MapDelimitersWithAlternative('//','', '/*','*/')
365     elseif a:filetype ==? "javacc"
366         call s:MapDelimitersWithAlternative('//','', '/*','*/')
367     elseif a:filetype ==? "javascript"
368         call s:MapDelimitersWithAlternative('//','', '/*','*/')
369     elseif a:filetype == "javascript.jquery"
370         call s:MapDelimitersWithAlternative('//','', '/*','*/')
371     elseif a:filetype ==? "jess"
372         call s:MapDelimiters(';', '')
373     elseif a:filetype ==? "jgraph"
374         call s:MapDelimiters('(*','*)')
375     elseif a:filetype ==? "jproperties"
376         call s:MapDelimiters('#','')
377     elseif a:filetype ==? "jsp"
378         call s:MapDelimiters('<%--', '--%>')
379     elseif a:filetype ==? "kix"
380         call s:MapDelimiters(';', '')
381     elseif a:filetype ==? "kscript"
382         call s:MapDelimitersWithAlternative('//','', '/*','*/')
383     elseif a:filetype ==? "lace"
384         call s:MapDelimiters('--', '')
385     elseif a:filetype ==? "ldif"
386         call s:MapDelimiters('#', '')
387     elseif a:filetype ==? "lilo"
388         call s:MapDelimiters('#', '')
389     elseif a:filetype ==? "lilypond"
390         call s:MapDelimiters('%', '')
391     elseif a:filetype ==? "liquid"
392         call s:MapDelimiters('{%', '%}')
393     elseif a:filetype ==? "lisp"
394         call s:MapDelimitersWithAlternative(';','', '#|', '|#')
395     elseif a:filetype ==? "llvm"
396         call s:MapDelimiters(';','')
397     elseif a:filetype ==? "lotos"
398         call s:MapDelimiters('(*','*)')
399     elseif a:filetype ==? "lout"
400         call s:MapDelimiters('#', '')
401     elseif a:filetype ==? "lprolog"
402         call s:MapDelimiters('%', '')
403     elseif a:filetype ==? "lscript"
404         call s:MapDelimiters("'", '')
405     elseif a:filetype ==? "lss"
406         call s:MapDelimiters('#', '')
407     elseif a:filetype ==? "lua"
408         call s:MapDelimitersWithAlternative('--','', '--[[', ']]')
409     elseif a:filetype ==? "lynx"
410         call s:MapDelimiters('#', '')
411     elseif a:filetype ==? "lytex"
412         call s:MapDelimiters('%', '')
413     elseif a:filetype ==? "mail"
414         call s:MapDelimiters('> ','')
415     elseif a:filetype ==? "mako"
416         call s:MapDelimiters('##', '')
417     elseif a:filetype ==? "man"
418         call s:MapDelimiters('."', '')
419     elseif a:filetype ==? "map"
420         call s:MapDelimiters('%', '')
421     elseif a:filetype ==? "maple"
422         call s:MapDelimiters('#', '')
423     elseif a:filetype ==? "markdown"
424         call s:MapDelimiters('<!--', '-->')
425     elseif a:filetype ==? "masm"
426         call s:MapDelimiters(';', '')
427     elseif a:filetype ==? "mason"
428         call s:MapDelimiters('<% #', '%>')
429     elseif a:filetype ==? "master"
430         call s:MapDelimiters('$', '')
431     elseif a:filetype ==? "matlab"
432         call s:MapDelimiters('%', '')
433     elseif a:filetype ==? "mel"
434         call s:MapDelimitersWithAlternative('//','', '/*','*/')
435     elseif a:filetype ==? "mib"
436         call s:MapDelimiters('--', '')
437     elseif a:filetype ==? "mkd"
438         call s:MapDelimiters('>', '')
439     elseif a:filetype ==? "mma"
440         call s:MapDelimiters('(*','*)')
441     elseif a:filetype ==? "model"
442         call s:MapDelimiters('$','$')
443     elseif a:filetype =~ "moduala."
444         call s:MapDelimiters('(*','*)')
445     elseif a:filetype ==? "modula2"
446         call s:MapDelimiters('(*','*)')
447     elseif a:filetype ==? "modula3"
448         call s:MapDelimiters('(*','*)')
449     elseif a:filetype ==? "monk"
450         call s:MapDelimiters(';', '')
451     elseif a:filetype ==? "mush"
452         call s:MapDelimiters('#', '')
453     elseif a:filetype ==? "named"
454         call s:MapDelimitersWithAlternative('//','', '/*','*/')
455     elseif a:filetype ==? "nasm"
456         call s:MapDelimiters(';', '')
457     elseif a:filetype ==? "nastran"
458         call s:MapDelimiters('$', '')
459     elseif a:filetype ==? "natural"
460         call s:MapDelimiters('/*', '')
461     elseif a:filetype ==? "ncf"
462         call s:MapDelimiters(';', '')
463     elseif a:filetype ==? "newlisp"
464         call s:MapDelimiters(';','')
465     elseif a:filetype ==? "nroff"
466         call s:MapDelimiters('\"', '')
467     elseif a:filetype ==? "nsis"
468         call s:MapDelimiters('#', '')
469     elseif a:filetype ==? "ntp"
470         call s:MapDelimiters('#', '')
471     elseif a:filetype ==? "objc"
472         call s:MapDelimitersWithAlternative('//','', '/*','*/')
473     elseif a:filetype ==? "objcpp"
474         call s:MapDelimitersWithAlternative('//','', '/*','*/')
475     elseif a:filetype ==? "objj"
476         call s:MapDelimitersWithAlternative('//','', '/*','*/')
477     elseif a:filetype ==? "ocaml"
478         call s:MapDelimiters('(*','*)')
479     elseif a:filetype ==? "occam"
480         call s:MapDelimiters('--','')
481     elseif a:filetype ==? "omlet"
482         call s:MapDelimiters('(*','*)')
483     elseif a:filetype ==? "omnimark"
484         call s:MapDelimiters(';', '')
485     elseif a:filetype ==? "openroad"
486         call s:MapDelimiters('//', '')
487     elseif a:filetype ==? "opl"
488         call s:MapDelimiters("REM", "")
489     elseif a:filetype ==? "ora"
490         call s:MapDelimiters('#', '')
491     elseif a:filetype ==? "ox"
492         call s:MapDelimiters('//', '')
493     elseif a:filetype ==? "pascal"
494         call s:MapDelimitersWithAlternative('{','}', '(*', '*)')
495     elseif a:filetype ==? "patran"
496         call s:MapDelimitersWithAlternative('$','','/*', '*/')
497     elseif a:filetype ==? "pcap"
498         call s:MapDelimiters('#', '')
499     elseif a:filetype ==? "pccts"
500         call s:MapDelimitersWithAlternative('//','', '/*','*/')
501     elseif a:filetype ==? "pdf"
502         call s:MapDelimiters('%', '')
503     elseif a:filetype ==? "pfmain"
504         call s:MapDelimiters('//', '')
505     elseif a:filetype ==? "php"
506         call s:MapDelimitersWithAlternative('//','','/*', '*/')
507     elseif a:filetype ==? "pic"
508         call s:MapDelimiters(';', '')
509     elseif a:filetype ==? "pike"
510         call s:MapDelimitersWithAlternative('//','', '/*','*/')
511     elseif a:filetype ==? "pilrc"
512         call s:MapDelimitersWithAlternative('//','', '/*','*/')
513     elseif a:filetype ==? "pine"
514         call s:MapDelimiters('#', '')
515     elseif a:filetype ==? "plm"
516         call s:MapDelimitersWithAlternative('//','', '/*','*/')
517     elseif a:filetype ==? "plsql"
518         call s:MapDelimitersWithAlternative('--', '', '/*', '*/')
519     elseif a:filetype ==? "po"
520         call s:MapDelimiters('#', '')
521     elseif a:filetype ==? "postscr"
522         call s:MapDelimiters('%', '')
523     elseif a:filetype ==? "pov"
524         call s:MapDelimitersWithAlternative('//','', '/*','*/')
525     elseif a:filetype ==? "povini"
526         call s:MapDelimiters(';', '')
527     elseif a:filetype ==? "ppd"
528         call s:MapDelimiters('%', '')
529     elseif a:filetype ==? "ppwiz"
530         call s:MapDelimiters(';;', '')
531     elseif a:filetype ==? "processing"
532         call s:MapDelimitersWithAlternative('//','', '/*','*/')
533     elseif a:filetype ==? "prolog"
534         call s:MapDelimitersWithAlternative('%','','/*','*/')
535     elseif a:filetype ==? "ps1"
536         call s:MapDelimiters('#', '')
537     elseif a:filetype ==? "psf"
538         call s:MapDelimiters('#', '')
539     elseif a:filetype ==? "ptcap"
540         call s:MapDelimiters('#', '')
541     elseif a:filetype ==? "radiance"
542         call s:MapDelimiters('#', '')
543     elseif a:filetype ==? "ratpoison"
544         call s:MapDelimiters('#', '')
545     elseif a:filetype ==? "r"
546         call s:MapDelimiters('#', '')
547     elseif a:filetype ==? "rc"
548         call s:MapDelimitersWithAlternative('//','', '/*','*/')
549     elseif a:filetype ==? "rebol"
550         call s:MapDelimiters(';', '')
551     elseif a:filetype ==? "registry"
552         call s:MapDelimiters(';', '')
553     elseif a:filetype ==? "remind"
554         call s:MapDelimiters('#', '')
555     elseif a:filetype ==? "resolv"
556         call s:MapDelimiters('#', '')
557     elseif a:filetype ==? "rgb"
558         call s:MapDelimiters('!', '')
559     elseif a:filetype ==? "rib"
560         call s:MapDelimiters('#','')
561     elseif a:filetype ==? "robots"
562         call s:MapDelimiters('#', '')
563     elseif a:filetype ==? "sa"
564         call s:MapDelimiters('--','')
565     elseif a:filetype ==? "samba"
566         call s:MapDelimitersWithAlternative(';','', '#', '')
567     elseif a:filetype ==? "sass"
568         call s:MapDelimitersWithAlternative('//','', '/*', '')
569     elseif a:filetype ==? "sather"
570         call s:MapDelimiters('--', '')
571     elseif a:filetype ==? "scala"
572         call s:MapDelimitersWithAlternative('//','', '/*','*/')
573     elseif a:filetype ==? "scilab"
574         call s:MapDelimiters('//', '')
575     elseif a:filetype ==? "scsh"
576         call s:MapDelimiters(';', '')
577     elseif a:filetype ==? "sed"
578         call s:MapDelimiters('#', '')
579     elseif a:filetype ==? "sgmldecl"
580         call s:MapDelimiters('--','--')
581     elseif a:filetype ==? "sgmllnx"
582         call s:MapDelimiters('<!--','-->')
583     elseif a:filetype ==? "sicad"
584         call s:MapDelimiters('*', '')
585     elseif a:filetype ==? "simula"
586         call s:MapDelimitersWithAlternative('%', '', '--', '')
587     elseif a:filetype ==? "sinda"
588         call s:MapDelimiters('$', '')
589     elseif a:filetype ==? "skill"
590         call s:MapDelimiters(';', '')
591     elseif a:filetype ==? "slang"
592         call s:MapDelimiters('%', '')
593     elseif a:filetype ==? "slice"
594         call s:MapDelimitersWithAlternative('//','', '/*','*/')
595     elseif a:filetype ==? "slrnrc"
596         call s:MapDelimiters('%', '')
597     elseif a:filetype ==? "sm"
598         call s:MapDelimiters('#', '')
599     elseif a:filetype ==? "smarty"
600         call s:MapDelimiters('{*', '*}')
601     elseif a:filetype ==? "smil"
602         call s:MapDelimiters('<!','>')
603     elseif a:filetype ==? "smith"
604         call s:MapDelimiters(';', '')
605     elseif a:filetype ==? "sml"
606         call s:MapDelimiters('(*','*)')
607     elseif a:filetype ==? "snnsnet"
608         call s:MapDelimiters('#', '')
609     elseif a:filetype ==? "snnspat"
610         call s:MapDelimiters('#', '')
611     elseif a:filetype ==? "snnsres"
612         call s:MapDelimiters('#', '')
613     elseif a:filetype ==? "snobol4"
614         call s:MapDelimiters('*', '')
615     elseif a:filetype ==? "spec"
616         call s:MapDelimiters('#', '')
617     elseif a:filetype ==? "specman"
618         call s:MapDelimiters('//', '')
619     elseif a:filetype ==? "spectre"
620         call s:MapDelimitersWithAlternative('//', '', '*', '')
621     elseif a:filetype ==? "spice"
622         call s:MapDelimiters('$', '')
623     elseif a:filetype ==? "sql"
624         call s:MapDelimiters('--', '')
625     elseif a:filetype ==? "sqlforms"
626         call s:MapDelimiters('--', '')
627     elseif a:filetype ==? "sqlj"
628         call s:MapDelimiters('--', '')
629     elseif a:filetype ==? "sqr"
630         call s:MapDelimiters('!', '')
631     elseif a:filetype ==? "squid"
632         call s:MapDelimiters('#', '')
633     elseif a:filetype ==? "st"
634         call s:MapDelimiters('"','')
635     elseif a:filetype ==? "stp"
636         call s:MapDelimiters('--', '')
637     elseif a:filetype ==? "systemverilog"
638         call s:MapDelimitersWithAlternative('//','', '/*','*/')
639     elseif a:filetype ==? "tads"
640         call s:MapDelimitersWithAlternative('//','', '/*','*/')
641     elseif a:filetype ==? "tags"
642         call s:MapDelimiters(';', '')
643     elseif a:filetype ==? "tak"
644         call s:MapDelimiters('$', '')
645     elseif a:filetype ==? "tasm"
646         call s:MapDelimiters(';', '')
647     elseif a:filetype ==? "tcl"
648         call s:MapDelimiters('#','')
649     elseif a:filetype ==? "texinfo"
650         call s:MapDelimiters("@c ", "")
651     elseif a:filetype ==? "texmf"
652         call s:MapDelimiters('%', '')
653     elseif a:filetype ==? "tf"
654         call s:MapDelimiters(';', '')
655     elseif a:filetype ==? "tidy"
656         call s:MapDelimiters('#', '')
657     elseif a:filetype ==? "tli"
658         call s:MapDelimiters('#', '')
659     elseif a:filetype ==? "trasys"
660         call s:MapDelimiters("$", "")
661     elseif a:filetype ==? "tsalt"
662         call s:MapDelimitersWithAlternative('//','', '/*','*/')
663     elseif a:filetype ==? "tsscl"
664         call s:MapDelimiters('#', '')
665     elseif a:filetype ==? "tssgm"
666         call s:MapDelimiters("comment = '","'")
667     elseif a:filetype ==? "txt2tags"
668         call s:MapDelimiters('%','')
669     elseif a:filetype ==? "uc"
670         call s:MapDelimitersWithAlternative('//','', '/*','*/')
671     elseif a:filetype ==? "uil"
672         call s:MapDelimiters('!', '')
673     elseif a:filetype ==? "vb"
674         call s:MapDelimiters("'","")
675     elseif a:filetype ==? "velocity"
676         call s:MapDelimitersWithAlternative("##","", '#*', '*#')
677     elseif a:filetype ==? "vera"
678         call s:MapDelimitersWithAlternative('/*','*/','//','')
679     elseif a:filetype ==? "verilog"
680         call s:MapDelimitersWithAlternative('//','', '/*','*/')
681     elseif a:filetype ==? "verilog_systemverilog"
682         call s:MapDelimitersWithAlternative('//','', '/*','*/')
683     elseif a:filetype ==? "vgrindefs"
684         call s:MapDelimiters('#', '')
685     elseif a:filetype ==? "vhdl"
686         call s:MapDelimiters('--', '')
687     elseif a:filetype ==? "vimperator"
688         call s:MapDelimiters('"','')
689     elseif a:filetype ==? "virata"
690         call s:MapDelimiters('%', '')
691     elseif a:filetype ==? "vrml"
692         call s:MapDelimiters('#', '')
693     elseif a:filetype ==? "vsejcl"
694         call s:MapDelimiters('/*', '')
695     elseif a:filetype ==? "webmacro"
696         call s:MapDelimiters('##', '')
697     elseif a:filetype ==? "wget"
698         call s:MapDelimiters('#', '')
699     elseif a:filetype ==? "Wikipedia"
700         call s:MapDelimiters('<!--','-->')
701     elseif a:filetype ==? "winbatch"
702         call s:MapDelimiters(';', '')
703     elseif a:filetype ==? "wml"
704         call s:MapDelimiters('#', '')
705     elseif a:filetype ==? "wvdial"
706         call s:MapDelimiters(';', '')
707     elseif a:filetype ==? "xdefaults"
708         call s:MapDelimiters('!', '')
709     elseif a:filetype ==? "xkb"
710         call s:MapDelimiters('//', '')
711     elseif a:filetype ==? "xmath"
712         call s:MapDelimiters('#', '')
713     elseif a:filetype ==? "xpm2"
714         call s:MapDelimiters('!', '')
715     elseif a:filetype ==? "xquery"
716         call s:MapDelimiters('(:',':)')
717     elseif a:filetype ==? "z8a"
718         call s:MapDelimiters(';', '')
719
720     else
721
722         "extract the delims from &commentstring
723         let left= substitute(&commentstring, '\([^ \t]*\)\s*%s.*', '\1', '')
724         let right= substitute(&commentstring, '.*%s\s*\(.*\)', '\1', 'g')
725         call s:MapDelimiters(left,right)
726
727     endif
728 endfunction
729
730 " Function: s:MapDelimiters(left, right) function {{{2
731 " This function is a wrapper for s:MapDelimiters(left, right, leftAlt, rightAlt, useAlt) and is called when there
732 " is no alternative comment delimiters for the current filetype
733 "
734 " Args:
735 "   -left: the left comment delimiter
736 "   -right: the right comment delimiter
737 function s:MapDelimiters(left, right)
738     call s:MapDelimitersWithAlternative(a:left, a:right, "", "")
739 endfunction
740
741 " Function: s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt) function {{{2
742 " this function sets up the comment delimiter buffer variables
743 "
744 " Args:
745 "   -left:  the string defining the comment start delimiter
746 "   -right: the string defining the comment end delimiter
747 "   -leftAlt:  the string for the alternative comment style defining the comment start delimiter
748 "   -rightAlt: the string for the alternative comment style defining the comment end delimiter
749 function s:MapDelimitersWithAlternative(left, right, leftAlt, rightAlt)
750     if !exists('g:NERD_' . &filetype . '_alt_style')
751         let b:NERDLeft = a:left
752         let b:NERDRight = a:right
753         let b:NERDLeftAlt = a:leftAlt
754         let b:NERDRightAlt = a:rightAlt
755     else
756         let b:NERDLeft = a:leftAlt
757         let b:NERDRight = a:rightAlt
758         let b:NERDLeftAlt = a:left
759         let b:NERDRightAlt = a:right
760     endif
761 endfunction
762
763 " Function: s:SwitchToAlternativeDelimiters(printMsgs) function {{{2
764 " This function is used to swap the delimiters that are being used to the
765 " alternative delimiters for that filetype. For example, if a c++ file is
766 " being edited and // comments are being used, after this function is called
767 " /**/ comments will be used.
768 "
769 " Args:
770 "   -printMsgs: if this is 1 then a message is echoed to the user telling them
771 "    if this function changed the delimiters or not
772 function s:SwitchToAlternativeDelimiters(printMsgs)
773     "if both of the alternative delimiters are empty then there is no
774     "alternative comment style so bail out
775     if b:NERDLeftAlt == "" && b:NERDRightAlt == ""
776         if a:printMsgs
777             call s:NerdEcho("Cannot use alternative delimiters, none are specified", 0)
778         endif
779         return 0
780     endif
781
782     "save the current delimiters
783     let tempLeft = b:NERDLeft
784     let tempRight = b:NERDRight
785
786     "swap current delimiters for alternative
787     let b:NERDLeft = b:NERDLeftAlt
788     let b:NERDRight = b:NERDRightAlt
789
790     "set the previously current delimiters to be the new alternative ones
791     let b:NERDLeftAlt = tempLeft
792     let b:NERDRightAlt = tempRight
793
794     "tell the user what comment delimiters they are now using
795     if a:printMsgs
796         let leftNoEsc = b:NERDLeft
797         let rightNoEsc = b:NERDRight
798         call s:NerdEcho("Now using " . leftNoEsc . " " . rightNoEsc . " to delimit comments", 1)
799     endif
800
801     return 1
802 endfunction
803
804 " Section: Comment delimiter add/removal functions {{{1
805 " ============================================================================
806 " Function: s:AppendCommentToLine(){{{2
807 " This function appends comment delimiters at the EOL and places the cursor in
808 " position to start typing the comment
809 function s:AppendCommentToLine()
810     let left = s:GetLeft(0,1,0)
811     let right = s:GetRight(0,1,0)
812
813     " get the len of the right delim
814     let lenRight = strlen(right)
815
816     let isLineEmpty = strlen(getline(".")) == 0
817     let insOrApp = (isLineEmpty==1 ? 'i' : 'A')
818
819     "stick the delimiters down at the end of the line. We have to format the
820     "comment with spaces as appropriate
821     execute ":normal! " . insOrApp . (isLineEmpty ? '' : ' ') . left . right . " "
822
823     " if there is a right delimiter then we gotta move the cursor left
824     " by the len of the right delimiter so we insert between the delimiters
825     if lenRight > 0
826         let leftMoveAmount = lenRight
827         execute ":normal! " . leftMoveAmount . "h"
828     endif
829     startinsert
830 endfunction
831
832 " Function: s:CommentBlock(top, bottom, lSide, rSide, forceNested ) {{{2
833 " This function is used to comment out a region of code. This region is
834 " specified as a bounding box by arguments to the function.
835 "
836 " Args:
837 "   -top: the line number for the top line of code in the region
838 "   -bottom: the line number for the bottom line of code in the region
839 "   -lSide: the column number for the left most column in the region
840 "   -rSide: the column number for the right most column in the region
841 "   -forceNested: a flag indicating whether comments should be nested
842 function s:CommentBlock(top, bottom, lSide, rSide, forceNested )
843     " we need to create local copies of these arguments so we can modify them
844     let top = a:top
845     let bottom = a:bottom
846     let lSide = a:lSide
847     let rSide = a:rSide
848
849     "if the top or bottom line starts with tabs we have to adjust the left and
850     "right boundaries so that they are set as though the tabs were spaces
851     let topline = getline(top)
852     let bottomline = getline(bottom)
853     if s:HasLeadingTabs(topline, bottomline)
854
855         "find out how many tabs are in the top line and adjust the left
856         "boundary accordingly
857         let numTabs = s:NumberOfLeadingTabs(topline)
858         if lSide < numTabs
859             let lSide = &ts * lSide
860         else
861             let lSide = (lSide - numTabs) + (&ts * numTabs)
862         endif
863
864         "find out how many tabs are in the bottom line and adjust the right
865         "boundary accordingly
866         let numTabs = s:NumberOfLeadingTabs(bottomline)
867         let rSide = (rSide - numTabs) + (&ts * numTabs)
868     endif
869
870     "we must check that bottom IS actually below top, if it is not then we
871     "swap top and bottom. Similarly for left and right.
872     if bottom < top
873         let temp = top
874         let top = bottom
875         let bottom = top
876     endif
877     if rSide < lSide
878         let temp = lSide
879         let lSide = rSide
880         let rSide = temp
881     endif
882
883     "if the current delimiters arent multipart then we will switch to the
884     "alternative delims (if THEY are) as the comment will be better and more
885     "accurate with multipart delims
886     let switchedDelims = 0
887     if !s:Multipart() && g:NERDAllowAnyVisualDelims && s:AltMultipart()
888         let switchedDelims = 1
889         call s:SwitchToAlternativeDelimiters(0)
890     endif
891
892     "start the commenting from the top and keep commenting till we reach the
893     "bottom
894     let currentLine=top
895     while currentLine <= bottom
896
897         "check if we are allowed to comment this line
898         if s:CanCommentLine(a:forceNested, currentLine)
899
900             "convert the leading tabs into spaces
901             let theLine = getline(currentLine)
902             let lineHasLeadTabs = s:HasLeadingTabs(theLine)
903             if lineHasLeadTabs
904                 let theLine = s:ConvertLeadingTabsToSpaces(theLine)
905             endif
906
907             "dont comment lines that begin after the right boundary of the
908             "block unless the user has specified to do so
909             if theLine !~ '^ \{' . rSide . '\}' || !g:NERDBlockComIgnoreEmpty
910
911                 "attempt to place the cursor in on the left of the boundary box,
912                 "then check if we were successful, if not then we cant comment this
913                 "line
914                 call setline(currentLine, theLine)
915                 if s:CanPlaceCursor(currentLine, lSide)
916
917                     let leftSpaced = s:GetLeft(0,1,0)
918                     let rightSpaced = s:GetRight(0,1,0)
919
920                     "stick the left delimiter down
921                     let theLine = strpart(theLine, 0, lSide-1) . leftSpaced . strpart(theLine, lSide-1)
922
923                     if s:Multipart()
924                         "stick the right delimiter down
925                         let theLine = strpart(theLine, 0, rSide+strlen(leftSpaced)) . rightSpaced . strpart(theLine, rSide+strlen(leftSpaced))
926
927                         let firstLeftDelim = s:FindDelimiterIndex(b:NERDLeft, theLine)
928                         let lastRightDelim = s:LastIndexOfDelim(b:NERDRight, theLine)
929
930                         if firstLeftDelim != -1 && lastRightDelim != -1
931                             let searchStr = strpart(theLine, 0, lastRightDelim)
932                             let searchStr = strpart(searchStr, firstLeftDelim+strlen(b:NERDLeft))
933
934                             "replace the outter most delims in searchStr with
935                             "place-holders
936                             let theLineWithPlaceHolders = s:ReplaceDelims(b:NERDLeft, b:NERDRight, g:NERDLPlace, g:NERDRPlace, searchStr)
937
938                             "add the right delimiter onto the line
939                             let theLine = strpart(theLine, 0, firstLeftDelim+strlen(b:NERDLeft)) . theLineWithPlaceHolders . strpart(theLine, lastRightDelim)
940                         endif
941                     endif
942                 endif
943             endif
944
945             "restore tabs if needed
946             if lineHasLeadTabs
947                 let theLine = s:ConvertLeadingSpacesToTabs(theLine)
948             endif
949
950             call setline(currentLine, theLine)
951         endif
952
953         let currentLine = currentLine + 1
954     endwhile
955
956     "if we switched delims then we gotta go back to what they were before
957     if switchedDelims == 1
958         call s:SwitchToAlternativeDelimiters(0)
959     endif
960 endfunction
961
962 " Function: s:CommentLines(forceNested, alignLeft, alignRight, firstLine, lastLine) {{{2
963 " This function comments a range of lines.
964 "
965 " Args:
966 "   -forceNested: a flag indicating whether the called is requesting the comment
967 "    to be nested if need be
968 "   -align: should be "left" or "both" or "none"
969 "   -firstLine/lastLine: the top and bottom lines to comment
970 function s:CommentLines(forceNested, align, firstLine, lastLine)
971     " we need to get the left and right indexes of the leftmost char in the
972     " block of of lines and the right most char so that we can do alignment of
973     " the delimiters if the user has specified
974     let leftAlignIndx = s:LeftMostIndx(a:forceNested, 0, a:firstLine, a:lastLine)
975     let rightAlignIndx = s:RightMostIndx(a:forceNested, 0, a:firstLine, a:lastLine)
976
977     " gotta add the length of the left delimiter onto the rightAlignIndx cos
978     " we'll be adding a left delim to the line
979     let rightAlignIndx = rightAlignIndx + strlen(s:GetLeft(0,1,0))
980
981     " now we actually comment the lines. Do it line by line
982     let currentLine = a:firstLine
983     while currentLine <= a:lastLine
984
985         " get the next line, check commentability and convert spaces to tabs
986         let theLine = getline(currentLine)
987         let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
988         let theLine = s:ConvertLeadingTabsToSpaces(theLine)
989         if s:CanCommentLine(a:forceNested, currentLine)
990             "if the user has specified forceNesting then we check to see if we
991             "need to switch delimiters for place-holders
992             if a:forceNested && g:NERDUsePlaceHolders
993                 let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
994             endif
995
996             " find out if the line is commented using normal delims and/or
997             " alternate ones
998             let isCommented = s:IsCommented(b:NERDLeft, b:NERDRight, theLine) || s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine)
999
1000             " check if we can comment this line
1001             if !isCommented || g:NERDUsePlaceHolders || s:Multipart()
1002                 if a:align == "left" || a:align == "both"
1003                     let theLine = s:AddLeftDelimAligned(s:GetLeft(0,1,0), theLine, leftAlignIndx)
1004                 else
1005                     let theLine = s:AddLeftDelim(s:GetLeft(0,1,0), theLine)
1006                 endif
1007                 if a:align == "both"
1008                     let theLine = s:AddRightDelimAligned(s:GetRight(0,1,0), theLine, rightAlignIndx)
1009                 else
1010                     let theLine = s:AddRightDelim(s:GetRight(0,1,0), theLine)
1011                 endif
1012             endif
1013         endif
1014
1015         " restore leading tabs if appropriate
1016         if lineHasLeadingTabs
1017             let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1018         endif
1019
1020         " we are done with this line
1021         call setline(currentLine, theLine)
1022         let currentLine = currentLine + 1
1023     endwhile
1024
1025 endfunction
1026
1027 " Function: s:CommentLinesMinimal(firstLine, lastLine) {{{2
1028 " This function comments a range of lines in a minimal style. I
1029 "
1030 " Args:
1031 "   -firstLine/lastLine: the top and bottom lines to comment
1032 function s:CommentLinesMinimal(firstLine, lastLine)
1033     "check that minimal comments can be done on this filetype
1034     if !s:HasMultipartDelims()
1035         throw 'NERDCommenter.Delimiters exception: Minimal comments can only be used for filetypes that have multipart delimiters'
1036     endif
1037
1038     "if we need to use place holders for the comment, make sure they are
1039     "enabled for this filetype
1040     if !g:NERDUsePlaceHolders && s:DoesBlockHaveMultipartDelim(a:firstLine, a:lastLine)
1041         throw 'NERDCommenter.Settings exception: Placeoholders are required but disabled.'
1042     endif
1043
1044     "get the left and right delims to smack on
1045     let left = s:GetSexyComLeft(g:NERDSpaceDelims,0)
1046     let right = s:GetSexyComRight(g:NERDSpaceDelims,0)
1047
1048     "make sure all multipart delims on the lines are replaced with
1049     "placeholders to prevent illegal syntax
1050     let currentLine = a:firstLine
1051     while(currentLine <= a:lastLine)
1052         let theLine = getline(currentLine)
1053         let theLine = s:ReplaceDelims(left, right, g:NERDLPlace, g:NERDRPlace, theLine)
1054         call setline(currentLine, theLine)
1055         let currentLine = currentLine + 1
1056     endwhile
1057
1058     "add the delim to the top line
1059     let theLine = getline(a:firstLine)
1060     let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
1061     let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1062     let theLine = s:AddLeftDelim(left, theLine)
1063     if lineHasLeadingTabs
1064         let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1065     endif
1066     call setline(a:firstLine, theLine)
1067
1068     "add the delim to the bottom line
1069     let theLine = getline(a:lastLine)
1070     let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
1071     let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1072     let theLine = s:AddRightDelim(right, theLine)
1073     if lineHasLeadingTabs
1074         let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1075     endif
1076     call setline(a:lastLine, theLine)
1077 endfunction
1078
1079 " Function: s:CommentLinesSexy(topline, bottomline) function {{{2
1080 " This function is used to comment lines in the 'Sexy' style. eg in c:
1081 " /*
1082 "  * This is a sexy comment
1083 "  */
1084 " Args:
1085 "   -topline: the line num of the top line in the sexy comment
1086 "   -bottomline: the line num of the bottom line in the sexy comment
1087 function s:CommentLinesSexy(topline, bottomline)
1088     let left = s:GetSexyComLeft(0, 0)
1089     let right = s:GetSexyComRight(0, 0)
1090
1091     "check if we can do a sexy comment with the available delimiters
1092     if left == -1 || right == -1
1093         throw 'NERDCommenter.Delimiters exception: cannot perform sexy comments with available delimiters.'
1094     endif
1095
1096     "make sure the lines arent already commented sexually
1097     if !s:CanSexyCommentLines(a:topline, a:bottomline)
1098         throw 'NERDCommenter.Nesting exception: cannot nest sexy comments'
1099     endif
1100
1101
1102     let sexyComMarker = s:GetSexyComMarker(0,0)
1103     let sexyComMarkerSpaced = s:GetSexyComMarker(1,0)
1104
1105
1106     " we jam the comment as far to the right as possible
1107     let leftAlignIndx = s:LeftMostIndx(1, 1, a:topline, a:bottomline)
1108
1109     "check if we should use the compact style i.e that the left/right
1110     "delimiters should appear on the first and last lines of the code and not
1111     "on separate lines above/below the first/last lines of code
1112     if g:NERDCompactSexyComs
1113         let spaceString = (g:NERDSpaceDelims ? s:spaceStr : '')
1114
1115         "comment the top line
1116         let theLine = getline(a:topline)
1117         let lineHasTabs = s:HasLeadingTabs(theLine)
1118         if lineHasTabs
1119             let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1120         endif
1121         let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
1122         let theLine = s:AddLeftDelimAligned(left . spaceString, theLine, leftAlignIndx)
1123         if lineHasTabs
1124             let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1125         endif
1126         call setline(a:topline, theLine)
1127
1128         "comment the bottom line
1129         if a:bottomline != a:topline
1130             let theLine = getline(a:bottomline)
1131             let lineHasTabs = s:HasLeadingTabs(theLine)
1132             if lineHasTabs
1133                 let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1134             endif
1135             let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
1136         endif
1137         let theLine = s:AddRightDelim(spaceString . right, theLine)
1138         if lineHasTabs
1139             let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1140         endif
1141         call setline(a:bottomline, theLine)
1142     else
1143
1144         " add the left delimiter one line above the lines that are to be commented
1145         call cursor(a:topline, 1)
1146         execute 'normal! O'
1147         call setline(a:topline, repeat(' ', leftAlignIndx) . left )
1148
1149         " add the right delimiter after bottom line (we have to add 1 cos we moved
1150         " the lines down when we added the left delim
1151         call cursor(a:bottomline+1, 1)
1152         execute 'normal! o'
1153         call setline(a:bottomline+2, repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . right )
1154
1155     endif
1156
1157     " go thru each line adding the sexyComMarker marker to the start of each
1158     " line in the appropriate place to align them with the comment delims
1159     let currentLine = a:topline+1
1160     while currentLine <= a:bottomline + !g:NERDCompactSexyComs
1161         " get the line and convert the tabs to spaces
1162         let theLine = getline(currentLine)
1163         let lineHasTabs = s:HasLeadingTabs(theLine)
1164         if lineHasTabs
1165             let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1166         endif
1167
1168         let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
1169
1170         " add the sexyComMarker
1171         let theLine = repeat(' ', leftAlignIndx) . repeat(' ', strlen(left)-strlen(sexyComMarker)) . sexyComMarkerSpaced . strpart(theLine, leftAlignIndx)
1172
1173         if lineHasTabs
1174             let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1175         endif
1176
1177
1178         " set the line and move onto the next one
1179         call setline(currentLine, theLine)
1180         let currentLine = currentLine + 1
1181     endwhile
1182
1183 endfunction
1184
1185 " Function: s:CommentLinesToggle(forceNested, firstLine, lastLine) {{{2
1186 " Applies "toggle" commenting to the given range of lines
1187 "
1188 " Args:
1189 "   -forceNested: a flag indicating whether the called is requesting the comment
1190 "    to be nested if need be
1191 "   -firstLine/lastLine: the top and bottom lines to comment
1192 function s:CommentLinesToggle(forceNested, firstLine, lastLine)
1193     let currentLine = a:firstLine
1194     while currentLine <= a:lastLine
1195
1196         " get the next line, check commentability and convert spaces to tabs
1197         let theLine = getline(currentLine)
1198         let lineHasLeadingTabs = s:HasLeadingTabs(theLine)
1199         let theLine = s:ConvertLeadingTabsToSpaces(theLine)
1200         if s:CanToggleCommentLine(a:forceNested, currentLine)
1201
1202             "if the user has specified forceNesting then we check to see if we
1203             "need to switch delimiters for place-holders
1204             if g:NERDUsePlaceHolders
1205                 let theLine = s:SwapOutterMultiPartDelimsForPlaceHolders(theLine)
1206             endif
1207
1208             let theLine = s:AddLeftDelim(s:GetLeft(0, 1, 0), theLine)
1209             let theLine = s:AddRightDelim(s:GetRight(0, 1, 0), theLine)
1210         endif
1211
1212         " restore leading tabs if appropriate
1213         if lineHasLeadingTabs
1214             let theLine = s:ConvertLeadingSpacesToTabs(theLine)
1215         endif
1216
1217         " we are done with this line
1218         call setline(currentLine, theLine)
1219         let currentLine = currentLine + 1
1220     endwhile
1221
1222 endfunction
1223
1224 " Function: s:CommentRegion(topline, topCol, bottomLine, bottomCol) function {{{2
1225 " This function comments chunks of text selected in visual mode.
1226 " It will comment exactly the text that they have selected.
1227 " Args:
1228 "   -topLine: the line num of the top line in the sexy comment
1229 "   -topCol: top left col for this comment
1230 "   -bottomline: the line num of the bottom line in the sexy comment
1231 "   -bottomCol: the bottom right col for this comment
1232 "   -forceNested: whether the caller wants comments to be nested if the
1233 "    line(s) are already commented
1234 function s:CommentRegion(topLine, topCol, bottomLine, bottomCol, forceNested)
1235
1236     "switch delims (if we can) if the current set isnt multipart
1237     let switchedDelims = 0
1238     if !s:Multipart() && s:AltMultipart() && !g:NERDAllowAnyVisualDelims
1239         let switchedDelims = 1
1240         call s:SwitchToAlternativeDelimiters(0)
1241     endif
1242
1243     "if there is only one line in the comment then just do it
1244     if a:topLine == a:bottomLine
1245         call s:CommentBlock(a:topLine, a:bottomLine, a:topCol, a:bottomCol, a:forceNested)
1246
1247     "there are multiple lines in the comment
1248     else
1249         "comment the top line
1250         call s:CommentBlock(a:topLine, a:topLine, a:topCol, strlen(getline(a:topLine)), a:forceNested)
1251
1252         "comment out all the lines in the middle of the comment
1253         let topOfRange = a:topLine+1
1254         let bottomOfRange = a:bottomLine-1
1255         if topOfRange <= bottomOfRange
1256             call s:CommentLines(a:forceNested, "none", topOfRange, bottomOfRange)
1257         endif
1258
1259         "comment the bottom line
1260         let bottom = getline(a:bottomLine)
1261         let numLeadingSpacesTabs = strlen(substitute(bottom, '^\([ \t]*\).*$', '\1', ''))
1262         call s:CommentBlock(a:bottomLine, a:bottomLine, numLeadingSpacesTabs+1, a:bottomCol, a:forceNested)
1263
1264     endif
1265
1266     "stick the cursor back on the char it was on before the comment
1267     call cursor(a:topLine, a:topCol + strlen(b:NERDLeft) + g:NERDSpaceDelims)
1268
1269     "if we switched delims then we gotta go back to what they were before
1270     if switchedDelims == 1
1271         call s:SwitchToAlternativeDelimiters(0)
1272     endif
1273
1274 endfunction
1275
1276 " Function: s:InvertComment(firstLine, lastLine) function {{{2
1277 " Inverts the comments on the lines between and including the given line
1278 " numbers i.e all commented lines are uncommented and vice versa
1279 " Args:
1280 "   -firstLine: the top of the range of lines to be inverted
1281 "   -lastLine: the bottom of the range of lines to be inverted
1282 function s:InvertComment(firstLine, lastLine)
1283
1284     " go thru all lines in the given range
1285     let currentLine = a:firstLine
1286     while currentLine <= a:lastLine
1287         let theLine = getline(currentLine)
1288
1289         let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine)
1290
1291         " if the line is commented normally, uncomment it
1292         if s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine)
1293             call s:UncommentLines(currentLine, currentLine)
1294             let currentLine = currentLine + 1
1295
1296         " check if the line is commented sexually
1297         elseif !empty(sexyComBounds)
1298             let numLinesBeforeSexyComRemoved = s:NumLinesInBuf()
1299             call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1])
1300
1301             "move to the line after last line of the sexy comment
1302             let numLinesAfterSexyComRemoved = s:NumLinesInBuf()
1303             let currentLine = bottomBound - (numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved) + 1
1304
1305         " the line isnt commented
1306         else
1307             call s:CommentLinesToggle(1, currentLine, currentLine)
1308             let currentLine = currentLine + 1
1309         endif
1310
1311     endwhile
1312 endfunction
1313
1314 " Function: NERDComment(isVisual, type) function {{{2
1315 " This function is a Wrapper for the main commenting functions
1316 "
1317 " Args:
1318 "   -isVisual: a flag indicating whether the comment is requested in visual
1319 "    mode or not
1320 "   -type: the type of commenting requested. Can be 'sexy', 'invert',
1321 "    'minimal', 'toggle', 'alignLeft', 'alignBoth', 'norm',
1322 "    'nested', 'toEOL', 'append', 'insert', 'uncomment', 'yank'
1323 function! NERDComment(isVisual, type) range
1324     " we want case sensitivity when commenting
1325     let oldIgnoreCase = &ignorecase
1326     set noignorecase
1327
1328     if a:isVisual
1329         let firstLine = line("'<")
1330         let lastLine = line("'>")
1331         let firstCol = col("'<")
1332         let lastCol = col("'>") - (&selection == 'exclusive' ? 1 : 0)
1333     else
1334         let firstLine = a:firstline
1335         let lastLine = a:lastline
1336     endif
1337
1338     let countWasGiven = (a:isVisual == 0 && firstLine != lastLine)
1339
1340     let forceNested = (a:type == 'nested' || g:NERDDefaultNesting)
1341
1342     if a:type == 'norm' || a:type == 'nested'
1343         if a:isVisual && visualmode() == "\16"
1344             call s:CommentBlock(firstLine, lastLine, firstCol, lastCol, forceNested)
1345         elseif a:isVisual && visualmode() == "v" && (g:NERDCommentWholeLinesInVMode==0 || (g:NERDCommentWholeLinesInVMode==2 && s:HasMultipartDelims()))
1346             call s:CommentRegion(firstLine, firstCol, lastLine, lastCol, forceNested)
1347         else
1348             call s:CommentLines(forceNested, "none", firstLine, lastLine)
1349         endif
1350
1351     elseif a:type == 'alignLeft' || a:type == 'alignBoth'
1352         let align = "none"
1353         if a:type == "alignLeft"
1354             let align = "left"
1355         elseif a:type == "alignBoth"
1356             let align = "both"
1357         endif
1358         call s:CommentLines(forceNested, align, firstLine, lastLine)
1359
1360     elseif a:type == 'invert'
1361         call s:InvertComment(firstLine, lastLine)
1362
1363     elseif a:type == 'sexy'
1364         try
1365             call s:CommentLinesSexy(firstLine, lastLine)
1366         catch /NERDCommenter.Delimiters/
1367             call s:CommentLines(forceNested, "none", firstLine, lastLine)
1368         catch /NERDCommenter.Nesting/
1369             call s:NerdEcho("Sexy comment aborted. Nested sexy cannot be nested", 0)
1370         endtry
1371
1372     elseif a:type == 'toggle'
1373         let theLine = getline(firstLine)
1374
1375         if s:IsInSexyComment(firstLine) || s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine)
1376             call s:UncommentLines(firstLine, lastLine)
1377         else
1378             call s:CommentLinesToggle(forceNested, firstLine, lastLine)
1379         endif
1380
1381     elseif a:type == 'minimal'
1382         try
1383             call s:CommentLinesMinimal(firstLine, lastLine)
1384         catch /NERDCommenter.Delimiters/
1385             call s:NerdEcho("Minimal comments can only be used for filetypes that have multipart delimiters.", 0)
1386         catch /NERDCommenter.Settings/
1387             call s:NerdEcho("Place holders are required but disabled.", 0)
1388         endtry
1389
1390     elseif a:type == 'toEOL'
1391         call s:SaveScreenState()
1392         call s:CommentBlock(firstLine, firstLine, col("."), col("$")-1, 1)
1393         call s:RestoreScreenState()
1394
1395     elseif a:type == 'append'
1396         call s:AppendCommentToLine()
1397
1398     elseif a:type == 'insert'
1399         call s:PlaceDelimitersAndInsBetween()
1400
1401     elseif a:type == 'uncomment'
1402         call s:UncommentLines(firstLine, lastLine)
1403
1404     elseif a:type == 'yank'
1405         if a:isVisual
1406             normal! gvy
1407         elseif countWasGiven
1408             execute firstLine .','. lastLine .'yank'
1409         else
1410             normal! yy
1411         endif
1412         execute firstLine .','. lastLine .'call NERDComment('. a:isVisual .', "norm")'
1413     endif
1414
1415     let &ignorecase = oldIgnoreCase
1416 endfunction
1417
1418 " Function: s:PlaceDelimitersAndInsBetween() function {{{2
1419 " This is function is called to place comment delimiters down and place the
1420 " cursor between them
1421 function s:PlaceDelimitersAndInsBetween()
1422     " get the left and right delimiters without any escape chars in them
1423     let left = s:GetLeft(0, 1, 0)
1424     let right = s:GetRight(0, 1, 0)
1425
1426     let theLine = getline(".")
1427     let lineHasLeadTabs = s:HasLeadingTabs(theLine) || (theLine =~ '^ *$' && !&expandtab)
1428
1429     "convert tabs to spaces and adjust the cursors column to take this into
1430     "account
1431     let untabbedCol = s:UntabbedCol(theLine, col("."))
1432     call setline(line("."), s:ConvertLeadingTabsToSpaces(theLine))
1433     call cursor(line("."), untabbedCol)
1434
1435     " get the len of the right delim
1436     let lenRight = strlen(right)
1437
1438     let isDelimOnEOL = col(".") >= strlen(getline("."))
1439
1440     " if the cursor is in the first col then we gotta insert rather than
1441     " append the comment delimiters here
1442     let insOrApp = (col(".")==1 ? 'i' : 'a')
1443
1444     " place the delimiters down. We do it differently depending on whether
1445     " there is a left AND right delimiter
1446     if lenRight > 0
1447         execute ":normal! " . insOrApp . left . right
1448         execute ":normal! " . lenRight . "h"
1449     else
1450         execute ":normal! " . insOrApp . left
1451
1452         " if we are tacking the delim on the EOL then we gotta add a space
1453         " after it cos when we go out of insert mode the cursor will move back
1454         " one and the user wont be in position to type the comment.
1455         if isDelimOnEOL
1456             execute 'normal! a '
1457         endif
1458     endif
1459     normal! l
1460
1461     "if needed convert spaces back to tabs and adjust the cursors col
1462     "accordingly
1463     if lineHasLeadTabs
1464         let tabbedCol = s:TabbedCol(getline("."), col("."))
1465         call setline(line("."), s:ConvertLeadingSpacesToTabs(getline(".")))
1466         call cursor(line("."), tabbedCol)
1467     endif
1468
1469     startinsert
1470 endfunction
1471
1472 " Function: s:RemoveDelimiters(left, right, line) {{{2
1473 " this function is called to remove the first left comment delimiter and the
1474 " last right delimiter of the given line.
1475 "
1476 " The args left and right must be strings. If there is no right delimiter (as
1477 " is the case for e.g vim file comments) them the arg right should be ""
1478 "
1479 " Args:
1480 "   -left: the left comment delimiter
1481 "   -right: the right comment delimiter
1482 "   -line: the line to remove the delimiters from
1483 function s:RemoveDelimiters(left, right, line)
1484
1485     let l:left = a:left
1486     let l:right = a:right
1487     let lenLeft = strlen(left)
1488     let lenRight = strlen(right)
1489
1490     let delimsSpaced = (g:NERDSpaceDelims || g:NERDRemoveExtraSpaces)
1491
1492     let line = a:line
1493
1494     "look for the left delimiter, if we find it, remove it.
1495     let leftIndx = s:FindDelimiterIndex(a:left, line)
1496     if leftIndx != -1
1497         let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+lenLeft)
1498
1499         "if the user has specified that there is a space after the left delim
1500         "then check for the space and remove it if it is there
1501         if delimsSpaced && strpart(line, leftIndx, s:lenSpaceStr) == s:spaceStr
1502             let line = strpart(line, 0, leftIndx) . strpart(line, leftIndx+s:lenSpaceStr)
1503         endif
1504     endif
1505
1506     "look for the right delimiter, if we find it, remove it
1507     let rightIndx = s:FindDelimiterIndex(a:right, line)
1508     if rightIndx != -1
1509         let line = strpart(line, 0, rightIndx) . strpart(line, rightIndx+lenRight)
1510
1511         "if the user has specified that there is a space before the right delim
1512         "then check for the space and remove it if it is there
1513         if delimsSpaced && strpart(line, rightIndx-s:lenSpaceStr, s:lenSpaceStr) == s:spaceStr && s:Multipart()
1514             let line = strpart(line, 0, rightIndx-s:lenSpaceStr) . strpart(line, rightIndx)
1515         endif
1516     endif
1517
1518     return line
1519 endfunction
1520
1521 " Function: s:UncommentLines(topLine, bottomLine) {{{2
1522 " This function uncomments the given lines
1523 "
1524 " Args:
1525 " topLine: the top line of the visual selection to uncomment
1526 " bottomLine: the bottom line of the visual selection to uncomment
1527 function s:UncommentLines(topLine, bottomLine)
1528     "make local copies of a:firstline and a:lastline and, if need be, swap
1529     "them around if the top line is below the bottom
1530     let l:firstline = a:topLine
1531     let l:lastline = a:bottomLine
1532     if firstline > lastline
1533         let firstline = lastline
1534         let lastline = a:topLine
1535     endif
1536
1537     "go thru each line uncommenting each line removing sexy comments
1538     let currentLine = firstline
1539     while currentLine <= lastline
1540
1541         "check the current line to see if it is part of a sexy comment
1542         let sexyComBounds = s:FindBoundingLinesOfSexyCom(currentLine)
1543         if !empty(sexyComBounds)
1544
1545             "we need to store the num lines in the buf before the comment is
1546             "removed so we know how many lines were removed when the sexy com
1547             "was removed
1548             let numLinesBeforeSexyComRemoved = s:NumLinesInBuf()
1549
1550             call s:UncommentLinesSexy(sexyComBounds[0], sexyComBounds[1])
1551
1552             "move to the line after last line of the sexy comment
1553             let numLinesAfterSexyComRemoved = s:NumLinesInBuf()
1554             let numLinesRemoved = numLinesBeforeSexyComRemoved - numLinesAfterSexyComRemoved
1555             let currentLine = sexyComBounds[1] - numLinesRemoved + 1
1556             let lastline = lastline - numLinesRemoved
1557
1558         "no sexy com was detected so uncomment the line as normal
1559         else
1560             call s:UncommentLinesNormal(currentLine, currentLine)
1561             let currentLine = currentLine + 1
1562         endif
1563     endwhile
1564
1565 endfunction
1566
1567 " Function: s:UncommentLinesSexy(topline, bottomline) {{{2
1568 " This function removes all the comment characters associated with the sexy
1569 " comment spanning the given lines
1570 " Args:
1571 "   -topline/bottomline: the top/bottom lines of the sexy comment
1572 function s:UncommentLinesSexy(topline, bottomline)
1573     let left = s:GetSexyComLeft(0,1)
1574     let right = s:GetSexyComRight(0,1)
1575
1576
1577     "check if it is even possible for sexy comments to exist with the
1578     "available delimiters
1579     if left == -1 || right == -1
1580         throw 'NERDCommenter.Delimiters exception: cannot uncomment sexy comments with available delimiters.'
1581     endif
1582
1583     let leftUnEsc = s:GetSexyComLeft(0,0)
1584     let rightUnEsc = s:GetSexyComRight(0,0)
1585
1586     let sexyComMarker = s:GetSexyComMarker(0, 1)
1587     let sexyComMarkerUnEsc = s:GetSexyComMarker(0, 0)
1588
1589     "the markerOffset is how far right we need to move the sexyComMarker to
1590     "line it up with the end of the left delim
1591     let markerOffset = strlen(leftUnEsc)-strlen(sexyComMarkerUnEsc)
1592
1593     " go thru the intermediate lines of the sexy comment and remove the
1594     " sexy comment markers (eg the '*'s on the start of line in a c sexy
1595     " comment)
1596     let currentLine = a:topline+1
1597     while currentLine < a:bottomline
1598         let theLine = getline(currentLine)
1599
1600         " remove the sexy comment marker from the line. We also remove the
1601         " space after it if there is one and if appropriate options are set
1602         let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc)
1603         if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr  && g:NERDSpaceDelims
1604             let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr)
1605         else
1606             let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc))
1607         endif
1608
1609         let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
1610
1611         let theLine = s:ConvertLeadingWhiteSpace(theLine)
1612
1613         " move onto the next line
1614         call setline(currentLine, theLine)
1615         let currentLine = currentLine + 1
1616     endwhile
1617
1618     " gotta make a copy of a:bottomline cos we modify the position of the
1619     " last line  it if we remove the topline
1620     let bottomline = a:bottomline
1621
1622     " get the first line so we can remove the left delim from it
1623     let theLine = getline(a:topline)
1624
1625     " if the first line contains only the left delim then just delete it
1626     if theLine =~ '^[ \t]*' . left . '[ \t]*$' && !g:NERDCompactSexyComs
1627         call cursor(a:topline, 1)
1628         normal! dd
1629         let bottomline = bottomline - 1
1630
1631     " topline contains more than just the left delim
1632     else
1633
1634         " remove the delim. If there is a space after it
1635         " then remove this too if appropriate
1636         let delimIndx = stridx(theLine, leftUnEsc)
1637         if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr && g:NERDSpaceDelims
1638             let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc)+s:lenSpaceStr)
1639         else
1640             let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(leftUnEsc))
1641         endif
1642         let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
1643         call setline(a:topline, theLine)
1644     endif
1645
1646     " get the last line so we can remove the right delim
1647     let theLine = getline(bottomline)
1648
1649     " if the bottomline contains only the right delim then just delete it
1650     if theLine =~ '^[ \t]*' . right . '[ \t]*$'
1651         call cursor(bottomline, 1)
1652         normal! dd
1653
1654     " the last line contains more than the right delim
1655     else
1656         " remove the right delim. If there is a space after it and
1657         " if the appropriate options are set then remove this too.
1658         let delimIndx = s:LastIndexOfDelim(rightUnEsc, theLine)
1659         if strpart(theLine, delimIndx+strlen(leftUnEsc), s:lenSpaceStr) == s:spaceStr  && g:NERDSpaceDelims
1660             let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc)+s:lenSpaceStr)
1661         else
1662             let theLine = strpart(theLine, 0, delimIndx) . strpart(theLine, delimIndx+strlen(rightUnEsc))
1663         endif
1664
1665         " if the last line also starts with a sexy comment marker then we
1666         " remove this as well
1667         if theLine =~ '^[ \t]*' . sexyComMarker
1668
1669             " remove the sexyComMarker. If there is a space after it then
1670             " remove that too
1671             let sexyComMarkerIndx = stridx(theLine, sexyComMarkerUnEsc)
1672             if strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc), s:lenSpaceStr) == s:spaceStr  && g:NERDSpaceDelims
1673                 let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc)+s:lenSpaceStr)
1674             else
1675                 let theLine = strpart(theLine, 0, sexyComMarkerIndx - markerOffset ) . strpart(theLine, sexyComMarkerIndx+strlen(sexyComMarkerUnEsc))
1676             endif
1677         endif
1678
1679         let theLine = s:SwapOutterPlaceHoldersForMultiPartDelims(theLine)
1680         call setline(bottomline, theLine)
1681     endif
1682 endfunction
1683
1684 " Function: s:UncommentLineNormal(line) {{{2
1685 " uncomments the given line and returns the result
1686 " Args:
1687 "   -line: the line to uncomment
1688 function s:UncommentLineNormal(line)
1689     let line = a:line
1690
1691     "get the comment status on the line so we know how it is commented
1692     let lineCommentStatus =  s:IsCommentedOuttermost(b:NERDLeft, b:NERDRight, b:NERDLeftAlt, b:NERDRightAlt, line)
1693
1694     "it is commented with b:NERDLeft and b:NERDRight so remove these delims
1695     if lineCommentStatus == 1
1696         let line = s:RemoveDelimiters(b:NERDLeft, b:NERDRight, line)
1697
1698     "it is commented with b:NERDLeftAlt and b:NERDRightAlt so remove these delims
1699     elseif lineCommentStatus == 2 && g:NERDRemoveAltComs
1700         let line = s:RemoveDelimiters(b:NERDLeftAlt, b:NERDRightAlt, line)
1701
1702     "it is not properly commented with any delims so we check if it has
1703     "any random left or right delims on it and remove the outtermost ones
1704     else
1705         "get the positions of all delim types on the line
1706         let indxLeft = s:FindDelimiterIndex(b:NERDLeft, line)
1707         let indxLeftAlt = s:FindDelimiterIndex(b:NERDLeftAlt, line)
1708         let indxRight = s:FindDelimiterIndex(b:NERDRight, line)
1709         let indxRightAlt = s:FindDelimiterIndex(b:NERDRightAlt, line)
1710
1711         "remove the outter most left comment delim
1712         if indxLeft != -1 && (indxLeft < indxLeftAlt || indxLeftAlt == -1)
1713             let line = s:RemoveDelimiters(b:NERDLeft, '', line)
1714         elseif indxLeftAlt != -1
1715             let line = s:RemoveDelimiters(b:NERDLeftAlt, '', line)
1716         endif
1717
1718         "remove the outter most right comment delim
1719         if indxRight != -1 && (indxRight < indxRightAlt || indxRightAlt == -1)
1720             let line = s:RemoveDelimiters('', b:NERDRight, line)
1721         elseif indxRightAlt != -1
1722             let line = s:RemoveDelimiters('', b:NERDRightAlt, line)
1723         endif
1724     endif
1725
1726
1727     let indxLeft = s:FindDelimiterIndex(b:NERDLeft, line)
1728     let indxLeftAlt = s:FindDelimiterIndex(b:NERDLeftAlt, line)
1729     let indxLeftPlace = s:FindDelimiterIndex(g:NERDLPlace, line)
1730
1731     let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line)
1732     let indxRightAlt = s:FindDelimiterIndex(b:NERDRightAlt, line)
1733     let indxRightPlace = s:FindDelimiterIndex(g:NERDRPlace, line)
1734
1735     let right = b:NERDRight
1736     let left = b:NERDLeft
1737     if !s:Multipart()
1738         let right = b:NERDRightAlt
1739         let left = b:NERDLeftAlt
1740     endif
1741
1742
1743     "if there are place-holders on the line then we check to see if they are
1744     "the outtermost delimiters on the line. If so then we replace them with
1745     "real delimiters
1746     if indxLeftPlace != -1
1747         if (indxLeftPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1)
1748             let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line)
1749         endif
1750     elseif indxRightPlace != -1
1751         if (indxRightPlace < indxLeft || indxLeft==-1) && (indxLeftPlace < indxLeftAlt || indxLeftAlt==-1)
1752             let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, line)
1753         endif
1754
1755     endif
1756
1757     let line = s:ConvertLeadingWhiteSpace(line)
1758
1759     return line
1760 endfunction
1761
1762 " Function: s:UncommentLinesNormal(topline, bottomline) {{{2
1763 " This function is called to uncomment lines that arent a sexy comment
1764 " Args:
1765 "   -topline/bottomline: the top/bottom line numbers of the comment
1766 function s:UncommentLinesNormal(topline, bottomline)
1767     let currentLine = a:topline
1768     while currentLine <= a:bottomline
1769         let line = getline(currentLine)
1770         call setline(currentLine, s:UncommentLineNormal(line))
1771         let currentLine = currentLine + 1
1772     endwhile
1773 endfunction
1774
1775
1776 " Section: Other helper functions {{{1
1777 " ============================================================================
1778
1779 " Function: s:AddLeftDelim(delim, theLine) {{{2
1780 " Args:
1781 function s:AddLeftDelim(delim, theLine)
1782     return substitute(a:theLine, '^\([ \t]*\)', '\1' . a:delim, '')
1783 endfunction
1784
1785 " Function: s:AddLeftDelimAligned(delim, theLine) {{{2
1786 " Args:
1787 function s:AddLeftDelimAligned(delim, theLine, alignIndx)
1788
1789     "if the line is not long enough then bung some extra spaces on the front
1790     "so we can align the delim properly
1791     let theLine = a:theLine
1792     if strlen(theLine) < a:alignIndx
1793         let theLine = repeat(' ', a:alignIndx - strlen(theLine))
1794     endif
1795
1796     return strpart(theLine, 0, a:alignIndx) . a:delim . strpart(theLine, a:alignIndx)
1797 endfunction
1798
1799 " Function: s:AddRightDelim(delim, theLine) {{{2
1800 " Args:
1801 function s:AddRightDelim(delim, theLine)
1802     if a:delim == ''
1803         return a:theLine
1804     else
1805         return substitute(a:theLine, '$', a:delim, '')
1806     endif
1807 endfunction
1808
1809 " Function: s:AddRightDelimAligned(delim, theLine, alignIndx) {{{2
1810 " Args:
1811 function s:AddRightDelimAligned(delim, theLine, alignIndx)
1812     if a:delim == ""
1813         return a:theLine
1814     else
1815
1816         " when we align the right delim we are just adding spaces
1817         " so we get a string containing the needed spaces (it
1818         " could be empty)
1819         let extraSpaces = ''
1820         let extraSpaces = repeat(' ', a:alignIndx-strlen(a:theLine))
1821
1822         " add the right delim
1823         return substitute(a:theLine, '$', extraSpaces . a:delim, '')
1824     endif
1825 endfunction
1826
1827 " Function: s:AltMultipart() {{{2
1828 " returns 1 if the alternative delims are multipart
1829 function s:AltMultipart()
1830     return b:NERDRightAlt != ''
1831 endfunction
1832
1833 " Function: s:CanCommentLine(forceNested, line) {{{2
1834 "This function is used to determine whether the given line can be commented.
1835 "It returns 1 if it can be and 0 otherwise
1836 "
1837 " Args:
1838 "   -forceNested: a flag indicating whether the caller wants comments to be nested
1839 "    if the current line is already commented
1840 "   -lineNum: the line num of the line to check for commentability
1841 function s:CanCommentLine(forceNested, lineNum)
1842     let theLine = getline(a:lineNum)
1843
1844     " make sure we don't comment lines that are just spaces or tabs or empty.
1845     if theLine =~ "^[ \t]*$"
1846         return 0
1847     endif
1848
1849     "if the line is part of a sexy comment then just flag it...
1850     if s:IsInSexyComment(a:lineNum)
1851         return 0
1852     endif
1853
1854     let isCommented = s:IsCommentedNormOrSexy(a:lineNum)
1855
1856     "if the line isnt commented return true
1857     if !isCommented
1858         return 1
1859     endif
1860
1861     "if the line is commented but nesting is allowed then return true
1862     if a:forceNested && (!s:Multipart() || g:NERDUsePlaceHolders)
1863         return 1
1864     endif
1865
1866     return 0
1867 endfunction
1868
1869 " Function: s:CanPlaceCursor(line, col) {{{2
1870 " returns 1 if the cursor can be placed exactly in the given position
1871 function s:CanPlaceCursor(line, col)
1872     let c = col(".")
1873     let l = line(".")
1874     call cursor(a:line, a:col)
1875     let success = (line(".") == a:line && col(".") == a:col)
1876     call cursor(l,c)
1877     return success
1878 endfunction
1879
1880 " Function: s:CanSexyCommentLines(topline, bottomline) {{{2
1881 " Return: 1 if the given lines can be commented sexually, 0 otherwise
1882 function s:CanSexyCommentLines(topline, bottomline)
1883     " see if the selected regions have any sexy comments
1884     let currentLine = a:topline
1885     while(currentLine <= a:bottomline)
1886         if s:IsInSexyComment(currentLine)
1887             return 0
1888         endif
1889         let currentLine = currentLine + 1
1890     endwhile
1891     return 1
1892 endfunction
1893 " Function: s:CanToggleCommentLine(forceNested, line) {{{2
1894 "This function is used to determine whether the given line can be toggle commented.
1895 "It returns 1 if it can be and 0 otherwise
1896 "
1897 " Args:
1898 "   -lineNum: the line num of the line to check for commentability
1899 function s:CanToggleCommentLine(forceNested, lineNum)
1900     let theLine = getline(a:lineNum)
1901     if (s:IsCommentedFromStartOfLine(b:NERDLeft, theLine) || s:IsCommentedFromStartOfLine(b:NERDLeftAlt, theLine)) && !a:forceNested
1902         return 0
1903     endif
1904
1905     " make sure we don't comment lines that are just spaces or tabs or empty.
1906     if theLine =~ "^[ \t]*$"
1907         return 0
1908     endif
1909
1910     "if the line is part of a sexy comment then just flag it...
1911     if s:IsInSexyComment(a:lineNum)
1912         return 0
1913     endif
1914
1915     return 1
1916 endfunction
1917
1918 " Function: s:ConvertLeadingSpacesToTabs(line) {{{2
1919 " This function takes a line and converts all leading tabs on that line into
1920 " spaces
1921 "
1922 " Args:
1923 "   -line: the line whose leading tabs will be converted
1924 function s:ConvertLeadingSpacesToTabs(line)
1925     let toReturn  = a:line
1926     while toReturn =~ '^\t*' . s:TabSpace() . '\(.*\)$'
1927         let toReturn = substitute(toReturn, '^\(\t*\)' . s:TabSpace() . '\(.*\)$'  ,  '\1\t\2' , "")
1928     endwhile
1929
1930     return toReturn
1931 endfunction
1932
1933
1934 " Function: s:ConvertLeadingTabsToSpaces(line) {{{2
1935 " This function takes a line and converts all leading spaces on that line into
1936 " tabs
1937 "
1938 " Args:
1939 "   -line: the line whose leading spaces will be converted
1940 function s:ConvertLeadingTabsToSpaces(line)
1941     let toReturn  = a:line
1942     while toReturn =~ '^\( *\)\t'
1943         let toReturn = substitute(toReturn, '^\( *\)\t',  '\1' . s:TabSpace() , "")
1944     endwhile
1945
1946     return toReturn
1947 endfunction
1948
1949 " Function: s:ConvertLeadingWhiteSpace(line) {{{2
1950 " Converts the leading white space to tabs/spaces depending on &ts
1951 "
1952 " Args:
1953 "   -line: the line to convert
1954 function s:ConvertLeadingWhiteSpace(line)
1955     let toReturn = a:line
1956     while toReturn =~ '^ *\t'
1957         let toReturn = substitute(toReturn, '^ *\zs\t\ze', s:TabSpace(), "g")
1958     endwhile
1959
1960     if !&expandtab
1961         let toReturn = s:ConvertLeadingSpacesToTabs(toReturn)
1962     endif
1963
1964     return toReturn
1965 endfunction
1966
1967
1968 " Function: s:CountNonESCedOccurances(str, searchstr, escChar) {{{2
1969 " This function counts the number of substrings contained in another string.
1970 " These substrings are only counted if they are not escaped with escChar
1971 " Args:
1972 "   -str: the string to look for searchstr in
1973 "   -searchstr: the substring to search for in str
1974 "   -escChar: the escape character which, when preceding an instance of
1975 "    searchstr, will cause it not to be counted
1976 function s:CountNonESCedOccurances(str, searchstr, escChar)
1977     "get the index of the first occurrence of searchstr
1978     let indx = stridx(a:str, a:searchstr)
1979
1980     "if there is an instance of searchstr in str process it
1981     if indx != -1
1982         "get the remainder of str after this instance of searchstr is removed
1983         let lensearchstr = strlen(a:searchstr)
1984         let strLeft = strpart(a:str, indx+lensearchstr)
1985
1986         "if this instance of searchstr is not escaped, add one to the count
1987         "and recurse. If it is escaped, just recurse
1988         if !s:IsEscaped(a:str, indx, a:escChar)
1989             return 1 + s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar)
1990         else
1991             return s:CountNonESCedOccurances(strLeft, a:searchstr, a:escChar)
1992         endif
1993     endif
1994 endfunction
1995 " Function: s:DoesBlockHaveDelim(delim, top, bottom) {{{2
1996 " Returns 1 if the given block of lines has a delimiter (a:delim) in it
1997 " Args:
1998 "   -delim: the comment delimiter to check the block for
1999 "   -top: the top line number of the block
2000 "   -bottom: the bottom line number of the block
2001 function s:DoesBlockHaveDelim(delim, top, bottom)
2002     let currentLine = a:top
2003     while currentLine < a:bottom
2004         let theline = getline(currentLine)
2005         if s:FindDelimiterIndex(a:delim, theline) != -1
2006             return 1
2007         endif
2008         let currentLine = currentLine + 1
2009     endwhile
2010     return 0
2011 endfunction
2012
2013 " Function: s:DoesBlockHaveMultipartDelim(top, bottom) {{{2
2014 " Returns 1 if the given block has a >= 1 multipart delimiter in it
2015 " Args:
2016 "   -top: the top line number of the block
2017 "   -bottom: the bottom line number of the block
2018 function s:DoesBlockHaveMultipartDelim(top, bottom)
2019     if s:HasMultipartDelims()
2020         if s:Multipart()
2021             return s:DoesBlockHaveDelim(b:NERDLeft, a:top, a:bottom) || s:DoesBlockHaveDelim(b:NERDRight, a:top, a:bottom)
2022         else
2023             return s:DoesBlockHaveDelim(b:NERDLeftAlt, a:top, a:bottom) || s:DoesBlockHaveDelim(b:NERDRightAlt, a:top, a:bottom)
2024         endif
2025     endif
2026     return 0
2027 endfunction
2028
2029
2030 " Function: s:Esc(str) {{{2
2031 " Escapes all the tricky chars in the given string
2032 function s:Esc(str)
2033     let charsToEsc = '*/\."&$+'
2034     return escape(a:str, charsToEsc)
2035 endfunction
2036
2037 " Function: s:FindDelimiterIndex(delimiter, line) {{{2
2038 " This function is used to get the string index of the input comment delimiter
2039 " on the input line. If no valid comment delimiter is found in the line then
2040 " -1 is returned
2041 " Args:
2042 "   -delimiter: the delimiter we are looking to find the index of
2043 "   -line: the line we are looking for delimiter on
2044 function s:FindDelimiterIndex(delimiter, line)
2045
2046     "make sure the delimiter isnt empty otherwise we go into an infinite loop.
2047     if a:delimiter == ""
2048         return -1
2049     endif
2050
2051
2052     let l:delimiter = a:delimiter
2053     let lenDel = strlen(l:delimiter)
2054
2055     "get the index of the first occurrence of the delimiter
2056     let delIndx = stridx(a:line, l:delimiter)
2057
2058     "keep looping thru the line till we either find a real comment delimiter
2059     "or run off the EOL
2060     while delIndx != -1
2061
2062         "if we are not off the EOL get the str before the possible delimiter
2063         "in question and check if it really is a delimiter. If it is, return
2064         "its position
2065         if delIndx != -1
2066             if s:IsDelimValid(l:delimiter, delIndx, a:line)
2067                 return delIndx
2068             endif
2069         endif
2070
2071         "we have not yet found a real comment delimiter so move past the
2072         "current one we are lookin at
2073         let restOfLine = strpart(a:line, delIndx + lenDel)
2074         let distToNextDelim = stridx(restOfLine , l:delimiter)
2075
2076         "if distToNextDelim is -1 then there is no more potential delimiters
2077         "on the line so set delIndx to -1. Otherwise, move along the line by
2078         "distToNextDelim
2079         if distToNextDelim == -1
2080             let delIndx = -1
2081         else
2082             let delIndx = delIndx + lenDel + distToNextDelim
2083         endif
2084     endwhile
2085
2086     "there is no comment delimiter on this line
2087     return -1
2088 endfunction
2089
2090 " Function: s:FindBoundingLinesOfSexyCom(lineNum) {{{2
2091 " This function takes in a line number and tests whether this line number is
2092 " the top/bottom/middle line of a sexy comment. If it is then the top/bottom
2093 " lines of the sexy comment are returned
2094 " Args:
2095 "   -lineNum: the line number that is to be tested whether it is the
2096 "    top/bottom/middle line of a sexy com
2097 " Returns:
2098 "   A string that has the top/bottom lines of the sexy comment encoded in it.
2099 "   The format is 'topline,bottomline'. If a:lineNum turns out not to be the
2100 "   top/bottom/middle of a sexy comment then -1 is returned
2101 function s:FindBoundingLinesOfSexyCom(lineNum)
2102
2103     "find which delimiters to look for as the start/end delims of the comment
2104     let left = ''
2105     let right = ''
2106     if s:Multipart()
2107         let left = s:GetLeft(0,0,1)
2108         let right = s:GetRight(0,0,1)
2109     elseif s:AltMultipart()
2110         let left = s:GetLeft(1,0,1)
2111         let right = s:GetRight(1,0,1)
2112     else
2113         return []
2114     endif
2115
2116     let sexyComMarker = s:GetSexyComMarker(0, 1)
2117
2118     "initialise the top/bottom line numbers of the sexy comment to -1
2119     let top = -1
2120     let bottom = -1
2121
2122     let currentLine = a:lineNum
2123     while top == -1 || bottom == -1
2124         let theLine = getline(currentLine)
2125
2126         "check if the current line is the top of the sexy comment
2127         if currentLine <= a:lineNum && theLine =~ '^[ \t]*' . left && theLine !~ '.*' . right && currentLine < s:NumLinesInBuf()
2128             let top = currentLine
2129             let currentLine = a:lineNum
2130
2131         "check if the current line is the bottom of the sexy comment
2132         elseif theLine =~ '^[ \t]*' . right && theLine !~ '.*' . left && currentLine > 1
2133             let bottom = currentLine
2134
2135         "the right delimiter is on the same line as the last sexyComMarker
2136         elseif theLine =~ '^[ \t]*' . sexyComMarker . '.*' . right
2137             let bottom = currentLine
2138
2139         "we have not found the top or bottom line so we assume currentLine is an
2140         "intermediate line and look to prove otherwise
2141         else
2142
2143             "if the line doesnt start with a sexyComMarker then it is not a sexy
2144             "comment
2145             if theLine !~ '^[ \t]*' . sexyComMarker
2146                 return []
2147             endif
2148
2149         endif
2150
2151         "if top is -1 then we havent found the top yet so keep looking up
2152         if top == -1
2153             let currentLine = currentLine - 1
2154         "if we have found the top line then go down looking for the bottom
2155         else
2156             let currentLine = currentLine + 1
2157         endif
2158
2159     endwhile
2160
2161     return [top, bottom]
2162 endfunction
2163
2164
2165 " Function: s:GetLeft(alt, space, esc) {{{2
2166 " returns the left/left-alternative delimiter
2167 " Args:
2168 "   -alt: specifies whether to get left or left-alternative delim
2169 "   -space: specifies whether the delim should be spaced or not
2170 "    (the space string will only be added if NERDSpaceDelims is set)
2171 "   -esc: specifies whether the tricky chars in the delim should be ESCed
2172 function s:GetLeft(alt, space, esc)
2173     let delim = b:NERDLeft
2174
2175     if a:alt
2176         if b:NERDLeftAlt == ''
2177             return ''
2178         else
2179             let delim = b:NERDLeftAlt
2180         endif
2181     endif
2182     if delim == ''
2183         return ''
2184     endif
2185
2186     if a:space && g:NERDSpaceDelims
2187         let delim = delim . s:spaceStr
2188     endif
2189
2190     if a:esc
2191         let delim = s:Esc(delim)
2192     endif
2193
2194     return delim
2195 endfunction
2196
2197 " Function: s:GetRight(alt, space, esc) {{{2
2198 " returns the right/right-alternative delimiter
2199 " Args:
2200 "   -alt: specifies whether to get right or right-alternative delim
2201 "   -space: specifies whether the delim should be spaced or not
2202 "   (the space string will only be added if NERDSpaceDelims is set)
2203 "   -esc: specifies whether the tricky chars in the delim should be ESCed
2204 function s:GetRight(alt, space, esc)
2205     let delim = b:NERDRight
2206
2207     if a:alt
2208         if !s:AltMultipart()
2209             return ''
2210         else
2211             let delim = b:NERDRightAlt
2212         endif
2213     endif
2214     if delim == ''
2215         return ''
2216     endif
2217
2218     if a:space && g:NERDSpaceDelims
2219         let delim = s:spaceStr . delim
2220     endif
2221
2222     if a:esc
2223         let delim = s:Esc(delim)
2224     endif
2225
2226     return delim
2227 endfunction
2228
2229
2230 " Function: s:GetSexyComMarker() {{{2
2231 " Returns the sexy comment marker for the current filetype.
2232 "
2233 " C style sexy comments are assumed if possible. If not then the sexy comment
2234 " marker is the last char of the delimiter pair that has both left and right
2235 " delims and has the longest left delim
2236 "
2237 " Args:
2238 "   -space: specifies whether the marker is to have a space string after it
2239 "    (the space string will only be added if NERDSpaceDelims is set)
2240 "   -esc: specifies whether the tricky chars in the marker are to be ESCed
2241 function s:GetSexyComMarker(space, esc)
2242     let sexyComMarker = b:NERDSexyComMarker
2243
2244     "if there is no hardcoded marker then we find one
2245     if sexyComMarker == ''
2246
2247         "if the filetype has c style comments then use standard c sexy
2248         "comments
2249         if s:HasCStyleComments()
2250             let sexyComMarker = '*'
2251         else
2252             "find a comment marker by getting the longest available left delim
2253             "(that has a corresponding right delim) and taking the last char
2254             let lenLeft = strlen(b:NERDLeft)
2255             let lenLeftAlt = strlen(b:NERDLeftAlt)
2256             let left = ''
2257             let right = ''
2258             if s:Multipart() && lenLeft >= lenLeftAlt
2259                 let left = b:NERDLeft
2260             elseif s:AltMultipart()
2261                 let left = b:NERDLeftAlt
2262             else
2263                 return -1
2264             endif
2265
2266             "get the last char of left
2267             let sexyComMarker = strpart(left, strlen(left)-1)
2268         endif
2269     endif
2270
2271     if a:space && g:NERDSpaceDelims
2272         let sexyComMarker = sexyComMarker . s:spaceStr
2273     endif
2274
2275     if a:esc
2276         let sexyComMarker = s:Esc(sexyComMarker)
2277     endif
2278
2279     return sexyComMarker
2280 endfunction
2281
2282 " Function: s:GetSexyComLeft(space, esc) {{{2
2283 " Returns the left delimiter for sexy comments for this filetype or -1 if
2284 " there is none. C style sexy comments are used if possible
2285 " Args:
2286 "   -space: specifies if the delim has a space string on the end
2287 "   (the space string will only be added if NERDSpaceDelims is set)
2288 "   -esc: specifies whether the tricky chars in the string are ESCed
2289 function s:GetSexyComLeft(space, esc)
2290     let lenLeft = strlen(b:NERDLeft)
2291     let lenLeftAlt = strlen(b:NERDLeftAlt)
2292     let left = ''
2293
2294     "assume c style sexy comments if possible
2295     if s:HasCStyleComments()
2296         let left = '/*'
2297     else
2298         "grab the longest left delim that has a right
2299         if s:Multipart() && lenLeft >= lenLeftAlt
2300             let left = b:NERDLeft
2301         elseif s:AltMultipart()
2302             let left = b:NERDLeftAlt
2303         else
2304             return -1
2305         endif
2306     endif
2307
2308     if a:space && g:NERDSpaceDelims
2309         let left = left . s:spaceStr
2310     endif
2311
2312     if a:esc
2313         let left = s:Esc(left)
2314     endif
2315
2316     return left
2317 endfunction
2318
2319 " Function: s:GetSexyComRight(space, esc) {{{2
2320 " Returns the right delimiter for sexy comments for this filetype or -1 if
2321 " there is none. C style sexy comments are used if possible.
2322 " Args:
2323 "   -space: specifies if the delim has a space string on the start
2324 "   (the space string will only be added if NERDSpaceDelims
2325 "   is specified for the current filetype)
2326 "   -esc: specifies whether the tricky chars in the string are ESCed
2327 function s:GetSexyComRight(space, esc)
2328     let lenLeft = strlen(b:NERDLeft)
2329     let lenLeftAlt = strlen(b:NERDLeftAlt)
2330     let right = ''
2331
2332     "assume c style sexy comments if possible
2333     if s:HasCStyleComments()
2334         let right = '*/'
2335     else
2336         "grab the right delim that pairs with the longest left delim
2337         if s:Multipart() && lenLeft >= lenLeftAlt
2338             let right = b:NERDRight
2339         elseif s:AltMultipart()
2340             let right = b:NERDRightAlt
2341         else
2342             return -1
2343         endif
2344     endif
2345
2346     if a:space && g:NERDSpaceDelims
2347         let right = s:spaceStr . right
2348     endif
2349
2350     if a:esc
2351         let right = s:Esc(right)
2352     endif
2353
2354     return right
2355 endfunction
2356
2357 " Function: s:HasMultipartDelims() {{{2
2358 " Returns 1 iff the current filetype has at least one set of multipart delims
2359 function s:HasMultipartDelims()
2360     return s:Multipart() || s:AltMultipart()
2361 endfunction
2362
2363 " Function: s:HasLeadingTabs(...) {{{2
2364 " Returns 1 if any of the given strings have leading tabs
2365 function s:HasLeadingTabs(...)
2366     for s in a:000
2367         if s =~ '^\t.*'
2368             return 1
2369         end
2370     endfor
2371     return 0
2372 endfunction
2373 " Function: s:HasCStyleComments() {{{2
2374 " Returns 1 iff the current filetype has c style comment delimiters
2375 function s:HasCStyleComments()
2376     return (b:NERDLeft == '/*' && b:NERDRight == '*/') || (b:NERDLeftAlt == '/*' && b:NERDRightAlt == '*/')
2377 endfunction
2378
2379 " Function: s:IsCommentedNormOrSexy(lineNum) {{{2
2380 "This function is used to determine whether the given line is commented with
2381 "either set of delimiters or if it is part of a sexy comment
2382 "
2383 " Args:
2384 "   -lineNum: the line number of the line to check
2385 function s:IsCommentedNormOrSexy(lineNum)
2386     let theLine = getline(a:lineNum)
2387
2388     "if the line is commented normally return 1
2389     if s:IsCommented(b:NERDLeft, b:NERDRight, theLine) || s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine)
2390         return 1
2391     endif
2392
2393     "if the line is part of a sexy comment return 1
2394     if s:IsInSexyComment(a:lineNum)
2395         return 1
2396     endif
2397     return 0
2398 endfunction
2399
2400 " Function: s:IsCommented(left, right, line) {{{2
2401 "This function is used to determine whether the given line is commented with
2402 "the given delimiters
2403 "
2404 " Args:
2405 "   -line: the line that to check if commented
2406 "   -left/right: the left and right delimiters to check for
2407 function s:IsCommented(left, right, line)
2408     "if the line isnt commented return true
2409     if s:FindDelimiterIndex(a:left, a:line) != -1 && (s:FindDelimiterIndex(a:right, a:line) != -1 || !s:Multipart())
2410         return 1
2411     endif
2412     return 0
2413 endfunction
2414
2415 " Function: s:IsCommentedFromStartOfLine(left, line) {{{2
2416 "This function is used to determine whether the given line is commented with
2417 "the given delimiters at the start of the line i.e the left delimiter is the
2418 "first thing on the line (apart from spaces\tabs)
2419 "
2420 " Args:
2421 "   -line: the line that to check if commented
2422 "   -left: the left delimiter to check for
2423 function s:IsCommentedFromStartOfLine(left, line)
2424     let theLine = s:ConvertLeadingTabsToSpaces(a:line)
2425     let numSpaces = strlen(substitute(theLine, '^\( *\).*$', '\1', ''))
2426     let delimIndx = s:FindDelimiterIndex(a:left, theLine)
2427     return delimIndx == numSpaces
2428 endfunction
2429
2430 " Function: s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line) {{{2
2431 " Finds the type of the outtermost delims on the line
2432 "
2433 " Args:
2434 "   -line: the line that to check if the outtermost comments on it are
2435 "    left/right
2436 "   -left/right: the left and right delimiters to check for
2437 "   -leftAlt/rightAlt: the left and right alternative delimiters to check for
2438 "
2439 " Returns:
2440 "   0 if the line is not commented with either set of delims
2441 "   1 if the line is commented with the left/right delim set
2442 "   2 if the line is commented with the leftAlt/rightAlt delim set
2443 function s:IsCommentedOuttermost(left, right, leftAlt, rightAlt, line)
2444     "get the first positions of the left delims and the last positions of the
2445     "right delims
2446     let indxLeft = s:FindDelimiterIndex(a:left, a:line)
2447     let indxLeftAlt = s:FindDelimiterIndex(a:leftAlt, a:line)
2448     let indxRight = s:LastIndexOfDelim(a:right, a:line)
2449     let indxRightAlt = s:LastIndexOfDelim(a:rightAlt, a:line)
2450
2451     "check if the line has a left delim before a leftAlt delim
2452     if (indxLeft <= indxLeftAlt || indxLeftAlt == -1) && indxLeft != -1
2453         "check if the line has a right delim after any rightAlt delim
2454         if (indxRight > indxRightAlt && indxRight > indxLeft) || !s:Multipart()
2455             return 1
2456         endif
2457
2458         "check if the line has a leftAlt delim before a left delim
2459     elseif (indxLeftAlt <= indxLeft || indxLeft == -1) && indxLeftAlt != -1
2460         "check if the line has a rightAlt delim after any right delim
2461         if (indxRightAlt > indxRight && indxRightAlt > indxLeftAlt) || !s:AltMultipart()
2462             return 2
2463         endif
2464     else
2465         return 0
2466     endif
2467
2468     return 0
2469
2470 endfunction
2471
2472
2473 " Function: s:IsDelimValid(delimiter, delIndx, line) {{{2
2474 " This function is responsible for determining whether a given instance of a
2475 " comment delimiter is a real delimiter or not. For example, in java the
2476 " // string is a comment delimiter but in the line:
2477 "               System.out.println("//");
2478 " it does not count as a comment delimiter. This function is responsible for
2479 " distinguishing between such cases. It does so by applying a set of
2480 " heuristics that are not fool proof but should work most of the time.
2481 "
2482 " Args:
2483 "   -delimiter: the delimiter we are validating
2484 "   -delIndx: the position of delimiter in line
2485 "   -line: the line that delimiter occurs in
2486 "
2487 " Returns:
2488 " 0 if the given delimiter is not a real delimiter (as far as we can tell) ,
2489 " 1 otherwise
2490 function s:IsDelimValid(delimiter, delIndx, line)
2491     "get the delimiter without the escchars
2492     let l:delimiter = a:delimiter
2493
2494     "get the strings before and after the delimiter
2495     let preComStr = strpart(a:line, 0, a:delIndx)
2496     let postComStr = strpart(a:line, a:delIndx+strlen(delimiter))
2497
2498     "to check if the delimiter is real, make sure it isnt preceded by
2499     "an odd number of quotes and followed by the same (which would indicate
2500     "that it is part of a string and therefore is not a comment)
2501     if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, '"', "\\"))
2502         return 0
2503     endif
2504     if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "'", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "'", "\\"))
2505         return 0
2506     endif
2507     if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, "`", "\\")) && !s:IsNumEven(s:CountNonESCedOccurances(postComStr, "`", "\\"))
2508         return 0
2509     endif
2510
2511
2512     "if the comment delimiter is escaped, assume it isnt a real delimiter
2513     if s:IsEscaped(a:line, a:delIndx, "\\")
2514         return 0
2515     endif
2516
2517     "vim comments are so fuckin stupid!! Why the hell do they have comment
2518     "delimiters that are used elsewhere in the syntax?!?! We need to check
2519     "some conditions especially for vim
2520     if &filetype == "vim"
2521         if !s:IsNumEven(s:CountNonESCedOccurances(preComStr, '"', "\\"))
2522             return 0
2523         endif
2524
2525         "if the delimiter is on the very first char of the line or is the
2526         "first non-tab/space char on the line then it is a valid comment delimiter
2527         if a:delIndx == 0 || a:line =~ "^[ \t]\\{" . a:delIndx . "\\}\".*$"
2528             return 1
2529         endif
2530
2531         let numLeftParen =s:CountNonESCedOccurances(preComStr, "(", "\\")
2532         let numRightParen =s:CountNonESCedOccurances(preComStr, ")", "\\")
2533
2534         "if the quote is inside brackets then assume it isnt a comment
2535         if numLeftParen > numRightParen
2536             return 0
2537         endif
2538
2539         "if the line has an even num of unescaped "'s then we can assume that
2540         "any given " is not a comment delimiter
2541         if s:IsNumEven(s:CountNonESCedOccurances(a:line, "\"", "\\"))
2542             return 0
2543         endif
2544     endif
2545
2546     return 1
2547
2548 endfunction
2549
2550 " Function: s:IsNumEven(num) {{{2
2551 " A small function the returns 1 if the input number is even and 0 otherwise
2552 " Args:
2553 "   -num: the number to check
2554 function s:IsNumEven(num)
2555     return (a:num % 2) == 0
2556 endfunction
2557
2558 " Function: s:IsEscaped(str, indx, escChar) {{{2
2559 " This function takes a string, an index into that string and an esc char and
2560 " returns 1 if the char at the index is escaped (i.e if it is preceded by an
2561 " odd number of esc chars)
2562 " Args:
2563 "   -str: the string to check
2564 "   -indx: the index into str that we want to check
2565 "   -escChar: the escape char the char at indx may be ESCed with
2566 function s:IsEscaped(str, indx, escChar)
2567     "initialise numEscChars to 0 and look at the char before indx
2568     let numEscChars = 0
2569     let curIndx = a:indx-1
2570
2571     "keep going back thru str until we either reach the start of the str or
2572     "run out of esc chars
2573     while curIndx >= 0 && strpart(a:str, curIndx, 1) == a:escChar
2574
2575         "we have found another esc char so add one to the count and move left
2576         "one char
2577         let numEscChars  = numEscChars + 1
2578         let curIndx = curIndx - 1
2579
2580     endwhile
2581
2582     "if there is an odd num of esc chars directly before the char at indx then
2583     "the char at indx is escaped
2584     return !s:IsNumEven(numEscChars)
2585 endfunction
2586
2587 " Function: s:IsInSexyComment(line) {{{2
2588 " returns 1 if the given line number is part of a sexy comment
2589 function s:IsInSexyComment(line)
2590     return !empty(s:FindBoundingLinesOfSexyCom(a:line))
2591 endfunction
2592
2593 " Function: s:IsSexyComment(topline, bottomline) {{{2
2594 " This function takes in 2 line numbers and returns 1 if the lines between and
2595 " including the given line numbers are a sexy comment. It returns 0 otherwise.
2596 " Args:
2597 "   -topline: the line that the possible sexy comment starts on
2598 "   -bottomline: the line that the possible sexy comment stops on
2599 function s:IsSexyComment(topline, bottomline)
2600
2601     "get the delim set that would be used for a sexy comment
2602     let left = ''
2603     let right = ''
2604     if s:Multipart()
2605         let left = b:NERDLeft
2606         let right = b:NERDRight
2607     elseif s:AltMultipart()
2608         let left = b:NERDLeftAlt
2609         let right = b:NERDRightAlt
2610     else
2611         return 0
2612     endif
2613
2614     "swap the top and bottom line numbers around if need be
2615     let topline = a:topline
2616     let bottomline = a:bottomline
2617     if bottomline < topline
2618         topline = bottomline
2619         bottomline = a:topline
2620     endif
2621
2622     "if there is < 2 lines in the comment it cannot be sexy
2623     if (bottomline - topline) <= 0
2624         return 0
2625     endif
2626
2627     "if the top line doesnt begin with a left delim then the comment isnt sexy
2628     if getline(a:topline) !~ '^[ \t]*' . left
2629         return 0
2630     endif
2631
2632     "if there is a right delim on the top line then this isnt a sexy comment
2633     if s:FindDelimiterIndex(right, getline(a:topline)) != -1
2634         return 0
2635     endif
2636
2637     "if there is a left delim on the bottom line then this isnt a sexy comment
2638     if s:FindDelimiterIndex(left, getline(a:bottomline)) != -1
2639         return 0
2640     endif
2641
2642     "if the bottom line doesnt begin with a right delim then the comment isnt
2643     "sexy
2644     if getline(a:bottomline) !~ '^.*' . right . '$'
2645         return 0
2646     endif
2647
2648     let sexyComMarker = s:GetSexyComMarker(0, 1)
2649
2650     "check each of the intermediate lines to make sure they start with a
2651     "sexyComMarker
2652     let currentLine = a:topline+1
2653     while currentLine < a:bottomline
2654         let theLine = getline(currentLine)
2655
2656         if theLine !~ '^[ \t]*' . sexyComMarker
2657             return 0
2658         endif
2659
2660         "if there is a right delim in an intermediate line then the block isnt
2661         "a sexy comment
2662         if s:FindDelimiterIndex(right, theLine) != -1
2663             return 0
2664         endif
2665
2666         let currentLine = currentLine + 1
2667     endwhile
2668
2669     "we have not found anything to suggest that this isnt a sexy comment so
2670     return 1
2671
2672 endfunction
2673
2674 " Function: s:LastIndexOfDelim(delim, str) {{{2
2675 " This function takes a string and a delimiter and returns the last index of
2676 " that delimiter in string
2677 " Args:
2678 "   -delim: the delimiter to look for
2679 "   -str: the string to look for delim in
2680 function s:LastIndexOfDelim(delim, str)
2681     let delim = a:delim
2682     let lenDelim = strlen(delim)
2683
2684     "set index to the first occurrence of delim. If there is no occurrence then
2685     "bail
2686     let indx = s:FindDelimiterIndex(delim, a:str)
2687     if indx == -1
2688         return -1
2689     endif
2690
2691     "keep moving to the next instance of delim in str till there is none left
2692     while 1
2693
2694         "search for the next delim after the previous one
2695         let searchStr = strpart(a:str, indx+lenDelim)
2696         let indx2 = s:FindDelimiterIndex(delim, searchStr)
2697
2698         "if we find a delim update indx to record the position of it, if we
2699         "dont find another delim then indx is the last one so break out of
2700         "this loop
2701         if indx2 != -1
2702             let indx = indx + indx2 + lenDelim
2703         else
2704             break
2705         endif
2706     endwhile
2707
2708     return indx
2709
2710 endfunction
2711
2712 " Function: s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2
2713 " This function takes in 2 line numbers and returns the index of the left most
2714 " char (that is not a space or a tab) on all of these lines.
2715 " Args:
2716 "   -countCommentedLines: 1 if lines that are commented are to be checked as
2717 "    well. 0 otherwise
2718 "   -countEmptyLines: 1 if empty lines are to be counted in the search
2719 "   -topline: the top line to be checked
2720 "   -bottomline: the bottom line to be checked
2721 function s:LeftMostIndx(countCommentedLines, countEmptyLines, topline, bottomline)
2722
2723     " declare the left most index as an extreme value
2724     let leftMostIndx = 1000
2725
2726     " go thru the block line by line updating leftMostIndx
2727     let currentLine = a:topline
2728     while currentLine <= a:bottomline
2729
2730         " get the next line and if it is allowed to be commented, or is not
2731         " commented, check it
2732         let theLine = getline(currentLine)
2733         if a:countEmptyLines || theLine !~ '^[ \t]*$'
2734             if a:countCommentedLines || (!s:IsCommented(b:NERDLeft, b:NERDRight, theLine) && !s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine))
2735                 " convert spaces to tabs and get the number of leading spaces for
2736                 " this line and update leftMostIndx if need be
2737                 let theLine = s:ConvertLeadingTabsToSpaces(theLine)
2738                 let leadSpaceOfLine = strlen( substitute(theLine, '\(^[ \t]*\).*$','\1','') )
2739                 if leadSpaceOfLine < leftMostIndx
2740                     let leftMostIndx = leadSpaceOfLine
2741                 endif
2742             endif
2743         endif
2744
2745         " move on to the next line
2746         let currentLine = currentLine + 1
2747     endwhile
2748
2749     if leftMostIndx == 1000
2750         return 0
2751     else
2752         return leftMostIndx
2753     endif
2754 endfunction
2755
2756 " Function: s:Multipart() {{{2
2757 " returns 1 if the current delims are multipart
2758 function s:Multipart()
2759     return b:NERDRight != ''
2760 endfunction
2761
2762 " Function: s:NerdEcho(msg, typeOfMsg) {{{2
2763 " Args:
2764 "   -msg: the message to echo
2765 "   -typeOfMsg: 0 = warning message
2766 "               1 = normal message
2767 function s:NerdEcho(msg, typeOfMsg)
2768     if a:typeOfMsg == 0
2769         echohl WarningMsg
2770         echo 'NERDCommenter:' . a:msg
2771         echohl None
2772     elseif a:typeOfMsg == 1
2773         echo 'NERDCommenter:' . a:msg
2774     endif
2775 endfunction
2776
2777 " Function: s:NumberOfLeadingTabs(s) {{{2
2778 " returns the number of leading tabs in the given string
2779 function s:NumberOfLeadingTabs(s)
2780     return strlen(substitute(a:s, '^\(\t*\).*$', '\1', ""))
2781 endfunction
2782
2783 " Function: s:NumLinesInBuf() {{{2
2784 " Returns the number of lines in the current buffer
2785 function s:NumLinesInBuf()
2786     return line('$')
2787 endfunction
2788
2789 " Function: s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str) {{{2
2790 " This function takes in a string, 2 delimiters in that string and 2 strings
2791 " to replace these delimiters with.
2792 "
2793 " Args:
2794 "   -toReplace1: the first delimiter to replace
2795 "   -toReplace2: the second delimiter to replace
2796 "   -replacor1: the string to replace toReplace1 with
2797 "   -replacor2: the string to replace toReplace2 with
2798 "   -str: the string that the delimiters to be replaced are in
2799 function s:ReplaceDelims(toReplace1, toReplace2, replacor1, replacor2, str)
2800     let line = s:ReplaceLeftMostDelim(a:toReplace1, a:replacor1, a:str)
2801     let line = s:ReplaceRightMostDelim(a:toReplace2, a:replacor2, line)
2802     return line
2803 endfunction
2804
2805 " Function: s:ReplaceLeftMostDelim(toReplace, replacor, str) {{{2
2806 " This function takes a string and a delimiter and replaces the left most
2807 " occurrence of this delimiter in the string with a given string
2808 "
2809 " Args:
2810 "   -toReplace: the delimiter in str that is to be replaced
2811 "   -replacor: the string to replace toReplace with
2812 "   -str: the string that contains toReplace
2813 function s:ReplaceLeftMostDelim(toReplace, replacor, str)
2814     let toReplace = a:toReplace
2815     let replacor = a:replacor
2816     "get the left most occurrence of toReplace
2817     let indxToReplace = s:FindDelimiterIndex(toReplace, a:str)
2818
2819     "if there IS an occurrence of toReplace in str then replace it and return
2820     "the resulting string
2821     if indxToReplace != -1
2822         let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace))
2823         return line
2824     endif
2825
2826     return a:str
2827 endfunction
2828
2829 " Function: s:ReplaceRightMostDelim(toReplace, replacor, str) {{{2
2830 " This function takes a string and a delimiter and replaces the right most
2831 " occurrence of this delimiter in the string with a given string
2832 "
2833 " Args:
2834 "   -toReplace: the delimiter in str that is to be replaced
2835 "   -replacor: the string to replace toReplace with
2836 "   -str: the string that contains toReplace
2837 "
2838 function s:ReplaceRightMostDelim(toReplace, replacor, str)
2839     let toReplace = a:toReplace
2840     let replacor = a:replacor
2841     let lenToReplace = strlen(toReplace)
2842
2843     "get the index of the last delim in str
2844     let indxToReplace = s:LastIndexOfDelim(toReplace, a:str)
2845
2846     "if there IS a delimiter in str, replace it and return the result
2847     let line = a:str
2848     if indxToReplace != -1
2849         let line = strpart(a:str, 0, indxToReplace) . replacor . strpart(a:str, indxToReplace+strlen(toReplace))
2850     endif
2851     return line
2852 endfunction
2853
2854 "FUNCTION: s:RestoreScreenState() {{{2
2855 "
2856 "Sets the screen state back to what it was when s:SaveScreenState was last
2857 "called.
2858 "
2859 function s:RestoreScreenState()
2860     if !exists("t:NERDComOldTopLine") || !exists("t:NERDComOldPos")
2861         throw 'NERDCommenter exception: cannot restore screen'
2862     endif
2863
2864     call cursor(t:NERDComOldTopLine, 0)
2865     normal! zt
2866     call setpos(".", t:NERDComOldPos)
2867 endfunction
2868
2869 " Function: s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline) {{{2
2870 " This function takes in 2 line numbers and returns the index of the right most
2871 " char on all of these lines.
2872 " Args:
2873 "   -countCommentedLines: 1 if lines that are commented are to be checked as
2874 "    well. 0 otherwise
2875 "   -countEmptyLines: 1 if empty lines are to be counted in the search
2876 "   -topline: the top line to be checked
2877 "   -bottomline: the bottom line to be checked
2878 function s:RightMostIndx(countCommentedLines, countEmptyLines, topline, bottomline)
2879     let rightMostIndx = -1
2880
2881     " go thru the block line by line updating rightMostIndx
2882     let currentLine = a:topline
2883     while currentLine <= a:bottomline
2884
2885         " get the next line and see if it is commentable, otherwise it doesnt
2886         " count
2887         let theLine = getline(currentLine)
2888         if a:countEmptyLines || theLine !~ '^[ \t]*$'
2889
2890             if a:countCommentedLines || (!s:IsCommented(b:NERDLeft, b:NERDRight, theLine) && !s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, theLine))
2891
2892                 " update rightMostIndx if need be
2893                 let theLine = s:ConvertLeadingTabsToSpaces(theLine)
2894                 let lineLen = strlen(theLine)
2895                 if lineLen > rightMostIndx
2896                     let rightMostIndx = lineLen
2897                 endif
2898             endif
2899         endif
2900
2901         " move on to the next line
2902         let currentLine = currentLine + 1
2903     endwhile
2904
2905     return rightMostIndx
2906 endfunction
2907
2908 "FUNCTION: s:SaveScreenState() {{{2
2909 "Saves the current cursor position in the current buffer and the window
2910 "scroll position
2911 function s:SaveScreenState()
2912     let t:NERDComOldPos = getpos(".")
2913     let t:NERDComOldTopLine = line("w0")
2914 endfunction
2915
2916 " Function: s:SwapOutterMultiPartDelimsForPlaceHolders(line) {{{2
2917 " This function takes a line and swaps the outter most multi-part delims for
2918 " place holders
2919 " Args:
2920 "   -line: the line to swap the delims in
2921 "
2922 function s:SwapOutterMultiPartDelimsForPlaceHolders(line)
2923     " find out if the line is commented using normal delims and/or
2924     " alternate ones
2925     let isCommented = s:IsCommented(b:NERDLeft, b:NERDRight, a:line)
2926     let isCommentedAlt = s:IsCommented(b:NERDLeftAlt, b:NERDRightAlt, a:line)
2927
2928     let line2 = a:line
2929
2930     "if the line is commented and there is a right delimiter, replace
2931     "the delims with place-holders
2932     if isCommented && s:Multipart()
2933         let line2 = s:ReplaceDelims(b:NERDLeft, b:NERDRight, g:NERDLPlace, g:NERDRPlace, a:line)
2934
2935     "similarly if the line is commented with the alternative
2936     "delimiters
2937     elseif isCommentedAlt && s:AltMultipart()
2938         let line2 = s:ReplaceDelims(b:NERDLeftAlt, b:NERDRightAlt, g:NERDLPlace, g:NERDRPlace, a:line)
2939     endif
2940
2941     return line2
2942 endfunction
2943
2944 " Function: s:SwapOutterPlaceHoldersForMultiPartDelims(line) {{{2
2945 " This function takes a line and swaps the outtermost place holders for
2946 " multi-part delims
2947 " Args:
2948 "   -line: the line to swap the delims in
2949 "
2950 function s:SwapOutterPlaceHoldersForMultiPartDelims(line)
2951     let left = ''
2952     let right = ''
2953     if s:Multipart()
2954         let left = b:NERDLeft
2955         let right = b:NERDRight
2956     elseif s:AltMultipart()
2957         let left = b:NERDLeftAlt
2958         let right = b:NERDRightAlt
2959     endif
2960
2961     let line = s:ReplaceDelims(g:NERDLPlace, g:NERDRPlace, left, right, a:line)
2962     return line
2963 endfunction
2964 " Function: s:TabbedCol(line, col) {{{2
2965 " Gets the col number for given line and existing col number. The new col
2966 " number is the col number when all leading spaces are converted to tabs
2967 " Args:
2968 "   -line:the line to get the rel col for
2969 "   -col: the abs col
2970 function s:TabbedCol(line, col)
2971     let lineTruncated = strpart(a:line, 0, a:col)
2972     let lineSpacesToTabs = substitute(lineTruncated, s:TabSpace(), '\t', 'g')
2973     return strlen(lineSpacesToTabs)
2974 endfunction
2975 "FUNCTION: s:TabSpace() {{{2
2976 "returns a string of spaces equal in length to &tabstop
2977 function s:TabSpace()
2978     let tabSpace = ""
2979     let spacesPerTab = &tabstop
2980     while spacesPerTab > 0
2981         let tabSpace = tabSpace . " "
2982         let spacesPerTab = spacesPerTab - 1
2983     endwhile
2984     return tabSpace
2985 endfunction
2986
2987 " Function: s:UnEsc(str, escChar) {{{2
2988 " This function removes all the escape chars from a string
2989 " Args:
2990 "   -str: the string to remove esc chars from
2991 "   -escChar: the escape char to be removed
2992 function s:UnEsc(str, escChar)
2993     return substitute(a:str, a:escChar, "", "g")
2994 endfunction
2995
2996 " Function: s:UntabbedCol(line, col) {{{2
2997 " Takes a line and a col and returns the absolute column of col taking into
2998 " account that a tab is worth 3 or 4 (or whatever) spaces.
2999 " Args:
3000 "   -line:the line to get the abs col for
3001 "   -col: the col that doesnt take into account tabs
3002 function s:UntabbedCol(line, col)
3003     let lineTruncated = strpart(a:line, 0, a:col)
3004     let lineTabsToSpaces = substitute(lineTruncated, '\t', s:TabSpace(), 'g')
3005     return strlen(lineTabsToSpaces)
3006 endfunction
3007 " Section: Comment mapping setup {{{1
3008 " ===========================================================================
3009
3010 " switch to/from alternative delimiters
3011 nnoremap <plug>NERDCommenterAltDelims :call <SID>SwitchToAlternativeDelimiters(1)<cr>
3012
3013 " comment out lines
3014 nnoremap <silent> <plug>NERDCommenterComment :call NERDComment(0, "norm")<cr>
3015 vnoremap <silent> <plug>NERDCommenterComment <ESC>:call NERDComment(1, "norm")<cr>
3016
3017 " toggle comments
3018 nnoremap <silent> <plug>NERDCommenterToggle :call NERDComment(0, "toggle")<cr>
3019 vnoremap <silent> <plug>NERDCommenterToggle <ESC>:call NERDComment(1, "toggle")<cr>
3020
3021 " minimal comments
3022 nnoremap <silent> <plug>NERDCommenterMinimal :call NERDComment(0, "minimal")<cr>
3023 vnoremap <silent> <plug>NERDCommenterMinimal <ESC>:call NERDComment(1, "minimal")<cr>
3024
3025 " sexy comments
3026 nnoremap <silent> <plug>NERDCommenterSexy :call NERDComment(0, "sexy")<CR>
3027 vnoremap <silent> <plug>NERDCommenterSexy <ESC>:call NERDComment(1, "sexy")<CR>
3028
3029 " invert comments
3030 nnoremap <silent> <plug>NERDCommenterInvert :call NERDComment(0, "invert")<CR>
3031 vnoremap <silent> <plug>NERDCommenterInvert <ESC>:call NERDComment(1, "invert")<CR>
3032
3033 " yank then comment
3034 nmap <silent> <plug>NERDCommenterYank :call NERDComment(0, "yank")<CR>
3035 vmap <silent> <plug>NERDCommenterYank <ESC>:call NERDComment(1, "yank")<CR>
3036
3037 " left aligned comments
3038 nnoremap <silent> <plug>NERDCommenterAlignLeft :call NERDComment(0, "alignLeft")<cr>
3039 vnoremap <silent> <plug>NERDCommenterAlignLeft <ESC>:call NERDComment(1, "alignLeft")<cr>
3040
3041 " left and right aligned comments
3042 nnoremap <silent> <plug>NERDCommenterAlignBoth :call NERDComment(0, "alignBoth")<cr>
3043 vnoremap <silent> <plug>NERDCommenterAlignBoth <ESC>:call NERDComment(1, "alignBoth")<cr>
3044
3045 " nested comments
3046 nnoremap <silent> <plug>NERDCommenterNest :call NERDComment(0, "nested")<cr>
3047 vnoremap <silent> <plug>NERDCommenterNest <ESC>:call NERDComment(1, "nested")<cr>
3048
3049 " uncomment
3050 nnoremap <silent> <plug>NERDCommenterUncomment :call NERDComment(0, "uncomment")<cr>
3051 vnoremap <silent> <plug>NERDCommenterUncomment :call NERDComment(1, "uncomment")<cr>
3052
3053 " comment till the end of the line
3054 nnoremap <silent> <plug>NERDCommenterToEOL :call NERDComment(0, "toEOL")<cr>
3055
3056 " append comments
3057 nmap <silent> <plug>NERDCommenterAppend :call NERDComment(0, "append")<cr>
3058
3059 " insert comments
3060 inoremap <silent> <plug>NERDCommenterInInsert <SPACE><BS><ESC>:call NERDComment(0, "insert")<CR>
3061
3062
3063 function! s:CreateMaps(target, combo)
3064     if !hasmapto(a:target, 'n')
3065         exec 'nmap ' . a:combo . ' ' . a:target
3066     endif
3067
3068     if !hasmapto(a:target, 'v')
3069         exec 'vmap ' . a:combo . ' ' . a:target
3070     endif
3071 endfunction
3072
3073 if g:NERDCreateDefaultMappings
3074     call s:CreateMaps('<plug>NERDCommenterComment',    ',cc')
3075     call s:CreateMaps('<plug>NERDCommenterToggle',     ',c<space>')
3076     call s:CreateMaps('<plug>NERDCommenterMinimal',    ',cm')
3077     call s:CreateMaps('<plug>NERDCommenterSexy',       ',cs')
3078     call s:CreateMaps('<plug>NERDCommenterInvert',     ',ci')
3079     call s:CreateMaps('<plug>NERDCommenterYank',       ',cy')
3080     call s:CreateMaps('<plug>NERDCommenterAlignLeft',  ',cl')
3081     call s:CreateMaps('<plug>NERDCommenterAlignBoth',  ',cb')
3082     call s:CreateMaps('<plug>NERDCommenterNest',       ',cn')
3083     call s:CreateMaps('<plug>NERDCommenterUncomment',  ',cu')
3084     call s:CreateMaps('<plug>NERDCommenterToEOL',      ',c$')
3085     call s:CreateMaps('<plug>NERDCommenterAppend',     ',cA')
3086
3087     if !hasmapto('<plug>NERDCommenterAltDelims', 'n')
3088         nmap ,ca <plug>NERDCommenterAltDelims
3089     endif
3090 endif
3091
3092
3093
3094 " Section: Menu item setup {{{1
3095 " ===========================================================================
3096 "check if the user wants the menu to be displayed
3097 if g:NERDMenuMode != 0
3098
3099     let menuRoot = ""
3100     if g:NERDMenuMode == 1
3101         let menuRoot = 'comment'
3102     elseif g:NERDMenuMode == 2
3103         let menuRoot = '&comment'
3104     elseif g:NERDMenuMode == 3
3105         let menuRoot = '&Plugin.&comment'
3106     endif
3107
3108     function! s:CreateMenuItems(target, desc, root)
3109         exec 'nmenu <silent> ' . a:root . '.' . a:desc . ' ' . a:target
3110         exec 'vmenu <silent> ' . a:root . '.' . a:desc . ' ' . a:target
3111     endfunction
3112     call s:CreateMenuItems("<plug>NERDCommenterComment",    'Comment', menuRoot)
3113     call s:CreateMenuItems("<plug>NERDCommenterToggle",     'Toggle', menuRoot)
3114     call s:CreateMenuItems('<plug>NERDCommenterMinimal',    'Minimal', menuRoot)
3115     call s:CreateMenuItems('<plug>NERDCommenterNest',       'Nested', menuRoot)
3116     exec 'nmenu <silent> '. menuRoot .'.To\ EOL <plug>NERDCommenterToEOL'
3117     call s:CreateMenuItems('<plug>NERDCommenterInvert',     'Invert', menuRoot)
3118     call s:CreateMenuItems('<plug>NERDCommenterSexy',       'Sexy', menuRoot)
3119     call s:CreateMenuItems('<plug>NERDCommenterYank',       'Yank\ then\ comment', menuRoot)
3120     exec 'nmenu <silent> '. menuRoot .'.Append <plug>NERDCommenterAppend'
3121     exec 'menu <silent> '. menuRoot .'.-Sep-    :'
3122     call s:CreateMenuItems('<plug>NERDCommenterAlignLeft',  'Left\ aligned', menuRoot)
3123     call s:CreateMenuItems('<plug>NERDCommenterAlignBoth',  'Left\ and\ right\ aligned', menuRoot)
3124     exec 'menu <silent> '. menuRoot .'.-Sep2-    :'
3125     call s:CreateMenuItems('<plug>NERDCommenterUncomment',  'Uncomment', menuRoot)
3126     exec 'nmenu <silent> '. menuRoot .'.Switch\ Delimiters <plug>NERDCommenterAltDelims'
3127     exec 'imenu <silent> '. menuRoot .'.Insert\ Comment\ Here <plug>NERDCommenterInInsert'
3128     exec 'menu <silent> '. menuRoot .'.-Sep3-    :'
3129     exec 'menu <silent>'. menuRoot .'.Help :help NERDCommenterContents<CR>'
3130 endif
3131 " vim: set foldmethod=marker :