*fswitch.txt*	For Vim version 7.2 and above	Last change: 2009 Mar 23

			       ---------------
 			        File Switcher
			       ---------------

Author:  Derek Wyatt (derek at myfirstnamemylastname dot org)

							*fswitch-copyright*
Copyright: The VIM LICENSE applies to fswitch.vim, and fswitch.txt
	   (see |copyright|) except use "fswitch" instead of "Vim".
	   No warranty, express or implied.
	   Use At-Your-Own-Risk!

==============================================================================
						*fswitch* *fswitch-contents*
1. Contents~

	1. Contents .............................: |fswitch-contents|
	2. About ................................: |fswitch-about|
	3. Features .............................: |fswitch-features|
	4. Setup ................................: |fswitch-setup|
	5. Configuration ........................: |fswitch-configure|
	6. "Creating" the Alternate File ........: |fswitch-altcreate|
	7. Useful Mappings ......................: |fswitch-mappings|
	8. FSwitch() ............................: |fswitch-function|
	9. FSReturnCompanionFilenameString().....: |fswitch-getcompanion|
       10. FSReturnReadableCompanionFilename()...: |fswitch-getreadablecomp|
       11. The Default Settings .................: |fswitch-defaults|
       12. Examples .............................: |fswitch-examples|
       13. Troubleshooting ......................: |fswitch-trouble|
        A. Change History .......................: |fswitch-changes|

==============================================================================
							*fswitch-about*
2. About~

FSwitch is designed to allow you to switch between companion files of source
code (e.g. "cpp" files and their corresponding "h" files).  The source for
this came from a home-grown script that was influenced later by the
"Alternate" (a.vim) script.

The original intention was to modify the existing a.vim script to do what the
home-grown version could do (choose to open the other file in an existing
window) but it was a rather complex script and modification looked difficult
so the choice was made to simply move the home-grown script forward a couple
of notches and produce a new plugin.  This doc file is twice the length of the
actual code at this point :)

==============================================================================
							*fswitch-features*
3. Features~

FSwitch has the following features:

  - Switches between a file and its companion file
  - Ability to create a new file using a preferential location
  - Simple configuration using buffer-local variables
  - It's got a really long doc file (... seriously, why is this thing so
    bloody long?)
  - Umm... other stuff?

==============================================================================
							*fswitch-setup*
4. Setup~

Most of the behaviour of FSwitch is customized via buffer-local variables.
You set up the variables with auto commands:
>
  au! BufEnter *.cpp let b:fswitchdst = 'hpp,h' | let b:fswitchlocs = '../inc'
<
That |:autocmd| will set the 'fswitchdst' and 'fswitchlocs' variables when the
|BufEnter| event takes place on a file whose name matches {*.cpp} (e.g. when
you enter the buffer containing the {MyFile.cpp} file).

The variables above state that the alternate file to {MyFile.cpp} are
{MyFile.hpp} and {MyFile.h} preferred in that order, and located in the {inc}
directory at the same level as the current directory.

That should get you there but there's more capability here if you want.  To
get that move on to |fswitch-configure|.

==============================================================================
							*fswitch-configure*
5. Configuration~


							*'fswitchdst'*
'fswitchdst'	string	(default depends on file in current buffer)
		local to buffer

	The 'fswitchdst' variable denotes the file extension that is the
	target extension of the current file's companion file.  For example:
>
		:let b:fswitchdst = 'cpp,cxx,C'
<
	The above specifies that the current buffer's file has a companion
	filename which can be found by replacing the current extension with
	{cpp}, {cxx} or {C}.  The extensions will be tried in this order and
	the first match wins.

	'fswitchdst' is taken relative to directories that are found in the
	'fswitchlocs' variable.

							*'fswitchlocs'*
'fswitchlocs'	string	(default depends on file in current buffer)
		local to buffer

	The 'fswitchlocs' variable contains a set of directives that indicate
	directory names that should be formulated when trying to find the
	alternate file.  For example:
