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