diff --git a/OpenInclude.Utility/OpenInclude.Utility.csproj b/OpenInclude.Utility/OpenInclude.Utility.csproj
new file mode 100644
index 0000000..b08b24c
--- /dev/null
+++ b/OpenInclude.Utility/OpenInclude.Utility.csproj
@@ -0,0 +1,49 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {471D9D31-DE12-4242-8AB5-B98FFC7E87AC}
+ Library
+ Properties
+ OpenInclude.Utility
+ OpenInclude.Utility
+ v4.7.2
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenInclude.Utility/Properties/AssemblyInfo.cs b/OpenInclude.Utility/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..45d8cc9
--- /dev/null
+++ b/OpenInclude.Utility/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenInclude.Utility")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("OpenInclude.Utility")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("471d9d31-de12-4242-8ab5-b98ffc7e87ac")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenInclude.Utility/UnityInclude.cs b/OpenInclude.Utility/UnityInclude.cs
new file mode 100644
index 0000000..ef11f57
--- /dev/null
+++ b/OpenInclude.Utility/UnityInclude.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace OpenInclude.Utility
+{
+ public class UnityInclude
+ {
+ private readonly string identityPath;
+ private readonly string packageDisplayName = string.Empty;
+ private readonly string packageSubDirectory = string.Empty;
+
+ public UnityInclude(string includeLine)
+ {
+ var removals = new string[] { "#include", " ", "\t", "\"" };
+ var path = includeLine;
+
+ foreach (var removal in removals)
+ {
+ path = path.Replace(removal, "");
+ }
+
+ identityPath = path;
+
+ var directoryBlocks = identityPath.Split('/');
+ for (int i = 0; i < directoryBlocks.Length; i++)
+ {
+ if (i == 0) { }
+ else if (i == 1)
+ {
+ packageDisplayName = directoryBlocks[i];
+ }
+ else
+ {
+ packageSubDirectory += directoryBlocks[i];
+ if (i != directoryBlocks.Length - 1) packageSubDirectory += "/";
+ }
+ }
+
+ Console.WriteLine(identityPath);
+ Console.WriteLine(packageDisplayName);
+ Console.WriteLine(packageSubDirectory);
+
+ }
+
+ public string PackageName
+ {
+ get { return packageDisplayName; }
+ }
+
+ public string SubDirectory
+ {
+ get { return packageSubDirectory; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenInclude.Utility/UnityPath.cs b/OpenInclude.Utility/UnityPath.cs
new file mode 100644
index 0000000..64e3d3f
--- /dev/null
+++ b/OpenInclude.Utility/UnityPath.cs
@@ -0,0 +1,73 @@
+using System;
+using System.IO;
+
+namespace OpenInclude.Utility
+{
+ public class UnityPath
+ {
+ private string m_path;
+
+ public string Root { get; }
+
+ public UnityPath(string path)
+ {
+ this.m_path = path;
+
+ if (path.Contains("Assets"))
+ {
+ Root = path.Substring(0, path.IndexOf("Assets", StringComparison.Ordinal) - 1);
+ }
+ else if (path.Contains("Packages"))
+ {
+ Root = path.Substring(0, path.IndexOf("Packages", StringComparison.Ordinal) - 1);
+ }
+ else if (path.Contains("Library\\PackageCache"))
+ {
+ Root = path.Substring(0, path.IndexOf("Library\\PackageCache", StringComparison.Ordinal) - 1);
+ }
+
+ Console.WriteLine($"ROOT - {Root}");
+ }
+
+ public string GetResolvedPath(string packageName)
+ {
+ DirectoryInfo di;
+ DirectoryInfo[] subDirs;
+
+ di = new DirectoryInfo(Root + "/Packages");
+ subDirs = di.GetDirectories();
+
+ foreach (var dir in subDirs)
+ {
+ if (dir.FullName.Contains(packageName))
+ {
+ return dir.FullName;
+ }
+ }
+
+ di = new DirectoryInfo(Root + "/Library/PackageCache");
+ subDirs = di.GetDirectories();
+
+ foreach (var dir in subDirs)
+ {
+ if (dir.FullName.Contains(packageName))
+ {
+ return dir.FullName;
+ }
+ }
+
+ di = new DirectoryInfo(Root + "/Assets");
+ subDirs = di.GetDirectories();
+
+ foreach (var dir in subDirs)
+ {
+ if (dir.FullName.Contains(packageName))
+ {
+ return dir.FullName;
+ }
+ }
+
+ return string.Empty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenInclude/OpenInclude.cs b/OpenInclude/OpenInclude.cs
new file mode 100644
index 0000000..7ada360
--- /dev/null
+++ b/OpenInclude/OpenInclude.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Text;
+using System.IO;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Language.Intellisense;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Operations;
+using Microsoft.VisualStudio.Utilities;
+using System.ComponentModel.Composition;
+using System.Threading;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using EnvDTE;
+using Microsoft.VisualStudio.Imaging.Interop;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using OpenInclude.Utility;
+using Task = System.Threading.Tasks.Task;
+
+namespace OpenInclude
+{
+ internal static class FileAndContentTypeDefinitions
+ {
+ [Export] [Name("shader")] [BaseDefinition("text")]
+ internal static ContentTypeDefinition shaderContentTypeDefinition;
+
+ [Export] [Name(".shader")] [ContentType("shader")]
+ internal static FileExtensionToContentTypeDefinition shaderFileExtensionDefinition;
+
+ [Export]
+ [Name(".hlsl")]
+ [ContentType("shader")]
+ internal static FileExtensionToContentTypeDefinition HLSLFileExtensionDefinition;
+
+ [Export]
+ [Name(".glsl")]
+ [ContentType("shader")]
+ internal static FileExtensionToContentTypeDefinition GLSLFileExtensionDefinition;
+
+ [Export]
+ [Name(".cginc")]
+ [ContentType("shader")]
+ internal static FileExtensionToContentTypeDefinition CGINCLUDEFileExtensionDefinition;
+ }
+
+ internal abstract class OpenIncludeSuggestedActionSourceProviderBase : ISuggestedActionsSourceProvider
+ {
+ [Import(typeof(ITextStructureNavigatorSelectorService))]
+ internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
+
+ public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer)
+ {
+ if (textBuffer == null || textView == null)
+ {
+ return null;
+ }
+
+ return new OpenIncludeSuggestedActionSource(this, textView, textBuffer);
+ }
+ }
+
+ [Export(typeof(ISuggestedActionsSourceProvider))]
+ [Name("Open Include Text Suggested Action ")]
+ [ContentType("text")]
+ internal class OpenIncludeSuggestedActionSourceProviderText : OpenIncludeSuggestedActionSourceProviderBase
+ {
+
+ }
+
+ [Export(typeof(ISuggestedActionsSourceProvider))]
+ [Name("Open Include Text Suggested Action ")]
+ [ContentType("shader")]
+ internal class OpenIncludeSuggestedActionSourceProviderShader : OpenIncludeSuggestedActionSourceProviderBase
+ {
+
+ }
+
+ internal class OpenDocumentAction : ISuggestedAction
+ {
+ private readonly ITrackingSpan m_span;
+ private readonly string m_display;
+ private readonly ITextSnapshot m_snapshot;
+
+ public OpenDocumentAction(ITrackingSpan span)
+ {
+ m_span = span;
+ m_snapshot = span.TextBuffer.CurrentSnapshot;
+
+ m_display = string.Format("Open '{0}'", GetExportedPath(GetTrimText(span)));
+ }
+
+ private string GetTrimText(ITrackingSpan span)
+ {
+ string text = span.GetText(m_snapshot);
+ return text.Replace(" ", "");
+ }
+
+ private string GetExportedPath(string trimmedText)
+ {
+ var text = trimmedText.Replace("#include", "").Replace("\"", "").Replace(" ", "");
+ return text;
+ }
+
+ public Task