>
		" Create the destination path by substituting any
		" 'include' string from the pathname with 'src'
		:let b:fswitchlocs = 'reg:/include/src/'

                " First try adding the relative path '../src' to the path
		" in which the file in the buffer exists and if that fails
		" then try using 'source' instead
		:let b:fswitchlocs = 'rel:../src,source'

		" Same as above but leaving off the optional 'rel:'
		:let b:fswitchlocs = '../src,../source'
<
	The following types of directives are understood:

							 *fswitch_reg*
	reg:~
		A regular expression.  The regular expression takes the form:
>
			{delim}{pat}{delim}{globsub}{delim}
<
		Where:
		
		{delim}   is something that doesn't appear in {pat} or
			  {globsub} used to delimit the {pat} and {globsub}

		{pat}	  is a standard pattern to search on

		{globsub} is a substitution string that will be run through
       		          the |glob()| function.

							 *fswitch_rel*
	rel:~
		A relative path.  The {rel:} is actually optional.  If you
		leave this off, then FSwitch will assume that the string is
		denoting a relative path.
	
							 *fswitch_ifrel*
	ifrel:~
		Takes the same form as {:reg} but the {globsub} part of the
		directive is a relative path.  The relative path is only used
		if the {pat} matches the existing path of the buffer.
	
							 *fswitch_abs*
	abs:~
		An absolute path.  I have no idea why you'd ever want to do
		this, but it's there if you want it.

							 *fswitch_ifabs*
	ifabs:~
		Takes the same form as {:reg} but the {globsub} part of the
		directive is an absolute path.  The absolute path is only used
		if the {pat} matches the existing path of the buffer.
	
	Why use the "if" variants?

	Here's the situation: You've got the following file:
>
		~/code/MyFile.h
<
	And you've set the following locations:
>
		For .h   -> reg:/include/src/,../src,./
		For .cpp -> reg:/src/include/,../include,./
<
	Here's what happens when run the following commands:
>
		FSwitch('%')
		# Creates a new file ~/src/MyFile.cpp due to the first
		# relative path in the list for .h
		FSwitch('%')
		# Creates a new file ~/include/MyFile.h due to the first
		# regular expression in the list for .cpp
<
	The problem is that you've unconditionally said you want to use
	{../src} for the alternate file but in reality you probably wanted to
	use {./}.  If you use {:ifrel} instead then you can say that you only
	want to use {../src} if the path to the current buffer contains
	{/include/} or something like that.  If you did this FSwitch would not
	have taken {../src} for the new file but would have chosen {./}

	So the "right" setup is:
>
		For .h   -> reg:/include/src/,ifrel:|/include/|../src|,./
		For .cpp -> reg:/src/include/,ifrel:|/src/|../include|,./
<
							*'fswitchdisablegloc'*
'fsdisablegloc'
		string	(default off)
		local to buffer
	
	Disables the appending of the default global locations to the local
	buffer definition.  Normally when processing alternate file locations
	FSwitch will append some default values to the list of locations.  If
	you define this variable then this will not happen.

	The default locations are currently set to "./" or ".\" depending on
	what slash your configuration evaluates to.

							*'fswitchfnames'*
'fswitchfnames'	string	(default depends on file in current buffer)
		local to buffer

	The 'fswitchfnames' variable contains a comma-separated list
	of possible substitution patterns over the base filename (without path
	and extension) that should be formulated when trying to find the
	alternate file.  The format of the substitution pattern is the same
	as |fswitch-reg|.

	This may be useful when the companion file may can be formulated by
	adding a suffix to the base filename. For instance, when the companion
	of MyClass.java is MyClassTest.java.
	
	For example:
>
		" Create the destination filename by appending 'Test'
		" to the filename
		:let b:fswitchfnames = '/$/Test/'

		" Create the destination filename by removing 'Test'
		" from the end of the filename.
		:let b:fswitchfnames = '/Test$//'
