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