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