<
							*'fswitchnonewfiles'*
'fsnonewfiles'
		string	(default off)
		local to buffer and global
	
	This variable is both global and local.  If you want to disable the
	creation of the alternate file when it doesn't already exist you can
	choose to do this on a per-extension basis or globally.  Set the
	global one to shut it off all the time and use the buffer version to
	shut it off locally.

							*'fsneednomatch'*
'fsneednomatch'
		string	(default off)
		local to buffer and global
	
	Normally when doing a regular expression alteration of the path (see
	{reg:} in 'fswitchdst' the pattern you're going to substitute the
	value with must actually match in the string.  When it doesn't matter
	whether or not that the match actually takes place, you can set this
	value.

	If you do set this then the failure to match actually results in
	nothing happening at all.  So if the right filename exists in the same
	directory as the one you're switching from then that's the one that
	will be switched to.

	Example:
>

		If the b:fswitchlocs is set to

		   reg:/src/include/,include

                and

		  # This is the file we're editing
		  ~/code/program/myfile.c

		  # These choices exist for the header file
		  ~/code/program/myfile.h
		  ~/code/program/include/myfile.h
<
	Then the first substitution will result in the first header file being
	chosen, not the second.

==============================================================================
							*fswitch-altcreate*
6. "Creating" the Alternate File~

If the file being switched to does not exist, and 'fsnonewfiles' has not been
set, then it will be created as a new, unwritten buffer.  If there are
multiple possibilities here, FSwitch prefers the first possible match.  For
example if the current buffer has a filename called {/code/src/a/b/MyFile.cpp}
and has the following set:
>
	let b:fswitchdst = 'h,hpp'
	let b:fswitchlocs = 'reg:/src/include/,../include,../inc'
<
then the created filename will be {/code/include/a/b/MyFile.cpp}.

As stated, this file hasn't actually been written to yet so you could easily
delete the buffer and there's no harm done but you also may not be able to
write the buffer very easily if the directory hierarchy doesn't yet exist.  In
this case, it's quite helpful to define a mapping for easily creating the
directory for you:
>
	nmap <Leader>md :!mkdir -p %:p:h<cr>
<
Then it's pretty easy to create the directory before writing the file.

==============================================================================
							*fswitch-mappings*
7. Useful Mappings~

I didn't bother putting mappings into the script directly as this might have
caused conflicts and I don't know how to avoid those.  I use the following
mappings myself:

 - Switch to the file and load it into the current window >
	nmap <silent> <Leader>of :FSHere<cr>
<
 - Switch to the file and load it into the window on the right >
	nmap <silent> <Leader>ol :FSRight<cr>
<
 - Switch to the file and load it into a new window split on the right >
	nmap <silent> <Leader>oL :FSSplitRight<cr>
<
 - Switch to the file and load it into the window on the left >
	nmap <silent> <Leader>oh :FSLeft<cr>
<
 - Switch to the file and load it into a new window split on the left >
	nmap <silent> <Leader>oH :FSSplitLeft<cr>
<
 - Switch to the file and load it into the window above >
	nmap <silent> <Leader>ok :FSAbove<cr>
<
 - Switch to the file and load it into a new window split above >
	nmap <silent> <Leader>oK :FSSplitAbove<cr>
<
 - Switch to the file and load it into the window below >
	nmap <silent> <Leader>oj :FSBelow<cr>
<
 - Switch to the file and load it into a new window split below >
	nmap <silent> <Leader>oJ :FSSplitBelow<cr>
<
==============================================================================
							*FSwitch()*
8. FSwitch()~

The main work is done by the FSwitch() function.  The reason it's documented
here is because you can use it to do something more interesting if you wish.
As it stands now, you get the "Split Above and Switch" functionality by
calling FSwitch() like this:
>
    FSwitch('%', 'split \| wincmd k')
<
There's probably not much to stop anyone from doing something more interesting
in the second argument.  If this string is non-empty then it will be run
through an |:execute| call.

==============================================================================
		     *fswitch-getcompanion* *FSReturnCompanionFilenameString()*

9. FSReturnCompanionFilenameString()~

This function is used by |FSwitch()| to return the pathname to the preferred
companion file.  In this case, the file need not actually exist on the
filesystem but would be the one created if you chose to do so.  As an
example:
>
    let path = FSReturnCompanionFilenameString('%')
<
The resultant path string contains the preferred companion file or nothing if
no preferred file could be discovered.

==============================================================================
		*fswitch-getreadablecomp* *FSReturnReadableCompanionFilename()*

10. FSReturnReadableCompanionFilename()~

This function returns the companion file, but the companion file must be
readable on the filesystem for it to be successfully returned.
>
    let path = FSReturnReadableCompanionFilename('%')
<
The resultant path string contains the preferred companion file or nothing if
no preferred file could be found on the filesystem.

In order to see what created the need for this function, see
|fswitch-example3|.

==============================================================================
							*fswitch-defaults*
11. The Default Settings~

By default FSwitch handles {c}, {cc}, {cpp}, {cxx} and {C}.  Note that the
difference between {c} and {C} is only case sensitivity.  This may mean that
weird stuff happens if your OS is case insensitive.

Also NOTE that when you use {cxx/hxx} or {C/H}, god kills a puppy.
Consider that next time you want to do such a silly thing.

For *.h files:
>
    let b:fswitchdst  = 'c,cpp'
    let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
<

For *.hpp files:
>
    let b:fswitchdst  = 'cpp'
    let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
<

For *.hxx files:
>
    let b:fswitchdst  = 'cxx'
    let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
<

For *.H files:
>
    let b:fswitchdst  = 'C'
    let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/,../src'
<

For *.c
>
    let b:fswitchdst  = 'h'
    let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
<

For *.cpp
>
    let b:fswitchdst  = 'hpp,h'
    let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
<

For *.cxx
>
    let b:fswitchdst  = 'hxx'
    let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
<

For *.C
>
    let b:fswitchdst  = 'H'
    let b:fswitchlocs = 'reg:/src/include/,reg:|src|include/**|,../include'
<

==============================================================================
							*fswitch-examples*
12. Examples~
							*fswitch-example1*
Let's say you have a C++ codebase and it has the following properties (this
level of insanity is a bit high but versions that are only slightly saner
exist in real life):

	- Source files with {.cpp}, {.cc} and {.C} extensions
	- Header files with {.h} extensions
	- Source files and header files in the same directory
	- Source files in the {src} directory and include files in the
	  {include} directory
	- Source files in the {src} directory and include files in the
	  {include/name/space} directory (i.e. subdirectories denoted by the
	  namespace).
	- Source files in {src/name/space} and header files in
	  {include/name/space} (i.e. subdirectories denoted by the namespace).

