diff --git a/.vim/doc/alternate.txt b/.vim/doc/alternate.txt new file mode 100644 index 0000000..1369494 --- /dev/null +++ b/.vim/doc/alternate.txt @@ -0,0 +1,177 @@ + +*alternate.txt* Alternate Plugin Sat May 13 15:35:38 CDT 2006 + +Author: Michael Sharpe +Copyright: (c) 2000-2006 Michael Sharpe + We grant permission to use, copy modify, distribute, and sell this + software for any purpose without fee, provided that the above + copyright notice and this text are not removed. We make no guarantee + about the suitability of this software for any purpose and we are + not liable for any damages resulting from its use. Further, we are + under no obligation to maintain or extend this software. It is + provided on an "as is" basis without any expressed or implied + warranty. + +============================================================================== +1. Contents *AS* *AV* *AT* *AN* *IH* *IHS* *IHV* *IHT* *IHN* *alternate* + + 1. Contents...........................: |alternate| + 2. Purpose............................: |alternate-purpose| + 3. Commands...........................: |alternate-commands| + 4. Configuration......................: |alternate-config| + 5. Installation.......................: |alternate-installation| + 6. Bugs/Enhancements..................: |alternate-support| + 7. Acknowledgments....................: |alternate-acknowledgments| + +============================================================================== +2. Purpose *alternate-purpose* + +The purpose of a.vim is to allow quick and easy switching between source files +and corresponding header files. Many languages (C, C++, ada, ocaml, lex/yacc) +have the concept of source/header files or the like. It is quite common during +development or review to need to edit both files together. This plugin attempts +to simplify that process. There are commands which while editing a source +file allow for the quick switching to the corresponding header and vice versa. +The only difference between the commands is how the switch occurs. More recent +functionality allow the switching to a file under the cursor too. In the +following sections the commands, configuration and installation procedures are +described. + +============================================================================== +3. Commands *alternate-commands* + +There are 4 commands provided by this plugin. They are + +:A switches to the header file corresponding to the source file in the current + buffer (or vice versa). + +:AS similar to :A except the current buffer is split horizontally such that the + source file is on one split and the header is in the other. + +:AV similar to :AS except that the split is vertical + +:AT similar to :AS and :AV except a new tab is opened instead of a split + +:IH switches to the file under cursor (or the file specified with the + command). This command uses the builtin a.vim search path support and the + &path variable in conjunction. + +:IHS similar to :IH execpt the current buffer is split horizontally first + +:IHS similar to :IH execpt the current buffer is split vertically first + +:IHS similar to :IH execpt a new tab is created for the file being switched to + +:IHN switches to the next matching file for the original selection + +In all cases if the corresponding alternate file is already loaded that buffer +is preferred. That is this plugin will never load the same file twice. + +Some maps are also provided for the IH command (mainly for example purposes) +ih - switches to the file under the cursor using the :IHS command +is - switches to the source file of the header file under the cursor + using the :IHS command and the :A command +ihn - switches to the next match in the sequence. + +============================================================================== +4. Configuration *alternate-config* + +It is possible to configure three separate pieces of behaviour of this plugin. + +a) Extensions: Each language has different extensions for identifying the +source and header files. Many languages support multiple different but related +extensions. As such this plugin allow for the complete specification of how +source and header files correspond to each other via extension maps. There are +a number of maps built in. For example, the following variable setting + + g:alternateExtensions_CPP = "inc,h,H,HPP,hpp" + +indicates that any file with a .CPP exetension can have a corresponding file +with any of the .inc, .h, .H, .HPP, .hpp extension. The inverse is not +specified by this map though. Typically each extension will have a mapping. So +there would exist maps for .h, .inc, .H, .HPP, .hpp too. Extension maps should +be specified before loading this plugin. Some of the builtin extension maps are +as follows, + +C and C++ +g:alternateExtensions_h = "c,cpp,cxx,cc,CC" +g:alternateExtensions_H' = "C,CPP,CXX,CC" +g:alternateExtensions_cpp' = "h,hpp" +g:alternateExtensions_CPP' = "H,HPP" +g:alternateExtensions_c' = "h" +g:alternateExtensions_C' = "H" +g:alternateExtensions_cxx' = "h" + +Ada +g:alternateExtensions_adb' = "ads" +g:alternateExtensions_ads' = "adb" + +Lex/Yacc +g:alternateExtensions_l' = "y,yacc,ypp" +g:alternateExtensions_lex' = "yacc,y,ypp" +g:alternateExtensions_lpp' = "ypp,y,yacc" +g:alternateExtensions_y' = "l,lex,lpp" +g:alternateExtensions_yacc' = "lex,l,lpp" +g:alternateExtensions_ypp' = "lpp,l,lex" + +b) Search Paths: In many projects the location of the source files and the +corresponding header files is not always the same directory. This plugin allows +the search path it uses to locate source and header files to be configured. +The search path is specified by setting the g:alternateSearchPath variable. The +default setting is as follows, + + g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc' + +This indicates that the corresponding file will be searched for in ../source, +../src. ../include and ../inc all relative to the current file being switched +from. The value of the g:alternateSearchPath variable is simply a comma +separated list of prefixes and directories. The "sfr:" prefix indicates that +the path is relative to the file. Other prefixes are "wdr:" which indicates +that the directory is relative to the current working directory and "abs:" +which indicates the path is absolute. If no prefix is specified "sfr:" is +assumed. + +c) Regex Paths: Another type of prefix which can appear in the +g:alternateSearchPath variable is that of "reg:". It is used to apply a regex +to the path of the file in the buffer being switched from to locate the +alternate file. E.g. 'reg:/inc/src/g/' will replace every instance of 'inc' +with 'src' in the source file path. It is possible to use match variables so +you could do something like: 'reg:|src/\([^/]*\)|inc/\1||' (see |substitute|, +|help pattern| and |sub-replace-special| for more details. The exact syntax of +a "reg:" specification is + reg: + + seperator character, we often use one of [/|%#] + is what you are looking for + is the output pattern + can be g for global replace or empty + +d) No Alternate Behaviour: When attempting to alternate/switch from a +source/header to its corresponding file it is possible that the corresponding +file does not exist. In this case this plugin will create the missing alternate +file in the same directory as the current file. Some users find this behaviour +irritating. This behaviour can be disabled by setting +g:alternateNoDefaultAlternate to 1. When this variable is not 0 a message will +be displayed indicating that no alternate file exists. + +============================================================================== +5. Installation *alternate-installation* + +To install this plugin simply drop the a.vim file in to $VIMRUNTIME/plugin +(global or local) or simply source the file from the vimrc file. Ensure that +any configuration occurs before the plugin is loaded/sourced. + +============================================================================== +6. Bugs/Enhancements *alternate-support* + +Whilst no formal support is provided for this plugin the author is always happy +to receive bug reports and enhancement requests. Please email all such +reports/requests to feline@irendi.com. + +============================================================================== +7. Acknowledgments *alternate-acknowledgments* + +The author would like to thank everyone who has submitted bug reports and +feature enhancement requests in the past. In particular Bindu Wavell provided +much of the original code implementing the search path and regex functionality. +vim:tw=78:ts=8:ft=help diff --git a/.vim/plugin/a.vim b/.vim/plugin/a.vim new file mode 100644 index 0000000..637feb5 --- /dev/null +++ b/.vim/plugin/a.vim @@ -0,0 +1,840 @@ +" Copyright (c) 1998-2006 +" Michael Sharpe +" +" We grant permission to use, copy modify, distribute, and sell this +" software for any purpose without fee, provided that the above copyright +" notice and this text are not removed. We make no guarantee about the +" suitability of this software for any purpose and we are not liable +" for any damages resulting from its use. Further, we are under no +" obligation to maintain or extend this software. It is provided on an +" "as is" basis without any expressed or implied warranty. + +" Directory & regex enhancements added by Bindu Wavell who is well known on +" vim.sf.net +" +" Patch for spaces in files/directories from Nathan Stien (also reported by +" Soeren Sonnenburg) + +" Do not load a.vim if is has already been loaded. +if exists("loaded_alternateFile") + finish +endif +if (v:progname == "ex") + finish +endif +let loaded_alternateFile = 1 + +let alternateExtensionsDict = {} + +" setup the default set of alternate extensions. The user can override in thier +" .vimrc if the defaults are not suitable. To override in a .vimrc simply set a +" g:alternateExtensions_ variable to a comma separated list of alternates, +" where is the extension to map. +" E.g. let g:alternateExtensions_CPP = "inc,h,H,HPP,hpp" +" let g:alternateExtensions_{'aspx.cs'} = "aspx" + + +" This variable will be increased when an extension with greater number of dots +" is added by the AddAlternateExtensionMapping call. +let s:maxDotsInExtension = 1 + +" Function : AddAlternateExtensionMapping (PRIVATE) +" Purpose : simple helper function to add the default alternate extension +" mappings. +" Args : extension -- the extension to map +" alternates -- comma separated list of alternates extensions +" Returns : nothing +" Author : Michael Sharpe +function! AddAlternateExtensionMapping(extension, alternates) + " This code does not actually work for variables like foo{'a.b.c.d.e'} + "let varName = "g:alternateExtensions_" . a:extension + "if (!exists(varName)) + " let g:alternateExtensions_{a:extension} = a:alternates + "endif + + " This code handles extensions which contains a dot. exists() fails with + " such names. + "let v:errmsg = "" + " FIXME this line causes ex to return 1 instead of 0 for some reason?? + "silent! echo g:alternateExtensions_{a:extension} + "if (v:errmsg != "") + "let g:alternateExtensions_{a:extension} = a:alternates + "endif + + let g:alternateExtensionsDict[a:extension] = a:alternates + let dotsNumber = strlen(substitute(a:extension, "[^.]", "", "g")) + if s:maxDotsInExtension < dotsNumber + let s:maxDotsInExtension = dotsNumber + endif +endfunction + + +" Add all the default extensions +" Mappings for C and C++ +call AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC") +call AddAlternateExtensionMapping('H',"C,CPP,CXX,CC") +call AddAlternateExtensionMapping('hpp',"cpp,c") +call AddAlternateExtensionMapping('HPP',"CPP,C") +call AddAlternateExtensionMapping('c',"h") +call AddAlternateExtensionMapping('C',"H") +call AddAlternateExtensionMapping('cpp',"h,hpp") +call AddAlternateExtensionMapping('CPP',"H,HPP") +call AddAlternateExtensionMapping('cc',"h") +call AddAlternateExtensionMapping('CC',"H,h") +call AddAlternateExtensionMapping('cxx',"h") +call AddAlternateExtensionMapping('CXX',"H") +" Mappings for PSL7 +call AddAlternateExtensionMapping('psl',"ph") +call AddAlternateExtensionMapping('ph',"psl") +" Mappings for ADA +call AddAlternateExtensionMapping('adb',"ads") +call AddAlternateExtensionMapping('ads',"adb") +" Mappings for lex and yacc files +call AddAlternateExtensionMapping('l',"y,yacc,ypp") +call AddAlternateExtensionMapping('lex',"yacc,y,ypp") +call AddAlternateExtensionMapping('lpp',"ypp,y,yacc") +call AddAlternateExtensionMapping('y',"l,lex,lpp") +call AddAlternateExtensionMapping('yacc',"lex,l,lpp") +call AddAlternateExtensionMapping('ypp',"lpp,l,lex") +" Mappings for OCaml +call AddAlternateExtensionMapping('ml',"mli") +call AddAlternateExtensionMapping('mli',"ml") +" ASP stuff +call AddAlternateExtensionMapping('aspx.cs', 'aspx') +call AddAlternateExtensionMapping('aspx.vb', 'aspx') +call AddAlternateExtensionMapping('aspx', 'aspx.cs,aspx.vb') + +" Setup default search path, unless the user has specified +" a path in their [._]vimrc. +if (!exists('g:alternateSearchPath')) + let g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc' +endif + +" If this variable is true then a.vim will not alternate to a file/buffer which +" does not exist. E.g while editing a.c and the :A will not swtich to a.h +" unless it exists. +if (!exists('g:alternateNoDefaultAlternate')) + " by default a.vim will alternate to a file which does not exist + let g:alternateNoDefaultAlternate = 0 +endif + +" If this variable is true then a.vim will convert the alternate filename to a +" filename relative to the current working directory. +" Feature by Nathan Huizinga +if (!exists('g:alternateRelativeFiles')) + " by default a.vim will not convert the filename to one relative to the + " current working directory + let g:alternateRelativeFiles = 0 +endif + + +" Function : GetNthItemFromList (PRIVATE) +" Purpose : Support reading items from a comma seperated list +" Used to iterate all the extensions in an extension spec +" Used to iterate all path prefixes +" Args : list -- the list (extension spec, file paths) to iterate +" n -- the extension to get +" Returns : the nth item (extension, path) from the list (extension +" spec), or "" for failure +" Author : Michael Sharpe +" History : Renamed from GetNthExtensionFromSpec to GetNthItemFromList +" to reflect a more generic use of this function. -- Bindu +function! GetNthItemFromList(list, n) + let itemStart = 0 + let itemEnd = -1 + let pos = 0 + let item = "" + let i = 0 + while (i != a:n) + let itemStart = itemEnd + 1 + let itemEnd = match(a:list, ",", itemStart) + let i = i + 1 + if (itemEnd == -1) + if (i == a:n) + let itemEnd = strlen(a:list) + endif + break + endif + endwhile + if (itemEnd != -1) + let item = strpart(a:list, itemStart, itemEnd - itemStart) + endif + return item +endfunction + +" Function : ExpandAlternatePath (PRIVATE) +" Purpose : Expand path info. A path with a prefix of "wdr:" will be +" treated as relative to the working directory (i.e. the +" directory where vim was started.) A path prefix of "abs:" will +" be treated as absolute. No prefix or "sfr:" will result in the +" path being treated as relative to the source file (see sfPath +" argument). +" +" A prefix of "reg:" will treat the pathSpec as a regular +" expression substitution that is applied to the source file +" path. The format is: +" +" reg: +" +" seperator character, we often use one of [/|%#] +" is what you are looking for +" is the output pattern +" can be g for global replace or empty +" +" EXAMPLE: 'reg:/inc/src/g/' will replace every instance +" of 'inc' with 'src' in the source file path. It is possible +" to use match variables so you could do something like: +" 'reg:|src/\([^/]*\)|inc/\1||' (see 'help :substitute', +" 'help pattern' and 'help sub-replace-special' for more details +" +" NOTE: a.vim uses ',' (comma) internally so DON'T use it +" in your regular expressions or other pathSpecs unless you update +" the rest of the a.vim code to use some other seperator. +" +" Args : pathSpec -- path component (or substitution patterns) +" sfPath -- source file path +" Returns : a path that can be used by AlternateFile() +" Author : Bindu Wavell +function! ExpandAlternatePath(pathSpec, sfPath) + let prfx = strpart(a:pathSpec, 0, 4) + if (prfx == "wdr:" || prfx == "abs:") + let path = strpart(a:pathSpec, 4) + elseif (prfx == "reg:") + let re = strpart(a:pathSpec, 4) + let sep = strpart(re, 0, 1) + let patend = match(re, sep, 1) + let pat = strpart(re, 1, patend - 1) + let subend = match(re, sep, patend + 1) + let sub = strpart(re, patend+1, subend - patend - 1) + let flag = strpart(re, strlen(re) - 2) + if (flag == sep) + let flag = '' + endif + let path = substitute(a:sfPath, pat, sub, flag) + "call confirm('PAT: [' . pat . '] SUB: [' . sub . ']') + "call confirm(a:sfPath . ' => ' . path) + else + let path = a:pathSpec + if (prfx == "sfr:") + let path = strpart(path, 4) + endif + let path = a:sfPath . "/" . path + endif + return path +endfunction + +" Function : FindFileInSearchPath (PRIVATE) +" Purpose : Searches for a file in the search path list +" Args : filename -- name of the file to search for +" pathList -- the path list to search +" relPathBase -- the path which relative paths are expanded from +" Returns : An expanded filename if found, the empty string otherwise +" Author : Michael Sharpe (feline@irendi.com) +" History : inline code written by Bindu Wavell originally +function! FindFileInSearchPath(fileName, pathList, relPathBase) + let filepath = "" + let m = 1 + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = ExpandAlternatePath(pathSpec, a:relPathBase) + let fullname = path . "/" . a:fileName + let foundMatch = BufferOrFileExists(fullname) + if (foundMatch) + let filepath = fullname + break + endif + else + break + endif + let m = m + 1 + endwhile + endif + return filepath +endfunction + +" Function : FindFileInSearchPathEx (PRIVATE) +" Purpose : Searches for a file in the search path list +" Args : filename -- name of the file to search for +" pathList -- the path list to search +" relPathBase -- the path which relative paths are expanded from +" count -- find the count'th occurence of the file on the path +" Returns : An expanded filename if found, the empty string otherwise +" Author : Michael Sharpe (feline@irendi.com) +" History : Based on FindFileInSearchPath() but with extensions +function! FindFileInSearchPathEx(fileName, pathList, relPathBase, count) + let filepath = "" + let m = 1 + let spath = "" + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = ExpandAlternatePath(pathSpec, a:relPathBase) + if (spath != "") + let spath = spath . ',' + endif + let spath = spath . path + else + break + endif + let m = m + 1 + endwhile + endif + + if (&path != "") + if (spath != "") + let spath = spath . ',' + endif + let spath = spath . &path + endif + + let filepath = findfile(a:fileName, spath, a:count) + return filepath +endfunction + +" Function : EnumerateFilesByExtension (PRIVATE) +" Purpose : enumerates all files by a particular list of alternate extensions. +" Args : path -- path of a file (not including the file) +" baseName -- base name of the file to be expanded +" extension -- extension whose alternates are to be enumerated +" Returns : comma separated list of files with extensions +" Author : Michael Sharpe +function! EnumerateFilesByExtension(path, baseName, extension) + let enumeration = "" + let extSpec = "" + let v:errmsg = "" + silent! echo g:alternateExtensions_{a:extension} + if (v:errmsg == "") + let extSpec = g:alternateExtensions_{a:extension} + endif + if (extSpec == "") + if (has_key(g:alternateExtensionsDict, a:extension)) + let extSpec = g:alternateExtensionsDict[a:extension] + endif + endif + if (extSpec != "") + let n = 1 + let done = 0 + while (!done) + let ext = GetNthItemFromList(extSpec, n) + if (ext != "") + if (a:path != "") + let newFilename = a:path . "/" . a:baseName . "." . ext + else + let newFilename = a:baseName . "." . ext + endif + if (enumeration == "") + let enumeration = newFilename + else + let enumeration = enumeration . "," . newFilename + endif + else + let done = 1 + endif + let n = n + 1 + endwhile + endif + return enumeration +endfunction + +" Function : EnumerateFilesByExtensionInPath (PRIVATE) +" Purpose : enumerates all files by expanding the path list and the extension +" list. +" Args : baseName -- base name of the file +" extension -- extension whose alternates are to be enumerated +" pathList -- the list of paths to enumerate +" relPath -- the path of the current file for expansion of relative +" paths in the path list. +" Returns : A comma separated list of paths with extensions +" Author : Michael Sharpe +function! EnumerateFilesByExtensionInPath(baseName, extension, pathList, relPathBase) + let enumeration = "" + let filepath = "" + let m = 1 + let pathListLen = strlen(a:pathList) + if (pathListLen > 0) + while (1) + let pathSpec = GetNthItemFromList(a:pathList, m) + if (pathSpec != "") + let path = ExpandAlternatePath(pathSpec, a:relPathBase) + let pe = EnumerateFilesByExtension(path, a:baseName, a:extension) + if (enumeration == "") + let enumeration = pe + else + let enumeration = enumeration . "," . pe + endif + else + break + endif + let m = m + 1 + endwhile + endif + return enumeration +endfunction + +" Function : DetermineExtension (PRIVATE) +" Purpose : Determines the extension of a filename based on the register +" alternate extension. This allow extension which contain dots to +" be considered. E.g. foo.aspx.cs to foo.aspx where an alternate +" exists for the aspx.cs extension. Note that this will only accept +" extensions which contain less than 5 dots. This is only +" implemented in this manner for simplicity...it is doubtful that +" this will be a restriction in non-contrived situations. +" Args : The path to the file to find the extension in +" Returns : The matched extension if any +" Author : Michael Sharpe (feline@irendi.com) +" History : idea from Tom-Erik Duestad +" Notes : there is some magic occuring here. The exists() function does not +" work well when the curly brace variable has dots in it. And why +" should it, dots are not valid in variable names. But the exists +" function is wierd too. Lets say foo_c does exist. Then +" exists("foo_c.e.f") will be true...even though the variable does +" not exist. However the curly brace variables do work when the +" variable has dots in it. E.g foo_{'c'} is different from +" foo_{'c.d.e'}...and foo_{'c'} is identical to foo_c and +" foo_{'c.d.e'} is identical to foo_c.d.e right? Yes in the current +" implementation of vim. To trick vim to test for existence of such +" variables echo the curly brace variable and look for an error +" message. +function! DetermineExtension(path) + let mods = ":t" + let i = 0 + while i <= s:maxDotsInExtension + let mods = mods . ":e" + let extension = fnamemodify(a:path, mods) + if (has_key(g:alternateExtensionsDict, extension)) + return extension + endif + let v:errmsg = "" + silent! echo g:alternateExtensions_{extension} + if (v:errmsg == "") + return extension + endif + let i = i + 1 + endwhile + return "" +endfunction + +" Function : AlternateFile (PUBLIC) +" Purpose : Opens a new buffer by looking at the extension of the current +" buffer and finding the corresponding file. E.g. foo.c <--> foo.h +" Args : accepts one argument. If present it used the argument as the new +" extension. +" Returns : nothing +" Author : Michael Sharpe +" History : + When an alternate can't be found in the same directory as the +" source file, a search path will be traversed looking for the +" alternates. +" + Moved some code into a separate function, minor optimization +" + rework to favor files in memory based on complete enumeration of +" all files extensions and paths +function! AlternateFile(splitWindow, ...) + let extension = DetermineExtension(expand("%:p")) + let baseName = substitute(expand("%:t"), "\." . extension . '$', "", "") + let currentPath = expand("%:p:h") + + if (a:0 != 0) + let newFullname = currentPath . "/" . baseName . "." . a:1 + call FindOrCreateBuffer(newFullname, a:splitWindow, 0) + else + let allfiles = "" + if (extension != "") + let allfiles1 = EnumerateFilesByExtension(currentPath, baseName, extension) + let allfiles2 = EnumerateFilesByExtensionInPath(baseName, extension, g:alternateSearchPath, currentPath) + + if (allfiles1 != "") + if (allfiles2 != "") + let allfiles = allfiles1 . ',' . allfiles2 + else + let allfiles = allfiles1 + endif + else + let allfiles = allfiles2 + endif + endif + + if (allfiles != "") + let bestFile = "" + let bestScore = 0 + let score = 0 + let n = 1 + + let onefile = GetNthItemFromList(allfiles, n) + let bestFile = onefile + while (onefile != "" && score < 2) + let score = BufferOrFileExists(onefile) + if (score > bestScore) + let bestScore = score + let bestFile = onefile + endif + let n = n + 1 + let onefile = GetNthItemFromList(allfiles, n) + endwhile + + if (bestScore == 0 && g:alternateNoDefaultAlternate == 1) + echo "No existing alternate available" + else + call FindOrCreateBuffer(bestFile, a:splitWindow, 1) + let b:AlternateAllFiles = allfiles + endif + else + echo "No alternate file/buffer available" + endif + endif +endfunction + +" Function : AlternateOpenFileUnderCursor (PUBLIC) +" Purpose : Opens file under the cursor +" Args : splitWindow -- indicates how to open the file +" Returns : Nothing +" Author : Michael Sharpe (feline@irendi.com) www.irendi.com +function! AlternateOpenFileUnderCursor(splitWindow,...) + let cursorFile = (a:0 > 0) ? a:1 : expand("") + let currentPath = expand("%:p:h") + let openCount = 1 + + let fileName = FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount) + if (fileName != "") + call FindOrCreateBuffer(fileName, a:splitWindow, 1) + let b:openCount = openCount + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + echo "Can't find file" + endif +endfunction + +" Function : AlternateOpenNextFile (PUBLIC) +" Purpose : Opens the next file corresponding to the search which found the +" current file +" Args : bang -- indicates what to do if the current file has not been +" saved +" Returns : nothing +" Author : Michael Sharpe (feline@irendi.com) www.irendi.com +function! AlternateOpenNextFile(bang) + let cursorFile = "" + if (exists("b:cursorFile")) + let cursorFile = b:cursorFile + endif + + let currentPath = "" + if (exists("b:currentPath")) + let currentPath = b:currentPath + endif + + let openCount = 0 + if (exists("b:openCount")) + let openCount = b:openCount + 1 + endif + + if (cursorFile != "" && currentPath != "" && openCount != 0) + let fileName = FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, openCount) + if (fileName != "") + call FindOrCreateBuffer(fileName, "n".a:bang, 0) + let b:openCount = openCount + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + let fileName = FindFileInSearchPathEx(cursorFile, g:alternateSearchPath, currentPath, 1) + if (fileName != "") + call FindOrCreateBuffer(fileName, "n".a:bang, 0) + let b:openCount = 1 + let b:cursorFile = cursorFile + let b:currentPath = currentPath + else + echo "Can't find next file" + endif + endif + endif +endfunction + +comm! -nargs=? -bang IH call AlternateOpenFileUnderCursor("n", ) +comm! -nargs=? -bang IHS call AlternateOpenFileUnderCursor("h", ) +comm! -nargs=? -bang IHV call AlternateOpenFileUnderCursor("v", ) +comm! -nargs=? -bang IHT call AlternateOpenFileUnderCursor("t", ) +comm! -nargs=? -bang IHN call AlternateOpenNextFile("") +imap ih :IHS +nmap ih :IHS +imap is :IHS:A +nmap is :IHS:A +imap ihn :IHN +nmap ihn :IHN + +"function! PrintList(theList) +" let n = 1 +" let oneFile = GetNthItemFromList(a:theList, n) +" while (oneFile != "") +" let n = n + 1 +" let oneFile = GetNthItemFromList(a:theList, n) +" endwhile +"endfunction + +" Function : NextAlternate (PUBLIC) +" Purpose : Used to cycle through any other alternate file which existed on +" the search path. +" Args : bang (IN) - used to implement the AN vs AN! functionality +" Returns : nothing +" Author : Michael Sharpe +function! NextAlternate(bang) + if (exists('b:AlternateAllFiles')) + let currentFile = expand("%") + let n = 1 + let onefile = GetNthItemFromList(b:AlternateAllFiles, n) + while (onefile != "" && !EqualFilePaths(fnamemodify(onefile,":p"), fnamemodify(currentFile,":p"))) + let n = n + 1 + let onefile = GetNthItemFromList(b:AlternateAllFiles, n) + endwhile + + if (onefile != "") + let stop = n + let n = n + 1 + let foundAlternate = 0 + let nextAlternate = "" + while (n != stop) + let nextAlternate = GetNthItemFromList(b:AlternateAllFiles, n) + if (nextAlternate == "") + let n = 1 + continue + endif + let n = n + 1 + if (EqualFilePaths(fnamemodify(nextAlternate, ":p"), fnamemodify(currentFile, ":p"))) + continue + endif + if (filereadable(nextAlternate)) + " on cygwin filereadable("foo.H") returns true if "foo.h" exists + if (has("unix") && $WINDIR != "" && fnamemodify(nextAlternate, ":p") ==? fnamemodify(currentFile, ":p")) + continue + endif + let foundAlternate = 1 + break + endif + endwhile + if (foundAlternate == 1) + let s:AlternateAllFiles = b:AlternateAllFiles + "silent! execute ":e".a:bang." " . nextAlternate + call FindOrCreateBuffer(nextAlternate, "n".a:bang, 0) + let b:AlternateAllFiles = s:AlternateAllFiles + else + echo "Only this alternate file exists" + endif + else + echo "Could not find current file in alternates list" + endif + else + echo "No other alternate files exist" + endif +endfunction + +comm! -nargs=? -bang A call AlternateFile("n", ) +comm! -nargs=? -bang AS call AlternateFile("h", ) +comm! -nargs=? -bang AV call AlternateFile("v", ) +comm! -nargs=? -bang AT call AlternateFile("t", ) +comm! -nargs=? -bang AN call NextAlternate("") + +" Function : BufferOrFileExists (PRIVATE) +" Purpose : determines if a buffer or a readable file exists +" Args : fileName (IN) - name of the file to check +" Returns : 2 if it exists in memory, 1 if it exists, 0 otherwise +" Author : Michael Sharpe +" History : Updated code to handle buffernames using just the +" filename and not the path. +function! BufferOrFileExists(fileName) + let result = 0 + + let lastBuffer = bufnr("$") + let i = 1 + while i <= lastBuffer + if EqualFilePaths(expand("#".i.":p"), a:fileName) + let result = 2 + break + endif + let i = i + 1 + endwhile + + if (!result) + let bufName = fnamemodify(a:fileName,":t") + let memBufName = bufname(bufName) + if (memBufName != "") + let memBufBasename = fnamemodify(memBufName, ":t") + if (bufName == memBufBasename) + let result = 2 + endif + endif + + if (!result) + let result = bufexists(bufName) || bufexists(a:fileName) || filereadable(a:fileName) + endif + endif + + if (!result) + let result = filereadable(a:fileName) + endif + return result +endfunction + +" Function : FindOrCreateBuffer (PRIVATE) +" Purpose : searches the buffer list (:ls) for the specified filename. If +" found, checks the window list for the buffer. If the buffer is in +" an already open window, it switches to the window. If the buffer +" was not in a window, it switches to that buffer. If the buffer did +" not exist, it creates it. +" Args : filename (IN) -- the name of the file +" doSplit (IN) -- indicates whether the window should be split +" ("v", "h", "n", "v!", "h!", "n!", "t", "t!") +" findSimilar (IN) -- indicate weather existing buffers should be +" prefered +" Returns : nothing +" Author : Michael Sharpe +" History : + bufname() was not working very well with the possibly strange +" paths that can abound with the search path so updated this +" slightly. -- Bindu +" + updated window switching code to make it more efficient -- Bindu +" Allow ! to be applied to buffer/split/editing commands for more +" vim/vi like consistency +" + implemented fix from Matt Perry +function! FindOrCreateBuffer(fileName, doSplit, findSimilar) + " Check to see if the buffer is already open before re-opening it. + let FILENAME = escape(a:fileName, ' ') + let bufNr = -1 + let lastBuffer = bufnr("$") + let i = 1 + if (a:findSimilar) + while i <= lastBuffer + if EqualFilePaths(expand("#".i.":p"), a:fileName) + let bufNr = i + break + endif + let i = i + 1 + endwhile + + if (bufNr == -1) + let bufName = bufname(a:fileName) + let bufFilename = fnamemodify(a:fileName,":t") + + if (bufName == "") + let bufName = bufname(bufFilename) + endif + + if (bufName != "") + let tail = fnamemodify(bufName, ":t") + if (tail != bufFilename) + let bufName = "" + endif + endif + if (bufName != "") + let bufNr = bufnr(bufName) + let FILENAME = bufName + endif + endif + endif + + if (g:alternateRelativeFiles == 1) + let FILENAME = fnamemodify(FILENAME, ":p:.") + endif + + let splitType = a:doSplit[0] + let bang = a:doSplit[1] + if (bufNr == -1) + " Buffer did not exist....create it + let v:errmsg="" + if (splitType == "h") + silent! execute ":split".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vsplit".bang." " . FILENAME + elseif (splitType == "t") + silent! execute ":tab split".bang." " . FILENAME + else + silent! execute ":e".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + else + + " Find the correct tab corresponding to the existing buffer + let tabNr = -1 + " iterate tab pages + for i in range(tabpagenr('$')) + " get the list of buffers in the tab + let tabList = tabpagebuflist(i + 1) + let idx = 0 + " iterate each buffer in the list + while idx < len(tabList) + " if it matches the buffer we are looking for... + if (tabList[idx] == bufNr) + " ... save the number + let tabNr = i + 1 + break + endif + let idx = idx + 1 + endwhile + if (tabNr != -1) + break + endif + endfor + " switch the the tab containing the buffer + if (tabNr != -1) + execute "tabn ".tabNr + endif + + " Buffer was already open......check to see if it is in a window + let bufWindow = bufwinnr(bufNr) + if (bufWindow == -1) + " Buffer was not in a window so open one + let v:errmsg="" + if (splitType == "h") + silent! execute ":sbuffer".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vert sbuffer " . FILENAME + elseif (splitType == "t") + silent! execute ":tab sbuffer " . FILENAME + else + silent! execute ":buffer".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + else + " Buffer is already in a window so switch to the window + execute bufWindow."wincmd w" + if (bufWindow != winnr()) + " something wierd happened...open the buffer + let v:errmsg="" + if (splitType == "h") + silent! execute ":split".bang." " . FILENAME + elseif (splitType == "v") + silent! execute ":vsplit".bang." " . FILENAME + elseif (splitType == "t") + silent! execute ":tab split".bang." " . FILENAME + else + silent! execute ":e".bang." " . FILENAME + endif + if (v:errmsg != "") + echo v:errmsg + endif + endif + endif + endif +endfunction + +" Function : EqualFilePaths (PRIVATE) +" Purpose : Compares two paths. Do simple string comparison anywhere but on +" Windows. On Windows take into account that file paths could differ +" in usage of separators and the fact that case does not matter. +" "c:\WINDOWS" is the same path as "c:/windows". has("win32unix") Vim +" version does not count as one having Windows path rules. +" Args : path1 (IN) -- first path +" path2 (IN) -- second path +" Returns : 1 if path1 is equal to path2, 0 otherwise. +" Author : Ilya Bobir +function! EqualFilePaths(path1, path2) + if has("win16") || has("win32") || has("win64") || has("win95") + return substitute(a:path1, "\/", "\\", "g") ==? substitute(a:path2, "\/", "\\", "g") + else + return a:path1 == a:path2 + endif +endfunction diff --git a/.vim/plugin/lid.vim b/.vim/plugin/lid.vim new file mode 100644 index 0000000..947e59f --- /dev/null +++ b/.vim/plugin/lid.vim @@ -0,0 +1,394 @@ +" File: lid.vim +" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +" Version: 2.4 +" Last Modified: May 29 2003 +" +" Overview +" -------- +" The lid.vim Vim plugin provides a way to interact with the lid tool to +" lookup keywords in the ID database. +" +" For more information about id utilities (lid, aid, etc), visit the +" following pages: +" +" http://www.delorie.com/gnu/docs/id-utils/id-utils_toc.html +" http://www.gnu.org/software/idutils/idutils.html + +" You can download the id-utils binaries for Windows (DJGPP version) +" from: +" +" ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/idu32b.zip +" +" Installation +" ------------ +" 1. Copy the lid.vim file to the $HOME/.vim/plugin directory. Refer to +" ':help add-plugin', ':help add-global-plugin' and ':help runtimepath' for +" more details about Vim plugins. +" 2. Set the LID_Cmd variable to point to the lid utility path. +" 3. Restart Vim. +" 4. You can use the ":Lid" command to search for a keyword. +" +" This plugin will not work in 'compatible' mode. Make sure the 'compatible' +" option is not set. +" +" Usage +" ----- +" You can lookup keywords in the ID database using the 'Lid' command. For +" example, +" +" :Lid +" +" This will prompt you for the keyword to lookup. The default is the current +" keyword under the cursor. You can retrieve previously entered keywords +" using the up and down arrow keys. You can cancel the lookup by pressing the +" escape key. +" +" You can map a key to invoke the Lid command: +" +" nnoremap :Lid +" +" Add the above mapping to your ~/.vimrc file. +" +" You can also specify the keyword to the Lid command like this: +" +" :Lid +" +" In the above command format, you can press the key to expand +" keywords from a tags file. +" +" You can use the "-p" and "-v" option to the 'Lid' command to selectively +" display lines from the lid output. You can use the "-p" option to the 'Lid' +" command to list only those lid matches that contain a pattern. You can use +" the "-v" option to the 'Lid' command to list only those lid matches that +" does not contain a pattern. Only one of the "-p" or "-v" options can be used +" at a time. +" +" :Lid -p +" :Lid -v +" +" If you use the any one of the above options, you will prompted to enter the +" pattern you are interested in. +" +" The output of the lid command will be listed in the Vim quickfix window. +" 1. You can select a line in the quickfix window and press or double +" click on a match to jump to that line. +" 2. You can use the ":cnext" and ":cprev" commands to the jump to the next or +" previous output line. +" 3. You can use the ":colder" and ":cnewer" commands to go between multiple +" Lid quickfix output windows. +" 4. The quickfix window need not be opened always to use the lid output. +" You can close the quickfix window and use the quickfix commands to jump +" to the lid matches. Use the ":copen" command to open the quickfix +" window again. +" +" For more information about other quickfix commands read ":help quickfix" +" +" Configuration +" ------------- +" By changing the following variables you can configure the behavior of this +" plugin. Set the following variables in your .vimrc file using the 'let' +" command. +" +" The path to the lid executable is specified by the 'LID_Cmd' variable. By +" default, this variable is set to lid. You can change the lid executable path +" by setting the 'LID_Cmd' variable: +" +" let LID_Cmd = '/my/path/lid' +" +" By default, this plugin uses 'ID' as the name of the database. This is +" defined by the 'LID_File' variable. You can change the name/location of the +" ID database by setting the 'LID_File' variable: +" +" let LID_File = '/my/path/ID' +" +" You can also specify more than one ID file names in the LID_File variable. +" The ID file names should be separated by a ',' character. +" +" let LID_File = '/my/path1/ID,/my/path2/ID,/my/path3/ID' +" +" The plugin will use the first ID file name and run lid using that filename. +" If a match is found, it will return the results. If a match is not found, +" then the second ID file name will be used and this will be repeated till +" either a match is found or all the specified ID file names are processed. +" +" If more than one ID file is specified using the 'LID_File' variable, you can +" set the 'LID_Search_Multiple_ID_Files' variable to 1 to always search for a +" keyword in all the specified ID files. By default, +" 'LID_Search_Multiple_ID_Files' variable is set to one. All the specified ID +" files are searched for the keyword. +" +" let LID_Search_Multiple_ID_Files = 0 +" +" By default, when you invoke the :Lid command the quickfix window will be +" opened with the lid output. You can disable opening the quickfix window, +" by setting the 'LID_OpenQuickfixWindow' variable to 1: +" +" let LID_OpenQuickfixWindow = 1 +" +" You can manually open the quickfix window using the :cwindow command. +" +" The 'LID_Shell_Quote_Char' variable specifies the character to use to +" escape the keyword before passing it to the lid command. By default, +" for MS-Windows systems, no shell quote character is set. For other +" systems, "'" is used as the the shell quote character. You can change +" this by setting the 'LID_Shell_Quote_Char' variable: +" +" let LID_Shell_Quote_Char = '"' +" +" By default, the ID file specified by the LID_File variable will be used. If +" you set the 'LID_Prompt_ID_Filename' variable to 1, then every time the LID +" command will prompt for the location of the ID file. The default ID file +" location displayed will be the last used value. You can again set the +" 'LID_Prompt_ID_Filename' variable back to 0, to turn off this prompt. The +" last supplied ID file will be used for further Lid invocations. +" +" let LID_Prompt_ID_Filename = 1 +" +" By default, when the lid output is displayed, the cursor will be +" automatically positioned at the first matching line. You can set the +" LID_Jump_To_Match variable to 0 to prevent this. In this case, only the +" output from LID will be displayed. +" +" let LID_Jump_To_Match = 0 +" +" +" --------------------- Do not modify after this line --------------------- +if exists('loaded_lid') || &cp + finish +endif +let loaded_lid = 1 + +" The default location of the lid tool. +if !exists('LID_Cmd') + let LID_Cmd = 'lid' +endif + +" Name of the ID file to supply to lid +if !exists('LID_File') + let LID_File = 'ID' +endif + +" Combine matches from more than one ID file? +if !exists('LID_Search_Multiple_ID_Files') + let LID_Search_Multiple_ID_Files = 1 +endif + +" Open the LID output window. Set this variable to zero, to not open +" the LID output window by default. You can open it manually by using +" the :cwindow command. +if !exists('LID_OpenQuickfixWindow') + let LID_OpenQuickfixWindow = 1 +endif + +" Character to use to escape patterns and filenames before passing to lid. +if !exists('LID_Shell_Quote_Char') + if has('win32') || has('win16') || has('win95') + let LID_Shell_Quote_Char = '' + else + let LID_Shell_Quote_Char = "'" + endif +endif + +" This option controls whether the user is asked for the ID file every +" time the Lid command is run. By default, the ID file specified by +" the LID_File variable is used. +if !exists('LID_Prompt_ID_Filename') + let LID_Prompt_ID_Filename = 0 +endif + +" By default, when the lid output is displayed, the cursor will be +" automatically positioned at the first matching line. You can set the +" LID_Jump_To_Match variable to 0 to prevent this. In this case, only the +" output from LID will be displayed. +if !exists('LID_Jump_To_Match') + let LID_Jump_To_Match = 1 +endif + +" Extract lines matching the supplied pattern from the supplied text +function! s:ExtractMatchingLines(txt, pattern) + let filter_output = '' + let t = a:txt + + let len = strlen(t) + + while t != '' + let one_line = strpart(t, 0, stridx(t, "\n")) + let t = strpart(t, stridx(t, "\n") + 1, len) + + if one_line =~# a:pattern + let filter_output = filter_output . one_line . "\n" + endif + endwhile + + return filter_output +endfunction + +" Remove lines matching the supplied pattern from the supplied text +function! s:RemoveMatchingLines(txt, pattern) + let filter_output = '' + let t = a:txt + + let len = strlen(t) + + while t != '' + let one_line = strpart(t, 0, stridx(t, "\n")) + let t = strpart(t, stridx(t, "\n") + 1, len) + + if one_line !~# a:pattern + let filter_output = filter_output . one_line . "\n" + endif + endwhile + + return filter_output +endfunction + +" Run lid using the supplied arguments +function! s:RunLid(...) + let usage = 'Usage: Lid [[-p] [-v] [-?] [-h] [identifier]]' + + " If the user wanted to select the ID file everytime, Lid is run, + " then ask for the location of the file. Use the last ID file as + " the default. First time, use the global value as the default. + if g:LID_Prompt_ID_Filename + if !exists('s:LID_last_ID_file') + let s:LID_last_ID_file = g:LID_File + endif + let id_file = input('Location of ID file: ', s:LID_last_ID_file) + if id_file == '' + return + endif + let s:LID_last_ID_file = id_file + else + let id_file = g:LID_File + endif + + let skip_pat = '' + let match_pat = '' + + if a:0 == 0 || a:1 == '-p' || a:1 == '-v' + " Get the identifier from the user, if it is not already supplied + let id = input('Lookup identifier: ', expand('')) + if id == '' + return + endif + endif + + " Process options + if a:0 != 0 + if a:1 == '-p' + let match_pat = input('Include only lines containing: ', '') + elseif a:1 == '-v' + let skip_pat = input('Exclude lines containing: ', '') + elseif a:1 == '-?' || a:1 == '-h' + echomsg usage + return 1 + else + let id = a:1 + endif + endif + + echo "\n" + + let cmd_output = '' + + while id_file != '' + if !g:LID_Search_Multiple_ID_Files && cmd_output != '' + break + endif + + let idx = stridx(id_file, ',') + if idx == -1 + let one_file = id_file + let id_file = '' + else + let one_file = strpart(id_file, 0, idx) + let id_file = strpart(id_file, idx + 1) + endif + + if !filereadable(one_file) + continue + endif + + let cmd = g:LID_Cmd . ' -R grep -f ' . one_file . ' ' + let cmd = cmd . g:LID_Shell_Quote_Char . id . g:LID_Shell_Quote_Char + + let output = system(cmd) + + if v:shell_error && output != '' + echohl WarningMsg | echomsg output | echohl None + return + endif + + let cmd_output = cmd_output . output + endwhile + + if cmd_output == '' + echohl WarningMsg | echomsg 'Error: Identifier ' . id . ' not found' | + \ echohl None + return + endif + + " Extract lines containing the user specified pattern + if match_pat != '' + let cmd_output = s:ExtractMatchingLines(cmd_output, match_pat) + + " No more remaining lines + if cmd_output == '' + echohl WarningMsg + echomsg 'Error: No matching lines containing "' . match_pat . '"' + echohl None + return + endif + endif + + " Remove lines containing the user specified pattern + if skip_pat != '' + let cmd_output = s:RemoveMatchingLines(cmd_output, skip_pat) + + " No more remaining lines + if cmd_output == '' + echohl WarningMsg + echomsg 'Error: No matching lines containing "' . skip_pat . '"' + echohl None + return + endif + endif + + + " Send the output to a temporary file to use with the :cfile command + let tmpfile = tempname() + + exe 'redir! > ' . tmpfile + silent echon cmd_output + redir END + + " Set the 'errorformat' to parse the lid output. + let old_efm = &efm + set efm=%f:%l:%m + + execute 'silent! cfile ' . tmpfile + + let &efm = old_efm + + if !g:LID_Jump_To_Match + execute "normal \" + endif + + " Open the lid output window + if g:LID_OpenQuickfixWindow == 1 + " Open the quickfix window below the current window + botright copen + endif + + " Jump to the first match. Otherwise, the cursor will be in the quickfix + " window + if g:LID_Jump_To_Match + cc + endif + + call delete(tmpfile) +endfunction + +" Define the Lid command to run lid +command! -nargs=? -complete=tag Lid call s:RunLid() +