diff --git a/Switch/SwitchCore/SwitchTargets/ExtensionSwitchTarget.cs b/Switch/SwitchCore/SwitchTargets/ExtensionSwitchTarget.cs index 6052a46..36592d4 100644 --- a/Switch/SwitchCore/SwitchTargets/ExtensionSwitchTarget.cs +++ b/Switch/SwitchCore/SwitchTargets/ExtensionSwitchTarget.cs @@ -1,4 +1,8 @@ -using EnvDTE; +using System; +using System.Text; +using System.IO; +using System.Windows.Forms; +using EnvDTE; using EnvDTE80; namespace SwitchCore.SwitchTargets @@ -44,7 +48,108 @@ public bool DoSwitch(DTE2 application, Document activeDocument) var mappedPath = path.Substring(0, path.Length - From.Length) + To; // Try and open the mapped path. - return SwitchHelper.TryOpenDocument(application, mappedPath); + bool success = SwitchHelper.TryOpenDocument(application, mappedPath); + + if (success) { + return success; + } + + // Try using common path names. + // + // In C++, header files are usually in 'include' directories + // and implementation files are in 'source' directories. These + // directories usually share a common root path. + // + // \include\... + // \source\... + // + // There is also the possibility that C++ project components are + // placed in their own sub-directory with corresponding 'include' + // and 'source' directories. + // + // \component_1\include\... + // \component_1\source\... + // \component_2\include\... + // \component_2\source\... + // + // What it does not yet do is scan for other directory layouts. + // An example is when 'include' files are placed into deeper + // subdirectories: + // + // \component_1\include\sub1\*.h + // \component_1\include\sub2\*.h + // \component_1\source\*.cpp + // + // ---- + // TODO: Create separate SwitchTarget for this scenario + + string filenameOnly = Path.GetFileNameWithoutExtension(path); + // Get the path without the leading path separator. + string pathOnly = path.Substring(0, path.Length - Path.GetFileName(path).Length - 1); + string[] directories = pathOnly.Split(Path.DirectorySeparatorChar); + + // Be safe + if (directories.Length == 0) return false; + + // Make sure the first directory path have a directory separator + // since Path.Combine does not seem to append one. + if (!directories[0].EndsWith(Path.DirectorySeparatorChar.ToString())) + { + directories[0] += Path.DirectorySeparatorChar; + } + + // Possible directories names; add more if necessary. + // Used to find possible directory names and substitute. + // + // TODO: Make configurable + + // TODO: Must be class members and initialized once only. + string[] includeFolder = new string[] { "includes", "include", "inc" }; + string[] sourceFolder = new string[] { "sources", "source", "src" }; + + // If the current document is a header file then we search in + // the source directory and vice versa. + string[] fromFolder = new string[3]; + string[] toFolder = new string[3]; + if (From.StartsWith("h")) { + fromFolder = includeFolder; + toFolder = sourceFolder; + } else { + fromFolder = sourceFolder; + toFolder = includeFolder; + } + + string newPath; + string newFilename; + // Temporary buffer + string originalDirectory; + + // Scan directories backwards. + for (int i = directories.Length - 1; i >= 0; i--) + { + if (Array.Exists(fromFolder, s => { return String.Compare(s, directories[i]) == 0; })) + { + // Save directory name so we can restore it later. + originalDirectory = directories[i]; + + foreach (string t in toFolder) + { + // Build the new path using the current possible directory name + directories[i] = t; + newPath = Path.Combine(directories) + Path.DirectorySeparatorChar; + newFilename = String.Concat(newPath, filenameOnly + "." + To); + MessageBox.Show("File to open: " + newFilename); + + success = SwitchHelper.TryOpenDocument(application, newFilename); + if (success) return success; + } + + // Restore original directory name. + directories[i] = originalDirectory; + } + } + + return success; } ///