As a final part to this, the "new" way of doing things in this source tree is
to put header files in a directory noted by namespace and to do the same with
source files and to name source files with a {cpp} extension.

In order to switch between files organized like this, you could specify the
following:
>
 augroup mycppfiles
   au!
   au BufEnter *.h let b:fswitchdst  = 'cpp,cc,C'
   au BufEnter *.h let b:fswitchlocs = 'reg:/include/src/,reg:/include.*/src/'
 augroup END
<
Here the setting of b:fswitchdst to {cpp,cc,C} handles the different C++
extensions, and prefers to use {cpp} and will create new files with that
extension.

The fswitchlocs setting allows for the following:

	reg:/include/src/~

		Take the pathname to the file in the current buffer and
		substitute "src" for "include".  This handles the following
		possibilities:

		- Files are in {include} and {src} respectively
		- Files are in {include/name/space} and {src/name/space}
		  respectively

	reg:/include.*/src/~

		Take the pathname to the file in the current buffer and
		substitute "src" for "include.*".  This handles the following
		possibility:

		- Files are in {include/name/space} and {src} respectively
	
	./~
		This one's a hiddden option.  The default location is the
		current directory already so we don't explicitly have to state
		this, but it is the last possibility:

		- Files are in the same directory

							*fswitch-example2*
Here we'll just show a quick example of making use of the globbing aspect of
the system.  Let's say you're working on a {cpp} file and you want to find the
matching header file, and you have your destination and locations set to the
following:
>
	let b:fswitchdst = 'h'
	let b:fswitchlocs = 'reg:|src|include/**|'
>
then if you have the a file {src/MyFile.cpp} then this will find the file
{include/name/space/MyFile.h}.

							*fswitch-example3*
At work I'm a Windows C++ programmer and at home I'm a OS X Objective-C
programmer.  There's a problem with this... C++ and Objective-C both use the
same extension for header files ({.h}).

At home I want to be able to use the XCode command line builder in the
'makeprg' setting when I'm working on the code.  I would like this to be set
when I am on a {.m} file or its companion {.h} file.  This is done with the
following function:
>
    function! SetMakeForXCode(filename)
        let isObjCFile = 0
        let ext = expand(a:filename . ":e")
        if ext == 'm' || ext == 'mm'
            let isObjCFile = 1
        elseif ext == 'h'
	    " Find the companion file
            let companionfile = FSReturnReadableCompanionFilename('%')
            " For some reason expand() doesn't work on the next line
            let companionext = substitute(companionfile, '.*\.', '', '')
            if companionext == 'm' || companionext == 'mm'
                let isObjCFile = 1
            endif
        endif
        if isObjCFile == 1
            setl makeprg=xcodebuild\ -configuration\ Debug
        endif
    endfunction
<
Yup, this could have been easier by using the 'filetype' or using some sort of
|grep| call but I wanted to use this particular hammer. :)  I'll probably end
up switching it to use the 'filetype' instead in the end...

==============================================================================
							*fswitch-trouble*
13. TroubleShooting~
							*fswitch-empty*
You may get the following error:
>
  Alternate has evaluated to nothing.  See :h fswitch-empty for more info.
<
It can happen... This is probably due to the fact that you've got a nicely
strict set of rules for your locations.  With |fswitch-reg| and
|fswitch-ifrel| and |fswitch-ifabs| you can get rather specific about whether
or not anything actually happens.  If you aren't letting anything really
happen, it's not going to happen and you're going to end up with an empty
path.

==============================================================================
							*fswitch-changes*
A. Change History~

0.9.5
	- Modified the autocommands to handle the myriad different
	  formulations of C++ files per Hong Xu's request. See:
	  https://github.com/derekwyatt/vim-fswitch/pull/3

0.9.4
	- Added fixes from Alexey Radkov to handle the 'sb' and 'spr' option
	  settings as well as the 'switchbuf' option.  See:
	  https://github.com/derekwyatt/vim-fswitch/pull/2
	- Made small fix to Alexey's fix to keep the 'sb' and 'spr' shadow
	  variable local to the buffer and unlet them at the end.
	- I made a quick regression check on this update but did not test
	  Alexey's features directly - I trust that he did that :)

0.9.3
	- Made sure that there's a check for 7.0 (Thanks Timon Kelter)

0.9.2
	- Fix for the splitting commands (Thanks Michael Henry)

0.9.1
	- Added :ifrel (|fswitch_ifrel|)
	- Added :ifabs (|fswitch_ifabs|)
	- Added |FSReturnReadableCompanionFilename()|
	- Added |FSReturnCompanionFilenameString()|
	- Changed default settings for .h to use :ifrel instead of :rel
	- Changed default settings for .c and .cpp to use :ifrel instead of
	  :rel

0.9.0
	- Initial release

vim:tw=78:sts=8:ts=8:sw=8:noet:ft=help:
