You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

a.vim 31KB

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