Features by Gerhard
[shutils.git] / dotfiles / vim / .vim / plugin / a.vim
1 " Copyright (c) 1998-2006
2 " Michael Sharpe <feline@irendi.com>
3 "
4 " We grant permission to use, copy modify, distribute, and sell this
5 " software for any purpose without fee, provided that the above copyright
6 " notice and this text are not removed. We make no guarantee about the
7 " suitability of this software for any purpose and we are not liable
8 " for any damages resulting from its use. Further, we are under no
9 " obligation to maintain or extend this software. It is provided on an
10 " "as is" basis without any expressed or implied warranty.
11
12 " Directory & regex enhancements added by Bindu Wavell who is well known on
13 " vim.sf.net
14 "
15 " Patch for spaces in files/directories from Nathan Stien (also reported by
16 " Soeren Sonnenburg)
17
18 " Do not load a.vim if is has already been loaded.
19 if exists("loaded_alternateFile")
20 finish
21 endif
22 if (v:progname == "ex")
23 finish
24 endif
25 let loaded_alternateFile = 1
26
27 let alternateExtensionsDict = {}
28
29 " setup the default set of alternate extensions. The user can override in thier
30 " .vimrc if the defaults are not suitable. To override in a .vimrc simply set a
31 " g:alternateExtensions_<EXT> variable to a comma separated list of alternates,
32 " where <EXT> is the extension to map.
33 " E.g. let g:alternateExtensions_CPP = "inc,h,H,HPP,hpp"
34 " let g:alternateExtensions_{'aspx.cs'} = "aspx"
35
36
37 " This variable will be increased when an extension with greater number of dots
38 " is added by the AddAlternateExtensionMapping call.
39 let s:maxDotsInExtension = 1
40
41 " Function : AddAlternateExtensionMapping (PRIVATE)
42 " Purpose : simple helper function to add the default alternate extension
43 " mappings.
44 " Args : extension -- the extension to map
45 " alternates -- comma separated list of alternates extensions
46 " Returns : nothing
47 " Author : Michael Sharpe <feline@irendi.com>
48 function! <SID>AddAlternateExtensionMapping(extension, alternates)
49 " This code does not actually work for variables like foo{'a.b.c.d.e'}
50 "let varName = "g:alternateExtensions_" . a:extension
51 "if (!exists(varName))
52 " let g:alternateExtensions_{a:extension} = a:alternates
53 "endif
54
55 " This code handles extensions which contains a dot. exists() fails with
56 " such names.
57 "let v:errmsg = ""
58 " FIXME this line causes ex to return 1 instead of 0 for some reason??
59 "silent! echo g:alternateExtensions_{a:extension}
60 "if (v:errmsg != "")
61 "let g:alternateExtensions_{a:extension} = a:alternates
62 "endif
63
64 let g:alternateExtensionsDict[a:extension] = a:alternates
65 let dotsNumber = strlen(substitute(a:extension, "[^.]", "", "g"))
66 if s:maxDotsInExtension < dotsNumber
67 let s:maxDotsInExtension = dotsNumber
68 endif
69 endfunction
70
71
72 " Add all the default extensions
73 " Mappings for C and C++
74 call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
75 call <SID>AddAlternateExtensionMapping('H',"C,CPP,CXX,CC")
76 call <SID>AddAlternateExtensionMapping('hpp',"cpp,c")
77 call <SID>AddAlternateExtensionMapping('HPP',"CPP,C")
78 call <SID>AddAlternateExtensionMapping('c',"h")
79 call <SID>AddAlternateExtensionMapping('C',"H")
80 call <SID>AddAlternateExtensionMapping('cpp',"h,hpp")
81 call <SID>AddAlternateExtensionMapping('CPP',"H,HPP")
82 call <SID>AddAlternateExtensionMapping('cc',"h")
83 call <SID>AddAlternateExtensionMapping('CC',"H,h")
84 call <SID>AddAlternateExtensionMapping('cxx',"h")
85 call <SID>AddAlternateExtensionMapping('CXX',"H")
86 " Mappings for PSL7
87 call <SID>AddAlternateExtensionMapping('psl',"ph")
88 call <SID>AddAlternateExtensionMapping('ph',"psl")
89 " Mappings for ADA
90 call <SID>AddAlternateExtensionMapping('adb',"ads")
91 call <SID>AddAlternateExtensionMapping('ads',"adb")
92 " Mappings for lex and yacc files
93 call <SID>AddAlternateExtensionMapping('l',"y,yacc,ypp")
94 call <SID>AddAlternateExtensionMapping('lex',"yacc,y,ypp")
95 call <SID>AddAlternateExtensionMapping('lpp',"ypp,y,yacc")
96 call <SID>AddAlternateExtensionMapping('y',"l,lex,lpp")
97 call <SID>AddAlternateExtensionMapping('yacc',"lex,l,lpp")
98 call <SID>AddAlternateExtensionMapping('ypp',"lpp,l,lex")
99 " Mappings for OCaml
100 call <SID>AddAlternateExtensionMapping('ml',"mli")
101 call <SID>AddAlternateExtensionMapping('mli',"ml")
102 " ASP stuff
103 call <SID>AddAlternateExtensionMapping('aspx.cs', 'aspx')
104 call <SID>AddAlternateExtensionMapping('aspx.vb', 'aspx')
105 call <SID>AddAlternateExtensionMapping('aspx', 'aspx.cs,aspx.vb')
106
107 " Setup default search path, unless the user has specified
108 " a path in their [._]vimrc.
109 if (!exists('g:alternateSearchPath'))
110 let g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc'
111 endif
112
113 " If this variable is true then a.vim will not alternate to a file/buffer which
114 " does not exist. E.g while editing a.c and the :A will not swtich to a.h
115 " unless it exists.
116 if (!exists('g:alternateNoDefaultAlternate'))
117 " by default a.vim will alternate to a file which does not exist
118 let g:alternateNoDefaultAlternate = 0
119 endif
120
121 " If this variable is true then a.vim will convert the alternate filename to a
122 " filename relative to the current working directory.
123 " Feature by Nathan Huizinga
124 if (!exists('g:alternateRelativeFiles'))
125 " by default a.vim will not convert the filename to one relative to the
126 " current working directory
127 let g:alternateRelativeFiles = 0
128 endif
129
130
131 " Function : GetNthItemFromList (PRIVATE)
132 " Purpose : Support reading items from a comma seperated list
133 " Used to iterate all the extensions in an extension spec
134 " Used to iterate all path prefixes
135 " Args : list -- the list (extension spec, file paths) to iterate
136 " n -- the extension to get
137 " Returns : the nth item (extension, path) from the list (extension
138 " spec), or "" for failure
139 " Author : Michael Sharpe <feline@irendi.com>
140 " History : Renamed from GetNthExtensionFromSpec to GetNthItemFromList
141 " to reflect a more generic use of this function. -- Bindu
142 function! <SID>GetNthItemFromList(list, n)
143 let itemStart = 0
144 let itemEnd = -1
145 let pos = 0
146 let item = ""
147 let i = 0
148 while (i != a:n)
149 let itemStart = itemEnd + 1
150 let itemEnd = match(a:list, ",", itemStart)
151 let i = i + 1
152 if (itemEnd == -1)
153 if (i == a:n)
154 let itemEnd = strlen(a:list)
155 endif
156 break
157 endif
158 endwhile
159 if (itemEnd != -1)
160 let item = strpart(a:list, itemStart, itemEnd - itemStart)
161 endif
162 return item
163 endfunction
164
165 " Function : ExpandAlternatePath (PRIVATE)
166 " Purpose : Expand path info. A path with a prefix of "wdr:" will be
167 " treated as relative to the working directory (i.e. the
168 " directory where vim was started.) A path prefix of "abs:" will
169 " be treated as absolute. No prefix or "sfr:" will result in the
170 " path being treated as relative to the source file (see sfPath
171 " argument).
172 "
173 " A prefix of "reg:" will treat the pathSpec as a regular
174 " expression substitution that is applied to the source file
175 " path. The format is:
176 "
177 " reg:<sep><pattern><sep><subst><sep><flag><sep>
178 "
179 " <sep> seperator character, we often use one of [/|%#]
180 " <pattern> is what you are looking for
181 " <subst> is the output pattern
182 " <flag> can be g for global replace or empty
183 "
184 " EXAMPLE: 'reg:/inc/src/g/' will replace every instance
185 " of 'inc' with 'src' in the source file path. It is possible
186 " to use match variables so you could do something like:
187 " 'reg:|src/\([^/]*\)|inc/\1||' (see 'help :substitute',
188 " 'help pattern' and 'help sub-replace-special' for more details
189 "
190 " NOTE: a.vim uses ',' (comma) internally so DON'T use it
191 " in your regular expressions or other pathSpecs unless you update
192 " the rest of the a.vim code to use some other seperator.
193 "
194 " Args : pathSpec -- path component (or substitution patterns)
195 " sfPath -- source file path
196 " Returns : a path that can be used by AlternateFile()
197 " Author : Bindu Wavell <bindu@wavell.net>
198 function! <SID>ExpandAlternatePath(pathSpec, sfPath)
199 let prfx = strpart(a:pathSpec, 0, 4)
200 if (prfx == "wdr:" || prfx == "abs:")
201 let path = strpart(a:pathSpec, 4)
202 elseif (prfx == "reg:")
203 let re = strpart(a:pathSpec, 4)
204 let sep = strpart(re, 0, 1)
205 let patend = match(re, sep, 1)
206 let pat = strpart(re, 1, patend - 1)
207 let subend = match(re, sep, patend + 1)
208 let sub = strpart(re, patend+1, subend - patend - 1)
209 let flag = strpart(re, strlen(re) - 2)
210 if (flag == sep)
211 let flag = ''
212 endif
213 let path = substitute(a:sfPath, pat, sub, flag)
214 "call confirm('PAT: [' . pat . '] SUB: [' . sub . ']')
215 "call confirm(a:sfPath . ' => ' . path)
216 else
217 let path = a:pathSpec
218 if (prfx == "sfr:")
219 let path = strpart(path, 4)
220 endif
221 let path = a:sfPath . "/" . path
222 endif
223 return path
224 endfunction
225
226 " Function : FindFileInSearchPath (PRIVATE)
227 " Purpose : Searches for a file in the search path list
228 " Args : filename -- name of the file to search for
229 " pathList -- the path list to search
230 " relPathBase -- the path which relative paths are expanded from
231 " Returns : An expanded filename if found, the empty string otherwise
232 " Author : Michael Sharpe (feline@irendi.com)
233 " History : inline code written by Bindu Wavell originally
234 function! <SID>FindFileInSearchPath(fileName, pathList, relPathBase)
235 let filepath = ""
236 let m = 1
237 let pathListLen = strlen(a:pathList)
238 if (pathListLen > 0)
239 while (1)
240 let pathSpec = <SID>GetNthItemFromList(a:pathList, m)
241 if (pathSpec != "")
242 let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase)
243 let fullname = path . "/" . a:fileName
244 let foundMatch = <SID>BufferOrFileExists(fullname)
245 if (foundMatch)
246 let filepath = fullname
247 break
248 endif
249 else
250 break
251 endif
252 let m = m + 1
253 endwhile
254 endif
255 return filepath
256 endfunction
257
258 " Function : FindFileInSearchPathEx (PRIVATE)
259 " Purpose : Searches for a file in the search path list
260 " Args : filename -- name of the file to search for
261 " pathList -- the path list to search
262 " relPathBase -- the path which relative paths are expanded from
263 " count -- find the count'th occurence of the file on the path
264 " Returns : An expanded filename if found, the empty string otherwise
265 " Author : Michael Sharpe (feline@irendi.com)
266 " History : Based on <SID>FindFileInSearchPath() but with extensions
267 function! <SID>FindFileInSearchPathEx(fileName, pathList, relPathBase, count)
268 let filepath = ""
269 let m = 1
270 let spath = ""
271 let pathListLen = strlen(a:pathList)
272 if (pathListLen > 0)
273 while (1)
274 let pathSpec = <SID>GetNthItemFromList(a:pathList, m)
275 if (pathSpec != "")
276 let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase)
277 if (spath != "")
278 let spath = spath . ','
279 endif
280 let spath = spath . path
281 else
282 break
283 endif
284 let m = m + 1
285 endwhile
286 endif
287
288 if (&path != "")
289 if (spath != "")
290 let spath = spath . ','
291 endif
292 let spath = spath . &path
293 endif
294
295 let filepath = findfile(a:fileName, spath, a:count)
296 return filepath
297 endfunction
298
299 " Function : EnumerateFilesByExtension (PRIVATE)
300 " Purpose : enumerates all files by a particular list of alternate extensions.
301 " Args : path -- path of a file (not including the file)
302 " baseName -- base name of the file to be expanded
303 " extension -- extension whose alternates are to be enumerated
304 " Returns : comma separated list of files with extensions
305 " Author : Michael Sharpe <feline@irendi.com>
306 function! EnumerateFilesByExtension(path, baseName, extension)
307 let enumeration = ""
308 let extSpec = ""
309 let v:errmsg = ""
310 silent! echo g:alternateExtensions_{a:extension}
311 if (v:errmsg == "")
312 let extSpec = g:alternateExtensions_{a:extension}
313 endif
314 if (extSpec == "")
315 if (has_key(g:alternateExtensionsDict, a:extension))
316 let extSpec = g:alternateExtensionsDict[a:extension]
317 endif
318 endif
319 if (extSpec != "")
320 let n = 1
321 let done = 0
322 while (!done)
323 let ext = <SID>GetNthItemFromList(extSpec, n)
324 if (ext != "")
325 if (a:path != "")
326 let newFilename = a:path . "/" . a:baseName . "." . ext
327 else
328 let newFilename = a:baseName . "." . ext
329 endif
330 if (enumeration == "")
331 let enumeration = newFilename
332 else
333 let enumeration = enumeration . "," . newFilename
334 endif
335 else
336 let done = 1
337 endif
338 let n = n + 1
339 endwhile
340 endif
341 return enumeration
342 endfunction
343
344 " Function : EnumerateFilesByExtensionInPath (PRIVATE)
345 " Purpose : enumerates all files by expanding the path list and the extension
346 " list.
347 " Args : baseName -- base name of the file
348 " extension -- extension whose alternates are to be enumerated
349 " pathList -- the list of paths to enumerate
350 " relPath -- the path of the current file for expansion of relative
351 " paths in the path list.
352 " Returns : A comma separated list of paths with extensions
353 " Author : Michael Sharpe <feline@irendi.com>
354 function! EnumerateFilesByExtensionInPath(baseName, extension, pathList, relPathBase)
355 let enumeration = ""
356 let filepath = ""
357 let m = 1
358 let pathListLen = strlen(a:pathList)
359 if (pathListLen > 0)
360 while (1)
361 let pathSpec = <SID>GetNthItemFromList(a:pathList, m)
362 if (pathSpec != "")
363 let path = <SID>ExpandAlternatePath(pathSpec, a:relPathBase)
364 let pe = EnumerateFilesByExtension(path, a:baseName, a:extension)
365 if (enumeration == "")
366 let enumeration = pe
367 else
368 let enumeration = enumeration . "," . pe
369 endif
370 else
371 break
372 endif
373 let m = m + 1
374 endwhile
375 endif
376 return enumeration
377 endfunction
378
379 " Function : DetermineExtension (PRIVATE)
380 " Purpose : Determines the extension of a filename based on the register
381 " alternate extension. This allow extension which contain dots to
382 " be considered. E.g. foo.aspx.cs to foo.aspx where an alternate
383 " exists for the aspx.cs extension. Note that this will only accept
384 " extensions which contain less than 5 dots. This is only
385 " implemented in this manner for simplicity...it is doubtful that
386 " this will be a restriction in non-contrived situations.
387 " Args : The path to the file to find the extension in
388 " Returns : The matched extension if any
389 " Author : Michael Sharpe (feline@irendi.com)
390 " History : idea from Tom-Erik Duestad
391 " Notes : there is some magic occuring here. The exists() function does not
392 " work well when the curly brace variable has dots in it. And why
393 " should it, dots are not valid in variable names. But the exists
394 " function is wierd too. Lets say foo_c does exist. Then
395 " exists("foo_c.e.f") will be true...even though the variable does
396 " not exist. However the curly brace variables do work when the
397 " variable has dots in it. E.g foo_{'c'} is different from
398 " foo_{'c.d.e'}...and foo_{'c'} is identical to foo_c and
399 " foo_{'c.d.e'} is identical to foo_c.d.e right? Yes in the current
400 " implementation of vim. To trick vim to test for existence of such
401 " variables echo the curly brace variable and look for an error
402 " message.
403 function! DetermineExtension(path)
404 let mods = ":t"
405 let i = 0
406 while i <= s:maxDotsInExtension
407 let mods = mods . ":e"
408 let extension = fnamemodify(a:path, mods)
409 if (has_key(g:alternateExtensionsDict, extension))
410 return extension
411 endif
412 let v:errmsg = ""
413 silent! echo g:alternateExtensions_{extension}
414 if (v:errmsg == "")
415 return extension
416 endif
417 let i = i + 1
418 endwhile
419 return ""
420 endfunction
421
422 " Function : AlternateFile (PUBLIC)
423 " Purpose : Opens a new buffer by looking at the extension of the current
424 " buffer and finding the corresponding file. E.g. foo.c <--> foo.h
425 " Args : accepts one argument. If present it used the argument as the new
426 " extension.
427 " Returns : nothing
428 " Author : Michael Sharpe <feline@irendi.com>
429 " History : + When an alternate can't be found in the same directory as the
430 " source file, a search path will be traversed looking for the
431 " alternates.
432 " + Moved some code into a separate function, minor optimization
433 " + rework to favor files in memory based on complete enumeration of
434 " all files extensions and paths
435 function! AlternateFile(splitWindow, ...)
436 let extension = DetermineExtension(expand("%:p"))
437 let baseName = substitute(expand("%:t"), "\." . extension . '$', "", "")
438 let currentPath = expand("%:p:h")
439
440 if (a:0 != 0)
441 let newFullname = currentPath . "/" . baseName . "." . a:1
442 call <SID>FindOrCreateBuffer(newFullname, a:splitWindow, 0)
443 else
444 let allfiles = ""
445 if (extension != "")
446 let allfiles1 = EnumerateFilesByExtension(currentPath, baseName, extension)
447 let allfiles2 = EnumerateFilesByExtensionInPath(baseName, extension, g:alternateSearchPath, currentPath)
448
449 if (allfiles1 != "")
450 if (allfiles2 != "")
451 let allfiles = allfiles1 . ',' . allfiles2
452 else
453 let allfiles = allfiles1
454 endif
455 else
456 let allfiles = allfiles2
457 endif
458 endif
459
460 if (allfiles != "")
461 let bestFile = ""
462 let bestScore = 0
463 let score = 0
464 let n = 1
465
466 let onefile = <SID>GetNthItemFromList(allfiles, n)
467 let bestFile = onefile
468 while (onefile != "" && score < 2)
469 let score = <SID>BufferOrFileExists(onefile)
470 if (score > bestScore)
471 let bestScore = score
472 let bestFile = onefile
473 endif
474 let n = n + 1
475 let onefile = <SID>GetNthItemFromList(allfiles, n)
476 endwhile
477
478 if (bestScore == 0 && g:alternateNoDefaultAlternate == 1)
479 echo "No existing alternate available"
480 else
481 call <SID>FindOrCreateBuffer(bestFile, a:splitWindow, 1)
482 let b:AlternateAllFiles = allfiles
483 endif
484 else
485 echo "No alternate file/buffer available"
486 endif
487 endif
488 endfunction
489
490 " Function : AlternateOpenFileUnderCursor (PUBLIC)
491 " Purpose : Opens file under the cursor
492 " Args : splitWindow -- indicates how to open the file
493 " Returns : Nothing
494 " Author : Michael Sharpe (feline@irendi.com) www.irendi.com
495 function! AlternateOpenFileUnderCursor(splitWindow,...)
496 let cursorFile = (a:0 > 0) ? a:1 : expand("<cfile>")
497 let currentPath = expand("%:p:h")
498 let openCount = 1
499
500 let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount)
501 if (fileName != "")
502 call <SID>FindOrCreateBuffer(fileName, a:splitWindow, 1)
503 let b:openCount = openCount
504 let b:cursorFile = cursorFile
505 let b:currentPath = currentPath
506 else
507 echo "Can't find file"
508 endif
509 endfunction
510
511 " Function : AlternateOpenNextFile (PUBLIC)
512 " Purpose : Opens the next file corresponding to the search which found the
513 " current file
514 " Args : bang -- indicates what to do if the current file has not been
515 " saved
516 " Returns : nothing
517 " Author : Michael Sharpe (feline@irendi.com) www.irendi.com
518 function! AlternateOpenNextFile(bang)
519 let cursorFile = ""
520 if (exists("b:cursorFile"))
521 let cursorFile = b:cursorFile
522 endif
523
524 let currentPath = ""
525 if (exists("b:currentPath"))
526 let currentPath = b:currentPath
527 endif
528
529 let openCount = 0
530 if (exists("b:openCount"))
531 let openCount = b:openCount + 1
532 endif
533
534 if (cursorFile != "" && currentPath != "" && openCount != 0)
535 let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount)
536 if (fileName != "")
537 call <SID>FindOrCreateBuffer(fileName, "n".a:bang, 0)
538 let b:openCount = openCount
539 let b:cursorFile = cursorFile
540 let b:currentPath = currentPath
541 else
542 let fileName = <SID>FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, 1)
543 if (fileName != "")
544 call <SID>FindOrCreateBuffer(fileName, "n".a:bang, 0)
545 let b:openCount = 1
546 let b:cursorFile = cursorFile
547 let b:currentPath = currentPath
548 else
549 echo "Can't find next file"
550 endif
551 endif
552 endif
553 endfunction
554
555 comm! -nargs=? -bang IH call AlternateOpenFileUnderCursor("n<bang>", <f-args>)
556 comm! -nargs=? -bang IHS call AlternateOpenFileUnderCursor("h<bang>", <f-args>)
557 comm! -nargs=? -bang IHV call AlternateOpenFileUnderCursor("v<bang>", <f-args>)
558 comm! -nargs=? -bang IHT call AlternateOpenFileUnderCursor("t<bang>", <f-args>)
559 comm! -nargs=? -bang IHN call AlternateOpenNextFile("<bang>")
560 imap <Leader>ih <ESC>:IHS<CR>
561 nmap <Leader>ih :IHS<CR>
562 imap <Leader>is <ESC>:IHS<CR>:A<CR>
563 nmap <Leader>is :IHS<CR>:A<CR>
564 imap <Leader>ihn <ESC>:IHN<CR>
565 nmap <Leader>ihn :IHN<CR>
566
567 "function! <SID>PrintList(theList)
568 " let n = 1
569 " let oneFile = <SID>GetNthItemFromList(a:theList, n)
570 " while (oneFile != "")
571 " let n = n + 1
572 " let oneFile = <SID>GetNthItemFromList(a:theList, n)
573 " endwhile
574 "endfunction
575
576 " Function : NextAlternate (PUBLIC)
577 " Purpose : Used to cycle through any other alternate file which existed on
578 " the search path.
579 " Args : bang (IN) - used to implement the AN vs AN! functionality
580 " Returns : nothing
581 " Author : Michael Sharpe <feline@irendi.com>
582 function! NextAlternate(bang)
583 if (exists('b:AlternateAllFiles'))
584 let currentFile = expand("%")
585 let n = 1
586 let onefile = <SID>GetNthItemFromList(b:AlternateAllFiles, n)
587 while (onefile != "" && !<SID>EqualFilePaths(fnamemodify(onefile,":p"), fnamemodify(currentFile,":p")))
588 let n = n + 1
589 let onefile = <SID>GetNthItemFromList(b:AlternateAllFiles, n)
590 endwhile
591
592 if (onefile != "")
593 let stop = n
594 let n = n + 1
595 let foundAlternate = 0
596 let nextAlternate = ""
597 while (n != stop)
598 let nextAlternate = <SID>GetNthItemFromList(b:AlternateAllFiles, n)
599 if (nextAlternate == "")
600 let n = 1
601 continue
602 endif
603 let n = n + 1
604 if (<SID>EqualFilePaths(fnamemodify(nextAlternate, ":p"), fnamemodify(currentFile, ":p")))
605 continue
606 endif
607 if (filereadable(nextAlternate))
608 " on cygwin filereadable("foo.H") returns true if "foo.h" exists
609 if (has("unix") && $WINDIR != "" && fnamemodify(nextAlternate, ":p") ==? fnamemodify(currentFile, ":p"))
610 continue
611 endif
612 let foundAlternate = 1
613 break
614 endif
615 endwhile
616 if (foundAlternate == 1)
617 let s:AlternateAllFiles = b:AlternateAllFiles
618 "silent! execute ":e".a:bang." " . nextAlternate
619 call <SID>FindOrCreateBuffer(nextAlternate, "n".a:bang, 0)
620 let b:AlternateAllFiles = s:AlternateAllFiles
621 else
622 echo "Only this alternate file exists"
623 endif
624 else
625 echo "Could not find current file in alternates list"
626 endif
627 else
628 echo "No other alternate files exist"
629 endif
630 endfunction
631
632 comm! -nargs=? -bang A call AlternateFile("n<bang>", <f-args>)
633 comm! -nargs=? -bang AS call AlternateFile("h<bang>", <f-args>)
634 comm! -nargs=? -bang AV call AlternateFile("v<bang>", <f-args>)
635 comm! -nargs=? -bang AT call AlternateFile("t<bang>", <f-args>)
636 comm! -nargs=? -bang AN call NextAlternate("<bang>")
637
638 " Function : BufferOrFileExists (PRIVATE)
639 " Purpose : determines if a buffer or a readable file exists
640 " Args : fileName (IN) - name of the file to check
641 " Returns : 2 if it exists in memory, 1 if it exists, 0 otherwise
642 " Author : Michael Sharpe <feline@irendi.com>
643 " History : Updated code to handle buffernames using just the
644 " filename and not the path.
645 function! <SID>BufferOrFileExists(fileName)
646 let result = 0
647
648 let lastBuffer = bufnr("$")
649 let i = 1
650 while i <= lastBuffer
651 if <SID>EqualFilePaths(expand("#".i.":p"), a:fileName)
652 let result = 2
653 break
654 endif
655 let i = i + 1
656 endwhile
657
658 if (!result)
659 let bufName = fnamemodify(a:fileName,":t")
660 let memBufName = bufname(bufName)
661 if (memBufName != "")
662 let memBufBasename = fnamemodify(memBufName, ":t")
663 if (bufName == memBufBasename)
664 let result = 2
665 endif
666 endif
667
668 if (!result)
669 let result = bufexists(bufName) || bufexists(a:fileName) || filereadable(a:fileName)
670 endif
671 endif
672
673 if (!result)
674 let result = filereadable(a:fileName)
675 endif
676 return result
677 endfunction
678
679 " Function : FindOrCreateBuffer (PRIVATE)
680 " Purpose : searches the buffer list (:ls) for the specified filename. If
681 " found, checks the window list for the buffer. If the buffer is in
682 " an already open window, it switches to the window. If the buffer
683 " was not in a window, it switches to that buffer. If the buffer did
684 " not exist, it creates it.
685 " Args : filename (IN) -- the name of the file
686 " doSplit (IN) -- indicates whether the window should be split
687 " ("v", "h", "n", "v!", "h!", "n!", "t", "t!")
688 " findSimilar (IN) -- indicate weather existing buffers should be
689 " prefered
690 " Returns : nothing
691 " Author : Michael Sharpe <feline@irendi.com>
692 " History : + bufname() was not working very well with the possibly strange
693 " paths that can abound with the search path so updated this
694 " slightly. -- Bindu
695 " + updated window switching code to make it more efficient -- Bindu
696 " Allow ! to be applied to buffer/split/editing commands for more
697 " vim/vi like consistency
698 " + implemented fix from Matt Perry
699 function! <SID>FindOrCreateBuffer(fileName, doSplit, findSimilar)
700 " Check to see if the buffer is already open before re-opening it.
701 let FILENAME = escape(a:fileName, ' ')
702 let bufNr = -1
703 let lastBuffer = bufnr("$")
704 let i = 1
705 if (a:findSimilar)
706 while i <= lastBuffer
707 if <SID>EqualFilePaths(expand("#".i.":p"), a:fileName)
708 let bufNr = i
709 break
710 endif
711 let i = i + 1
712 endwhile
713
714 if (bufNr == -1)
715 let bufName = bufname(a:fileName)
716 let bufFilename = fnamemodify(a:fileName,":t")
717
718 if (bufName == "")
719 let bufName = bufname(bufFilename)
720 endif
721
722 if (bufName != "")
723 let tail = fnamemodify(bufName, ":t")
724 if (tail != bufFilename)
725 let bufName = ""
726 endif
727 endif
728 if (bufName != "")
729 let bufNr = bufnr(bufName)
730 let FILENAME = bufName
731 endif
732 endif
733 endif
734
735 if (g:alternateRelativeFiles == 1)
736 let FILENAME = fnamemodify(FILENAME, ":p:.")
737 endif
738
739 let splitType = a:doSplit[0]
740 let bang = a:doSplit[1]
741 if (bufNr == -1)
742 " Buffer did not exist....create it
743 let v:errmsg=""
744 if (splitType == "h")
745 silent! execute ":split".bang." " . FILENAME
746 elseif (splitType == "v")
747 silent! execute ":vsplit".bang." " . FILENAME
748 elseif (splitType == "t")
749 silent! execute ":tab split".bang." " . FILENAME
750 else
751 silent! execute ":e".bang." " . FILENAME
752 endif
753 if (v:errmsg != "")
754 echo v:errmsg
755 endif
756 else
757
758 " Find the correct tab corresponding to the existing buffer
759 let tabNr = -1
760 " iterate tab pages
761 for i in range(tabpagenr('$'))
762 " get the list of buffers in the tab
763 let tabList = tabpagebuflist(i + 1)
764 let idx = 0
765 " iterate each buffer in the list
766 while idx < len(tabList)
767 " if it matches the buffer we are looking for...
768 if (tabList[idx] == bufNr)
769 " ... save the number
770 let tabNr = i + 1
771 break
772 endif
773 let idx = idx + 1
774 endwhile
775 if (tabNr != -1)
776 break
777 endif
778 endfor
779 " switch the the tab containing the buffer
780 if (tabNr != -1)
781 execute "tabn ".tabNr
782 endif
783
784 " Buffer was already open......check to see if it is in a window
785 let bufWindow = bufwinnr(bufNr)
786 if (bufWindow == -1)
787 " Buffer was not in a window so open one
788 let v:errmsg=""
789 if (splitType == "h")
790 silent! execute ":sbuffer".bang." " . FILENAME
791 elseif (splitType == "v")
792 silent! execute ":vert sbuffer " . FILENAME
793 elseif (splitType == "t")
794 silent! execute ":tab sbuffer " . FILENAME
795 else
796 silent! execute ":buffer".bang." " . FILENAME
797 endif
798 if (v:errmsg != "")
799 echo v:errmsg
800 endif
801 else
802 " Buffer is already in a window so switch to the window
803 execute bufWindow."wincmd w"
804 if (bufWindow != winnr())
805 " something wierd happened...open the buffer
806 let v:errmsg=""
807 if (splitType == "h")
808 silent! execute ":split".bang." " . FILENAME
809 elseif (splitType == "v")
810 silent! execute ":vsplit".bang." " . FILENAME
811 elseif (splitType == "t")
812 silent! execute ":tab split".bang." " . FILENAME
813 else
814 silent! execute ":e".bang." " . FILENAME
815 endif
816 if (v:errmsg != "")
817 echo v:errmsg
818 endif
819 endif
820 endif
821 endif
822 endfunction
823
824 " Function : EqualFilePaths (PRIVATE)
825 " Purpose : Compares two paths. Do simple string comparison anywhere but on
826 " Windows. On Windows take into account that file paths could differ
827 " in usage of separators and the fact that case does not matter.
828 " "c:\WINDOWS" is the same path as "c:/windows". has("win32unix") Vim
829 " version does not count as one having Windows path rules.
830 " Args : path1 (IN) -- first path
831 " path2 (IN) -- second path
832 " Returns : 1 if path1 is equal to path2, 0 otherwise.
833 " Author : Ilya Bobir <ilya@po4ta.com>
834 function! <SID>EqualFilePaths(path1, path2)
835 if has("win16") || has("win32") || has("win64") || has("win95")
836 return substitute(a:path1, "\/", "\\", "g") ==? substitute(a:path2, "\/", "\\", "g")
837 else
838 return a:path1 == a:path2
839 endif
840 endfunction