ignore on .netrwhist
[my-vim-dotfolder.git] / doc / fswitch.txt
blob56d45611435308d476892e57a9bb685a5dc85c96
1 *fswitch.txt*   For Vim version 7.2 and above   Last change: 2009 Mar 23
3                                ---------------
4                                 File Switcher
5                                ---------------
7 Author:  Derek Wyatt (derek at myfirstnamemylastname dot org)
9                                                         *fswitch-copyright*
10 Copyright: The VIM LICENSE applies to fswitch.vim, and fswitch.txt
11            (see |copyright|) except use "fswitch" instead of "Vim".
12            No warranty, express or implied.
13            Use At-Your-Own-Risk!
15 ==============================================================================
16                                                 *fswitch* *fswitch-contents*
17 1. Contents~
19         1. Contents .............................: |fswitch-contents|
20         2. About ................................: |fswitch-about|
21         3. Features .............................: |fswitch-features|
22         4. Setup ................................: |fswitch-setup|
23         5. Configuration ........................: |fswitch-configure|
24         6. "Creating" the Alternate File ........: |fswitch-altcreate|
25         7. Useful Mappings ......................: |fswitch-mappings|
26         8. FSwitch() ............................: |fswitch-function|
27         9. FSReturnCompanionFilenameString().....: |fswitch-getcompanion|
28        10. FSReturnReadableCompanionFilename()...: |fswitch-getreadablecomp|
29        11. The Default Settings .................: |fswitch-defaults|
30        12. Examples .............................: |fswitch-examples|
31        13. Troubleshooting ......................: |fswitch-trouble|
32         A. Change History .......................: |fswitch-changes|
34 ==============================================================================
35                                                         *fswitch-about*
36 2. About~
38 FSwitch is designed to allow you to switch between companion files of source
39 code (e.g. "cpp" files and their corresponding "h" files).  The source for
40 this came from a home-grown script that was influenced later by the
41 "Alternate" (a.vim) script.
43 The original intention was to modify the existing a.vim script to do what the
44 home-grown version could do (choose to open the other file in an existing
45 window) but it was a rather complex script and modification looked difficult
46 so the choice was made to simply move the home-grown script forward a couple
47 of notches and produce a new plugin.  This doc file is twice the length of the
48 actual code at this point :)
50 ==============================================================================
51                                                         *fswitch-features*
52 3. Features~
54 FSwitch has the following features:
56   - Switches between a file and its companion file
57   - Ability to create a new file using a preferential location
58   - Simple configuration using buffer-local variables
59   - It's got a really long doc file (... seriously, why is this thing so
60     bloody long?)
61   - Umm... other stuff?
63 ==============================================================================
64                                                         *fswitch-setup*
65 4. Setup~
67 Most of the behaviour of FSwitch is customized via buffer-local variables.
68 You set up the variables with auto commands:
70   au! BufEnter *.cpp let b:fswitchdst = 'hpp,h' | let b:fswitchlocs = '../inc'
72 That |:autocmd| will set the 'fswitchdst' and 'fswitchlocs' variables when the
73 |BufEnter| event takes place on a file whose name matches {*.cpp} (e.g. when
74 you enter the buffer containing the {MyFile.cpp} file).
76 The variables above state that the alternate file to {MyFile.cpp} are
77 {MyFile.hpp} and {MyFile.h} preferred in that order, and located in the {inc}
78 directory at the same level as the current directory.
80 That should get you there but there's more capability here if you want.  To
81 get that move on to |fswitch-configure|.
83 ==============================================================================
84                                                         *fswitch-configure*
85 5. Configuration~
88                                                         *'fswitchdst'*
89 'fswitchdst'    string  (default depends on file in current buffer)
90                 local to buffer
92         The 'fswitchdst' variable denotes the file extension that is the
93         target extension of the current file's companion file.  For example:
95                 :let b:fswitchdst = 'cpp,cxx,C'
97         The above specifies that the current buffer's file has a companion
98         filename which can be found by replacing the current extension with
99         {cpp}, {cxx} or {C}.  The extensions will be tried in this order and
100         the first match wins.
102         'fswitchdst' is taken relative to directories that are found in the
103         'fswitchlocs' variable.
105                                                         *'fswitchlocs'*
106 'fswitchlocs'   string  (default depends on filen in current buffer)
107                 local to buffer
109         The 'fswitchlocs' variable contains a set of directives that indicate
110         directory names that should be formulated when trying to find the
111         alternate file.  For example:
113                 " Create the destination path by substituting any
114                 " 'include' string from the pathname with 'src'
115                 :let b:fswitchlocs = 'reg:/include/src/'
117                 " First try adding the relative path '../src' to the path
118                 " in which the file in the buffer exists and if that fails
119                 " then try using 'source' instead
120                 :let b:fswitchlocs = 'rel:../src,source'
122                 " Same as above but leaving off the optional 'rel:'
123                 :let b:fswitchlocs = '../src,../source'
125         The following types of directives are understood:
127                                                          *fswitch_reg*
128         reg:~
129                 A regular expression.  The regular expression takes the form:
131                         {delim}{pat}{delim}{globsub}{delim}
133                 Where:
134                 
135                 {delim}   is something that doesn't appear in {pat} or
136                           {globsub} used to delimit the {pat} and {globsub}
138                 {pat}     is a standard pattern to search on
140                 {globsub} is a substitution string that will be run through
141                           the |glob()| function.
143                                                          *fswitch_rel*
144         rel:~
145                 A relative path.  The {rel:} is actually optional.  If you
146                 leave this off, then FSwitch will assume that the string is
147                 denoting a relative path.
148         
149                                                          *fswitch_ifrel*
150         ifrel:~
151                 Takes the same form as {:reg} but the {globsub} part of the
152                 directive is a relative path.  The relative path is only used
153                 if the {pat} matches the existing path of the buffer.
154         
155                                                          *fswitch_abs*
156         abs:~
157                 An absolute path.  I have no idea why you'd ever want to do
158                 this, but it's there if you want it.
160                                                          *fswitch_ifabs*
161         ifabs:~
162                 Takes the same form as {:reg} but the {globsub} part of the
163                 directive is an absolute path.  The absolute path is only used
164                 if the {pat} matches the existing path of the buffer.
165         
166         Why use the "if" variants?
168         Here's the situation: You've got the following file:
170                 ~/code/MyFile.h
172         And you've set the following locations:
174                 For .h   -> reg:/include/src/,../src,./
175                 For .cpp -> reg:/src/include/,../include,./
177         Here's what happens when run the following commands:
179                 FSwitch('%')
180                 # Creates a new file ~/src/MyFile.cpp due to the first
181                 # relative path in the list for .h
182                 FSwitch('%')
183                 # Creates a new file ~/include/MyFile.h due to the first
184                 # regular expression in the list for .cpp
186         The problem is that you've unconditionally said you want to use
187         {../src} for the alternate file but in reality you probably wanted to
188         use {./}.  If you use {:ifrel} instead then you can say that you only
189         want to use {../src} if the path to the current buffer contains
190         {/include/} or something like that.  If you did this FSwitch would not
191         have taken {../src} for the new file but would have chosen {./}
193         So the "right" setup is:
195                 For .h   -> reg:/include/src/,ifrel:|/include/|../src|,./
196                 For .cpp -> reg:/src/include/,ifrel:|/src/|../include|,./
198                                                         *'fswitchdisablegloc'*
199 'fsdisablegloc'
200                 string  (default off)
201                 local to buffer
202         
203         Disables the appending of the default global locations to the local
204         buffer definition.  Normally when processing alternate file locations
205         FSwitch will append some default values to the list of locations.  If
206         you define this variable then this will not happen.
208         The default locations are currently set to "./" or ".\" depending on
209         what slash your configuration evaluates to.
211                                                         *'fswitchnonewfiles'*
212 'fsnonewfiles'
213                 string  (default off)
214                 local to buffer and global
215         
216         This variable is both global and local.  If you want to disable the
217         creation of the alternate file when it doesn't already exist you can
218         choose to do this on a per-extension basis or globally.  Set the
219         global one to shut it off all the time and use the buffer version to
220         shut it off locally.
222                                                         *'fsneednomatch'*
223 'fsneednomatch'
224                 string  (default off)
225                 local to buffer and global
226         
227         Normally when doing a regular expression alteration of the path (see
228         {reg:} in 'fswitchdst' the pattern you're going to substitute the
229         value with must actually match in the string.  When it doesn't matter
230         whether or not that the match actually takes place, you can set this
231         value.
233         If you do set this then the failure to match actually results in
234         nothing happening at all.  So if the right filename exists in the same
235         directory as the one you're switching from then that's the one that
236         will be switched to.
238         Example:
241                 If the b:fswitchlocs is set to
243                    reg:/src/include/,include
245                 and
247                   # This is the file we're editing
248                   ~/code/program/myfile.c
250                   # These choices exist for the header file
251                   ~/code/program/myfile.h
252                   ~/code/program/include/myfile.h
254         Then the first substitution will result in the first header file being
255         chosen, not the second.
257 ==============================================================================
258                                                         *fswitch-altcreate*
259 6. "Creating" the Alternate File~
261 If the file being switched to does not exist, and 'fsnonewfiles' has not been
262 set, then it will be created as a new, unwritten buffer.  If there are
263 multiple possibilities here, FSwitch prefers the first possible match.  For
264 example if the current buffer has a filename called {/code/src/a/b/MyFile.cpp}
265 and has the following set:
267         let b:fswitchdst = 'h,hpp'
268         let b:fswitchlocs = 'reg:/src/include/,../include,../inc'
270 then the created filename will be {/code/include/a/b/MyFile.cpp}.
272 As stated, this file hasn't actually been written to yet so you could easily
273 delete the buffer and there's no harm done but you also may not be able to
274 write the buffer very easily if the directory hierarchy doesn't yet exist.  In
275 this case, it's quite helpful to define a mapping for easily creating the
276 directory for you:
278         nmap <Leader>md :!mkdir -p %:p:h<cr>
280 Then it's pretty easy to create the directory before writing the file.
282 ==============================================================================
283                                                         *fswitch-mappings*
284 7. Useful Mappings~
286 I didn't bother putting mappings into the script directly as this might have
287 caused conflicts and I don't know how to avoid those.  I use the following
288 mappings myself:
290  - Switch to the file and load it into the current window >
291         nmap <silent> <Leader>of :FSHere<cr>
293  - Switch to the file and load it into the window on the right >
294         nmap <silent> <Leader>ol :FSRight<cr>
296  - Switch to the file and load it into a new window split on the right >
297         nmap <silent> <Leader>oL :FSSplitRight<cr>
299  - Switch to the file and load it into the window on the left >
300         nmap <silent> <Leader>oh :FSLeft<cr>
302  - Switch to the file and load it into a new window split on the left >
303         nmap <silent> <Leader>oH :FSSplitLeft<cr>
305  - Switch to the file and load it into the window above >
306         nmap <silent> <Leader>ok :FSAbove<cr>
308  - Switch to the file and load it into a new window split above >
309         nmap <silent> <Leader>oK :FSSplitAbove<cr>
311  - Switch to the file and load it into the window below >
312         nmap <silent> <Leader>oj :FSBelow<cr>
314  - Switch to the file and load it into a new window split below >
315         nmap <silent> <Leader>oJ :FSSplitBelow<cr>
317 ==============================================================================
318                                                         *FSwitch()*
319 8. FSwitch()~
321 The main work is done by the FSwitch() function.  The reason it's documented
322 here is because you can use it to do something more interesting if you wish.
323 As it stands now, you get the "Split Above and Switch" functionality by
324 calling FSwitch() like this:
326     FSwitch('%', 'split \| wincmd k')
328 There's probably not much to stop anyone from doing something more interesting
329 in the second argument.  If this string is non-empty then it will be run
330 through an |:execute| call.
332 ==============================================================================
333                      *fswitch-getcompanion* *FSReturnCompanionFilenameString()*
335 9. FSReturnCompanionFilenameString()~
337 This function is used by |FSwitch()| to return the pathname to the preferred
338 companion file.  In this case, the file need not actually exist on the
339 filesystem but would be the one created if you chose to do so.  As an
340 example:
342     let path = FSReturnCompanionFilenameString('%')
344 The resultant path string contains the preferred companion file or nothing if
345 no preferred file could be discovered.
347 ==============================================================================
348                 *fswitch-getreadablecomp* *FSReturnReadableCompanionFilename()*
350 10. FSReturnReadableCompanionFilename()~
352 This function returns the companion file, but the companion file must be
353 readable on the filesystem for it to be successfully returned.
355     let path = FSReturnReadableCompanionFilename('%')
357 The resultant path string contains the preferred companion file or nothing if
358 no preferred file could be found on the filesystem.
360 In order to see what created the need for this function, see
361 |fswitch-example3|.
363 ==============================================================================
364                                                         *fswitch-defaults*
365 11. The Default Settings~
367 By default FSwitch handles {c} and {cpp} files, favouring {cpp}.
369 For *.h files:
371     let b:fswitchdst  = 'cpp,c'
372     let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
374 For *.c
376     let b:fswitchdst  = 'h'
377     let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
380 For *.cpp
382     let b:fswitchdst  = 'h'
383     let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
386 ==============================================================================
387                                                         *fswitch-examples*
388 12. Examples~
389                                                         *fswitch-example1*
390 Let's say you have a C++ codebase and it has the following properties (this
391 level of insanity is a bit high but versions that are only slightly saner
392 exist in real life):
394         - Source files with {.cpp}, {.cc} and {.C} extensions
395         - Header files with {.h} extensions
396         - Source files and header files in the same directory
397         - Source files in the {src} directory and include files in the
398           {include} directory
399         - Source files in the {src} directory and include files in the
400           {include/name/space} directory (i.e. subdirectories denoted by the
401           namespace).
402         - Source files in {src/name/space} and header files in
403           {include/name/space} (i.e. subdirectories denoted by the namespace).
405 As a final part to this, the "new" way of doing things in this source tree is
406 to put header files in a directory noted by namespace and to do the same with
407 source files and to name source files with a {cpp} extension.
409 In order to switch between files organized like this, you could specify the
410 following:
412  augroup mycppfiles
413    au!
414    au BufEnter *.h let b:fswitchdst  = 'cpp,cc,C'
415    au BufEnter *.h let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/'
416  augroup END
418 Here the setting of b:fswitchdst to {cpp,cc,C} handles the different C++
419 extensions, and prefers to use {cpp} and will create new files with that
420 extension.
422 The fswitchlocs setting allows for the following:
424         reg:/include/src/~
426                 Take the pathname to the file in the current buffer and
427                 substitute "src" for "include".  This handles the following
428                 possibilities:
430                 - Files are in {include} and {src} respectively
431                 - Files are in {include/name/space} and {src/name/space}
432                   respectively
434         reg:/include.*/src/~
436                 Take the pathname to the file in the current buffer and
437                 substitute "src" for "include.*".  This handles the following
438                 possibility:
440                 - Files are in {include/name/space} and {src} respectively
441         
442         ./~
443                 This one's a hiddden option.  The default location is the
444                 current directory already so we don't explicitly have to state
445                 this, but it is the last possibility:
447                 - Files are in the same directory
449                                                         *fswitch-example2*
450 Here we'll just show a quick example of making use of the globbing aspect of
451 the system.  Let's say you're working on a {cpp} file and you want to find the
452 matching header file, and you have your destination and locations set to the
453 following:
455         let b:fswitchdst = 'h'
456         let b:fswitchlocs = 'reg:|src|include/**|'
458 then if you have the a file {src/MyFile.cpp} then this will find the file
459 {include/name/space/MyFile.h}.
461                                                         *fswitch-example3*
462 At work I'm a Windows C++ programmer and at home I'm a OS X Objective-C
463 programmer.  There's a problem with this... C++ and Objective-C both use the
464 same extension for header files ({.h}).
466 At home I want to be able to use the XCode command line builder in the
467 'makeprg' setting when I'm working on the code.  I would like this to be set
468 when I am on a {.m} file or its companion {.h} file.  This is done with the
469 following function:
471     function! SetMakeForXCode(filename)
472         let isObjCFile = 0
473         let ext = expand(a:filename . ":e")
474         if ext == 'm' || ext == 'mm'
475             let isObjCFile = 1
476         elseif ext == 'h'
477             " Find the companion file
478             let companionfile = FSReturnReadableCompanionFilename('%')
479             " For some reason expand() doesn't work on the next line
480             let companionext = substitute(companionfile, '.*\.', '', '')
481             if companionext == 'm' || companionext == 'mm'
482                 let isObjCFile = 1
483             endif
484         endif
485         if isObjCFile == 1
486             setl makeprg=xcodebuild\ -configuration\ Debug
487         endif
488     endfunction
490 Yup, this could have been easier by using the 'filetype' or using some sort of
491 |grep| call but I wanted to use this particular hammer. :)  I'll probably end
492 up switching it to use the 'filetype' instead in the end...
494 ==============================================================================
495                                                         *fswitch-trouble*
496 13. TroubleShooting~
497                                                         *fswitch-empty*
498 You may get the following error:
500   Alternate has evaluated to nothing.  See :h fswitch-empty for more info.
502 It can happen... This is probably due to the fact that you've got a nicely
503 strict set of rules for your locations.  With |fswitch-reg| and
504 |fswitch-ifrel| and |fswitch-ifabs| you can get rather specific about whether
505 or not anything actually happens.  If you aren't letting anything really
506 happen, it's not going to happen and you're going to end up with an empty
507 path.
509 ==============================================================================
510                                                         *fswitch-changes*
511 A. Change History~
513 0.9.3
514         - Made sure that there's a check for 7.0 (Thanks Timon Kelter)
516 0.9.2
517         - Fix for the splitting commands (Thanks Michael Henry)
519 0.9.1
520         - Added :ifrel (|fswitch_ifrel|)
521         - Added :ifabs (|fswitch_ifabs|)
522         - Added |FSReturnReadableCompanionFilename()|
523         - Added |FSReturnCompanionFilenameString()|
524         - Changed default settings for .h to use :ifrel instead of :rel
525         - Changed default settings for .c and .cpp to use :ifrel instead of
526           :rel
528 0.9.0
529         - Initial release
531 vim:tw=78:sts=8:ts=8:sw=8:noet:ft=help: