a.vim 31 KB

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