diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..87d244f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,106 @@
+# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
+name: "Build"
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+ workflow_dispatch:
+
+jobs:
+ build:
+ outputs:
+ version: ${{ steps.vsix_version.outputs.version-number }}
+ name: Build
+ runs-on: windows-2022
+ env:
+ Configuration: Release
+ DeployExtension: False
+ Vsix2022ManifestPath: IncludeToolbox2022\source.extension.vsixmanifest
+ Vsix2019ManifestPath: IncludeToolbox2019\source.extension.vsixmanifest
+ VsixManifestSourcePath: IncludeToolbox2022\source.extension.cs
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Setup .NET build dependencies
+ uses: timheuer/bootstrap-dotnet@v1
+ with:
+ nuget: 'false'
+ sdk: 'false'
+ msbuild: 'true'
+
+ - name: Increment VSIX version
+ id: vsix_version
+ uses: timheuer/vsix-version-stamp@v1
+ with:
+ manifest-file: ${{ env.Vsix2022ManifestPath }}
+ vsix-token-source-file: ${{ env.VsixManifestSourcePath }}
+
+ - name: Sync 2019 version
+ uses: cezarypiatek/VsixVersionAction@1.0
+ with:
+ version: ${{ steps.vsix_version.outputs.version-number }}
+ vsix-manifest-file: ${{ env.Vsix2019ManifestPath }}
+
+ - name: Build
+ run: msbuild /v:m -restore /p:OutDir=\_built
+
+ - name: Setup test
+ uses: darenm/Setup-VSTest@v1
+
+ - name: Test
+ run: vstest.console.exe \_built\*Tests.dll
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ github.event.repository.name }}.vsix
+ path: /_built/**/*.vsix
+
+ publish:
+ if: ${{ (github.event_name == 'push' && contains(github.event.head_commit.message, '[release]')) || github.event_name == 'workflow_dispatch' }}
+ needs: build
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Download Package artifact
+ uses: actions/download-artifact@v2
+ with:
+ name: ${{ github.event.repository.name }}.vsix
+
+ - name: Upload 2022 to Open VSIX
+ uses: timheuer/openvsixpublish@v1
+ with:
+ vsix-file: ${{ github.event.repository.name }}2022.vsix
+
+ - name: Upload 2019 to Open VSIX
+ uses: timheuer/openvsixpublish@v1
+ with:
+ vsix-file: ${{ github.event.repository.name }}2019.vsix
+
+ - name: Tag and Release
+ id: tag_release
+ uses: softprops/action-gh-release@v1
+ with:
+ body: Release ${{ needs.build.outputs.version }}
+ tag_name: ${{ needs.build.outputs.version }}
+ files: |
+ **/*.vsix
+
+ - name: Publish 2022 extension to Marketplace
+ uses: cezarypiatek/VsixPublisherAction@0.2
+ with:
+ extension-file: '${{ github.event.repository.name }}2022.vsix'
+ publish-manifest-file: 'vs-publish2022.json'
+ personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
+
+ - name: Publish 2019 extension to Marketplace
+ uses: cezarypiatek/VsixPublisherAction@0.2
+ with:
+ extension-file: '${{ github.event.repository.name }}2019.vsix'
+ publish-manifest-file: 'vs-publish2019.json'
+ personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
diff --git a/IncludeToolBox.lutconfig b/IncludeToolBox.lutconfig
new file mode 100644
index 0000000..596a860
--- /dev/null
+++ b/IncludeToolBox.lutconfig
@@ -0,0 +1,6 @@
+
+
+ true
+ true
+ 180000
+
\ No newline at end of file
diff --git a/IncludeToolBox.sln b/IncludeToolBox.sln
index ac10aa2..0cbecbf 100644
--- a/IncludeToolBox.sln
+++ b/IncludeToolBox.sln
@@ -1,26 +1,67 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
-VisualStudioVersion = 16.0.28803.156
+VisualStudioVersion = 16.0.32630.194
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IncludeToolbox", "IncludeToolbox\IncludeToolbox.csproj", "{F9E250C6-A7AD-4888-8F17-6876736B8DCF}"
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "IncludeToolboxShared", "IncludeToolboxShared\IncludeToolboxShared.shproj", "{C50C4863-6200-4E51-837A-31FEBC09C8B2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{F577F5D2-5E3C-43BE-9030-AF2609A0917A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IncludeToolbox2022", "IncludeToolbox2022\IncludeToolbox2022.csproj", "{7D29CECE-07D3-4417-9D63-1362852F18F3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IncludeToolbox2019", "IncludeToolbox2019\IncludeToolbox2019.csproj", "{A81A5332-6A20-4F3B-90B4-E55985B9CF59}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests\Tests.csproj", "{34631D93-26A2-4682-8C7C-B2042CD7D872}"
EndProject
Global
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ IncludeToolboxShared\IncludeToolboxShared.projitems*{7d29cece-07d3-4417-9d63-1362852f18f3}*SharedItemsImports = 4
+ IncludeToolboxShared\IncludeToolboxShared.projitems*{a81a5332-6a20-4f3b-90b4-e55985b9cf59}*SharedItemsImports = 4
+ IncludeToolboxShared\IncludeToolboxShared.projitems*{c50c4863-6200-4e51-837a-31febc09c8b2}*SharedItemsImports = 13
+ EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|arm64 = Debug|arm64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|arm64 = Release|arm64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F9E250C6-A7AD-4888-8F17-6876736B8DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F9E250C6-A7AD-4888-8F17-6876736B8DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F9E250C6-A7AD-4888-8F17-6876736B8DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F9E250C6-A7AD-4888-8F17-6876736B8DCF}.Release|Any CPU.Build.0 = Release|Any CPU
- {F577F5D2-5E3C-43BE-9030-AF2609A0917A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F577F5D2-5E3C-43BE-9030-AF2609A0917A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F577F5D2-5E3C-43BE-9030-AF2609A0917A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F577F5D2-5E3C-43BE-9030-AF2609A0917A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|arm64.Build.0 = Debug|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|x86.ActiveCfg = Debug|x86
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Debug|x86.Build.0 = Debug|x86
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|arm64.ActiveCfg = Release|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|arm64.Build.0 = Release|Any CPU
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|x86.ActiveCfg = Release|x86
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}.Release|x86.Build.0 = Release|x86
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|arm64.Build.0 = Debug|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|x86.ActiveCfg = Debug|x86
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Debug|x86.Build.0 = Debug|x86
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|arm64.ActiveCfg = Release|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|arm64.Build.0 = Release|Any CPU
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|x86.ActiveCfg = Release|x86
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}.Release|x86.Build.0 = Release|x86
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|arm64.Build.0 = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Debug|x86.Build.0 = Debug|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|arm64.ActiveCfg = Release|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|arm64.Build.0 = Release|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|x86.ActiveCfg = Release|Any CPU
+ {34631D93-26A2-4682-8C7C-B2042CD7D872}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/IncludeToolbox.vsix b/IncludeToolbox.vsix
deleted file mode 100644
index 740b214..0000000
--- a/IncludeToolbox.vsix
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:d410334bec97304d46120a2621251cefa9e703237c249f5891d3cac2c4b6eb36
-size 71090
diff --git a/IncludeToolbox/Commands/CommandBase.cs b/IncludeToolbox/Commands/CommandBase.cs
deleted file mode 100644
index f8a4c2c..0000000
--- a/IncludeToolbox/Commands/CommandBase.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-
-using System;
-using System.ComponentModel.Design;
-using Microsoft.VisualStudio.Shell;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- internal abstract class CommandBase where T : CommandBase, new()
- {
- ///
- /// Initializes the singleton instance of the command.
- ///
- /// Owner package, not null.
- public static void Initialize(Package package)
- {
- if (package == null)
- throw new ArgumentNullException("package");
- ThreadHelper.ThrowIfNotOnUIThread();
-
- Instance = new T();
- Instance.Package = package;
- Instance.SetupMenuCommand();
- }
-
- protected virtual void SetupMenuCommand()
- {
- OleMenuCommandService commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
- if(commandService == null)
- {
- Output.Instance.WriteLine("Failed to retrieve MenuCommandService. No commands could be registered!");
- return;
- }
-
- EventHandler callback = async (sender, e) =>
- {
- try
- {
- await this.MenuItemCallback(sender, e);
- }
- catch (Exception exception)
- {
- await Output.Instance.ErrorMsg("Unexpected Error: {0}", exception.ToString());
- }
- };
-
- menuCommand = new OleMenuCommand(callback, CommandID);
- commandService.AddCommand(menuCommand);
- }
-
-
- ///
- /// Gets the instance of the command.
- ///
- public static T Instance
- {
- get;
- private set;
- }
-
- ///
- /// VS Package that provides this command, not null.
- ///
- protected Package Package { get; private set; }
-
- protected IServiceProvider ServiceProvider => Package;
-
- protected OleMenuCommand menuCommand;
-
- public abstract CommandID CommandID { get; }
-
- protected abstract Task MenuItemCallback(object sender, EventArgs e);
- }
-}
diff --git a/IncludeToolbox/Commands/CommandSetGuids.cs b/IncludeToolbox/Commands/CommandSetGuids.cs
deleted file mode 100644
index f68c636..0000000
--- a/IncludeToolbox/Commands/CommandSetGuids.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-
-namespace IncludeToolbox.Commands
-{
- static class CommandSetGuids
- {
- ///
- /// Command menu group (command set GUID) for document menu.
- ///
- public static readonly Guid MenuGroup = new Guid("aef3a531-8af4-4b7b-800a-e32503dfc6e2");
-
- ///
- /// Command menu group (command set GUID) for tool menu.
- ///
- public static readonly Guid ToolGroup = new Guid("032eb795-1f1c-440d-af98-43cdc1de7a8b");
-
- ///
- /// Command menu group for commands in the project menu.
- ///
- public static readonly Guid ProjectGroup = new Guid("1970ECF3-6C03-4CCF-B422-8DD07F774ED8");
-
- ///
- /// Commandset for all toolbar elements in the include graph toolwindow.
- ///
- public static readonly Guid GraphWindowToolbarCmdSet = new Guid("0B242452-870A-489B-8336-88FD01AEF0C1");
- }
-}
diff --git a/IncludeToolbox/Commands/FormatIncludes.cs b/IncludeToolbox/Commands/FormatIncludes.cs
deleted file mode 100644
index 80ca1d5..0000000
--- a/IncludeToolbox/Commands/FormatIncludes.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.ComponentModel.Design;
-using System.Linq;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Text;
-using Microsoft.VisualStudio.Text.Editor;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- ///
- /// Command handler
- ///
- internal sealed class FormatIncludes : CommandBase
- {
- public override CommandID CommandID => new CommandID(CommandSetGuids.MenuGroup, 0x0100);
-
- public FormatIncludes()
- {
- }
-
- protected override void SetupMenuCommand()
- {
- base.SetupMenuCommand();
- menuCommand.BeforeQueryStatus += UpdateVisibility;
- }
-
- private void UpdateVisibility(object sender, EventArgs e)
- {
- // Check whether any includes are selected.
- var viewHost = VSUtils.GetCurrentTextViewHost();
- var selectionSpan = GetSelectionSpan(viewHost);
- var lines = Formatter.IncludeLineInfo.ParseIncludes(selectionSpan.GetText(), Formatter.ParseOptions.RemoveEmptyLines);
-
- menuCommand.Visible = lines.Any(x => x.ContainsActiveInclude);
- }
-
- ///
- /// Returns process selection range - whole lines!
- ///
- SnapshotSpan GetSelectionSpan(IWpfTextViewHost viewHost)
- {
- var sel = viewHost.TextView.Selection.StreamSelectionSpan;
- var start = new SnapshotPoint(viewHost.TextView.TextSnapshot, sel.Start.Position).GetContainingLine().Start;
- var end = new SnapshotPoint(viewHost.TextView.TextSnapshot, sel.End.Position).GetContainingLine().End;
-
- return new SnapshotSpan(start, end);
- }
-
- ///
- /// This function is the callback used to execute the command when the menu item is clicked.
- /// See the constructor to see how the menu item is associated with this function using
- /// OleMenuCommandService service and MenuCommand class.
- ///
- /// Event sender.
- /// Event args.
- protected override async Task MenuItemCallback(object sender, EventArgs e)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- var settings = (FormatterOptionsPage)Package.GetDialogPage(typeof(FormatterOptionsPage));
-
- // Try to find absolute paths
- var document = VSUtils.GetDTE().ActiveDocument;
- var project = document.ProjectItem?.ContainingProject;
- if (project == null)
- {
- Output.Instance.WriteLine("The document '{0}' is not part of a project.", document.Name);
- return;
- }
- var includeDirectories = VSUtils.GetProjectIncludeDirectories(project);
-
- // Read.
- var viewHost = VSUtils.GetCurrentTextViewHost();
- var selectionSpan = GetSelectionSpan(viewHost);
-
- // Format
- string formatedText = Formatter.IncludeFormatter.FormatIncludes(selectionSpan.GetText(), document.FullName, includeDirectories, settings);
-
- // Overwrite.
- using (var edit = viewHost.TextView.TextBuffer.CreateEdit())
- {
- edit.Replace(selectionSpan, formatedText);
- edit.Apply();
- }
- }
- }
-}
diff --git a/IncludeToolbox/Commands/IncludeGraphToolWindow.cs b/IncludeToolbox/Commands/IncludeGraphToolWindow.cs
deleted file mode 100644
index 7dc6e9f..0000000
--- a/IncludeToolbox/Commands/IncludeGraphToolWindow.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.ComponentModel.Design;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- ///
- /// Command handler
- ///
- internal sealed class IncludeGraphToolWindow : CommandBase
- {
- public override CommandID CommandID => new CommandID(CommandSetGuids.ToolGroup, 0x0102);
-
- ///
- /// Shows the tool window when the menu item is clicked.
- ///
- /// The event sender.
- /// The event args.
- protected override async Task MenuItemCallback(object sender, EventArgs e)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- // Get the instance number 0 of this tool window. This window is single instance so this instance
- // is actually the only one.
- // The last flag is set to true so that if the tool window does not exists it will be created.
- ToolWindowPane window = Package.FindToolWindow(typeof(GraphWindow.IncludeGraphToolWindow), 0, true);
- if (window?.Frame == null)
- {
- await Output.Instance.ErrorMsg("Failed to open Include Graph window!");
- }
- else
- {
- IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
- windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_CmdUIGuid, GraphWindow.IncludeGraphToolWindow.GUIDString);
- Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show());
- }
- }
- }
-}
diff --git a/IncludeToolbox/Commands/IncludeWhatYouUse.cs b/IncludeToolbox/Commands/IncludeWhatYouUse.cs
deleted file mode 100644
index 2871ff7..0000000
--- a/IncludeToolbox/Commands/IncludeWhatYouUse.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-using IncludeToolbox.IncludeWhatYouUse;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using System;
-using System.ComponentModel.Design;
-using System.IO;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- ///
- /// Command handler
- ///
- internal sealed class IncludeWhatYouUse : CommandBase
- {
- public override CommandID CommandID => new CommandID(CommandSetGuids.MenuGroup, 0x0103);
-
- ///
- /// Whether we already checked for updates.
- ///
- private bool checkedForUpdatesThisSession = false;
-
- public IncludeWhatYouUse()
- {
- }
-
- protected override void SetupMenuCommand()
- {
- base.SetupMenuCommand();
- menuCommand.BeforeQueryStatus += UpdateVisibility;
- }
-
- private void UpdateVisibility(object sender, EventArgs e)
- {
- // Needs to be part of a VCProject to be applicable.
- var document = VSUtils.GetDTE()?.ActiveDocument;
- menuCommand.Visible = VSUtils.VCUtils.IsVCProject(document?.ProjectItem?.ContainingProject);
- }
-
- private async Task DownloadIWYUWithProgressBar(string executablePath, IVsThreadedWaitDialogFactory dialogFactory)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- IVsThreadedWaitDialog2 progressDialog;
- dialogFactory.CreateInstance(out progressDialog);
- if (progressDialog == null)
- {
- Output.Instance.WriteLine("Failed to get create wait dialog.");
- return false;
- }
-
- progressDialog.StartWaitDialogWithPercentageProgress(
- szWaitCaption: "Include Toolbox - Downloading include-what-you-use",
- szWaitMessage: "", // comes in later.
- szProgressText: null,
- varStatusBmpAnim: null,
- szStatusBarText: "Downloading include-what-you-use",
- fIsCancelable: true,
- iDelayToShowDialog: 0,
- iTotalSteps: 100,
- iCurrentStep: 0);
-
- var cancellationToken = new System.Threading.CancellationTokenSource();
-
- try
- {
- await IWYUDownload.DownloadIWYU(executablePath, delegate (string section, string status, float percentage)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- bool canceled;
- progressDialog.UpdateProgress(
- szUpdatedWaitMessage: section,
- szProgressText: status,
- szStatusBarText: $"Downloading include-what-you-use - {section} - {status}",
- iCurrentStep: (int)(percentage * 100),
- iTotalSteps: 100,
- fDisableCancel: true,
- pfCanceled: out canceled);
- if (canceled)
- {
- cancellationToken.Cancel();
- }
- }, cancellationToken.Token);
- }
- catch (Exception e)
- {
- await Output.Instance.ErrorMsg("Failed to download include-what-you-use: {0}", e);
- return false;
- }
- finally
- {
- progressDialog.EndWaitDialog();
- }
-
- return true;
- }
-
- private async Task OptionalDownloadOrUpdate(IncludeWhatYouUseOptionsPage settings, IVsThreadedWaitDialogFactory dialogFactory)
- {
- // Check existence, offer to download if it's not there.
- bool downloadedNewIwyu = false;
- if (!File.Exists(settings.ExecutablePath))
- {
- if (await Output.Instance.YesNoMsg($"Can't find include-what-you-use in '{settings.ExecutablePath}'. Do you want to download it from '{IWYUDownload.DisplayRepositorURL}'?") != Output.MessageResult.Yes)
- {
- return;
- }
-
- downloadedNewIwyu = await DownloadIWYUWithProgressBar(settings.ExecutablePath, dialogFactory);
- if (!downloadedNewIwyu)
- return;
- }
- else if (settings.AutomaticCheckForUpdates && !checkedForUpdatesThisSession)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- IVsThreadedWaitDialog2 dialog = null;
- dialogFactory.CreateInstance(out dialog);
- dialog?.StartWaitDialog("Include Toolbox", "Running Include-What-You-Use", null, null, "Checking for Updates for include-what-you-use", 0, false, true);
- bool newVersionAvailable = await IWYUDownload.IsNewerVersionAvailableOnline(settings.ExecutablePath);
- dialog?.EndWaitDialog();
-
- if (newVersionAvailable)
- {
- checkedForUpdatesThisSession = true;
- if (await Output.Instance.YesNoMsg($"There is a new version of include-what-you-use available. Do you want to download it from '{IWYUDownload.DisplayRepositorURL}'?") == Output.MessageResult.Yes)
- {
- downloadedNewIwyu = await DownloadIWYUWithProgressBar(settings.ExecutablePath, dialogFactory);
- }
- }
- }
- if (downloadedNewIwyu)
- settings.AddMappingFiles(IWYUDownload.GetMappingFilesNextToIwyuPath(settings.ExecutablePath));
- }
-
- ///
- /// This function is the callback used to execute the command when the menu item is clicked.
- /// See the constructor to see how the menu item is associated with this function using
- /// OleMenuCommandService service and MenuCommand class.
- ///
- /// Event sender.
- /// Event args.
- protected override async Task MenuItemCallback(object sender, EventArgs e)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- var settingsIwyu = (IncludeWhatYouUseOptionsPage)Package.GetDialogPage(typeof(IncludeWhatYouUseOptionsPage));
- Output.Instance.Clear();
-
- var document = VSUtils.GetDTE().ActiveDocument;
- if (document == null)
- {
- Output.Instance.WriteLine("No active document!");
- return;
- }
- var project = document.ProjectItem?.ContainingProject;
- if (project == null)
- {
- Output.Instance.WriteLine("The document {0} is not part of a project.", document.Name);
- return;
- }
-
- var dialogFactory = ServiceProvider.GetService(typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
- if (dialogFactory == null)
- {
- Output.Instance.WriteLine("Failed to get IVsThreadedWaitDialogFactory service.");
- return;
- }
-
- await OptionalDownloadOrUpdate(settingsIwyu, dialogFactory);
-
- // We should really have it now, but just in case our update or download method screwed up.
- if (!File.Exists(settingsIwyu.ExecutablePath))
- {
- await Output.Instance.ErrorMsg("Unexpected error: Can't find include-what-you-use.exe after download/update.");
- return;
- }
- checkedForUpdatesThisSession = true;
-
- // Save all documents.
- try
- {
- document.DTE.Documents.SaveAll();
- }
- catch(Exception saveException)
- {
- Output.Instance.WriteLine("Failed to get save all documents: {0}", saveException);
- }
-
- // Start wait dialog.
- {
- IVsThreadedWaitDialog2 dialog = null;
- dialogFactory.CreateInstance(out dialog);
- dialog?.StartWaitDialog("Include Toolbox", "Running include-what-you-use", null, null, "Running include-what-you-use", 0, false, true);
-
- string output = await IWYU.RunIncludeWhatYouUse(document.FullName, project, settingsIwyu);
- if (settingsIwyu.ApplyProposal && output != null)
- {
- var settingsFormatting = (FormatterOptionsPage)Package.GetDialogPage(typeof(FormatterOptionsPage));
- await IWYU.Apply(output, settingsIwyu.RunIncludeFormatter, settingsFormatting);
- }
-
- dialog?.EndWaitDialog();
- }
- }
- }
-}
diff --git a/IncludeToolbox/Commands/TrialAndErrorRemoval_CodeWindow.cs b/IncludeToolbox/Commands/TrialAndErrorRemoval_CodeWindow.cs
deleted file mode 100644
index 99b27f7..0000000
--- a/IncludeToolbox/Commands/TrialAndErrorRemoval_CodeWindow.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-using System;
-using System.ComponentModel.Design;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Windows;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using EnvDTE;
-using Microsoft.VisualStudio.Text;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- ///
- /// Command handler
- ///
- internal sealed class TrialAndErrorRemoval_CodeWindow : CommandBase
- {
- public override CommandID CommandID => new CommandID(CommandSetGuids.MenuGroup, 0x0104);
-
- private TrialAndErrorRemoval impl;
-
- public TrialAndErrorRemoval_CodeWindow()
- {
- }
-
- protected override void SetupMenuCommand()
- {
- base.SetupMenuCommand();
-
- impl = new TrialAndErrorRemoval();
- menuCommand.BeforeQueryStatus += UpdateVisibility;
- }
-
- private async void UpdateVisibility(object sender, EventArgs e)
- {
- menuCommand.Visible = (await VSUtils.VCUtils.IsCompilableFile(VSUtils.GetDTE().ActiveDocument)).Result;
- }
-
- ///
- /// This function is the callback used to execute the command when the menu item is clicked.
- /// See the constructor to see how the menu item is associated with this function using
- /// OleMenuCommandService service and MenuCommand class.
- ///
- /// Event sender.
- /// Event args.
- protected override async Task MenuItemCallback(object sender, EventArgs e)
- {
- var document = VSUtils.GetDTE().ActiveDocument;
- if (document != null)
- await impl.PerformTrialAndErrorIncludeRemoval(document, (TrialAndErrorRemovalOptionsPage)Package.GetDialogPage(typeof(TrialAndErrorRemovalOptionsPage)));
- }
- }
-}
\ No newline at end of file
diff --git a/IncludeToolbox/Commands/TrialAndErrorRemoval_Project.cs b/IncludeToolbox/Commands/TrialAndErrorRemoval_Project.cs
deleted file mode 100644
index 06a8917..0000000
--- a/IncludeToolbox/Commands/TrialAndErrorRemoval_Project.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-using EnvDTE;
-using Microsoft.VisualStudio;
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.Design;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.Commands
-{
- ///
- /// Command handler
- ///
- internal sealed class TrialAndErrorRemoval_Project : CommandBase
- {
- public override CommandID CommandID => new CommandID(CommandSetGuids.ProjectGroup, 0x0100);
-
- private TrialAndErrorRemoval impl;
- private TrialAndErrorRemovalOptionsPage settings;
-
- private ProjectItems projectItems = null;
- private int numTotalRemovedIncludes = 0;
- private Queue projectFiles;
-
- public TrialAndErrorRemoval_Project()
- {
- projectFiles = new Queue();
- }
-
- protected override void SetupMenuCommand()
- {
- base.SetupMenuCommand();
-
- impl = new TrialAndErrorRemoval();
- impl.OnFileFinished += OnDocumentIncludeRemovalFinished;
- menuCommand.BeforeQueryStatus += UpdateVisibility;
-
- settings = (TrialAndErrorRemovalOptionsPage)Package.GetDialogPage(typeof(TrialAndErrorRemovalOptionsPage));
- }
-
- private void OnDocumentIncludeRemovalFinished(int removedIncludes, bool canceled)
- {
- _ = Task.Run(async () =>
- {
- numTotalRemovedIncludes += removedIncludes;
- if (canceled || !await ProcessNextFile())
- {
- _ = Output.Instance.InfoMsg("Removed total of {0} #include directives from project.", numTotalRemovedIncludes);
- numTotalRemovedIncludes = 0;
- }
- });
- }
-
- private void UpdateVisibility(object sender, EventArgs e)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- string reason;
- var project = GetSelectedCppProject(out reason);
- menuCommand.Visible = project != null;
- }
-
- static Project GetSelectedCppProject(out string reasonForFailure)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- reasonForFailure = "";
-
- var selectedItems = VSUtils.GetDTE().SelectedItems;
- if (selectedItems.Count < 1)
- {
- reasonForFailure = "Selection is empty!";
- return null;
- }
-
- // Reading .Item(object) behaves weird, but iterating works.
- foreach (SelectedItem item in selectedItems)
- {
- Project vcProject = item?.Project;
- if (VSUtils.VCUtils.IsVCProject(vcProject))
- {
- return vcProject;
- }
- }
-
- reasonForFailure = "Selection does not contain a C++ project!";
- return null;
- }
-
- private async Task ProcessNextFile()
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- while (projectFiles.Count > 0)
- {
- ProjectItem projectItem = projectFiles.Dequeue();
-
- Document document = null;
- try
- {
- document = projectItem.Open().Document;
- }
- catch (Exception)
- {
- }
- if (document == null)
- continue;
-
- bool started = await impl.PerformTrialAndErrorIncludeRemoval(document, settings);
- if (started)
- return true;
- }
- return false;
- }
-
- private static void RecursiveFindFilesInProject(ProjectItems items, ref Queue projectFiles)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- var e = items.GetEnumerator();
- while (e.MoveNext())
- {
- var item = e.Current;
- if (item == null)
- continue;
- var projectItem = item as ProjectItem;
- if (projectItem == null)
- continue;
-
- Guid projectItemKind = new Guid(projectItem.Kind);
- if (projectItemKind == VSConstants.GUID_ItemType_VirtualFolder ||
- projectItemKind == VSConstants.GUID_ItemType_PhysicalFolder)
- {
- RecursiveFindFilesInProject(projectItem.ProjectItems, ref projectFiles);
- }
- else if (projectItemKind == VSConstants.GUID_ItemType_PhysicalFile)
- {
- projectFiles.Enqueue(projectItem);
- }
- else
- {
- Output.Instance.WriteLine("Unexpected Error: Unknown projectItem {0} of Kind {1}", projectItem.Name, projectItem.Kind);
- }
- }
- }
-
- private async Task PerformTrialAndErrorRemoval(Project project)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- projectItems = project.ProjectItems;
-
- projectFiles.Clear();
- RecursiveFindFilesInProject(projectItems, ref projectFiles);
-
- if (projectFiles.Count > 2)
- {
- if (await Output.Instance.YesNoMsg("Attention! Trial and error include removal on large projects make take up to several hours! In this time you will not be able to use Visual Studio. Are you sure you want to continue?")
- != Output.MessageResult.Yes)
- {
- return;
- }
- }
-
- numTotalRemovedIncludes = 0;
- await ProcessNextFile();
- }
-
-
- ///
- /// This function is the callback used to execute the command when the menu item is clicked.
- /// See the constructor to see how the menu item is associated with this function using
- /// OleMenuCommandService service and MenuCommand class.
- ///
- /// Event sender.
- /// Event args.
- protected override async Task MenuItemCallback(object sender, EventArgs e)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- if (TrialAndErrorRemoval.WorkInProgress)
- {
- await Output.Instance.ErrorMsg("Trial and error include removal already in progress!");
- return;
- }
-
- try
- {
- Project project = GetSelectedCppProject(out string reasonForFailure);
- if (project == null)
- {
- Output.Instance.WriteLine(reasonForFailure);
- return;
- }
-
- await PerformTrialAndErrorRemoval(project);
- }
- finally
- {
- projectItems = null;
- }
- }
- }
-}
diff --git a/IncludeToolbox/Formatter/IncludeFormatter.cs b/IncludeToolbox/Formatter/IncludeFormatter.cs
deleted file mode 100644
index 0b5f61f..0000000
--- a/IncludeToolbox/Formatter/IncludeFormatter.cs
+++ /dev/null
@@ -1,254 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-
-namespace IncludeToolbox.Formatter
-{
- public static class IncludeFormatter
- {
- public static string FormatPath(string absoluteIncludeFilename, FormatterOptionsPage.PathMode pathformat, IEnumerable includeDirectories)
- {
- if (pathformat == FormatterOptionsPage.PathMode.Absolute)
- {
- return absoluteIncludeFilename;
- }
- else
- {
- // todo: Treat std library files special?
- if (absoluteIncludeFilename != null)
- {
- int bestLength = Int32.MaxValue;
- string bestCandidate = null;
-
- foreach (string includeDirectory in includeDirectories)
- {
- string proposal = Utils.MakeRelative(includeDirectory, absoluteIncludeFilename);
-
- if (proposal.Length < bestLength)
- {
- if (pathformat == FormatterOptionsPage.PathMode.Shortest ||
- (proposal.IndexOf("../") < 0 && proposal.IndexOf("..\\") < 0))
- {
- bestCandidate = proposal;
- bestLength = proposal.Length;
- }
- }
- }
-
- return bestCandidate;
- }
- }
-
- return null;
- }
-
- ///
- /// Formats the paths of a given list of include line info.
- ///
- private static void FormatPaths(IEnumerable lines, FormatterOptionsPage.PathMode pathformat, IEnumerable includeDirectories)
- {
- if (pathformat == FormatterOptionsPage.PathMode.Unchanged)
- return;
-
- foreach (var line in lines)
- {
- string absoluteIncludeDir = line.TryResolveInclude(includeDirectories, out bool resolvedPath);
- if (resolvedPath)
- line.IncludeContent = FormatPath(absoluteIncludeDir, pathformat, includeDirectories) ?? line.IncludeContent;
- }
- }
-
- private static void FormatDelimiters(IEnumerable lines, FormatterOptionsPage.DelimiterMode delimiterMode)
- {
- switch (delimiterMode)
- {
- case FormatterOptionsPage.DelimiterMode.AngleBrackets:
- foreach (var line in lines)
- line.SetDelimiterType(IncludeLineInfo.DelimiterType.AngleBrackets);
- break;
- case FormatterOptionsPage.DelimiterMode.Quotes:
- foreach (var line in lines)
- line.SetDelimiterType(IncludeLineInfo.DelimiterType.Quotes);
- break;
- }
- }
-
- private static void FormatSlashes(IEnumerable lines, FormatterOptionsPage.SlashMode slashMode)
- {
- switch (slashMode)
- {
- case FormatterOptionsPage.SlashMode.ForwardSlash:
- foreach (var line in lines)
- line.IncludeContent = line.IncludeContent.Replace('\\', '/');
- break;
- case FormatterOptionsPage.SlashMode.BackSlash:
- foreach (var line in lines)
- line.IncludeContent = line.IncludeContent.Replace('/', '\\');
- break;
- }
- }
-
- private static List SortIncludes(IList lines, FormatterOptionsPage settings, string documentName)
- {
- string[] precedenceRegexes = RegexUtils.FixupRegexes(settings.PrecedenceRegexes, documentName);
-
- List outSortedList = new List(lines.Count);
-
- IEnumerable includeBatch;
- int numConsumedItems = 0;
-
- do
- {
- // Fill in all non-include items between batches.
- var nonIncludeItems = lines.Skip(numConsumedItems).TakeWhile(x => !x.ContainsActiveInclude);
- numConsumedItems += nonIncludeItems.Count();
- outSortedList.AddRange(nonIncludeItems);
-
- // Process until we hit a preprocessor directive that is not an include.
- // Those are boundaries for the sorting which we do not want to cross.
- includeBatch = lines.Skip(numConsumedItems).TakeWhile(x => x.ContainsActiveInclude || !x.ContainsPreProcessorDirective);
- numConsumedItems += includeBatch.Count();
-
- } while (SortIncludeBatch(settings, precedenceRegexes, outSortedList, includeBatch) && numConsumedItems != lines.Count);
-
- return outSortedList;
- }
-
- private static bool SortIncludeBatch(FormatterOptionsPage settings, string[] precedenceRegexes,
- List outSortedList, IEnumerable includeBatch)
- {
- // Get enumerator and cancel if batch is empty.
- if (!includeBatch.Any())
- return false;
-
- // Fetch settings.
- FormatterOptionsPage.TypeSorting typeSorting = settings.SortByType;
- bool regexIncludeDelimiter = settings.RegexIncludeDelimiter;
- bool blankAfterRegexGroupMatch = settings.BlankAfterRegexGroupMatch;
-
- // Select only valid include lines and sort them. They'll stay in this relative sorted
- // order when rearranged by regex precedence groups.
- var includeLines = includeBatch
- .Where(x => x.ContainsActiveInclude)
- .OrderBy(x => x.IncludeContent)
- .ToList();
-
- if (settings.RemoveDuplicates)
- {
- HashSet uniqueIncludes = new HashSet();
- includeLines.RemoveAll(x => !x.ShouldBePreserved &&
- !uniqueIncludes.Add(x.GetIncludeContentWithDelimiters()));
- }
-
- // Group the includes by the index of the precedence regex they match, or
- // precedenceRegexes.Length for no match, and sort the groups by index.
- var includeGroups = includeLines
- .GroupBy(x =>
- {
- var includeContent = regexIncludeDelimiter ? x.GetIncludeContentWithDelimiters() : x.IncludeContent;
- for (int precedence = 0; precedence < precedenceRegexes.Count(); ++precedence)
- {
- if (Regex.Match(includeContent, precedenceRegexes[precedence]).Success)
- return precedence;
- }
-
- return precedenceRegexes.Length;
- }, x => x)
- .OrderBy(x => x.Key);
-
- // Optional newlines between regex match groups
- var groupStarts = new HashSet();
- if (blankAfterRegexGroupMatch && precedenceRegexes.Length > 0 && includeLines.Count() > 1)
- {
- // Set flag to prepend a newline to each group's first include
- foreach (var grouping in includeGroups)
- groupStarts.Add(grouping.First());
- }
-
- // Flatten the groups
- var sortedIncludes = includeGroups.SelectMany(x => x.Select(y => y));
-
- // Sort by angle or quoted delimiters if either of those options were selected
- if (typeSorting == FormatterOptionsPage.TypeSorting.AngleBracketsFirst)
- sortedIncludes = sortedIncludes.OrderBy(x => x.LineDelimiterType == IncludeLineInfo.DelimiterType.AngleBrackets ? 0 : 1);
- else if (typeSorting == FormatterOptionsPage.TypeSorting.QuotedFirst)
- sortedIncludes = sortedIncludes.OrderBy(x => x.LineDelimiterType == IncludeLineInfo.DelimiterType.Quotes ? 0 : 1);
-
- // Merge sorted includes with original non-include lines
- var sortedIncludeEnumerator = sortedIncludes.GetEnumerator();
- var sortedLines = includeBatch.Select(originalLine =>
- {
- if (originalLine.ContainsActiveInclude)
- {
- // Replace original include with sorted includes
- return sortedIncludeEnumerator.MoveNext() ? sortedIncludeEnumerator.Current : new IncludeLineInfo();
- }
- return originalLine;
- });
-
- if (settings.RemoveEmptyLines)
- {
- // Removing duplicates may have introduced new empty lines
- sortedLines = sortedLines.Where(sortedLine => !string.IsNullOrWhiteSpace(sortedLine.RawLine));
- }
-
- // Finally, update the actual lines
- {
- bool firstLine = true;
- foreach (var sortedLine in sortedLines)
- {
- // Handle prepending a newline if requested, as long as:
- // - this include is the begin of a new group
- // - it's not the first line
- // - the previous line isn't already a non-include
- if (groupStarts.Contains(sortedLine) && !firstLine && outSortedList[outSortedList.Count - 1].ContainsActiveInclude)
- {
- outSortedList.Add(new IncludeLineInfo());
- }
- outSortedList.Add(sortedLine);
- firstLine = false;
- }
- }
-
- return true;
- }
-
- ///
- /// Formats all includes in a given piece of text.
- ///
- /// Text to be parsed for includes.
- /// Path to the document the edit is occuring in.
- /// A list of include directories
- /// Settings that determine how the formating should be done.
- /// Formated text.
- public static string FormatIncludes(string text, string documentPath, IEnumerable includeDirectories, FormatterOptionsPage settings)
- {
- string documentDir = Path.GetDirectoryName(documentPath);
- string documentName = Path.GetFileNameWithoutExtension(documentPath);
-
- includeDirectories = new string[] { Microsoft.VisualStudio.PlatformUI.PathUtil.Normalize(documentDir) + Path.DirectorySeparatorChar }.Concat(includeDirectories);
-
- string newLineChars = Utils.GetDominantNewLineSeparator(text);
-
- var lines = IncludeLineInfo.ParseIncludes(text, settings.RemoveEmptyLines ? ParseOptions.RemoveEmptyLines : ParseOptions.None);
-
- // Format.
- IEnumerable formatingDirs = includeDirectories;
- if (settings.IgnoreFileRelative)
- {
- formatingDirs = formatingDirs.Skip(1);
- }
- FormatPaths(lines, settings.PathFormat, formatingDirs);
- FormatDelimiters(lines, settings.DelimiterFormatting);
- FormatSlashes(lines, settings.SlashFormatting);
-
- // Sorting. Ignores non-include lines.
- lines = SortIncludes(lines, settings, documentName);
-
- // Combine again.
- return string.Join(newLineChars, lines.Select(x => x.RawLine));
- }
- }
-}
diff --git a/IncludeToolbox/Formatter/IncludeLineInfo.cs b/IncludeToolbox/Formatter/IncludeLineInfo.cs
deleted file mode 100644
index b9a8867..0000000
--- a/IncludeToolbox/Formatter/IncludeLineInfo.cs
+++ /dev/null
@@ -1,338 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-
-namespace IncludeToolbox.Formatter
-{
- [Flags]
- public enum ParseOptions
- {
- None = 0,
-
- ///
- /// Whether IncludeLineInfo objects should be created for empty lines.
- ///
- RemoveEmptyLines = 1,
-
- ///
- /// Marks all includes that are within preprocessor conditionals as inactive/non-includes
- ///
- IgnoreIncludesInPreprocessorConditionals = 2,
-
- ///
- /// Keep only lines that contain valid includes.
- ///
- KeepOnlyValidIncludes = 4 | RemoveEmptyLines,
- }
-
- ///
- /// A line of text + information about the include directive in this line if any.
- /// Allows for manipulation of the former.
- ///
- ///
- /// This is obviously not a high performance representation of text, but very easy to use for our purposes here.
- ///
- public class IncludeLineInfo
- {
- ///
- /// Parses a given text into IncludeLineInfo objects.
- ///
- /// A list of parsed lines.
- public static List ParseIncludes(string text, ParseOptions options)
- {
- StringReader reader = new StringReader(text);
-
- var outInfo = new List();
-
- // Simplistic parsing.
- int openMultiLineComments = 0;
- int openIfdefs = 0;
- string lineText;
- for (int lineNumber = 0; true; ++lineNumber)
- {
- lineText = reader.ReadLine();
- if (lineText == null)
- break;
-
- if (options.HasFlag(ParseOptions.RemoveEmptyLines) && string.IsNullOrWhiteSpace(lineText))
- continue;
-
- int commentedSectionStart = int.MaxValue;
- int commentedSectionEnd = int.MaxValue;
-
- // Check for single line comment.
- {
- int singleLineCommentStart = lineText.IndexOf("//");
- if (singleLineCommentStart != -1)
- commentedSectionStart = singleLineCommentStart;
- }
-
- // Check for multi line comments.
- {
- int multiLineCommentStart = lineText.IndexOf("/*");
- if (multiLineCommentStart > -1 && multiLineCommentStart < commentedSectionStart)
- {
- ++openMultiLineComments;
- commentedSectionStart = multiLineCommentStart;
- }
-
- int multiLineCommentEnd = lineText.IndexOf("*/");
- if (multiLineCommentEnd > -1)
- {
- --openMultiLineComments;
- commentedSectionEnd = multiLineCommentEnd;
- }
- }
-
- bool isCommented(int pos) => (commentedSectionStart == int.MaxValue && openMultiLineComments > 0) || (pos > commentedSectionStart && pos < commentedSectionEnd);
-
- // Check for #if / #ifdefs.
- if (options.HasFlag(ParseOptions.IgnoreIncludesInPreprocessorConditionals))
- {
- // There can be only a single preprocessor directive per line, so no need to parse more than this.
- // (in theory it must be the first thing in the line, but MSVC is not strict on this, so we aren't either.
- int ifdefStart = lineText.IndexOf("#if");
- int ifdefEnd = lineText.IndexOf("#endif");
- if (ifdefStart > -1 && !isCommented(ifdefStart))
- {
- ++openIfdefs;
- }
- else if (ifdefEnd > -1 && !isCommented(ifdefEnd))
- {
- --openIfdefs;
- }
- }
-
- int includeOccurence = lineText.IndexOf("#include");
-
- // Not a valid include.
- if (includeOccurence == -1 || // Include not found
- isCommented(includeOccurence) || // Include commented out
- openIfdefs > 0) // Inside an #ifdef block
- {
- if (!options.HasFlag(ParseOptions.KeepOnlyValidIncludes))
- outInfo.Add(new IncludeLineInfo() { lineText = lineText, LineNumber = lineNumber });
- }
- // A valid include
- else
- {
- // Parse include delimiters.
- int delimiter1 = -1;
- int delimiter0 = lineText.IndexOf('\"', includeOccurence + "#include".Length);
- if (delimiter0 == -1)
- {
- delimiter0 = lineText.IndexOf('<', includeOccurence + "#include".Length);
- if (delimiter0 != -1)
- delimiter1 = lineText.IndexOf('>', delimiter0 + 1);
- }
- else
- {
- delimiter1 = lineText.IndexOf('\"', delimiter0 + 1);
- }
-
- // Might not be valid after all!
- if (delimiter0 != -1 && delimiter1 != -1)
- outInfo.Add(new IncludeLineInfo() { lineText = lineText, LineNumber = lineNumber, delimiter0 = delimiter0, delimiter1 = delimiter1 });
- else if (!options.HasFlag(ParseOptions.KeepOnlyValidIncludes))
- outInfo.Add(new IncludeLineInfo() { lineText = lineText, LineNumber = lineNumber });
- }
- }
-
- return outInfo;
- }
-
- ///
- /// Whether the line includes an enabled include.
- ///
- ///
- /// A line that contains a valid #include may still be ContainsActiveInclude==false if it is commented or (depending on parsing options) #if(def)'ed out.
- ///
- public bool ContainsActiveInclude => delimiter0 != -1;
-
- public enum DelimiterType
- {
- Quotes,
- AngleBrackets,
- None
- }
-
- public DelimiterType LineDelimiterType
- {
- get
- {
- if (ContainsActiveInclude)
- {
- DelimiterSanityCheck();
-
- if (lineText[delimiter0] == '<')
- return DelimiterType.AngleBrackets;
- else if (lineText[delimiter0] == '\"')
- return DelimiterType.Quotes;
- }
- return DelimiterType.None;
- }
- }
-
- ///
- /// Changes the type of this line.
- /// Has only an effect if ContainsActiveInclude is true.
- ///
- public void SetDelimiterType(DelimiterType newDelimiterType)
- {
- if (LineDelimiterType != newDelimiterType && ContainsActiveInclude)
- {
- DelimiterSanityCheck();
-
- if (newDelimiterType == DelimiterType.AngleBrackets)
- {
- StringBuilder sb = new StringBuilder(lineText);
- sb[delimiter0] = '<';
- sb[delimiter1] = '>';
- lineText = sb.ToString();
- }
- else if (newDelimiterType == DelimiterType.Quotes)
- {
- StringBuilder sb = new StringBuilder(lineText);
- sb[delimiter0] = '"';
- sb[delimiter1] = '"';
- lineText = sb.ToString();
- }
- }
- }
-
- ///
- /// Whether the line contains a preprocessor directive.
- /// Does not take into account surrounding block comments.
- ///
- public bool ContainsPreProcessorDirective
- {
- get
- {
- // In theory the '#' of a preprocessor directive MUST come first, but just like MSVC we relax the rules a bit here.
- foreach (char c in lineText)
- {
- if (c == '#')
- return true;
- else if (!char.IsWhiteSpace(c))
- return false;
- }
-
- return false;
- }
- }
-
- ///
- /// Tries to resolve the include (if any) using a list of directories.
- ///
- /// Include directories. Keep in mind that IncludeLineInfo does not know the path of the file this include is from.
- /// Empty string if this is not an include, absolute include path if possible or raw include if not.
- public string TryResolveInclude(IEnumerable includeDirectories, out bool success)
- {
- if (!ContainsActiveInclude)
- {
- success = false;
- return "";
- }
-
- string includeContent = IncludeContent;
-
- foreach (string dir in includeDirectories)
- {
- string candidate = Path.Combine(dir, includeContent);
- if (File.Exists(candidate))
- {
- success = true;
- return Utils.GetExactPathName(candidate);
- }
- }
-
- Output.Instance.WriteLine("Unable to resolve include: '{0}'", includeContent);
- success = false;
- return includeContent;
- }
-
- ///
- /// Include content with added delimiters.
- ///
- public string GetIncludeContentWithDelimiters()
- {
- if (ContainsActiveInclude)
- {
- DelimiterSanityCheck();
- return lineText.Substring(delimiter0, delimiter1 - delimiter0 + 1);
- }
- else
- return string.Empty;
- }
-
-
- ///
- /// Changes in the include content will NOT be reflected immediately in the raw line text.
- ///
- ///
- public string IncludeContent
- {
- get
- {
- if (ContainsActiveInclude)
- {
- DelimiterSanityCheck();
- int length = delimiter1 - delimiter0 - 1;
- return length > 0 ? RawLine.Substring(delimiter0 + 1, length) : "";
- }
- else
- return string.Empty;
- }
- set
- {
- if (!ContainsActiveInclude)
- return;
-
- lineText = lineText.Remove(delimiter0 + 1, delimiter1 - delimiter0 - 1);
- lineText = lineText.Insert(delimiter0 + 1, value);
- delimiter1 = delimiter0 + value.Length + 1;
- }
- }
-
- ///
- /// Raw line text as found.
- ///
- public string RawLine
- {
- get { return lineText; }
- }
- private string lineText = "";
-
- ///
- /// Line number in which this include line occurred within its original file.
- ///
- ///
- /// Starts of course with 0 unlike displayed line numbers.
- ///
- public int LineNumber { get; private set; } = -1;
-
- public static bool ContainsPreserveFlag(string lineText)
- {
- return lineText.Contains("$include-toolbox-preserve$");
- }
-
- ///
- /// Whether the include line should not be removed by iwyu and Trial & Error Removal.
- ///
- public bool ShouldBePreserved { get { return ContainsPreserveFlag(lineText); } }
-
- private int delimiter0 = -1;
- private int delimiter1 = -1;
-
- [System.Diagnostics.Conditional("DEBUG")]
- private void DelimiterSanityCheck()
- {
- System.Diagnostics.Debug.Assert(delimiter0 >= 0 && delimiter0 < lineText.Length);
- System.Diagnostics.Debug.Assert(delimiter1 >= 0 && delimiter1 < lineText.Length);
- System.Diagnostics.Debug.Assert(delimiter0 < delimiter1);
- }
- }
-}
diff --git a/IncludeToolbox/IncludeToolbox.args.json b/IncludeToolbox/IncludeToolbox.args.json
deleted file mode 100644
index 7c62244..0000000
--- a/IncludeToolbox/IncludeToolbox.args.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "DataCollection": [
- {
- "Id": "3856631e-87ac-4b3d-8d21-fccdd977a1ae",
- "Command": "/rootsuffix Exp"
- }
- ]
-}
\ No newline at end of file
diff --git a/IncludeToolbox/IncludeToolbox.csproj b/IncludeToolbox/IncludeToolbox.csproj
deleted file mode 100644
index 4bb30e5..0000000
--- a/IncludeToolbox/IncludeToolbox.csproj
+++ /dev/null
@@ -1,183 +0,0 @@
-
-
-
- 16.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
-
-
-
-
- Debug
- AnyCPU
- 2.0
- {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- {F9E250C6-A7AD-4888-8F17-6876736B8DCF}
- Library
- Properties
- IncludeToolbox
- IncludeToolbox
- v4.8
- true
- true
- true
- false
- false
- true
- true
- Program
- $(DevEnvDir)devenv.exe
- /rootsuffix Exp
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- False
-
-
- VSTHRD200
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
- Component
-
-
- Component
-
-
- Component
-
-
- Component
-
-
- Component
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- IncludeGraphControl.xaml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- compile; build; native; contentfiles; analyzers; buildtransitive
-
-
- 17.2.32505.173
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
- Always
- true
-
-
-
-
-
- true
-
-
-
-
-
- Menus.ctmenu
-
-
-
-
-
-
-
- MSBuild:Compile
- Designer
-
-
- MSBuild:Compile
- Designer
-
-
-
-
-
- copy "$(TargetDir)IncludeToolbox.vsix" "$(SolutionDir)IncludeToolbox.vsix"
-
-
-
\ No newline at end of file
diff --git a/IncludeToolbox/IncludeWhatYouUse/IWYU.cs b/IncludeToolbox/IncludeWhatYouUse/IWYU.cs
deleted file mode 100644
index 93b2e7d..0000000
--- a/IncludeToolbox/IncludeWhatYouUse/IWYU.cs
+++ /dev/null
@@ -1,360 +0,0 @@
-using EnvDTE;
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox.IncludeWhatYouUse
-{
- ///
- /// Command handler for include what you use.
- ///
- static public class IWYU
- {
- private static readonly Regex RegexRemoveLine = new Regex(@"^-\s+.+\s+\/\/ lines (\d+)-(\d+)$");
-
- private class FormatTask
- {
- public override string ToString()
- {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.Append("Remove:\n");
- foreach (int i in linesToRemove)
- stringBuilder.AppendFormat("{0},", i);
- stringBuilder.Append("\nAdd:\n");
- foreach (string s in linesToAdd)
- stringBuilder.AppendFormat("{0}\n", s);
- return stringBuilder.ToString();
- }
-
- public readonly HashSet linesToRemove = new HashSet();
- public readonly List linesToAdd = new List();
- };
-
-
- static private Dictionary ParseOutput(string iwyuOutput)
- {
- Dictionary fileTasks = new Dictionary();
- FormatTask currentTask = null;
- bool removeCommands = true;
-
- //- #include // lines 3-3
-
- // Parse what to do.
- var lines = Regex.Split(iwyuOutput, "\r\n|\r|\n");
- bool lastLineWasEmpty = false;
- foreach (string line in lines)
- {
- if (line.Length == 0)
- {
- if (lastLineWasEmpty)
- {
- currentTask = null;
- }
-
- lastLineWasEmpty = true;
- continue;
- }
-
- int i = line.IndexOf(" should add these lines:");
- if (i < 0)
- {
- i = line.IndexOf(" should remove these lines:");
- if (i >= 0)
- removeCommands = true;
- }
- else
- {
- removeCommands = false;
- }
-
- if (i >= 0)
- {
- string file = line.Substring(0, i);
-
- if (!fileTasks.TryGetValue(file, out currentTask))
- {
- currentTask = new FormatTask();
- fileTasks.Add(file, currentTask);
- }
- }
- else if (currentTask != null)
- {
- if (removeCommands)
- {
- var match = RegexRemoveLine.Match(line);
- if (match.Success)
- {
- int removeStart, removeEnd;
- if (int.TryParse(match.Groups[1].Value, out removeStart) &&
- int.TryParse(match.Groups[2].Value, out removeEnd))
- {
- for (int lineIdx = removeStart; lineIdx <= removeEnd; ++lineIdx)
- currentTask.linesToRemove.Add(lineIdx - 1);
- }
- }
- else if (lastLineWasEmpty)
- {
- currentTask = null;
- }
- }
- else
- {
- if (!string.IsNullOrWhiteSpace(line))
- {
- currentTask.linesToAdd.Add(line);
- }
- else if (lastLineWasEmpty)
- {
- currentTask = null;
- }
- }
- }
-
- lastLineWasEmpty = false;
- }
-
- return fileTasks;
- }
-
- static private async Task ApplyTasks(Dictionary tasks, bool applyFormatting, FormatterOptionsPage formatSettings)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- var dte = VSUtils.GetDTE();
-
- foreach (KeyValuePair entry in tasks)
- {
- string filename = entry.Key.Replace('/', '\\'); // Classy. But Necessary.
- EnvDTE.Window fileWindow = dte.ItemOperations.OpenFile(filename);
- if (fileWindow == null)
- {
- await Output.Instance.ErrorMsg("Failed to open File {0}", filename);
- continue;
- }
- fileWindow.Activate();
-
- var viewHost = VSUtils.GetCurrentTextViewHost();
- using (var edit = viewHost.TextView.TextBuffer.CreateEdit())
- {
- var originalLines = edit.Snapshot.Lines.ToArray();
-
- // Determine which line ending to use by majority.
- string lineEndingToBeUsed = Utils.GetDominantNewLineSeparator(edit.Snapshot.GetText());
-
- // Add lines.
- {
- // Find last include.
- // Will find even if commented out, but we don't care.
- int lastIncludeLine = -1;
- for (int line = originalLines.Length - 1; line >= 0; --line)
- {
- if (originalLines[line].GetText().Contains("#include"))
- {
- lastIncludeLine = line;
- break;
- }
- }
-
- // Build replacement string
- StringBuilder stringToInsertBuilder = new StringBuilder();
- foreach (string lineToAdd in entry.Value.linesToAdd)
- {
- stringToInsertBuilder.Append(lineToAdd);
- stringToInsertBuilder.Append(lineEndingToBeUsed);
- }
- string stringToInsert = stringToInsertBuilder.ToString();
-
-
- // optional, format before adding.
- if (applyFormatting)
- {
- var includeDirectories = VSUtils.GetProjectIncludeDirectories(fileWindow.Document.ProjectItem?.ContainingProject);
- stringToInsert = Formatter.IncludeFormatter.FormatIncludes(stringToInsert, fileWindow.Document.FullName, includeDirectories, formatSettings);
-
- // Add a newline if we removed it.
- if (formatSettings.RemoveEmptyLines)
- stringToInsert += lineEndingToBeUsed;
- }
-
- // Insert.
- int insertPosition = 0;
- if (lastIncludeLine >= 0 && lastIncludeLine < originalLines.Length)
- {
- insertPosition = originalLines[lastIncludeLine].EndIncludingLineBreak;
- }
- edit.Insert(insertPosition, stringToInsert.ToString());
- }
-
- // Remove lines.
- // It should safe to do that last since we added includes at the bottom, this way there is no confusion with the text snapshot.
- {
- foreach (int lineToRemove in entry.Value.linesToRemove.Reverse())
- {
- if (!Formatter.IncludeLineInfo.ContainsPreserveFlag(originalLines[lineToRemove].GetText()))
- edit.Delete(originalLines[lineToRemove].ExtentIncludingLineBreak);
- }
- }
-
- edit.Apply();
- }
-
- // For Debugging:
- //Output.Instance.WriteLine("");
- //Output.Instance.WriteLine(entry.Key);
- //Output.Instance.WriteLine(entry.Value.ToString());
- }
- }
-
- static public async Task Apply(string iwyuOutput, bool applyFormatter, FormatterOptionsPage formatOptions)
- {
- var tasks = ParseOutput(iwyuOutput);
- await ApplyTasks(tasks, applyFormatter, formatOptions);
- }
-
- ///
- /// Runs iwyu. Blocks until finished.
- ///
- static public async Task RunIncludeWhatYouUse(string fullFileName, EnvDTE.Project project, IncludeWhatYouUseOptionsPage settings)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- string preprocessorDefintions;
- try
- {
- preprocessorDefintions = VSUtils.VCUtils.GetCompilerSetting_PreprocessorDefinitions(project);
- }
- catch (VCQueryFailure e)
- {
- await Output.Instance.ErrorMsg("Can't run IWYU: {0}", e.Message);
- return null;
- }
-
- string output = "";
- using (var process = new System.Diagnostics.Process())
- {
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.RedirectStandardError = true;
- process.StartInfo.FileName = settings.ExecutablePath;
-
- // Clang options
- var clangOptionList = new List();
- // Disable all diagnostics
- clangOptionList.Add("-w");
- // ... despite of that "invalid token paste" comes through a lot. Disable it.
- clangOptionList.Add("-Wno-invalid-token-paste");
- // MSVC specific. See https://clang.llvm.org/docs/UsersManual.html#microsoft-extensions
- clangOptionList.Add("-fms-compatibility -fms-extensions -fdelayed-template-parsing");
-
- // icwyu options
- var iwyuOptionList = new List();
- iwyuOptionList.Add("--verbose=" + settings.LogVerbosity);
- for (int i = 0; i < settings.MappingFiles.Length; ++i)
- iwyuOptionList.Add("--mapping_file=\"" + settings.MappingFiles[i] + "\"");
- if (settings.NoDefaultMappings)
- iwyuOptionList.Add("--no_default_mappings");
- if (settings.PCHInCode)
- iwyuOptionList.Add("--pch_in_code");
- switch (settings.PrefixHeaderIncludes)
- {
- case IncludeWhatYouUseOptionsPage.PrefixHeaderMode.Add:
- iwyuOptionList.Add("--prefix_header_includes=add");
- break;
- case IncludeWhatYouUseOptionsPage.PrefixHeaderMode.Remove:
- iwyuOptionList.Add("--prefix_header_includes=remove");
- break;
- case IncludeWhatYouUseOptionsPage.PrefixHeaderMode.Keep:
- iwyuOptionList.Add("--prefix_header_includes=keep");
- break;
- }
- if (settings.TransitiveIncludesOnly)
- iwyuOptionList.Add("--transitive_includes_only");
-
-
-
- // Set max line length so something large so we don't loose comment information.
- // Documentation:
- // --max_line_length: maximum line length for includes. Note that this only affects comments and alignment thereof,
- // the maximum line length can still be exceeded with long file names(default: 80).
- iwyuOptionList.Add("--max_line_length=1024");
-
- /// write support file with includes, defines and the targetgile. Long argument lists lead to an error. Support files are the solution here.
- /// https://github.com/Wumpf/IncludeToolbox/issues/36
- // Include-paths and Preprocessor.
- var includes = string.Join(" ", VSUtils.GetProjectIncludeDirectories(project, false).Select(x => "-I \"" + x.Replace("\\", "\\\\") + "\""));
- var defines = preprocessorDefintions.Length == 0 ? "" : string.Join(" ", preprocessorDefintions.Split(';').Select(x => "-D" + x));
- var filename = "\"" + fullFileName.Replace("\\", "\\\\") + "\"";
-
- var iwyuOptions = string.Join(" ", iwyuOptionList.Select(x => " -Xiwyu " + x));
-
- var ext = Path.GetExtension(fullFileName);
-
- string co = "";
- switch (settings.Commentary)
- {
- case Commentaries.Always: co = (" -Xiwyu --update_comments"); break;
- case Commentaries.Never: co = (" -Xiwyu --no_comments"); break;
- case Commentaries.Default: break;
- }
-
- iwyuOptions += co;
-
- if (ext == ".h" || ext == ".hpp")
- {
- var tmp_cpp = Path.GetTempFileName();
- tmp_cpp = Path.ChangeExtension(tmp_cpp, ".cpp");
- File.WriteAllText(tmp_cpp, "#include \"" + fullFileName + "\"");
- iwyuOptions += " -Xiwyu --check_also=" + filename;
- filename = "\"" + tmp_cpp.Replace("\\", "\\\\") + "\"";
- }
- else if (settings.HeaderPrefix != "" && Directory.Exists(Path.GetDirectoryName(fullFileName) + settings.HeaderPrefix))
- {
- var correspond_h = settings.HeaderPrefix + '\\' + Path.GetFileNameWithoutExtension(fullFileName) + ".h";
- var correspond_hpp = Path.ChangeExtension(correspond_h, ".hpp");
- if (!File.Exists(correspond_h))
- iwyuOptions += " -Xiwyu --check_also=" + "\"" + correspond_h.Replace("\\", "\\\\") + "\"";
- else if (!File.Exists(correspond_hpp))
- iwyuOptions += " -Xiwyu --check_also=" + "\"" + correspond_hpp.Replace("\\", "\\\\") + "\"";
- }
-
- var supportFilePath = Path.GetTempFileName();
- File.WriteAllText(supportFilePath, includes + " " + defines + " " + filename);
-
- var clangOptions = string.Join(" ", clangOptionList);
- // each include-what-you-use parameter has an -Xiwyu prefix
- process.StartInfo.Arguments = $"{clangOptions} {iwyuOptions} {settings.AdditionalParameters} \"@{supportFilePath}\"";
-
- Output.Instance.Write("Running command '{0}' with following arguments:\n{1}\n\n", process.StartInfo.FileName, process.StartInfo.Arguments);
-
- // Start the child process.
- process.EnableRaisingEvents = true;
- process.OutputDataReceived += (s, args) =>
- {
- Output.Instance.WriteLine(args.Data);
- output += args.Data + "\n";
- };
- process.ErrorDataReceived += (s, args) =>
- {
- Output.Instance.WriteLine(args.Data);
- output += args.Data + "\n";
- };
- process.Start();
-
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- process.WaitForExit();
- process.CancelOutputRead();
- process.CancelErrorRead();
- }
-
- return output;
- }
- }
-}
diff --git a/IncludeToolbox/IncludeWhatYouUse/IWYUDownload.cs b/IncludeToolbox/IncludeWhatYouUse/IWYUDownload.cs
deleted file mode 100644
index 1e3677a..0000000
--- a/IncludeToolbox/IncludeWhatYouUse/IWYUDownload.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace IncludeToolbox.IncludeWhatYouUse
-{
- ///
- /// Functions for downloading and versioning of the iwyu installation.
- ///
- static public class IWYUDownload
- {
- public const string DisplayRepositorURL = @"https://github.com/Wumpf/iwyu_for_vs_includetoolbox";
- private const string DownloadRepositorURL = @"https://github.com/Wumpf/iwyu_for_vs_includetoolbox/archive/master.zip";
- private const string LatestCommitQuery = @"https://api.github.com/repos/Wumpf/iwyu_for_vs_includetoolbox/git/refs/heads/master";
-
- private static async Task GetCurrentVersionOnline()
- {
- using (var httpClient = new HttpClient())
- {
- // User agent is always required for github api.
- // https://developer.github.com/v3/#user-agent-required
- httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("IncludeToolbox");
-
- string latestCommitResponse;
- try
- {
- latestCommitResponse = await httpClient.GetStringAsync(LatestCommitQuery);
- }
- catch (HttpRequestException e)
- {
- Output.Instance.WriteLine($"Failed to query IWYU version from {DownloadRepositorURL}: {e}");
- return "";
- }
-
- // Poor man's json parsing in lack of a json parser.
- var shaRegex = new Regex(@"\""sha\""\w*:\w*\""([a-z0-9]+)\""");
- return shaRegex.Match(latestCommitResponse).Groups[1].Value;
- }
- }
-
- public static string GetVersionFilePath(string iwyuExectuablePath)
- {
- string directory = Path.GetDirectoryName(iwyuExectuablePath);
- return Path.Combine(directory, "version");
- }
-
- private static string GetCurrentVersionHarddrive(string iwyuExectuablePath)
- {
- // Read current version.
- try
- {
- return File.ReadAllText(GetVersionFilePath(iwyuExectuablePath));
- }
- catch
- {
- return "";
- }
- }
-
- public static async Task IsNewerVersionAvailableOnline(string executablePath)
- {
- string currentVersion = GetCurrentVersionHarddrive(executablePath);
- string onlineVersion = await GetCurrentVersionOnline();
- return currentVersion != onlineVersion;
- }
-
- ///
- /// Callback for download progress.
- ///
- /// General stage.
- /// Sub status, may be empty.
- /// Progress in percent for current section. -1 is there is none.
- public delegate void DownloadProgressUpdate(string section, string status, float percentage);
-
- ///
- /// Downloads iwyu from default download repository.
- ///
- ///
- /// Throws an exception if anything goes wrong (and there's a lot that can!)
- ///
- /// If cancellation token is used.
- static public async Task DownloadIWYU(string executablePath, DownloadProgressUpdate onProgressUpdate, CancellationToken cancellationToken)
- {
- string targetDirectory = Path.GetDirectoryName(executablePath);
- Directory.CreateDirectory(targetDirectory);
- string targetZipFile = Path.Combine(targetDirectory, "download.zip");
-
- // Delete existing zip file.
- try
- {
- File.Delete(targetZipFile);
- }
- catch { }
-
- // Download.
- onProgressUpdate("Connecting...", "", -1.0f);
-
- // In contrast to GetCurrentVersionOnline we're not using HttpClient here since WebClient makes downloading files so much nicer.
- // (in HttpClient we would need to do the whole buffering + queuing and file writing ourselves)
- using (var client = new WebClient())
- {
- var cancelRegistration = cancellationToken.Register(() =>
- {
- client.CancelAsync();
- throw new TaskCanceledException();
- });
-
- client.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) =>
- {
- int kbTodo = (int)System.Math.Ceiling((double)e.TotalBytesToReceive / 1024);
- int kbDownloaded = (int)System.Math.Ceiling((double)e.BytesReceived / 1024);
- onProgressUpdate("Downloading", kbTodo > 0 ? $"{kbTodo} / {kbDownloaded} kB" : $"{kbDownloaded} kB", e.ProgressPercentage * 0.01f);
- };
-
- await client.DownloadFileTaskAsync(DownloadRepositorURL, targetZipFile);
-
- cancelRegistration.Dispose();
- }
-
- // Unpacking. Looks like there is no async api, so we're just moving this to a task.
- onProgressUpdate("Unpacking...", "", -1.0f);
- await Task.Run(() =>
- {
- using (var zipArchive = new ZipArchive(File.OpenRead(targetZipFile), ZipArchiveMode.Read))
- {
- // Don't want to have the top level folder if any,
- string topLevelFolderName = "";
-
- for (int i = 0; i < zipArchive.Entries.Count; ++i)
- {
- var file = zipArchive.Entries[i];
-
- string targetName = file.FullName.Substring(topLevelFolderName.Length);
- string completeFileName = Path.Combine(targetDirectory, targetName);
-
- // If name is empty it should be a directory.
- if (file.Name == "")
- {
- if (i == 0) // We assume that if the first thing we encounter is a folder, it is a toplevel one.
- topLevelFolderName = file.FullName;
- else
- Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
- }
- else
- {
- using (var destination = File.Open(completeFileName, FileMode.Create, FileAccess.Write, FileShare.None))
- {
- using (var stream = file.Open())
- stream.CopyTo(destination);
- }
- }
-
- if (cancellationToken.IsCancellationRequested)
- return;
- }
- }
-
- }, cancellationToken);
-
- // Save version.
- onProgressUpdate("Saving Version", "", -1.0f);
- string version = await GetCurrentVersionOnline();
- File.WriteAllText(GetVersionFilePath(executablePath), version);
- }
-
- static public IEnumerable GetMappingFilesNextToIwyuPath(string executablePath)
- {
- string targetDirectory = Path.GetDirectoryName(executablePath);
-
- var impFiles = Directory.EnumerateFiles(targetDirectory).
- Where(file => Path.GetExtension(file).Equals(".imp", System.StringComparison.InvariantCultureIgnoreCase));
- foreach (string dirs in Directory.EnumerateDirectories(targetDirectory))
- {
- impFiles.Concat(
- Directory.EnumerateFiles(targetDirectory).
- Where(file => Path.GetExtension(file).Equals(".imp", System.StringComparison.InvariantCultureIgnoreCase))
- );
- }
-
- return impFiles;
- }
- }
-}
diff --git a/IncludeToolbox/Options/Constants.cs b/IncludeToolbox/Options/Constants.cs
deleted file mode 100644
index ff696d1..0000000
--- a/IncludeToolbox/Options/Constants.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IncludeToolbox.Options
-{
- public static class Constants
- {
- public const string Category = "Include Toolbox";
- }
-}
diff --git a/IncludeToolbox/Options/FormatterOptionsPage.cs b/IncludeToolbox/Options/FormatterOptionsPage.cs
deleted file mode 100644
index 3f2bff5..0000000
--- a/IncludeToolbox/Options/FormatterOptionsPage.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.ComponentModel;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace IncludeToolbox
-{
- [Guid("B822F53B-32C0-4560-9A84-2F9DA7AB0E4C")]
- public class FormatterOptionsPage : OptionsPage
- {
- public const string SubCategory = "Include Formatter";
- private const string collectionName = "IncludeFormatter";
-
- #region Path
-
- public enum PathMode
- {
- Unchanged,
- Shortest,
- Shortest_AvoidUpSteps,
- Absolute,
- }
- [Category("Path")]
- [DisplayName("Mode")]
- [Description("Changes the path mode to the given pattern.")]
- public PathMode PathFormat { get; set; } = PathMode.Shortest_AvoidUpSteps;
-
- [Category("Path")]
- [DisplayName("Ignore File Relative")]
- [Description("If true, include directives will not take the path of the file into account.")]
- public bool IgnoreFileRelative { get; set; } = false;
-
- //[Category("Path")]
- //[DisplayName("Ignore Standard Library")]
- //[Description("")]
- //public bool IgnorePathForStdLib { get; set; } = true;
-
- #endregion
-
- #region Formatting
-
- public enum DelimiterMode
- {
- Unchanged,
- AngleBrackets,
- Quotes,
- }
- [Category("Formatting")]
- [DisplayName("Delimiter Mode")]
- [Description("Optionally changes all delimiters to either angle brackets <...> or quotes \"...\".")]
- public DelimiterMode DelimiterFormatting { get; set; } = DelimiterMode.Unchanged;
-
- public enum SlashMode
- {
- Unchanged,
- ForwardSlash,
- BackSlash,
- }
- [Category("Formatting")]
- [DisplayName("Slash Mode")]
- [Description("Changes all slashes to the given type.")]
- public SlashMode SlashFormatting { get; set; } = SlashMode.ForwardSlash;
-
- [Category("Formatting")]
- [DisplayName("Remove Empty Lines")]
- [Description("If true, all empty lines of a include selection will be removed.")]
- public bool RemoveEmptyLines { get; set; } = true;
-
- #endregion
-
- #region Sorting
-
- [Category("Sorting")]
- [DisplayName("Include delimiters in precedence regexes")]
- [Description("If true, precedence regexes will consider delimiters (angle brackets or quotes.)")]
- public bool RegexIncludeDelimiter { get; set; } = false;
-
- [Category("Sorting")]
- [DisplayName("Insert blank line between precedence regex match groups")]
- [Description("If true, a blank line will be inserted after each group matching one of the precedence regexes.")]
- public bool BlankAfterRegexGroupMatch { get; set; } = false;
-
- [Category("Sorting")]
- [DisplayName("Precedence Regexes")]
- [Description("Earlier match means higher sorting priority.\n\"" + RegexUtils.CurrentFileNameKey + "\" will be replaced with the current file name without extension.")]
- public string[] PrecedenceRegexes {
- get { return precedenceRegexes; }
- set { precedenceRegexes = value.Where(x => x.Length > 0).ToArray(); } // Remove empty lines.
- }
- private string[] precedenceRegexes = new string[] { $"(?i){RegexUtils.CurrentFileNameKey}\\.(h|hpp|hxx|inl|c|cpp|cxx)(?-i)" };
-
- public enum TypeSorting
- {
- None,
- AngleBracketsFirst,
- QuotedFirst,
- }
- [Category("Sorting")]
- [DisplayName("Sort by Include Type")]
- [Description("Optionally put either includes with angle brackets <...> or quotes \"...\" first.")]
- public TypeSorting SortByType { get; set; } = TypeSorting.QuotedFirst;
-
- [Category("Sorting")]
- [DisplayName("Remove duplicates")]
- [Description("If true, duplicate includes will be removed.")]
- public bool RemoveDuplicates { get; set; } = true;
-
- #endregion
-
- public override void SaveSettingsToStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (!settingsStore.CollectionExists(collectionName))
- settingsStore.CreateCollection(collectionName);
-
- settingsStore.SetInt32(collectionName, nameof(PathFormat), (int)PathFormat);
- settingsStore.SetBoolean(collectionName, nameof(IgnoreFileRelative), IgnoreFileRelative);
-
- settingsStore.SetInt32(collectionName, nameof(DelimiterFormatting), (int)DelimiterFormatting);
- settingsStore.SetInt32(collectionName, nameof(SlashFormatting), (int)SlashFormatting);
- settingsStore.SetBoolean(collectionName, nameof(RemoveEmptyLines), RemoveEmptyLines);
-
- settingsStore.SetBoolean(collectionName, nameof(RegexIncludeDelimiter), RegexIncludeDelimiter);
- settingsStore.SetBoolean(collectionName, nameof(BlankAfterRegexGroupMatch), BlankAfterRegexGroupMatch);
- var value = string.Join("\n", PrecedenceRegexes);
- settingsStore.SetString(collectionName, nameof(PrecedenceRegexes), value);
- settingsStore.SetInt32(collectionName, nameof(SortByType), (int)SortByType);
- settingsStore.SetBoolean(collectionName, nameof(RemoveDuplicates), RemoveDuplicates);
- }
-
- public override void LoadSettingsFromStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (settingsStore.PropertyExists(collectionName, nameof(PathFormat)))
- PathFormat = (PathMode)settingsStore.GetInt32(collectionName, nameof(PathFormat));
- if (settingsStore.PropertyExists(collectionName, nameof(IgnoreFileRelative)))
- IgnoreFileRelative = settingsStore.GetBoolean(collectionName, nameof(IgnoreFileRelative));
-
- if (settingsStore.PropertyExists(collectionName, nameof(DelimiterFormatting)))
- DelimiterFormatting = (DelimiterMode) settingsStore.GetInt32(collectionName, nameof(DelimiterFormatting));
- if (settingsStore.PropertyExists(collectionName, nameof(SlashFormatting)))
- SlashFormatting = (SlashMode) settingsStore.GetInt32(collectionName, nameof(SlashFormatting));
- if (settingsStore.PropertyExists(collectionName, nameof(RemoveEmptyLines)))
- RemoveEmptyLines = settingsStore.GetBoolean(collectionName, nameof(RemoveEmptyLines));
-
- if (settingsStore.PropertyExists(collectionName, nameof(RegexIncludeDelimiter)))
- RegexIncludeDelimiter = settingsStore.GetBoolean(collectionName, nameof(RegexIncludeDelimiter));
- if (settingsStore.PropertyExists(collectionName, nameof(BlankAfterRegexGroupMatch)))
- BlankAfterRegexGroupMatch = settingsStore.GetBoolean(collectionName, nameof(BlankAfterRegexGroupMatch));
- if (settingsStore.PropertyExists(collectionName, nameof(PrecedenceRegexes)))
- {
- var value = settingsStore.GetString(collectionName, nameof(PrecedenceRegexes));
- PrecedenceRegexes = value.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
- }
- if (settingsStore.PropertyExists(collectionName, nameof(SortByType)))
- SortByType = (TypeSorting) settingsStore.GetInt32(collectionName, nameof(SortByType));
- if (settingsStore.PropertyExists(collectionName, nameof(RemoveDuplicates)))
- RemoveDuplicates = settingsStore.GetBoolean(collectionName, nameof(RemoveDuplicates));
- }
- }
-}
diff --git a/IncludeToolbox/Options/IncludeWhatYouUseOptionsPage.cs b/IncludeToolbox/Options/IncludeWhatYouUseOptionsPage.cs
deleted file mode 100644
index 32ab9e1..0000000
--- a/IncludeToolbox/Options/IncludeWhatYouUseOptionsPage.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace IncludeToolbox
-{
- [Guid("69CFD797-2E2B-497E-9231-334BCDC41407")]
- public class IncludeWhatYouUseOptionsPage : OptionsPage
- {
- public const string SubCategory = "Include-What-You-Use";
- private const string collectionName = "IncludeFormatter";
-
- #region iwyu source
-
- [Category("iwyu general")]
- [DisplayName("Executable Path")]
- [Description("File path of include-what-you-use.exe. If automatic download is active, this folder will be used.")]
- public string ExecutablePath { get; set; } = "";
-
- [Category("iwyu general")]
- [DisplayName("Automatic Updates")]
- [Description("If true, automatic check for updates will be done on first use each session. Will download from https://github.com/Wumpf/iwyu_for_vs_includetoolbox. " +
- "Set this to false if you want to use your own include-what-you-use version.")]
- public bool AutomaticCheckForUpdates { get; set; } = true;
-
- static public string GetDefaultExecutablePath()
- {
- return System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "iwyu", "include-what-you-use.exe");
- }
-
- #endregion
-
- #region iwyu options
-
- [Category("iwyu options")]
- [DisplayName("Log Verbosity")]
- [Description("The higher the level, the more output. A level lower 1 might disable automatic include replacing (--verbose)")]
- public int LogVerbosity { get; set; } = 2;
-
- [Category("iwyu options")]
- [DisplayName("Mapping File")]
- [Description("Gives iwyu a mapping file. (--mapping_file)")]
- public string[] MappingFiles { get; set; } = new string[0];
-
- ///
- /// Adds a list of mapping files and eliminates duplicates.
- ///
- public void AddMappingFiles(IEnumerable mappingFilesPaths)
- {
- var resolvedNewFiles = mappingFilesPaths.Select(x => new KeyValuePair(Path.GetFullPath(x), x));
- var resolvedOldFiles = MappingFiles.Select(x => new KeyValuePair(Path.GetFullPath(x), x));
- MappingFiles = resolvedNewFiles.Union(resolvedOldFiles).Select(x => x.Value).ToArray();
- }
-
- [Category("iwyu options")]
- [DisplayName("No Default Mappings")]
- [Description("Do not add iwyu's default mappings. (--no_default_mappings)")]
- public bool NoDefaultMappings { get; set; } = false;
-
- [Category("iwyu options")]
- [DisplayName("Commentaries")]
- [Description("Change the output mode of the commentaries from IWYU.")]
- [TypeConverter(typeof(EnumConverter))]
- public Commentaries Commentary { get; set; } = Commentaries.Default;
-
- [Category("iwyu options")]
- [DisplayName("PCH in Code")]
- [Description("Mark the first include in a translation unit as a precompiled header. Use to prevent IWYU from removing necessary PCH includes. Though Clang forces PCHs to be listed as prefix headers, the PCH in code pattern can be used with GCC and is standard practice on MSVC (e.g.stdafx.h). (--pch_in_code)")]
- public bool PCHInCode { get; set; } = true;
-
- public enum PrefixHeaderMode
- {
- Add,
- Keep,
- Remove
- }
- [Category("iwyu options")]
- [DisplayName("Prefix Header Include Mode")]
- [Description("Tells iwyu what to do with in-source includes and forward declarations involving prefix headers. Prefix header is a file included via command-line option -include. If prefix header makes include or forward declaration obsolete, presence of such include can be controlled with the following values:\nAdd: new lines are added\nKeep: new lines aren't added, existing are kept intact\nRemove: new lines aren't added, existing are removed. (--prefix_header_includes)")]
- public PrefixHeaderMode PrefixHeaderIncludes { get; set; } = PrefixHeaderMode.Add;
-
- [Category("iwyu options")]
- [DisplayName("Transitive Includes Only")]
- [Description("Do not suggest that a file add foo.h unless foo.h is already visible in the file's transitive includes. (--transitive_includes_only)")]
- public bool TransitiveIncludesOnly { get; set; } = false;
-
- [Category("iwyu options")]
- [DisplayName("Additional Parameters")]
- [Description("This string is inserted after all other parameters and before the filename of the file iwyu is running on.")]
- public string AdditionalParameters { get; set; } = "";
-
-
- [Category("iwyu options")]
- [DisplayName("Header file prefix")]
- [Description("Specify Header file prefix folder. Used to find pair header eg. a.cpp has an include/proj/a.h path to it, so the value should be ./include/proj. Works only relative to file.")]
- public string HeaderPrefix { get; set; } = "";
-
- #endregion
-
- #region postprocessing
-
- [Category("Post Processing")]
- [DisplayName("Apply Proposal")]
- [Description("Applies iwyu's proposal directly to all files. Requires at least Log Verbosity of 1.")]
- public bool ApplyProposal { get; set; } = true;
-
- [Category("Post Processing")]
- [DisplayName("Run Include Formatter on Changes")]
- [Description("Runs the Include Formatter on all changed lines.")]
- public bool RunIncludeFormatter { get; set; } = true;
-
-
- #endregion
-
- public override void SaveSettingsToStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (!settingsStore.CollectionExists(collectionName))
- settingsStore.CreateCollection(collectionName);
-
- settingsStore.SetString(collectionName, nameof(ExecutablePath), ExecutablePath);
- settingsStore.SetBoolean(collectionName, nameof(AutomaticCheckForUpdates), AutomaticCheckForUpdates);
-
- settingsStore.SetInt32(collectionName, nameof(LogVerbosity), LogVerbosity);
-
- var value = string.Join("\n", MappingFiles);
- settingsStore.SetString(collectionName, nameof(MappingFiles), value);
-
- settingsStore.SetBoolean(collectionName, nameof(NoDefaultMappings), NoDefaultMappings);
- settingsStore.SetBoolean(collectionName, nameof(PCHInCode), PCHInCode);
- settingsStore.SetInt32(collectionName, nameof(PrefixHeaderIncludes), (int)PrefixHeaderIncludes);
- settingsStore.SetBoolean(collectionName, nameof(TransitiveIncludesOnly), TransitiveIncludesOnly);
- settingsStore.SetString(collectionName, nameof(AdditionalParameters), AdditionalParameters);
- settingsStore.SetString(collectionName, nameof(HeaderPrefix), HeaderPrefix);
-
- settingsStore.SetBoolean(collectionName, nameof(ApplyProposal), ApplyProposal);
- settingsStore.SetBoolean(collectionName, nameof(RunIncludeFormatter), RunIncludeFormatter);
- }
-
- public override void LoadSettingsFromStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (settingsStore.PropertyExists(collectionName, nameof(ExecutablePath)))
- ExecutablePath = settingsStore.GetString(collectionName, nameof(ExecutablePath));
- else
- ExecutablePath = GetDefaultExecutablePath();
- if (settingsStore.PropertyExists(collectionName, nameof(AutomaticCheckForUpdates)))
- AutomaticCheckForUpdates = settingsStore.GetBoolean(collectionName, nameof(AutomaticCheckForUpdates));
-
- if (settingsStore.PropertyExists(collectionName, nameof(LogVerbosity)))
- LogVerbosity = settingsStore.GetInt32(collectionName, nameof(LogVerbosity));
- if (settingsStore.PropertyExists(collectionName, nameof(MappingFiles)))
- {
- var value = settingsStore.GetString(collectionName, nameof(MappingFiles));
- MappingFiles = value.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
- }
- if (settingsStore.PropertyExists(collectionName, nameof(NoDefaultMappings)))
- NoDefaultMappings = settingsStore.GetBoolean(collectionName, nameof(NoDefaultMappings));
- if (settingsStore.PropertyExists(collectionName, nameof(PCHInCode)))
- PCHInCode = settingsStore.GetBoolean(collectionName, nameof(PCHInCode));
- if (settingsStore.PropertyExists(collectionName, nameof(PrefixHeaderIncludes)))
- PrefixHeaderIncludes = (PrefixHeaderMode)settingsStore.GetInt32(collectionName, nameof(PrefixHeaderIncludes));
- if (settingsStore.PropertyExists(collectionName, nameof(TransitiveIncludesOnly)))
- TransitiveIncludesOnly = settingsStore.GetBoolean(collectionName, nameof(TransitiveIncludesOnly));
- if (settingsStore.PropertyExists(collectionName, nameof(AdditionalParameters)))
- AdditionalParameters = settingsStore.GetString(collectionName, nameof(AdditionalParameters));
- if (settingsStore.PropertyExists(collectionName, nameof(HeaderPrefix)))
- HeaderPrefix = settingsStore.GetString(collectionName, nameof(HeaderPrefix));
-
- if (settingsStore.PropertyExists(collectionName, nameof(ApplyProposal)))
- ApplyProposal = settingsStore.GetBoolean(collectionName, nameof(ApplyProposal));
- if (settingsStore.PropertyExists(collectionName, nameof(RunIncludeFormatter)))
- RunIncludeFormatter = settingsStore.GetBoolean(collectionName, nameof(RunIncludeFormatter));
- }
- }
-
- public enum Commentaries
- {
- Default,
- Always,
- Never
- }
-
-}
diff --git a/IncludeToolbox/Options/OptionsPage.cs b/IncludeToolbox/Options/OptionsPage.cs
deleted file mode 100644
index e00c83f..0000000
--- a/IncludeToolbox/Options/OptionsPage.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Microsoft.VisualStudio.Settings;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Settings;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace IncludeToolbox
-{
- ///
- /// Base class for all option pages.
- ///
- public abstract class OptionsPage : DialogPage
- {
- ///
- /// Initializes either with a in place created TaskContext for tests or, if our VS package is acutally active with the standard context.
- ///
- public OptionsPage() : base(IncludeToolboxPackage.Instance == null ?
-#pragma warning disable VSSDK005 // Avoid instantiating JoinableTaskContext
- new Microsoft.VisualStudio.Threading.JoinableTaskContext() : ThreadHelper.JoinableTaskContext)
-#pragma warning restore VSSDK005 // Avoid instantiating JoinableTaskContext
- { }
-
- // In theory the whole save/load mechanism should be done automatically.
- // But *something* is or was broken there.
- // see http://stackoverflow.com/questions/32751040/store-array-in-options-using-dialogpage
-
- static protected WritableSettingsStore GetSettingsStore()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsManager = new ShellSettingsManager(ServiceProvider.GlobalProvider);
- return settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings);
- }
-
- abstract public override void SaveSettingsToStorage();
- abstract public override void LoadSettingsFromStorage();
- }
-}
diff --git a/IncludeToolbox/Options/TrialAndErrorRemovalOptionsPage.cs b/IncludeToolbox/Options/TrialAndErrorRemovalOptionsPage.cs
deleted file mode 100644
index 9eba438..0000000
--- a/IncludeToolbox/Options/TrialAndErrorRemovalOptionsPage.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-
-namespace IncludeToolbox
-{
- [Guid("DBC8A65D-8B86-4296-9F1F-E785B182B550")]
- public class TrialAndErrorRemovalOptionsPage : OptionsPage
- {
- public const string SubCategory = "Trial and Error Include Removal";
- private const string collectionName = "TryAndErrorRemoval"; // All "try and error" were updated to "trial and error", but need to keep old string here to preserve existing settings files.
-
- public enum IncludeRemovalOrder
- {
- BottomToTop,
- TopToBottom,
- }
- [Category(SubCategory)]
- [DisplayName("Removal Order")]
- [Description("Gives the order which #includes are removed.")]
- public IncludeRemovalOrder RemovalOrder { get; set; } = IncludeRemovalOrder.BottomToTop;
-
- [Category(SubCategory)]
- [DisplayName("Ignore First Include")]
- [Description("If true, the first include of a file will never be removed (useful for ignoring PCH).")]
- public bool IgnoreFirstInclude { get; set; } = true;
-
- [Category(SubCategory)]
- [DisplayName("Ignore List")]
- [Description("List of regexes. If the content of a #include directive match with any of these, it will be ignored." +
- "\n\"" + RegexUtils.CurrentFileNameKey + "\" will be replaced with the current file name without extension.")]
- public string[] IgnoreList { get; set; } = new string[] { $"(\\/|\\\\|^){RegexUtils.CurrentFileNameKey}\\.(h|hpp|hxx|inl|c|cpp|cxx)$", ".inl", "_inl.h" };
-
- [Category(SubCategory)]
- [DisplayName("Keep Line Breaks")]
- [Description("If true, removed includes will leave an empty line.")]
- public bool KeepLineBreaks { get; set; } = false;
-
-
- public override void SaveSettingsToStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (!settingsStore.CollectionExists(collectionName))
- settingsStore.CreateCollection(collectionName);
-
- settingsStore.SetInt32(collectionName, nameof(RemovalOrder), (int)RemovalOrder);
- settingsStore.SetBoolean(collectionName, nameof(IgnoreFirstInclude), IgnoreFirstInclude);
-
- var value = string.Join("\n", IgnoreList);
- settingsStore.SetString(collectionName, nameof(IgnoreList), value);
-
- settingsStore.SetBoolean(collectionName, nameof(KeepLineBreaks), KeepLineBreaks);
- }
-
- public override void LoadSettingsFromStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (settingsStore.PropertyExists(collectionName, nameof(RemovalOrder)))
- RemovalOrder = (IncludeRemovalOrder)settingsStore.GetInt32(collectionName, nameof(RemovalOrder));
- if (settingsStore.PropertyExists(collectionName, nameof(IgnoreFirstInclude)))
- IgnoreFirstInclude = settingsStore.GetBoolean(collectionName, nameof(IgnoreFirstInclude));
-
- if (settingsStore.PropertyExists(collectionName, nameof(IgnoreList)))
- {
- var value = settingsStore.GetString(collectionName, nameof(IgnoreList));
- IgnoreList = value.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- if (settingsStore.PropertyExists(collectionName, nameof(KeepLineBreaks)))
- KeepLineBreaks = settingsStore.GetBoolean(collectionName, nameof(KeepLineBreaks));
- }
- }
-}
diff --git a/IncludeToolbox/Options/ViewerOptionsPage.cs b/IncludeToolbox/Options/ViewerOptionsPage.cs
deleted file mode 100644
index fb7d159..0000000
--- a/IncludeToolbox/Options/ViewerOptionsPage.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using Microsoft.VisualStudio.Shell;
-using System;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-
-namespace IncludeToolbox
-{
- [Guid("769AFCC2-25E2-459A-B2A3-89D7308800BD")]
- public class ViewerOptionsPage : OptionsPage
- {
- public const string SubCategory = "Include Viewer & Graph";
- private const string collectionName = "IncludeViewer";
-
- [Category("Include Graph Parsing")]
- [DisplayName("Graph Endpoint Directories")]
- [Description("List of absolute directory paths. For any include below these paths, the graph parsing will stop.")]
- public string[] NoParsePaths
- {
- get { return noParsePaths; }
- set
- {
- // It is critical that the paths are "exact" since we want to use them as with string comparison.
- noParsePaths = value;
- for (int i = 0; i < noParsePaths.Length; ++i)
- noParsePaths[i] = Utils.GetExactPathName(noParsePaths[i]);
- }
- }
- private string[] noParsePaths;
-
- [Category("Include Graph DGML")]
- [DisplayName("Create Group Nodes by Folders")]
- [Description("Creates folders like in the folder hierarchy view of Include Graph.")]
- public bool CreateGroupNodesForFolders { get; set; } = true;
-
- [Category("Include Graph DGML")]
- [DisplayName("Expand Folder Group Nodes")]
- [Description("If true all folder nodes start out expanded, otherwise they are collapsed.")]
- public bool ExpandFolderGroupNodes { get; set; } = false;
-
- [Category("Include Graph DGML")]
- [DisplayName("Colorize by Number of Includes")]
- [Description("If true each node gets color coded according to its number of unique transitive includes.")]
- public bool ColorCodeNumTransitiveIncludes { get; set; } = true;
-
- [Category("Include Graph DGML")]
- [DisplayName("No Children Color")]
- [Description("See \"Colorize by Number of Includes\". Color for no children at all.")]
- public System.Drawing.Color NoChildrenColor { get; set; } = System.Drawing.Color.White;
-
- [Category("Include Graph DGML")]
- [DisplayName("Max Children Color")]
- [Description("See \"Colorize by Number of Includes\". Color for highest number of children.")]
- public System.Drawing.Color MaxChildrenColor { get; set; } = System.Drawing.Color.Red;
-
- public override void SaveSettingsToStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (!settingsStore.CollectionExists(collectionName))
- settingsStore.CreateCollection(collectionName);
-
- var value = string.Join("\n", NoParsePaths);
- settingsStore.SetString(collectionName, nameof(NoParsePaths), value);
-
-
- settingsStore.SetBoolean(collectionName, nameof(CreateGroupNodesForFolders), CreateGroupNodesForFolders);
- settingsStore.SetBoolean(collectionName, nameof(ExpandFolderGroupNodes), ExpandFolderGroupNodes);
- settingsStore.SetBoolean(collectionName, nameof(ColorCodeNumTransitiveIncludes), ColorCodeNumTransitiveIncludes);
- settingsStore.SetInt32(collectionName, nameof(NoChildrenColor), NoChildrenColor.ToArgb());
- settingsStore.SetInt32(collectionName, nameof(MaxChildrenColor), MaxChildrenColor.ToArgb());
- }
-
- public override void LoadSettingsFromStorage()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var settingsStore = GetSettingsStore();
-
- if (settingsStore.PropertyExists(collectionName, nameof(NoParsePaths)))
- {
- var value = settingsStore.GetString(collectionName, nameof(NoParsePaths));
- NoParsePaths = value.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
- }
- else
- {
- // It is surprisingly hard to get to the standard library paths.
- // Even finding the VS install path is not easy and - as it turns out - not necessarily where the standard library files reside.
- // So in lack of a better idea, we just put everything under the program files folder in here.
- string programFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
- string programFilesX86 = Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%");
- if(programFiles == programFilesX86) // If somebody uses still x86 system.
- NoParsePaths = new string[] { programFiles };
- else
- NoParsePaths = new string[] { programFiles, programFilesX86 };
- }
-
-
- if (settingsStore.PropertyExists(collectionName, nameof(CreateGroupNodesForFolders)))
- CreateGroupNodesForFolders = settingsStore.GetBoolean(collectionName, nameof(CreateGroupNodesForFolders));
- if (settingsStore.PropertyExists(collectionName, nameof(ExpandFolderGroupNodes)))
- ExpandFolderGroupNodes = settingsStore.GetBoolean(collectionName, nameof(ExpandFolderGroupNodes));
-
- if (settingsStore.PropertyExists(collectionName, nameof(ColorCodeNumTransitiveIncludes)))
- ColorCodeNumTransitiveIncludes = settingsStore.GetBoolean(collectionName, nameof(ColorCodeNumTransitiveIncludes));
- if (settingsStore.PropertyExists(collectionName, nameof(NoChildrenColor)))
- NoChildrenColor = System.Drawing.Color.FromArgb(settingsStore.GetInt32(collectionName, nameof(NoChildrenColor)));
- if (settingsStore.PropertyExists(collectionName, nameof(MaxChildrenColor)))
- MaxChildrenColor = System.Drawing.Color.FromArgb(settingsStore.GetInt32(collectionName, nameof(MaxChildrenColor)));
- }
- }
-}
diff --git a/IncludeToolbox/Output.cs b/IncludeToolbox/Output.cs
deleted file mode 100644
index 630cddc..0000000
--- a/IncludeToolbox/Output.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using EnvDTE;
-using EnvDTE80;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox
-{
- public class Output
- {
- static public Output Instance { private set; get; } = new Output();
-
- public enum MessageResult
- {
- Yes,
- No
- }
-
- private const int VsMessageBoxResult_Yes = 6;
-
- private Output()
- {
- }
-
- private OutputWindowPane outputWindowPane = null;
-
- private void Init()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- DTE2 dte = Package.GetGlobalService(typeof(EnvDTE.DTE)) as DTE2;
- if (dte == null)
- return;
-
- OutputWindow outputWindow = dte.ToolWindows.OutputWindow;
- outputWindowPane = outputWindow.OutputWindowPanes.Add("IncludeToolbox");
- }
-
- public void Clear()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- if (outputWindowPane == null)
- Init();
- outputWindowPane.Clear();
- }
-
- public async Task WriteInternal(string text)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- if (outputWindowPane == null)
- Init();
-
- if (outputWindowPane != null)
- {
- System.Diagnostics.Debug.Assert(outputWindowPane != null);
- outputWindowPane.OutputString(text);
- }
- }
-
- public void Write(string text)
- {
- // Typically we don't care if the message was already written, so let's move this to a different (main-thread) task.
- _ = WriteInternal(text);
- }
-
- public void Write(string text, params object[] stringParams)
- {
- string output = string.Format(text, stringParams);
- Write(output);
- }
-
- public void WriteLine(string line)
- {
- Write(line + '\n');
- }
-
- public void WriteLine(string line, params object[] stringParams)
- {
- string output = string.Format(line, stringParams);
- WriteLine(output);
- }
-
- public async Task ErrorMsg(string message, params object[] stringParams)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- string output = string.Format(message, stringParams);
- WriteLine(output);
- VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, output, "Include Toolbox", OLEMSGICON.OLEMSGICON_CRITICAL, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
- }
-
- public async Task InfoMsg(string message, params object[] stringParams)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- string output = string.Format(message, stringParams);
- WriteLine(output);
- VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, output, "Include Toolbox", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
- }
-
- public async Task YesNoMsg(string message)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- int result = VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, message, "Include Toolbox", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_YESNO, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
- return result == VsMessageBoxResult_Yes ? MessageResult.Yes : MessageResult.No;
- }
-
- public void OutputToForeground()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- outputWindowPane.Activate();
- }
- }
-}
diff --git a/IncludeToolbox/Package/CommandDefinitions.vsct b/IncludeToolbox/Package/CommandDefinitions.vsct
deleted file mode 100644
index 7180394..0000000
--- a/IncludeToolbox/Package/CommandDefinitions.vsct
+++ /dev/null
@@ -1,210 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- DropDown Combo:
- Graph Retrieval Method
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/IncludeToolbox/Package/IncludeToolboxPackage.cs b/IncludeToolbox/Package/IncludeToolboxPackage.cs
deleted file mode 100644
index 0ed6224..0000000
--- a/IncludeToolbox/Package/IncludeToolboxPackage.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Threading;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox
-{
- [ProvideBindingPath(SubPath = "")] // Necessary to find packaged assemblies.
-
- [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
- [ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
- [ProvideMenuResource("Menus.ctmenu", 1)]
-
- [ProvideOptionPage(typeof(FormatterOptionsPage), Options.Constants.Category, FormatterOptionsPage.SubCategory, 1000, 1001, true)]
- [ProvideOptionPage(typeof(IncludeWhatYouUseOptionsPage), Options.Constants.Category, IncludeWhatYouUseOptionsPage.SubCategory, 1000, 1002, true)]
- [ProvideOptionPage(typeof(TrialAndErrorRemovalOptionsPage), Options.Constants.Category, TrialAndErrorRemovalOptionsPage.SubCategory, 1000, 1003, true)]
- [ProvideOptionPage(typeof(ViewerOptionsPage), Options.Constants.Category, ViewerOptionsPage.SubCategory, 1000, 1004, true)]
-
- [ProvideToolWindow(typeof(GraphWindow.IncludeGraphToolWindow))]
- [Guid(IncludeToolboxPackage.PackageGuidString)]
- [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
- [InstalledProductRegistration("#110", "#112", "0.2", IconResourceID = 400)]
- public sealed class IncludeToolboxPackage : AsyncPackage
- {
- ///
- /// IncludeToolboxPackage GUID string.
- ///
- public const string PackageGuidString = "5c2743c4-1b3f-4edd-b6a0-4379f867d47f";
-
- static public Package Instance { get; private set; }
-
- public IncludeToolboxPackage()
- {
- // Inside this method you can place any initialization code that does not require
- // any Visual Studio service because at this point the package object is created but
- // not sited yet inside Visual Studio environment. The place to do all the other
- // initialization is the Initialize method.
- Instance = this;
- }
-
- #region Package Members
-
- protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
- {
- await base.InitializeAsync(cancellationToken, progress);
-
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- Commands.IncludeGraphToolWindow.Initialize(this);
- Commands.FormatIncludes.Initialize(this);
- Commands.IncludeWhatYouUse.Initialize(this);
- Commands.TrialAndErrorRemoval_CodeWindow.Initialize(this);
- Commands.TrialAndErrorRemoval_Project.Initialize(this);
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- }
-
- #endregion
- }
-}
diff --git a/IncludeToolbox/Package/Key.snk b/IncludeToolbox/Package/Key.snk
deleted file mode 100644
index f4625de..0000000
Binary files a/IncludeToolbox/Package/Key.snk and /dev/null differ
diff --git a/IncludeToolbox/Package/VSPackage.resx b/IncludeToolbox/Package/VSPackage.resx
deleted file mode 100644
index 04a830f..0000000
--- a/IncludeToolbox/Package/VSPackage.resx
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
- IncludeToolbox Extension
-
-
- Tools around C/C++ #includes
-
-
-
\ No newline at end of file
diff --git a/IncludeToolbox/Package/source.extension.vsixmanifest b/IncludeToolbox/Package/source.extension.vsixmanifest
deleted file mode 100644
index 5ef82bd..0000000
--- a/IncludeToolbox/Package/source.extension.vsixmanifest
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
- IncludeToolbox
- Various tools for managing C/C++ #includes: Formatting, sorting, exploring, pruning.
- license.txt
- Resources\IncludeFormatterPackage.png
- Resources\IncludeFormatterPackage.png
- Include, Include What You Use, IWYU, Include Formatting, Include Sorting, #include, Include Removal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/IncludeToolbox/Properties/AssemblyInfo.cs b/IncludeToolbox/Properties/AssemblyInfo.cs
deleted file mode 100644
index e6839c2..0000000
--- a/IncludeToolbox/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-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("IncludeToolbox")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("IncludeToolbox")]
-[assembly: AssemblyCopyright("")]
-[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)]
-
-// 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/IncludeToolbox/Resources/IncludeFormatterIcons.pdn b/IncludeToolbox/Resources/IncludeFormatterIcons.pdn
deleted file mode 100644
index 4619200..0000000
Binary files a/IncludeToolbox/Resources/IncludeFormatterIcons.pdn and /dev/null differ
diff --git a/IncludeToolbox/Resources/IncludeFormatterPackage.png b/IncludeToolbox/Resources/IncludeFormatterPackage.png
deleted file mode 100644
index 042d01b..0000000
Binary files a/IncludeToolbox/Resources/IncludeFormatterPackage.png and /dev/null differ
diff --git a/IncludeToolbox/Resources/include13.png b/IncludeToolbox/Resources/include13.png
deleted file mode 100644
index 6f5e2b4..0000000
Binary files a/IncludeToolbox/Resources/include13.png and /dev/null differ
diff --git a/IncludeToolbox/Resources/include16.png b/IncludeToolbox/Resources/include16.png
deleted file mode 100644
index da17114..0000000
Binary files a/IncludeToolbox/Resources/include16.png and /dev/null differ
diff --git a/IncludeToolbox/TrialAndErrorRemoval.cs b/IncludeToolbox/TrialAndErrorRemoval.cs
deleted file mode 100644
index 40a2753..0000000
--- a/IncludeToolbox/TrialAndErrorRemoval.cs
+++ /dev/null
@@ -1,320 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using System.Windows;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Shell.Interop;
-using EnvDTE;
-using Microsoft.VisualStudio.Text;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox
-{
- ///
- /// Command handler for trial and error include removal
- ///
- internal sealed class TrialAndErrorRemoval
- {
- public delegate void FinishedEvent(int numRemovedIncludes, bool canceled);
- public event FinishedEvent OnFileFinished;
-
- public static bool WorkInProgress { get; private set; }
-
- private volatile bool lastBuildSuccessful;
- private AutoResetEvent outputWaitEvent = new AutoResetEvent(false);
- private const int timeoutMS = 600000; // 600 seconds, 10 minutes per file
-
- ///
- /// Need to keep build events object around as long as it is used, otherwise the events may not be fired!
- ///
- private BuildEvents buildEvents;
-
- public async Task PerformTrialAndErrorIncludeRemoval(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings)
- {
- if (document == null)
- return false;
-
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- var canCompile = await VSUtils.VCUtils.IsCompilableFile(document);
- if (canCompile.Result == false)
- {
- Output.Instance.WriteLine($"Can't compile file '{canCompile.Reason}': {document.Name}");
- return false;
- }
-
- if (WorkInProgress)
- {
- _ = Output.Instance.ErrorMsg("Trial and error include removal already in progress!");
- return false;
- }
- WorkInProgress = true;
-
- // Start wait dialog.
- IVsThreadedWaitDialog2 progressDialog = await StartProgressDialog(document.Name);
- if (progressDialog == null)
- return false;
-
- // Extract all includes.
- ITextBuffer textBuffer;
- Formatter.IncludeLineInfo[] includeLines;
- try
- {
- ExtractSelectionAndIncludes(document, settings, out textBuffer, out includeLines);
- }
- catch (Exception ex)
- {
- Output.Instance.WriteLine("Unexpected error while extracting include selection: {0}", ex);
- progressDialog.EndWaitDialog();
- return false;
- }
-
- // Hook into build events.
- SubscribeBuildEvents();
-
- // The rest runs in a separate thread since the compile function is non blocking and we want to use BuildEvents
- // We are not using Task, since we want to make use of WaitHandles - using this together with Task is a bit more complicated to get right.
- outputWaitEvent.Reset();
- var removalThread = new System.Threading.Thread(() => TrialAndErrorRemovalThreadFunc(document, settings, includeLines, progressDialog, textBuffer));
- removalThread.Start();
- return true;
- }
-
- private async Task StartProgressDialog(string documentName)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- var dialogFactory = Package.GetGlobalService(typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
- if (dialogFactory == null)
- {
- Output.Instance.WriteLine("Failed to get Dialog Factory for wait dialog.");
- return null;
- }
-
- IVsThreadedWaitDialog2 progressDialog;
- dialogFactory.CreateInstance(out progressDialog);
- if (progressDialog == null)
- {
- Output.Instance.WriteLine("Failed to get create wait dialog.");
- return null;
- }
- string waitMessage = $"Parsing '{documentName}' ... ";
- progressDialog.StartWaitDialogWithPercentageProgress(
- szWaitCaption: "Include Toolbox - Running Trial & Error Include Removal",
- szWaitMessage: waitMessage,
- szProgressText: null,
- varStatusBmpAnim: null,
- szStatusBarText: "Running Trial & Error Removal - " + waitMessage,
- fIsCancelable: true,
- iDelayToShowDialog: 0,
- iTotalSteps: 20, // Will be replaced.
- iCurrentStep: 0);
-
- return progressDialog;
- }
-
- private void ExtractSelectionAndIncludes(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings,
- out ITextBuffer textBuffer, out Formatter.IncludeLineInfo[] includeLinesArray)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- // Parsing.
- document.Activate();
- var documentTextView = VSUtils.GetCurrentTextViewHost();
- textBuffer = documentTextView.TextView.TextBuffer;
- string documentText = documentTextView.TextView.TextSnapshot.GetText();
- IEnumerable includeLines = Formatter.IncludeLineInfo.ParseIncludes(documentText, Formatter.ParseOptions.IgnoreIncludesInPreprocessorConditionals | Formatter.ParseOptions.KeepOnlyValidIncludes);
-
- // Optionally skip top most include.
- if (settings.IgnoreFirstInclude)
- includeLines = includeLines.Skip(1);
-
- // Skip everything with preserve flag.
- includeLines = includeLines.Where(x => !x.ShouldBePreserved);
-
- // Apply filter ignore regex.
- {
- string documentName = Path.GetFileNameWithoutExtension(document.FullName);
- string[] ignoreRegexList = RegexUtils.FixupRegexes(settings.IgnoreList, documentName);
- includeLines = includeLines.Where(line => !ignoreRegexList.Any(regexPattern =>
- new System.Text.RegularExpressions.Regex(regexPattern).Match(line.IncludeContent).Success));
- }
- // Reverse order if necessary.
- if (settings.RemovalOrder == TrialAndErrorRemovalOptionsPage.IncludeRemovalOrder.BottomToTop)
- includeLines = includeLines.Reverse();
-
- includeLinesArray = includeLines.ToArray();
- }
-
- private void TrialAndErrorRemovalThreadFunc(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings,
- Formatter.IncludeLineInfo[] includeLines, IVsThreadedWaitDialog2 progressDialog, ITextBuffer textBuffer)
- {
- int numRemovedIncludes = 0;
- bool canceled = false;
-
- try
- {
- int currentProgressStep = 0;
-
- // For ever include line..
- foreach (Formatter.IncludeLineInfo line in includeLines)
- {
- // If we are working from top to bottom, the line number may have changed!
- int currentLine = line.LineNumber;
- if (settings.RemovalOrder == TrialAndErrorRemovalOptionsPage.IncludeRemovalOrder.TopToBottom)
- currentLine -= numRemovedIncludes;
-
- ThreadHelper.JoinableTaskFactory.Run(async () =>
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- // Update progress.
- string waitMessage = $"Removing #includes from '{document.Name}'";
- string progressText = $"Trying to remove '{line.IncludeContent}' ...";
- progressDialog.UpdateProgress(
- szUpdatedWaitMessage: waitMessage,
- szProgressText: progressText,
- szStatusBarText: "Running Trial & Error Removal - " + waitMessage + " - " + progressText,
- iCurrentStep: currentProgressStep + 1,
- iTotalSteps: includeLines.Length + 1,
- fDisableCancel: false,
- pfCanceled: out canceled);
- if (!canceled)
- {
- ++currentProgressStep;
-
- // Remove include
- using (var edit = textBuffer.CreateEdit())
- {
- if (settings.KeepLineBreaks)
- edit.Delete(edit.Snapshot.Lines.ElementAt(currentLine).Extent);
- else
- edit.Delete(edit.Snapshot.Lines.ElementAt(currentLine).ExtentIncludingLineBreak);
- edit.Apply();
- }
- }
- outputWaitEvent.Set();
- });
- outputWaitEvent.WaitOne();
-
- if (canceled)
- break;
-
- // Compile - In rare cases VS tells us that we are still building which should not be possible because we have received OnBuildFinished
- // As a workaround we just try again a few times.
- ThreadHelper.JoinableTaskFactory.Run(async () =>
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- const int maxNumCompileAttempts = 3;
- for (int numCompileFails = 0; numCompileFails < maxNumCompileAttempts; ++numCompileFails)
- {
- // TODO: This happens on the main thread. Making the whole thread thing a bit pointless!!!
- try
- {
- await VSUtils.VCUtils.CompileSingleFile(document);
- }
- catch (Exception e)
- {
- Output.Instance.WriteLine("Compile Failed:\n{0}", e);
-
- if (numCompileFails == maxNumCompileAttempts - 1)
- {
- document.Undo();
- throw e;
- }
- else
- {
- // Try again.
- await System.Threading.Tasks.Task.Delay(100);
- continue;
- }
- }
- break;
- }
- });
-
- // Wait till woken.
- bool noTimeout = outputWaitEvent.WaitOne(timeoutMS);
-
- // Undo removal if compilation failed.
- if (!noTimeout || !lastBuildSuccessful)
- {
- Output.Instance.WriteLine("Could not remove #include: '{0}'", line.IncludeContent);
- ThreadHelper.JoinableTaskFactory.Run(async () =>
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
- document.Undo();
- if (!noTimeout)
- await Output.Instance.ErrorMsg("Compilation of {0} timeouted!", document.Name);
- });
-
- if (!noTimeout)
- break;
- }
- else
- {
- Output.Instance.WriteLine("Successfully removed #include: '{0}'", line.IncludeContent);
- ++numRemovedIncludes;
- }
- }
- }
- catch (Exception ex)
- {
- Output.Instance.WriteLine("Unexpected error: {0}", ex);
- }
- finally
- {
- _ = OnTrialAndErrorRemovalDone(progressDialog, document, numRemovedIncludes, canceled);
- }
- }
-
- private async Task OnTrialAndErrorRemovalDone(IVsThreadedWaitDialog2 progressDialog, EnvDTE.Document document, int numRemovedIncludes, bool canceled)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- // Close Progress bar.
- progressDialog.EndWaitDialog();
-
- // Remove build hook again.
- UnsubscribeBuildEvents();
-
- // Message.
- Output.Instance.WriteLine("Removed {0} #include directives from '{1}'", numRemovedIncludes, document.Name);
- Output.Instance.OutputToForeground();
-
- // Notify that we are done.
- WorkInProgress = false;
- OnFileFinished?.Invoke(numRemovedIncludes, canceled);
- }
-
- private void SubscribeBuildEvents()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- buildEvents = VSUtils.GetDTE().Events.BuildEvents;
- buildEvents.OnBuildDone += OnBuildFinished;
- buildEvents.OnBuildProjConfigDone += OnBuildConfigFinished;
- }
-
- private void UnsubscribeBuildEvents()
- {
- buildEvents.OnBuildDone -= OnBuildFinished;
- buildEvents.OnBuildProjConfigDone -= OnBuildConfigFinished;
- buildEvents = null;
- }
-
- private void OnBuildFinished(vsBuildScope scope, vsBuildAction action)
- {
- //Output.Instance.WriteLine("OnBuildFinished. scope: {0}, action: {1}", scope, action);
- outputWaitEvent.Set();
- }
-
- private void OnBuildConfigFinished(string project, string projectConfig, string platform, string solutionConfig, bool success)
- {
- //Output.Instance.WriteLine("OnBuildConfigFinished. project {0}, projectConfig {1}, platform {2}, solutionConfig {3}, success {4}", project, projectConfig, platform, solutionConfig, success);
- lastBuildSuccessful = success;
- }
- }
-}
\ No newline at end of file
diff --git a/IncludeToolbox/VCHelper.cs b/IncludeToolbox/VCHelper.cs
deleted file mode 100644
index 8dfd72e..0000000
--- a/IncludeToolbox/VCHelper.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-using EnvDTE;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.VCProjectEngine;
-using System.Threading.Tasks;
-using Task = System.Threading.Tasks.Task;
-
-namespace IncludeToolbox
-{
- public class VCQueryFailure : System.Exception
- {
- public VCQueryFailure(string message) : base(message)
- {
- }
- }
-
- public class VCHelper
- {
- public bool IsVCProject(Project project)
- {
- return project?.Object is VCProject;
- }
-
- private static async Task GetVCFileConfigForCompilation(Document document)
- {
- await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
-
- if (document == null)
- throw new VCQueryFailure("No document.");
-
- var vcProject = document.ProjectItem?.ContainingProject?.Object as VCProject;
- if (vcProject == null)
- throw new VCQueryFailure("The given document does not belong to a VC++ Project.");
-
- VCFile vcFile = document.ProjectItem?.Object as VCFile;
- if (vcFile == null)
- throw new VCQueryFailure("The given document is not a VC++ file.");
-
- if (vcFile.FileType != eFileType.eFileTypeCppCode)
- throw new VCQueryFailure("The given document is not a compileable VC++ file.");
-
- IVCCollection fileConfigCollection = vcFile.FileConfigurations as IVCCollection;
- VCFileConfiguration fileConfig = fileConfigCollection?.Item(vcProject.ActiveConfiguration.Name) as VCFileConfiguration;
- if (fileConfig == null)
- throw new VCQueryFailure("Failed to retrieve file config from document.");
-
- return fileConfig;
- }
-
- private static VCTool GetToolFromActiveConfiguration(Project project) where VCTool: class
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- VCProject vcProject = project?.Object as VCProject;
- if (vcProject == null)
- throw new VCQueryFailure($"Failed to retrieve VCCLCompilerTool since project \"{project.Name}\" is not a VCProject.");
-
- VCConfiguration activeConfiguration = vcProject.ActiveConfiguration;
- VCTool compilerTool = null;
- foreach (var tool in (IVCCollection)activeConfiguration.Tools)
- {
- compilerTool = tool as VCTool;
- if (compilerTool != null)
- break;
- }
-
- if (compilerTool == null)
- throw new VCQueryFailure($"Couldn't find a {typeof(VCTool).Name} in active configuration of VC++ Project \"{vcProject.Name}\"");
-
- return compilerTool;
- }
-
- public static VCLinkerTool GetLinkerTool(Project project)
- {
- VCProject vcProject = project?.Object as VCProject;
- if (vcProject == null)
- throw new VCQueryFailure("Failed to retrieve VCLinkerTool since project is not a VCProject.");
-
- VCConfiguration activeConfiguration = vcProject.ActiveConfiguration;
- var tools = activeConfiguration.Tools;
- VCLinkerTool linkerTool = null;
- foreach (var tool in (IVCCollection)activeConfiguration.Tools)
- {
- linkerTool = tool as VCLinkerTool;
- if (linkerTool != null)
- break;
- }
-
- if (linkerTool == null)
- throw new VCQueryFailure("Couldn't file a VCLinkerTool in VC++ Project.");
-
- return linkerTool;
- }
-
- public async Task IsCompilableFile(Document document)
- {
- try
- {
- await GetVCFileConfigForCompilation(document);
- }
- catch (VCQueryFailure queryFailure)
- {
- return new BoolWithReason()
- {
- Result = false,
- Reason = queryFailure.Message,
- };
- }
-
- return new BoolWithReason()
- {
- Result = true,
- Reason = "",
- };
- }
-
- public async Task CompileSingleFile(Document document)
- {
- var fileConfig = await GetVCFileConfigForCompilation(document);
- if (fileConfig != null)
- fileConfig.Compile(true, false); // WaitOnBuild==true always fails.
- }
-
- public string GetCompilerSetting_Includes(Project project)
- {
- VCQueryFailure queryFailure;
- try
- {
- VCCLCompilerTool compilerTool = GetToolFromActiveConfiguration(project);
- if (compilerTool != null)
- return compilerTool.FullIncludePath;
- else
- queryFailure = new VCQueryFailure("Unhandled error");
- }
- catch (VCQueryFailure e)
- {
- queryFailure = e;
- }
-
- // If querying the NMake tool fails, keep old reason for failure, since this is what we usually expect. Using NMake is seen as mere fallback.
- try
- {
- VCNMakeTool nmakeTool = GetToolFromActiveConfiguration(project);
- if (nmakeTool == null)
- throw queryFailure;
-
- return nmakeTool.IncludeSearchPath;
- }
- catch
- {
- throw queryFailure;
- }
- }
-
- public void SetCompilerSetting_ShowIncludes(Project project, bool show)
- {
- GetToolFromActiveConfiguration(project).ShowIncludes = show;
- }
-
- public bool GetCompilerSetting_ShowIncludes(Project project)
- {
- return GetToolFromActiveConfiguration(project).ShowIncludes;
- }
-
- public string GetCompilerSetting_PreprocessorDefinitions(Project project)
- {
- VCQueryFailure queryFailure;
- try
- {
- VCCLCompilerTool compilerTool = GetToolFromActiveConfiguration(project);
- if (compilerTool != null)
- return compilerTool.PreprocessorDefinitions.Replace("\\\"$(INHERIT)\\\";", "");
- else
- queryFailure = new VCQueryFailure("Unhandled error");
- }
- catch (VCQueryFailure e)
- {
- queryFailure = e;
- }
-
- // If querying the NMake tool fails, keep old reason for failure, since this is what we usually expect. Using NMake is seen as mere fallback.
- try
- {
- VCNMakeTool nmakeTool = GetToolFromActiveConfiguration(project);
- if (nmakeTool == null)
- throw queryFailure;
-
- return nmakeTool.PreprocessorDefinitions;
- }
- catch
- {
- throw queryFailure;
- }
- }
-
- // https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vcprojectengine.machinetypeoption.aspx
- public enum TargetMachineType
- {
- NotSet = 0,
- X86 = 1,
- AM33 = 2,
- ARM = 3,
- EBC = 4,
- IA64 = 5,
- M32R = 6,
- MIPS = 7,
- MIPS16 = 8,
- MIPSFPU = 9,
- MIPSFPU16 = 10,
- MIPSR41XX = 11,
- SH3 = 12,
- SH3DSP = 13,
- SH4 = 14,
- SH5 = 15,
- THUMB = 16,
- AMD64 = 17
- }
-
- public TargetMachineType GetLinkerSetting_TargetMachine(EnvDTE.Project project)
- {
- return (TargetMachineType)GetLinkerTool(project).TargetMachine;
- }
- }
-}
diff --git a/IncludeToolbox/VSUtils.cs b/IncludeToolbox/VSUtils.cs
deleted file mode 100644
index f1f9653..0000000
--- a/IncludeToolbox/VSUtils.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using Microsoft.VisualStudio.Editor;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Text.Editor;
-using Microsoft.VisualStudio.TextManager.Interop;
-using EnvDTE;
-using Microsoft.VisualStudio;
-using Microsoft.VisualStudio.Shell.Interop;
-
-namespace IncludeToolbox
-{
- public static class VSUtils
- {
- public static EnvDTE80.DTE2 GetDTE()
- {
- var dte = Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
- if (dte == null)
- {
- throw new System.Exception("Failed to retrieve DTE2!");
- }
- return dte;
- }
-
- // Historically, the GUIDs of the COM interfaces in VCProject/VCProjectEngine would change from version to version.
- // To work around this we had several builds of VCHelpers that we could choose from, each with a different dependency.
- // With VS2019, the older versions are no longer available and we're stuck with a single version for better or worse.
-
- public static VCHelper VCUtils = new VCHelper();
-
- ///
- /// Returns what the C++ macro _MSC_VER should resolve to.
- ///
- ///
- public static string GetMSCVerString()
- {
- // See http://stackoverflow.com/questions/70013/how-to-detect-if-im-compiling-code-with-visual-studio-2008
- var dte = GetDTE();
- var dteVersion = dte.Version;
- if (dte.Version.StartsWith("14."))
- return "1900";
- else if (dte.Version.StartsWith("15."))
- return "1915";
- else if (dte.Version.StartsWith("16."))
- return "1920";
- else
- throw new NotImplementedException("Unknown MSVC version!");
- }
-
- ///
- /// Tries to retrieve include directories from a project.
- /// For each encountered path it will try to resolve the paths to absolute paths.
- ///
- /// Empty list if include directory retrieval failed.
- public static List GetProjectIncludeDirectories(EnvDTE.Project project, bool endWithSeparator = true)
- {
- List pathStrings = new List();
- if (project == null)
- return pathStrings;
-
- string projectIncludeDirectories;
- try
- {
- projectIncludeDirectories = VCUtils.GetCompilerSetting_Includes(project);
- }
- catch (VCQueryFailure e)
- {
- Output.Instance.WriteLine(e.Message);
- return pathStrings;
- }
-
- ThreadHelper.ThrowIfNotOnUIThread();
- string projectPath = Path.GetDirectoryName(Path.GetFullPath(project.FileName));
-
- // According to documentation FullIncludePath has resolved macros.
-
- pathStrings.AddRange(projectIncludeDirectories.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries));
-
- for (int i = pathStrings.Count - 1; i >= 0; --i)
- {
- try
- {
- pathStrings[i] = pathStrings[i].Trim();
- if (!Path.IsPathRooted(pathStrings[i]))
- {
- pathStrings[i] = Path.Combine(projectPath, pathStrings[i]);
- }
- pathStrings[i] = Utils.GetExactPathName(Path.GetFullPath(pathStrings[i]));
-
- if (endWithSeparator)
- pathStrings[i] += Path.DirectorySeparatorChar;
- }
- catch
- {
- pathStrings.RemoveAt(i);
- }
- }
- return pathStrings;
- }
-
- public static IWpfTextViewHost GetCurrentTextViewHost()
- {
- IVsTextManager textManager = Package.GetGlobalService(typeof (SVsTextManager)) as IVsTextManager;
-
- IVsTextView textView = null;
- textManager.GetActiveView(1, null, out textView);
-
- var userData = textView as IVsUserData;
- if (userData == null)
- {
- return null;
- }
- else
- {
- Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
- object holder;
- userData.GetData(ref guidViewHost, out holder);
- var viewHost = (IWpfTextViewHost) holder;
-
- return viewHost;
- }
- }
-
- public static EnvDTE.Window OpenFileAndShowDocument(string filePath)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
-
- if (filePath == null)
- return null;
-
- var dte = VSUtils.GetDTE();
- EnvDTE.Window fileWindow = dte.ItemOperations.OpenFile(filePath);
- if (fileWindow == null)
- {
- Output.Instance.WriteLine("Failed to open File {0}", filePath);
- return null;
- }
- fileWindow.Activate();
- fileWindow.Visible = true;
-
- return fileWindow;
- }
-
- public static string GetOutputText()
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- var dte = GetDTE();
- if (dte == null)
- return "";
-
-
- OutputWindowPane buildOutputPane = null;
- foreach (OutputWindowPane pane in dte.ToolWindows.OutputWindow.OutputWindowPanes)
- {
- if (pane.Guid == VSConstants.OutputWindowPaneGuid.BuildOutputPane_string)
- {
- buildOutputPane = pane;
- break;
- }
- }
- if (buildOutputPane == null)
- {
- _ = Output.Instance.ErrorMsg("Failed to query for build output pane!");
- return null;
- }
- TextSelection sel = buildOutputPane.TextDocument.Selection;
-
- sel.StartOfDocument(false);
- sel.EndOfDocument(true);
-
- return sel.Text;
- }
- }
-}
diff --git a/IncludeToolbox/license.txt b/IncludeToolbox/license.txt
deleted file mode 100644
index 3ae5479..0000000
--- a/IncludeToolbox/license.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2017-2019 Andreas Reich
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/IncludeToolbox2019/IncludeToolbox2019.csproj b/IncludeToolbox2019/IncludeToolbox2019.csproj
new file mode 100644
index 0000000..9d1fe75
--- /dev/null
+++ b/IncludeToolbox2019/IncludeToolbox2019.csproj
@@ -0,0 +1,147 @@
+
+
+
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ latest
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {A81A5332-6A20-4F3B-90B4-E55985B9CF59}
+ Library
+ Properties
+ IncludeToolbox
+ IncludeToolbox2019
+ v4.8
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ Program
+ $(DevEnvDir)devenv.exe
+ /rootsuffix Exp
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ False
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ False
+
+
+
+ MyImageIds.vsct
+ True
+ True
+
+
+
+ True
+ True
+ source.extension.vsixmanifest
+
+
+
+
+ Menus.ctmenu
+ VsctGenerator
+ VSCommandTable.cs
+
+
+ True
+ True
+ VSCommandTable.vsct
+
+
+
+
+
+
+
+
+
+
+
+ License.txt
+ true
+ Always
+
+
+ true
+
+
+ Designer
+ VsixManifestGenerator
+ source.extension.cs
+
+
+
+
+
+
+
+
+
+
+
+ 16.0.451
+
+
+
+ 16.10.31320.204
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ 4.5.5
+
+
+
+
+
+
+
+
+
+
+ Always
+ true
+
+
+
+
+ VsctGenerator
+ MyImageIds.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2019/Monikers.imagemanifest b/IncludeToolbox2019/Monikers.imagemanifest
new file mode 100644
index 0000000..ba2c5bf
--- /dev/null
+++ b/IncludeToolbox2019/Monikers.imagemanifest
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2019/MyImageIds.cs b/IncludeToolbox2019/MyImageIds.cs
new file mode 100644
index 0000000..20fa28f
--- /dev/null
+++ b/IncludeToolbox2019/MyImageIds.cs
@@ -0,0 +1,34 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ using System;
+
+ ///
+ /// Helper class that exposes all GUIDs used across VS Package.
+ ///
+ internal sealed partial class PackageGuids
+ {
+ public const string MonikersGuidString = "36358583-08b5-4870-b61e-13efcebfd341";
+ public static Guid MonikersGuid = new Guid(MonikersGuidString);
+ }
+ ///
+ /// Helper class that encapsulates all CommandIDs uses across VS Package.
+ ///
+ internal sealed partial class PackageIds
+ {
+ public const int hash = 0x000C;
+ public const int IncludeFormatterIcons_0 = 0x0001;
+ public const int IncludeFormatterIcons_1 = 0x0002;
+ public const int IncludeFormatterIcons_2 = 0x0003;
+ public const int IncludeFormatterIcons_3 = 0x0004;
+ public const int IncludeFormatterPackage = 0x0005;
+ public const int IncludeGraphToolbarIcons_0 = 0x0006;
+ public const int IncludeGraphToolbarIcons_1 = 0x0007;
+ public const int IncludeFormatterIcons = 0x0000;
+ public const int IncludeGraphToolbarIcons = 0x0001;
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolbox2019/MyImageIds.vsct b/IncludeToolbox2019/MyImageIds.vsct
new file mode 100644
index 0000000..b3f00dd
--- /dev/null
+++ b/IncludeToolbox2019/MyImageIds.vsct
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2019/Properties/AssemblyInfo.cs b/IncludeToolbox2019/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..69b7933
--- /dev/null
+++ b/IncludeToolbox2019/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using IncludeToolbox;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle(Vsix.Name)]
+[assembly: AssemblyDescription(Vsix.Description)]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany(Vsix.Author)]
+[assembly: AssemblyProduct(Vsix.Name)]
+[assembly: AssemblyCopyright(Vsix.Author)]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: AssemblyVersion(Vsix.Version)]
+[assembly: AssemblyFileVersion(Vsix.Version)]
+
+namespace System.Runtime.CompilerServices
+{
+ public class IsExternalInit { }
+}
\ No newline at end of file
diff --git a/IncludeToolbox/Resources/IncludeFormatterIcons.png b/IncludeToolbox2019/Resources/IncludeFormatterIcons.png
similarity index 100%
rename from IncludeToolbox/Resources/IncludeFormatterIcons.png
rename to IncludeToolbox2019/Resources/IncludeFormatterIcons.png
diff --git a/IncludeToolbox2019/Resources/IncludeFormatterIcons_0.png b/IncludeToolbox2019/Resources/IncludeFormatterIcons_0.png
new file mode 100644
index 0000000..1620c38
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeFormatterIcons_0.png differ
diff --git a/IncludeToolbox2019/Resources/IncludeFormatterIcons_1.png b/IncludeToolbox2019/Resources/IncludeFormatterIcons_1.png
new file mode 100644
index 0000000..9cc4986
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeFormatterIcons_1.png differ
diff --git a/IncludeToolbox2019/Resources/IncludeFormatterIcons_2.png b/IncludeToolbox2019/Resources/IncludeFormatterIcons_2.png
new file mode 100644
index 0000000..5194c02
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeFormatterIcons_2.png differ
diff --git a/IncludeToolbox2019/Resources/IncludeFormatterIcons_3.png b/IncludeToolbox2019/Resources/IncludeFormatterIcons_3.png
new file mode 100644
index 0000000..18c76d8
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeFormatterIcons_3.png differ
diff --git a/IncludeToolbox2019/Resources/IncludeFormatterPackage.png b/IncludeToolbox2019/Resources/IncludeFormatterPackage.png
new file mode 100644
index 0000000..de73e8d
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeFormatterPackage.png differ
diff --git a/IncludeToolbox/Resources/IncludeGraphToolbarIcons.png b/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons.png
similarity index 100%
rename from IncludeToolbox/Resources/IncludeGraphToolbarIcons.png
rename to IncludeToolbox2019/Resources/IncludeGraphToolbarIcons.png
diff --git a/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_0.png b/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_0.png
new file mode 100644
index 0000000..2b66ea7
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_0.png differ
diff --git a/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_1.png b/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_1.png
new file mode 100644
index 0000000..dea3f17
Binary files /dev/null and b/IncludeToolbox2019/Resources/IncludeGraphToolbarIcons_1.png differ
diff --git a/IncludeToolbox2019/Resources/hash.png b/IncludeToolbox2019/Resources/hash.png
new file mode 100644
index 0000000..33218b4
Binary files /dev/null and b/IncludeToolbox2019/Resources/hash.png differ
diff --git a/IncludeToolbox2019/VSCommandTable.cs b/IncludeToolbox2019/VSCommandTable.cs
new file mode 100644
index 0000000..a2fac4e
--- /dev/null
+++ b/IncludeToolbox2019/VSCommandTable.cs
@@ -0,0 +1,45 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ using System;
+
+ ///
+ /// Helper class that exposes all GUIDs used across VS Package.
+ ///
+ internal sealed partial class PackageGuids
+ {
+ public const string IncludeToolboxString = "7473f721-f6c8-4e39-98c2-e08d79a89525";
+ public static Guid IncludeToolbox = new Guid(IncludeToolboxString);
+
+ public const string GHeaderOnlyString = "a34e853e-8679-4a07-918e-982a1b3b0a6b";
+ public static Guid GHeaderOnly = new Guid(GHeaderOnlyString);
+
+ public const string GOnlyVCString = "1175290a-3e8f-4718-868c-c08b5d2b09a7";
+ public static Guid GOnlyVC = new Guid(GOnlyVCString);
+ }
+ ///
+ /// Helper class that encapsulates all CommandIDs uses across VS Package.
+ ///
+ internal sealed partial class PackageIds
+ {
+ public const int ContextMenuGroup = 0x0001;
+ public const int MapMenuGroup = 0x0002;
+ public const int IncludeGraphGroup = 0x0003;
+ public const int ItemContextGroup = 0x021A;
+ public const int ProjectContextGroup = 0x021B;
+ public const int IncludeWhatYouUseId = 0x0100;
+ public const int FormatIncludesId = 0x0101;
+ public const int TrialAndError = 0x0102;
+ public const int IWYUProjId = 0x0103;
+ public const int GenMap = 0x0104;
+ public const int RemMap = 0x0105;
+ public const int IncludeGraphId = 0x0106;
+ public const int IncludeGraphCodeId = 0x0107;
+ public const int CompileHeader = 0x0108;
+ public const int ProjectWideTrialAndErrorRemoval = 0x0110;
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolbox2019/VSCommandTable.vsct b/IncludeToolbox2019/VSCommandTable.vsct
new file mode 100644
index 0000000..0b17cc1
--- /dev/null
+++ b/IncludeToolbox2019/VSCommandTable.vsct
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IncludeToolbox2019/source.extension.cs b/IncludeToolbox2019/source.extension.cs
new file mode 100644
index 0000000..1124384
--- /dev/null
+++ b/IncludeToolbox2019/source.extension.cs
@@ -0,0 +1,18 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ internal sealed partial class Vsix
+ {
+ public const string Id = "IncludeToolbox.Andreas Reich.075c2e2b-7b71-45ba-b2e6-c1dadc81cfac";
+ public const string Name = "Include Toolbox 2019";
+ public const string Description = @"Various tools for managing C/C++ #includes: Formatting, sorting, exploring, pruning. VS2019 compatibility version. For VS2022 visit https://marketplace.visualstudio.com/items?itemName=Agrael.IncludeToolbox2022";
+ public const string Language = "en-US";
+ public const string Version = "3.2.67";
+ public const string Author = "Wumpf";
+ public const string Tags = "Include;IWYU;Include Formatting;Include Sorting;C++;C;Coding";
+ }
+}
diff --git a/IncludeToolbox2019/source.extension.vsixmanifest b/IncludeToolbox2019/source.extension.vsixmanifest
new file mode 100644
index 0000000..f40cf89
--- /dev/null
+++ b/IncludeToolbox2019/source.extension.vsixmanifest
@@ -0,0 +1,21 @@
+
+
+
+
+ Include Toolbox 2019
+ Various tools for managing C/C++ #includes: Formatting, sorting, exploring, pruning. VS2019 compatibility version. For VS2022 visit https://marketplace.visualstudio.com/items?itemName=Agrael.IncludeToolbox2022
+ License.txt
+ Resources\IncludeFormatterPackage.png
+ Resources\IncludeFormatterPackage.png
+ Include;IWYU;Include Formatting;Include Sorting;C++;C;Coding
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IncludeToolbox2022/IncludeToolbox2022.csproj b/IncludeToolbox2022/IncludeToolbox2022.csproj
new file mode 100644
index 0000000..c3429f6
--- /dev/null
+++ b/IncludeToolbox2022/IncludeToolbox2022.csproj
@@ -0,0 +1,146 @@
+
+
+
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ latest
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {7D29CECE-07D3-4417-9D63-1362852F18F3}
+ Library
+ Properties
+ IncludeToolbox
+ IncludeToolbox2022
+ v4.8
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ Program
+ $(DevEnvDir)devenv.exe
+ /rootsuffix Exp
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ True
+ True
+ MyImageIds.vsct
+
+
+
+ True
+ True
+ source.extension.vsixmanifest
+
+
+
+
+ Menus.ctmenu
+ VsctGenerator
+ VSCommandTable.cs
+
+
+ True
+ True
+ VSCommandTable.vsct
+
+
+
+
+ true
+
+
+
+
+ VsctGenerator
+ MyImageIds.cs
+
+
+
+
+
+
+
+
+ Always
+ true
+
+
+
+ Designer
+ VsixManifestGenerator
+ source.extension.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ compile; build; native; contentfiles; analyzers; buildtransitive
+
+
+ 17.3.32804.24
+
+
+ 17.3.32804.24
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+ License.txt
+ true
+ Always
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2022/Monikers.imagemanifest b/IncludeToolbox2022/Monikers.imagemanifest
new file mode 100644
index 0000000..01cfa8c
--- /dev/null
+++ b/IncludeToolbox2022/Monikers.imagemanifest
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2022/MyImageIds.cs b/IncludeToolbox2022/MyImageIds.cs
new file mode 100644
index 0000000..ee4c73e
--- /dev/null
+++ b/IncludeToolbox2022/MyImageIds.cs
@@ -0,0 +1,36 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ using System;
+
+ ///
+ /// Helper class that exposes all GUIDs used across VS Package.
+ ///
+ internal sealed partial class PackageGuids
+ {
+ public const string MonikersGuidString = "41e64718-3ed1-4ace-aeb4-fbe550d81000";
+ public static Guid MonikersGuid = new Guid(MonikersGuidString);
+ }
+ ///
+ /// Helper class that encapsulates all CommandIDs uses across VS Package.
+ ///
+ internal sealed partial class PackageIds
+ {
+ public const int hash = 0x000C;
+ public const int include13 = 0x0001;
+ public const int include16 = 0x0002;
+ public const int IncludeFormatterIcons_0 = 0x0003;
+ public const int IncludeFormatterIcons_1 = 0x0004;
+ public const int IncludeFormatterIcons_2 = 0x0005;
+ public const int IncludeFormatterIcons_3 = 0x0006;
+ public const int IncludeFormatterPackage = 0x0007;
+ public const int IncludeGraphToolbarIcons_0 = 0x0008;
+ public const int IncludeGraphToolbarIcons_1 = 0x0009;
+ public const int IncludeFormatterIcons = 0x000A;
+ public const int IncludeGraphToolbarIcons = 0x000B;
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolbox2022/MyImageIds.vsct b/IncludeToolbox2022/MyImageIds.vsct
new file mode 100644
index 0000000..f3834c2
--- /dev/null
+++ b/IncludeToolbox2022/MyImageIds.vsct
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IncludeToolbox2022/Properties/AssemblyInfo.cs b/IncludeToolbox2022/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..69b7933
--- /dev/null
+++ b/IncludeToolbox2022/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using IncludeToolbox;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle(Vsix.Name)]
+[assembly: AssemblyDescription(Vsix.Description)]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany(Vsix.Author)]
+[assembly: AssemblyProduct(Vsix.Name)]
+[assembly: AssemblyCopyright(Vsix.Author)]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: AssemblyVersion(Vsix.Version)]
+[assembly: AssemblyFileVersion(Vsix.Version)]
+
+namespace System.Runtime.CompilerServices
+{
+ public class IsExternalInit { }
+}
\ No newline at end of file
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterIcons.png b/IncludeToolbox2022/Resources/IncludeFormatterIcons.png
new file mode 100644
index 0000000..1b5a301
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterIcons.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterIcons_0.png b/IncludeToolbox2022/Resources/IncludeFormatterIcons_0.png
new file mode 100644
index 0000000..1620c38
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterIcons_0.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterIcons_1.png b/IncludeToolbox2022/Resources/IncludeFormatterIcons_1.png
new file mode 100644
index 0000000..9cc4986
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterIcons_1.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterIcons_2.png b/IncludeToolbox2022/Resources/IncludeFormatterIcons_2.png
new file mode 100644
index 0000000..5194c02
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterIcons_2.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterIcons_3.png b/IncludeToolbox2022/Resources/IncludeFormatterIcons_3.png
new file mode 100644
index 0000000..18c76d8
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterIcons_3.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeFormatterPackage.png b/IncludeToolbox2022/Resources/IncludeFormatterPackage.png
new file mode 100644
index 0000000..de73e8d
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeFormatterPackage.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons.png b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons.png
new file mode 100644
index 0000000..8a857ba
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_0.png b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_0.png
new file mode 100644
index 0000000..2b66ea7
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_0.png differ
diff --git a/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_1.png b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_1.png
new file mode 100644
index 0000000..dea3f17
Binary files /dev/null and b/IncludeToolbox2022/Resources/IncludeGraphToolbarIcons_1.png differ
diff --git a/IncludeToolbox2022/Resources/hash.png b/IncludeToolbox2022/Resources/hash.png
new file mode 100644
index 0000000..33218b4
Binary files /dev/null and b/IncludeToolbox2022/Resources/hash.png differ
diff --git a/IncludeToolbox2022/VSCommandTable.cs b/IncludeToolbox2022/VSCommandTable.cs
new file mode 100644
index 0000000..f244e28
--- /dev/null
+++ b/IncludeToolbox2022/VSCommandTable.cs
@@ -0,0 +1,45 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ using System;
+
+ ///
+ /// Helper class that exposes all GUIDs used across VS Package.
+ ///
+ internal sealed partial class PackageGuids
+ {
+ public const string IncludeToolboxString = "2e77f2e4-5f04-4052-8e63-ca2b41cd0315";
+ public static Guid IncludeToolbox = new Guid(IncludeToolboxString);
+
+ public const string GHeaderOnlyString = "a34e853e-8679-4a07-918e-982a1b3b0a6b";
+ public static Guid GHeaderOnly = new Guid(GHeaderOnlyString);
+
+ public const string GOnlyVCString = "1175290a-3e8f-4718-868c-c08b5d2b09a7";
+ public static Guid GOnlyVC = new Guid(GOnlyVCString);
+ }
+ ///
+ /// Helper class that encapsulates all CommandIDs uses across VS Package.
+ ///
+ internal sealed partial class PackageIds
+ {
+ public const int ContextMenuGroup = 0x0001;
+ public const int MapMenuGroup = 0x0002;
+ public const int IncludeGraphGroup = 0x0003;
+ public const int ItemContextGroup = 0x021A;
+ public const int ProjectContextGroup = 0x021B;
+ public const int IncludeWhatYouUseId = 0x0100;
+ public const int FormatIncludesId = 0x0101;
+ public const int TrialAndError = 0x0102;
+ public const int IWYUProjId = 0x0103;
+ public const int GenMap = 0x0104;
+ public const int RemMap = 0x0105;
+ public const int IncludeGraphId = 0x0106;
+ public const int IncludeGraphCodeId = 0x0107;
+ public const int CompileHeader = 0x0108;
+ public const int ProjectWideTrialAndErrorRemoval = 0x0110;
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolbox2022/VSCommandTable.vsct b/IncludeToolbox2022/VSCommandTable.vsct
new file mode 100644
index 0000000..d7f67cc
--- /dev/null
+++ b/IncludeToolbox2022/VSCommandTable.vsct
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IncludeToolbox2022/source.extension.cs b/IncludeToolbox2022/source.extension.cs
new file mode 100644
index 0000000..c6ca726
--- /dev/null
+++ b/IncludeToolbox2022/source.extension.cs
@@ -0,0 +1,18 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace IncludeToolbox
+{
+ internal sealed partial class Vsix
+ {
+ public const string Id = "IncludeToolbox2022.d3cba4fe-8d65-479b-8436-18d743ee7b53";
+ public const string Name = "Include Toolbox 2022";
+ public const string Description = @"Various tools for managing C/C++ #includes: Formatting, sorting, exploring, pruning.";
+ public const string Language = "en-US";
+ public const string Version = "3.2.67";
+ public const string Author = "Agrael";
+ public const string Tags = "Include;IWYU;Include Formatting;Include Sorting;C++;C;Coding";
+ }
+}
diff --git a/IncludeToolbox2022/source.extension.vsixmanifest b/IncludeToolbox2022/source.extension.vsixmanifest
new file mode 100644
index 0000000..57ed73a
--- /dev/null
+++ b/IncludeToolbox2022/source.extension.vsixmanifest
@@ -0,0 +1,23 @@
+
+
+
+
+ Include Toolbox 2022
+ Various tools for managing C/C++ #includes: Formatting, sorting, exploring, pruning.
+ License.txt
+ Resources\IncludeFormatterPackage.png
+ Resources\IncludeFormatterPackage.png
+ Include;IWYU;Include Formatting;Include Sorting;C++;C;Coding
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IncludeToolboxShared/Commands/CompileHeader.cs b/IncludeToolboxShared/Commands/CompileHeader.cs
new file mode 100644
index 0000000..9996f0d
--- /dev/null
+++ b/IncludeToolboxShared/Commands/CompileHeader.cs
@@ -0,0 +1,58 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.VCProjectEngine;
+using System.IO;
+using System.Threading.Tasks;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.Commands
+{
+ [Command(PackageIds.CompileHeader)]
+ internal sealed class CompileHeader : BaseCommand
+ {
+ static string support_cpp_path = null;
+
+ protected override Task InitializeCompletedAsync()
+ {
+ Command.Supported = false;
+ support_cpp_path = Path.ChangeExtension(Path.GetTempFileName(), ".cpp");
+ return Task.CompletedTask;
+ }
+
+ private async Task TestCompileAsync(VCFileConfiguration config)
+ {
+ using AsyncDispatcher dispatcher = new();
+ return await dispatcher.CompileAsync(config);
+ }
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var x = await VS.Solutions.GetActiveItemAsync();
+
+ if ((await x.ToVCProjectItemAsync()) is not VCFile file)
+ return;
+
+ _ = Output.WriteLineAsync($"Starting Trial And Error Include removal on header file {file.FullPath}");
+ string xout = "";
+
+ var pch = VCUtil.GetPCH((VCProject)file.project);
+ if (!string.IsNullOrEmpty(pch))
+ xout = "#include \"" + pch + "\"\n";
+
+ File.WriteAllText(support_cpp_path, xout + "#include \"" + file.FullPath + "\"");
+
+ var proj = await VS.Solutions.GetActiveProjectAsync();
+ var vc = await proj.ToVCProjectAsync();
+ var xfile = (VCFile)vc.AddFile(support_cpp_path);
+ using TempGuard tg = new(xfile);
+
+ VCFileConfiguration config = VCUtil.GetVCFileConfig(xfile);
+ if (config != null)
+ {
+ await TestCompileAsync(config);
+ return;
+ }
+ _ = Output.WriteLineAsync($"{xfile.Name} has failed to yield a config.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolboxShared/Commands/FormatIncludes.cs b/IncludeToolboxShared/Commands/FormatIncludes.cs
new file mode 100644
index 0000000..1eb5475
--- /dev/null
+++ b/IncludeToolboxShared/Commands/FormatIncludes.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Task = System.Threading.Tasks.Task;
+
+
+namespace IncludeToolbox.Commands
+{
+ [Command(PackageIds.FormatIncludesId)]
+ internal sealed class FormatIncludes : BaseCommand
+ {
+ SnapshotSpan GetSelectionLines(IWpfTextView viewHost)
+ {
+ if (viewHost == null) return new SnapshotSpan();
+ var sel = viewHost.Selection.StreamSelectionSpan;
+ var start = new SnapshotPoint(viewHost.TextSnapshot, sel.Start.Position).GetContainingLine().Start;
+ var end = new SnapshotPoint(viewHost.TextSnapshot, sel.End.Position).GetContainingLine().EndIncludingLineBreak;
+
+ return new SnapshotSpan(start, end);
+ }
+ async Task GetSelectionLinesAsync()
+ {
+ IWpfTextView viewHost = (await VS.Documents.GetActiveDocumentViewAsync())?.TextView;
+ return GetSelectionLines(viewHost);
+ }
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Has to be synchronous")]
+ protected override void BeforeQueryStatus(EventArgs e)
+ {
+ var selection_span = GetSelectionLinesAsync().Result;
+ var lines = Parser.ParseInclues(selection_span.GetText().AsSpan(), false);// faster a times! tested with QPC
+ Command.Visible = lines.Count() != 0;
+ }
+
+
+
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs args)
+ {
+ var settings = await FormatOptions.GetLiveInstanceAsync();
+ var doc = await VS.Documents.GetActiveDocumentViewAsync();
+ // Read.
+ var selection_span = await GetSelectionLinesAsync();
+ var include_directories = await VCUtil.GetIncludeDirsAsync();
+ var text = selection_span.GetText();
+ // Format
+ var formated_lines = Formatter.IncludeFormatter.FormatIncludes(text.AsSpan(), doc.FilePath, include_directories, settings);
+
+ // Overwrite.
+ Formatter.IncludeFormatter.ApplyChanges(formated_lines, doc, text, selection_span.Start, settings.RemoveEmptyLines);
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/GenMap.cs b/IncludeToolboxShared/Commands/GenMap.cs
new file mode 100644
index 0000000..865a1c9
--- /dev/null
+++ b/IncludeToolboxShared/Commands/GenMap.cs
@@ -0,0 +1,64 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using System.Linq;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox
+{
+ [Command(PackageIds.GenMap)]
+ internal sealed class GenMap : BaseCommand
+ {
+ protected override Task InitializeCompletedAsync()
+ {
+ Command.Supported = false;
+ return Task.CompletedTask;
+ }
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var settings = await MapperOptions.GetLiveInstanceAsync();
+ if (settings.MapFile == "") { VS.MessageBox.ShowErrorAsync("Map output error", "Map output file is empty, go to Tools->Options->Include Minimizer->General and set the output file!").FireAndForget(); return; }
+ var doc = await VS.Documents.GetActiveDocumentViewAsync();
+ if (doc == null) return;
+
+ var path = doc.FilePath;
+ var relative_path = settings.Prefix != "" ? Utils.MakeRelative(settings.Prefix, path) : path;
+ relative_path = relative_path.Replace('\\', '/');
+
+
+ var snap = doc.TextBuffer.CurrentSnapshot;
+ var text = snap.GetText();
+
+ var sresult = Parser.ParseInclues(Utils.GetIncludeSpanRO(text), settings.Ignoreifdefs)
+ .Distinct();
+
+ string file_map = "";
+ switch (settings.Preference)
+ {
+ case MappingPreference.Quotes:
+ file_map = string.Format("\t{{ include: [ \"<{0}>\", private, \"\\\"{0}\\\"\", public ] }},\n", relative_path);
+ break;
+ case MappingPreference.AngleBrackets:
+ file_map = string.Format("\t{{ include: [ \"\\\"{0}\\\"\", private, \"<{0}>\", public ] }},\n", relative_path);
+ break;
+ default:
+ break;
+ }
+
+ foreach (var match in sresult)
+ switch (settings.Preference)
+ {
+ case MappingPreference.Quotes:
+ file_map += string.Format("\t{{ include: [ \"{0}\", public, \"\\\"{1}\\\"\", public ] }},\n", match.FullFile.Replace('\\', '/'), relative_path);
+ break;
+ default:
+ case MappingPreference.AngleBrackets:
+ file_map += string.Format("\t{{ include: [ \"{0}\", public, \"<{1}>\", public ] }},\n", match.FullFile.Replace('\\', '/'), relative_path);
+ break;
+ }
+ settings.Map.Map[relative_path] = file_map;
+
+ settings.Map.WriteMapAsync(settings).FireAndForget();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/IncludeGraph.cs b/IncludeToolboxShared/Commands/IncludeGraph.cs
new file mode 100644
index 0000000..a9440cd
--- /dev/null
+++ b/IncludeToolboxShared/Commands/IncludeGraph.cs
@@ -0,0 +1,15 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.Commands
+{
+ [Command(PackageIds.IncludeGraphId)]
+ internal sealed class IncludeGraph : BaseCommand
+ {
+ protected override Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ return IncludeGraphToolWindow.ShowAsync();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/IncludeGraph_CodeWindow.cs b/IncludeToolboxShared/Commands/IncludeGraph_CodeWindow.cs
new file mode 100644
index 0000000..6aa6ab1
--- /dev/null
+++ b/IncludeToolboxShared/Commands/IncludeGraph_CodeWindow.cs
@@ -0,0 +1,26 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.Commands
+{
+ [Command(PackageIds.IncludeGraphCodeId)]
+ internal class IncludeGraph_CodeWindow : BaseCommand
+ {
+ protected override Task InitializeCompletedAsync()
+ {
+ Command.Supported = false;
+ return Task.CompletedTask;
+ }
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var pane = await IncludeGraphToolWindow.ShowAsync();
+ var cont = (IncludeGraphControl)pane.Content;
+ var context = (IncludeGraphViewModel)cont.DataContext;
+ var file = await VS.Documents.GetActiveDocumentViewAsync();
+
+ context.RecalculateForAsync(file).FireAndForget();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/RemoveMap.cs b/IncludeToolboxShared/Commands/RemoveMap.cs
new file mode 100644
index 0000000..147dcb1
--- /dev/null
+++ b/IncludeToolboxShared/Commands/RemoveMap.cs
@@ -0,0 +1,33 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using System.Threading.Tasks;
+using System;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox
+{
+ [Command(PackageIds.RemMap)]
+ internal sealed class RemoveMap : BaseCommand
+ {
+ protected override Task InitializeCompletedAsync()
+ {
+ return base.InitializeCompletedAsync();
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Has to be synchronous")]
+ protected override void BeforeQueryStatus(EventArgs e)
+ {
+ Command.Visible = MapperOptions.Instance.IsInMapAsync().Result;
+ }
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var settings = await MapperOptions.GetLiveInstanceAsync();
+ var doc = await VS.Documents.GetActiveDocumentViewAsync();
+ var file = Utils.MakeRelative(settings.Prefix, doc.FilePath).Replace('\\', '/');
+
+ settings.Map.TryRemoveValue(file);
+ settings.Map.WriteMapAsync(settings).FireAndForget();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/RunIWYU.cs b/IncludeToolboxShared/Commands/RunIWYU.cs
new file mode 100644
index 0000000..eee88cf
--- /dev/null
+++ b/IncludeToolboxShared/Commands/RunIWYU.cs
@@ -0,0 +1,102 @@
+using Community.VisualStudio.Toolkit;
+using IncludeToolbox.IncludeWhatYouUse;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using System;
+using File = System.IO.File;
+using Task = System.Threading.Tasks.Task;
+
+
+
+namespace IncludeToolbox.Commands
+{
+ internal class CancelCallback : IVsThreadedWaitDialogCallback
+ {
+ public delegate void Cancel();
+ public Cancel cancel;
+ public CancelCallback(Cancel cancel)
+ {
+ this.cancel = cancel;
+ }
+
+ void IVsThreadedWaitDialogCallback.OnCanceled()
+ {
+ cancel();
+ }
+ }
+
+
+ [Command(PackageIds.IncludeWhatYouUseId)]
+ internal sealed class RunIWYU : BaseCommand
+ {
+ IWYU proc = new();
+ CancelCallback cancelCallback;
+
+
+ protected override async Task InitializeCompletedAsync()
+ {
+ Command.Supported = false;
+ cancelCallback = new(delegate { proc.CancelAsync().FireAndForget(); });
+ var settings = await IWYUOptions.GetLiveInstanceAsync();
+ settings.OnChange += proc.BuildCommandLine;
+ proc.BuildCommandLine(settings);
+ }
+
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ var settings = await IWYUOptions.GetLiveInstanceAsync();
+ var dlg = (IVsThreadedWaitDialogFactory)await VS.Services.GetThreadedWaitDialogAsync();
+
+ if ((settings.Executable == "" || !File.Exists(settings.Executable) || await settings.DownloadRequiredAsync())
+ && !await IWYUDownload.DownloadAsync(dlg, settings))
+ {
+ VS.MessageBox.ShowErrorAsync("IWYU Error", "No executable found, operation cannot be completed").FireAndForget();
+ return;
+ }
+
+ await VS.Commands.ExecuteAsync(KnownCommands.File_SaveSelectedItems);
+
+ var doc = await VS.Documents.GetActiveDocumentViewAsync();
+ if (doc == null) return;
+ if (settings.IgnoreHeader) IWYU.MoveHeader(doc);
+ var buf = doc.TextBuffer;
+ var str = buf.CurrentSnapshot.GetText();
+
+
+ var x = Parser.ParseAsync(str);
+
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ dlg.CreateInstance(out IVsThreadedWaitDialog2 xdialog);
+ IVsThreadedWaitDialog4 dialog = xdialog as IVsThreadedWaitDialog4;
+
+ dialog.StartWaitDialogWithCallback("Include Toolbox", "Running include-what-you-use", null, null, "Running include-what-you-use", true, 0, true, 0, 0, cancelCallback);
+
+ bool result = false;
+ try
+ {
+ result = await proc.StartAsync(doc.FilePath, settings.AlwaysRebuid);
+ }
+ catch (Exception ex)
+ {
+ _ = Output.WriteLineAsync("IWYU Failed with error:" + ex.Message);
+ }
+
+ if (dialog.EndWaitDialog() || result == false) return;
+
+ if (settings.Sub == Substitution.Precise)
+ await IWYUApply.ApplyPreciseAsync(settings, await x, proc.ProcOutput, VCUtil.Std);
+ else
+ await IWYUApply.ApplyAsync(settings, proc.ProcOutput);
+
+
+ if (settings.RemoveENS)
+ IWYUApply.ClearNamespaces(doc);
+ if (settings.Format)
+ await IWYUApply.FormatAsync(doc);
+ if (settings.FormatDoc)
+ await VS.Commands.ExecuteAsync(Microsoft.VisualStudio.VSConstants.VSStd2KCmdID.FORMATDOCUMENT);
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/RunIWYUProject.cs b/IncludeToolboxShared/Commands/RunIWYUProject.cs
new file mode 100644
index 0000000..3d12c41
--- /dev/null
+++ b/IncludeToolboxShared/Commands/RunIWYUProject.cs
@@ -0,0 +1,159 @@
+using Community.VisualStudio.Toolkit;
+using IncludeToolbox.IncludeWhatYouUse;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using File = System.IO.File;
+using Task = System.Threading.Tasks.Task;
+
+
+
+namespace IncludeToolbox.Commands
+{
+ [Command(PackageIds.IWYUProjId)]
+ internal sealed class RunIWYUProject : BaseCommand
+ {
+ static readonly Regex extension = new("^\\.[ch](?:pp|xx)?$");
+ IWYU task = new();
+ CancelCallback cancelCallback;
+
+ protected override async Task InitializeCompletedAsync()
+ {
+ cancelCallback = new(() => { task.CancelAsync().FireAndForget(); });
+ var settings = await IWYUOptions.GetLiveInstanceAsync();
+ settings.OnChange += task.BuildCommandLine;
+ task.BuildCommandLine(settings);
+ }
+
+
+ async Task CheckAsync()
+ {
+ Command.Visible = false;
+ var items = await VS.Solutions.GetActiveItemsAsync();
+
+ HashSet set = new();
+
+ bool b = items.All(s =>
+ {
+ if (s.Type == SolutionItemType.Project)
+ { _ = set.Add((Project)s); return true; }
+ if (s.Type != SolutionItemType.PhysicalFile) return false;
+ var parent = s.FindParent(SolutionItemType.Project);
+ if (parent == null) return false;
+ _ = set.Add((Project)parent);
+ return extension.IsMatch(Path.GetExtension(s.Name));
+ });
+
+ if (!b) return;
+
+ foreach (var item in set)
+ {
+ if (await item.ToVCProjectAsync() == null)
+ return;
+ }
+ Command.Visible = true;
+ }
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Has to be synchronous")]
+ protected override void BeforeQueryStatus(EventArgs e)
+ {
+ CheckAsync().Wait();
+ }
+
+ Dictionary>> SetTasks(IEnumerable items)
+ {
+ Dictionary>> set = new();
+ foreach (var item in items)
+ {
+ if (item.Type == SolutionItemType.PhysicalFile)
+ {
+ var parent = (Project)item.FindParent(SolutionItemType.Project);
+ var hash = parent.FullPath;
+ if (!set.ContainsKey(hash))
+ set[hash] = new KeyValuePair>(parent, new List());
+ set[hash].Value.Add(item.Name);
+ }
+ }
+ return set;
+ }
+
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var set = SetTasks(await VS.Solutions.GetActiveItemsAsync());
+ var settings = await IWYUOptions.GetLiveInstanceAsync();
+
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ var dlg = (IVsThreadedWaitDialogFactory)await VS.Services.GetThreadedWaitDialogAsync();
+
+ if ((settings.Executable == "" || !File.Exists(settings.Executable) || await settings.DownloadRequiredAsync())
+ && !await IWYUDownload.DownloadAsync(dlg, settings))
+ {
+ VS.MessageBox.ShowErrorAsync("IWYU Error", "No executable found, operation cannot be completed").FireAndForget();
+ return;
+ }
+
+ _ = dlg.CreateInstance(out IVsThreadedWaitDialog2 xdialog);
+ IVsThreadedWaitDialog4 dialog = xdialog as IVsThreadedWaitDialog4;
+
+ try
+ {
+ dialog.StartWaitDialogWithCallback("Include Toolbox", "Running include-what-you-use", null, null, "Running include-what-you-use", true, 0, true, set.Count, 0, cancelCallback);
+
+ // needs parallelization, but cancellation is doomed
+ foreach (var v in set)
+ {
+ for (int c = 0; c < v.Value.Value.Count;)
+ {
+ string f = v.Value.Value[c];
+ dialog.UpdateProgress("Working with project:",
+ $"Running IWYU - Working with {v.Value.Key.Name}; File: {f}",
+ $"Running IWYU - Working with {v.Value.Key.Name}",
+ ++c,
+ v.Value.Value.Count,
+ false,
+ out var cancelled);
+
+ var doc = await VS.Documents.OpenAsync(f);
+ if (doc == null) return;
+ if (settings.IgnoreHeader) IWYU.MoveHeader(doc);
+ var buf = doc.TextBuffer;
+ var str = buf.CurrentSnapshot.GetText();
+ await VS.Commands.ExecuteAsync(KnownCommands.File_SaveAll);
+
+ var x = Parser.ParseAsync(str);
+
+ bool result = await task.StartAsync(f, v.Value.Key, true); // process cannot be rerun
+
+ if (cancelled || result == false) return;
+
+ if (settings.Sub == Substitution.Precise)
+ await IWYUApply.ApplyPreciseAsync(settings, await x, task.ProcOutput, VCUtil.Std);
+ else
+ await IWYUApply.ApplyAsync(settings, task.ProcOutput);
+
+ if (settings.RemoveENS)
+ IWYUApply.ClearNamespaces(doc);
+ if (settings.Format)
+ await IWYUApply.FormatAsync(doc);
+ if (settings.FormatDoc)
+ {
+ await doc.WindowFrame.ShowAsync();
+ await VS.Commands.ExecuteAsync(Microsoft.VisualStudio.VSConstants.VSStd2KCmdID.FORMATDOCUMENT);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ VS.MessageBox.ShowErrorAsync(ex.Message).FireAndForget();
+ }
+ _ = dialog.EndWaitDialog();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Commands/TrialAndErrorRemoval_CodeWindow.cs b/IncludeToolboxShared/Commands/TrialAndErrorRemoval_CodeWindow.cs
new file mode 100644
index 0000000..e8915df
--- /dev/null
+++ b/IncludeToolboxShared/Commands/TrialAndErrorRemoval_CodeWindow.cs
@@ -0,0 +1,104 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.VCProjectEngine;
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.Commands
+{
+ internal sealed class TempGuard : IDisposable
+ {
+ readonly VCFile file;
+ private bool disposedValue = false;
+
+ private void Dispose()
+ {
+ if (!disposedValue)
+ {
+ file.Remove();
+ disposedValue = true;
+ }
+ }
+ public TempGuard(VCFile file)
+ {
+ this.file = file;
+ }
+
+ ~TempGuard()
+ {
+ Dispose();
+ }
+
+ void IDisposable.Dispose()
+ {
+ Dispose();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+
+ [Command(PackageIds.TrialAndError)]
+ internal sealed class TrialAndErrorRemoval_CodeWindow : BaseCommand
+ {
+ TrialAndErrorRemoval impl = new();
+ static string support_cpp_path = null;
+
+ protected override Task InitializeCompletedAsync()
+ {
+ Command.Supported = false;
+ support_cpp_path = Path.ChangeExtension(Path.GetTempFileName(), ".cpp");
+ return Task.CompletedTask;
+ }
+
+ async Task TAERHeaderAsync(VCFile file)
+ {
+ _ = Output.WriteLineAsync($"Starting Trial And Error Include removal on header file {file.FullPath}");
+ string xout = "";
+
+ var pch = VCUtil.GetPCH((VCProject)file.project);
+ if (!string.IsNullOrEmpty(pch))
+ xout = "#include \"" + pch + "\"\n";
+
+ File.WriteAllText(support_cpp_path, xout + "#include \"" + file.FullPath + "\"");
+
+ var proj = await VS.Solutions.GetActiveProjectAsync();
+ var vc = await proj.ToVCProjectAsync();
+ var xfile = (VCFile)vc.AddFile(support_cpp_path);
+ using TempGuard tg = new(xfile);
+
+ return await impl.StartHeaderAsync(file, xfile, await TrialAndErrorRemovalOptions.GetLiveInstanceAsync());
+ }
+ async Task TAERCodeAsync(VCFile file)
+ {
+ _ = Output.WriteLineAsync($"Starting Trial And Error Include removal on {file.FullPath}");
+ return await impl.StartAsync(file, await TrialAndErrorRemovalOptions.GetLiveInstanceAsync());
+ }
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ var x = await VS.Solutions.GetActiveItemAsync();
+
+ if ((await x.ToVCProjectItemAsync()) is not VCFile file)
+ return;
+ string err = "";
+
+ switch (file.FileType)
+ {
+ case eFileType.eFileTypeCppCode:
+ err = await TAERCodeAsync(file);
+ break;
+ case eFileType.eFileTypeCppHeader:
+ err = await TAERHeaderAsync(file);
+ break;
+ default:
+ break;
+ }
+
+ if (string.IsNullOrEmpty(err)) return;
+ _ = Output.WriteLineAsync(err);
+ }
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolboxShared/Commands/TrialAndErrorRemoval_Project.cs b/IncludeToolboxShared/Commands/TrialAndErrorRemoval_Project.cs
new file mode 100644
index 0000000..61011f0
--- /dev/null
+++ b/IncludeToolboxShared/Commands/TrialAndErrorRemoval_Project.cs
@@ -0,0 +1,94 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.VCProjectEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Project = Community.VisualStudio.Toolkit.Project;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.Commands
+{
+ internal class TAERDispatcher
+ {
+ public Queue files = new();
+ public int numTotalRemovedIncludes = 0;
+ readonly TrialAndErrorRemoval impl = new();
+
+
+ public async Task FindFilesAsync(Project project)
+ {
+ var vcproj = await project.ToVCProjectAsync();
+ var xfiles = (IVCCollection)vcproj.Files;
+
+ foreach (var item in xfiles)
+ {
+ if (item is not VCFile file || file.FileType != eFileType.eFileTypeCppCode)
+ continue;
+ files.Enqueue(file);
+ }
+ }
+
+ public async Task ProcessAsync()
+ {
+ foreach (var item in files)
+ {
+ _ = Output.WriteLineAsync($"\nStarting Trial And Error Include removal on {item.FullPath}");
+ string err = await impl.StartAsync(item, await TrialAndErrorRemovalOptions.GetLiveInstanceAsync());
+ if (string.IsNullOrEmpty(err)) continue;
+ _ = Output.WriteLineAsync(err);
+ }
+ _ = Output.WriteLineAsync($"\nTrial And Error Over project removed {impl.Removed} includes in total.");
+ }
+ }
+
+
+ [Command(PackageIds.ProjectWideTrialAndErrorRemoval)]
+ internal sealed class TrialAndErrorRemoval_Project : BaseCommand
+ {
+ protected override Task InitializeCompletedAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Has to be synchronous")]
+ protected override void BeforeQueryStatus(EventArgs e)
+ {
+ var project = GetSelectedVCProjectAsync().Result;
+ Command.Visible = project != null;
+ }
+
+
+ static async Task GetSelectedVCProjectAsync()
+ {
+ var selection = await VS.Solutions.GetActiveItemsAsync();
+ foreach (Project item in selection.Where(i => i is Project))
+ {
+ var vcp = await item.ToVCProjectAsync();
+ if (vcp != null) return vcp;
+ }
+ return null;
+ }
+
+
+ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
+ {
+ if (TrialAndErrorRemoval.WorkInProgress)
+ {
+ await VS.MessageBox.ShowErrorAsync("Trial and error include removal already in progress!");
+ return;
+ }
+
+ var proj = await VS.Solutions.GetActiveProjectAsync();
+ if (proj == null) return;
+
+ if (!await VS.MessageBox.ShowConfirmAsync("Attention! Trial and error include removal on large projects make take up to several hours! In this time you will not be able to use Visual Studio. Are you sure you want to continue?"))
+ return;
+
+ TAERDispatcher dispatcher = new();
+ await dispatcher.FindFilesAsync(proj);
+ await dispatcher.ProcessAsync();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/FolderIncludeTreeItem.cs b/IncludeToolboxShared/FolderIncludeTreeItem.cs
new file mode 100644
index 0000000..1524a70
--- /dev/null
+++ b/IncludeToolboxShared/FolderIncludeTreeItem.cs
@@ -0,0 +1,227 @@
+using IncludeToolbox.Graph;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IncludeToolbox.GraphWindow
+{
+ public class FolderIncludeTreeViewItem_Root : IncludeTreeViewItem
+ {
+ IReadOnlyCollection graphItems;
+ IncludeGraph.Item includingFile;
+
+ public override IReadOnlyList Children
+ {
+ get
+ {
+ if (cachedItems == null)
+ GenerateChildItems();
+ return cachedItems;
+ }
+ }
+ protected IReadOnlyList cachedItems;
+
+
+ public FolderIncludeTreeViewItem_Root(IReadOnlyCollection graphItems, IncludeGraph.Item includingFile)
+ {
+ this.graphItems = graphItems;
+ this.includingFile = includingFile;
+ this.Name = "Root";
+ this.AbsoluteFilename = "Root";
+ }
+
+ public void Reset(IReadOnlyCollection graphItems, IncludeGraph.Item includingFile)
+ {
+ this.graphItems = graphItems;
+ this.includingFile = includingFile;
+ this.cachedItems = null;
+ NotifyAllPropertiesChanged();
+ }
+
+ private void GenerateChildItems()
+ {
+ if (graphItems == null)
+ {
+ cachedItems = emptyList;
+ return;
+ }
+
+ var rootChildren = new List();
+ cachedItems = rootChildren;
+
+ // Create first layer of folder and leaf items
+ var leafItems = new List();
+ foreach (IncludeGraph.Item item in graphItems)
+ {
+ if (item == includingFile)
+ continue;
+
+ leafItems.Add(new FolderIncludeTreeViewItem_Leaf(item));
+ }
+
+ // Group by folder.
+ if (leafItems.Count > 0)
+ {
+ leafItems.Sort((x, y) => x.ParentFolder.CompareTo(y.ParentFolder));
+
+ var root = new FolderIncludeTreeViewItem_Folder("", 0);
+ GroupIncludeRecursively(root, leafItems, 0, leafItems.Count, 0);
+ rootChildren.AddRange(root.ChildrenList);
+ }
+ }
+
+ private string GetNextPathPrefix(string path, int begin)
+ {
+ int nextSlash = begin;
+ while (path.Length > nextSlash && path[nextSlash] != Path.DirectorySeparatorChar)
+ ++nextSlash;
+ return path.Substring(0, nextSlash);
+ }
+
+ private string LargestCommonFolderPrefixInRange(List allLeafItems, int begin, int end, string commonPrefix)
+ {
+ string folderBegin = allLeafItems[begin].ParentFolder;
+ string folderEnd = allLeafItems[end-1].ParentFolder;
+
+ string prefixCandidate = commonPrefix;
+ string previousPrefix = null;
+ do
+ {
+ if (folderBegin.Length == prefixCandidate.Length)
+ return prefixCandidate;
+
+ previousPrefix = prefixCandidate;
+ prefixCandidate = GetNextPathPrefix(folderBegin, previousPrefix.Length + 1);
+
+ } while (folderEnd.StartsWith(prefixCandidate));
+
+ return previousPrefix;
+ }
+
+ private void GroupIncludeRecursively(FolderIncludeTreeViewItem_Folder parentFolder, List allLeafItems, int begin, int end, int commonPrefixLength)
+ {
+ System.Diagnostics.Debug.Assert(begin < end);
+ System.Diagnostics.Debug.Assert(allLeafItems.Count >= end);
+
+ // Look through the sorted subsection of folders and find ranges where the prefix changes.
+ while(begin < end)
+ {
+ // New subgroup to look at!
+ string currentPrefix = GetNextPathPrefix(allLeafItems[begin].ParentFolder, commonPrefixLength + 1);
+
+ // Find end of the rest of the group and expand recurively.
+ for (int i = begin; i <= end; ++i)
+ {
+ if (i == end || !allLeafItems[i].ParentFolder.StartsWith(currentPrefix))
+ {
+ // Find maximal prefix of this group.
+ string largestPrefix = LargestCommonFolderPrefixInRange(allLeafItems, begin, i, currentPrefix);
+ var newGroup = new FolderIncludeTreeViewItem_Folder(largestPrefix, commonPrefixLength);
+ parentFolder.ChildrenList.Add(newGroup);
+
+ // If there are any direct children, they will be first due to sorting. Add them to the new group and ignore this part of the range.
+ while (allLeafItems[begin].ParentFolder.Length == largestPrefix.Length)
+ {
+ newGroup.ChildrenList.Add(allLeafItems[begin]);
+ ++begin;
+ if (begin == i)
+ break;
+ }
+
+ // What's left is non-direct children (== folders!) that we need to handle recursively.
+ int numFoldersInGroup = i - begin;
+ if (numFoldersInGroup > 0)
+ {
+ GroupIncludeRecursively(newGroup, allLeafItems, begin, i, largestPrefix.Length);
+ }
+
+ // Next group starts at this element.
+ begin = i;
+ break;
+ }
+ }
+ }
+ }
+
+ public override Task NavigateToInclude()
+ {
+ return Task.CompletedTask;
+ }
+ }
+
+ public class FolderIncludeTreeViewItem_Folder : IncludeTreeViewItem
+ {
+ public override IReadOnlyList Children => ChildrenList;
+ public List ChildrenList { get; private set; } = new List();
+
+ public const string UnresolvedFolderName = "";
+
+ public FolderIncludeTreeViewItem_Folder(string largestPrefix, int commonPrefixLength)
+ {
+ AbsoluteFilename = largestPrefix;
+
+ largestPrefix.Substring(commonPrefixLength);
+
+ if (largestPrefix != UnresolvedFolderName)
+ {
+ var stringBuilder = new StringBuilder(largestPrefix);
+ stringBuilder.Remove(0, commonPrefixLength);
+ stringBuilder.Append(Path.DirectorySeparatorChar);
+ if (stringBuilder[0] == Path.DirectorySeparatorChar)
+ stringBuilder.Remove(0, 1);
+
+ Name = stringBuilder.ToString();
+ }
+ else
+ {
+ Name = largestPrefix;
+ }
+ }
+
+ public override Task NavigateToInclude()
+ {
+ // todo?
+ return Task.CompletedTask;
+ }
+ }
+
+ public class FolderIncludeTreeViewItem_Leaf : IncludeTreeViewItem
+ {
+ public override IReadOnlyList Children => emptyList;
+
+ ///
+ /// Parent folder of this leaf. Needed during build-up.
+ ///
+ public string ParentFolder { get; private set; }
+
+ public FolderIncludeTreeViewItem_Leaf(IncludeGraph.Item item)
+ {
+ try
+ {
+ Name = Path.GetFileName(item.AbsoluteFilename);
+ }
+ catch
+ {
+ Name = item?.FormattedName;
+ }
+ AbsoluteFilename = item?.AbsoluteFilename;
+
+ if (string.IsNullOrWhiteSpace(AbsoluteFilename) || !Path.IsPathRooted(AbsoluteFilename))
+ ParentFolder = FolderIncludeTreeViewItem_Folder.UnresolvedFolderName;
+ else
+ ParentFolder = Path.GetDirectoryName(AbsoluteFilename);
+ }
+
+ public override async Task NavigateToInclude()
+ {
+ if (AbsoluteFilename != null && Path.IsPathRooted(AbsoluteFilename))
+ {
+ await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ var fileWindow = VSUtils.OpenFileAndShowDocument(AbsoluteFilename);
+ }
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Graph/IncludeGraph.cs b/IncludeToolboxShared/Graph/IncludeGraph.cs
new file mode 100644
index 0000000..0db3cf4
--- /dev/null
+++ b/IncludeToolboxShared/Graph/IncludeGraph.cs
@@ -0,0 +1,102 @@
+using Community.VisualStudio.Toolkit;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Path = System.IO.Path;
+
+namespace IncludeToolbox
+{
+ public interface IGraphModel
+ {
+ public Task TryEmplaceAsync(string absolute_filename, string name);
+ }
+
+ public class IncludeGraph : IGraphModel
+ {
+ public class Item
+ {
+ public Item(string absolute, string formatted, Include[] includes)
+ {
+ AbsoluteFilename = absolute;
+ FormattedName = formatted;
+ Includes = includes;
+ }
+
+ public string AbsoluteFilename { get; private set; }
+ public string FormattedName { get; set; }
+
+ public Include[] Includes { get; private set; }
+ }
+
+ public async Task- InitializeAsync(string filename)
+ {
+ graphItems = new();
+ return await TryEmplaceAsync(filename, Path.GetFileNameWithoutExtension(filename));
+ }
+
+
+ public async Task
- InitializeAsync(DocumentView document)
+ {
+ graphItems = new();
+ return await TryEmplaceAsync(document);
+ }
+ public async Task
- TryEmplaceAsync(DocumentView document)
+ {
+ var absolute_filename = document.FilePath;
+ bool is_new = !graphItems.TryGetValue(absolute_filename, out Item outItem);
+ if (!is_new) return outItem;
+
+ outItem = await ParseFileAsync(document);
+ graphItems.Add(absolute_filename, outItem);
+ return outItem;
+ }
+ private async Task
- ParseFileAsync(DocumentView document)
+ {
+ var incs = await VCUtil.GetIncludeDirsAsync();
+ var path = document.FilePath;
+ var doc_folder = Path.GetDirectoryName(path);
+
+ var inc_arr = new string[] {
+ Microsoft.VisualStudio.PlatformUI.PathUtil.Normalize(doc_folder)
+ + Path.DirectorySeparatorChar }
+ .Concat(incs).ToArray();
+
+ var text = document.TextBuffer.CurrentSnapshot.GetText();
+ var includes = Parser.ParseInclues(text.AsSpan());
+
+ return new Item(path, Path.GetFileName(path),
+ includes.Select(s => new Include(s, inc_arr)).ToArray());
+ }
+
+
+ public async Task
- TryEmplaceAsync(string absolute_filename, string name)
+ {
+ bool is_new = !graphItems.TryGetValue(absolute_filename, out Item outItem);
+ if (!is_new) return outItem;
+
+ outItem = await ParseFileAsync(absolute_filename, name);
+ graphItems.Add(absolute_filename, outItem);
+ return outItem;
+ }
+
+ private async Task
- ParseFileAsync(string absolute_filename, string name)
+ {
+ var doc = await VS.Documents.GetDocumentViewAsync(absolute_filename);
+ doc ??= await VS.Documents.OpenViaProjectAsync(absolute_filename);
+
+ var incs = await VCUtil.GetIncludeDirsAsync();
+ var doc_folder = Path.GetDirectoryName(absolute_filename);
+
+ var inc_arr = new string[] {
+ Microsoft.VisualStudio.PlatformUI.PathUtil.Normalize(doc_folder)
+ + Path.DirectorySeparatorChar }
+ .Concat(incs).ToArray();
+
+ var text = doc.TextBuffer.CurrentSnapshot.GetText();
+ return new Item(absolute_filename, name, Parser.ParseInclues(text.AsSpan()).Select(s=>new Include(s, inc_arr)).ToArray());
+ }
+
+ private Dictionary graphItems = new();
+ }
+}
diff --git a/IncludeToolbox/Graph/CompilationBasedGraphParser.cs b/IncludeToolboxShared/GraphEx/CompilationBasedGraphParser.cs
similarity index 100%
rename from IncludeToolbox/Graph/CompilationBasedGraphParser.cs
rename to IncludeToolboxShared/GraphEx/CompilationBasedGraphParser.cs
diff --git a/IncludeToolbox/Graph/CustomGraphParser.cs b/IncludeToolboxShared/GraphEx/CustomGraphParser.cs
similarity index 100%
rename from IncludeToolbox/Graph/CustomGraphParser.cs
rename to IncludeToolboxShared/GraphEx/CustomGraphParser.cs
diff --git a/IncludeToolbox/Graph/DGMLGraph.cs b/IncludeToolboxShared/GraphEx/DGMLGraph.cs
similarity index 100%
rename from IncludeToolbox/Graph/DGMLGraph.cs
rename to IncludeToolboxShared/GraphEx/DGMLGraph.cs
diff --git a/IncludeToolbox/Graph/IncludeGraph.cs b/IncludeToolboxShared/GraphEx/IncludeGraph.cs
similarity index 100%
rename from IncludeToolbox/Graph/IncludeGraph.cs
rename to IncludeToolboxShared/GraphEx/IncludeGraph.cs
diff --git a/IncludeToolbox/Graph/IncludeGraphToDGML.cs b/IncludeToolboxShared/GraphEx/IncludeGraphToDGML.cs
similarity index 100%
rename from IncludeToolbox/Graph/IncludeGraphToDGML.cs
rename to IncludeToolboxShared/GraphEx/IncludeGraphToDGML.cs
diff --git a/IncludeToolbox/GraphWindow/Commands/RefreshIncludeGraph.cs b/IncludeToolboxShared/GraphWindow/Commands/RefreshIncludeGraph.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/Commands/RefreshIncludeGraph.cs
rename to IncludeToolboxShared/GraphWindow/Commands/RefreshIncludeGraph.cs
diff --git a/IncludeToolbox/GraphWindow/Commands/RefreshModeComboBox.cs b/IncludeToolboxShared/GraphWindow/Commands/RefreshModeComboBox.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/Commands/RefreshModeComboBox.cs
rename to IncludeToolboxShared/GraphWindow/Commands/RefreshModeComboBox.cs
diff --git a/IncludeToolbox/GraphWindow/Commands/RefreshModeComboBoxOptions.cs b/IncludeToolboxShared/GraphWindow/Commands/RefreshModeComboBoxOptions.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/Commands/RefreshModeComboBoxOptions.cs
rename to IncludeToolboxShared/GraphWindow/Commands/RefreshModeComboBoxOptions.cs
diff --git a/IncludeToolbox/GraphWindow/Commands/SaveDGML.cs b/IncludeToolboxShared/GraphWindow/Commands/SaveDGML.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/Commands/SaveDGML.cs
rename to IncludeToolboxShared/GraphWindow/Commands/SaveDGML.cs
diff --git a/IncludeToolbox/GraphWindow/PropertyChangedBase.cs b/IncludeToolboxShared/GraphWindow/PropertyChangedBase.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/PropertyChangedBase.cs
rename to IncludeToolboxShared/GraphWindow/PropertyChangedBase.cs
diff --git a/IncludeToolbox/GraphWindow/View/IncludeGraphControl.xaml b/IncludeToolboxShared/GraphWindow/View/IncludeGraphControl.xaml
similarity index 100%
rename from IncludeToolbox/GraphWindow/View/IncludeGraphControl.xaml
rename to IncludeToolboxShared/GraphWindow/View/IncludeGraphControl.xaml
diff --git a/IncludeToolbox/GraphWindow/View/IncludeGraphControl.xaml.cs b/IncludeToolboxShared/GraphWindow/View/IncludeGraphControl.xaml.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/View/IncludeGraphControl.xaml.cs
rename to IncludeToolboxShared/GraphWindow/View/IncludeGraphControl.xaml.cs
diff --git a/IncludeToolbox/GraphWindow/View/IncludeGraphToolWindow.cs b/IncludeToolboxShared/GraphWindow/View/IncludeGraphToolWindow.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/View/IncludeGraphToolWindow.cs
rename to IncludeToolboxShared/GraphWindow/View/IncludeGraphToolWindow.cs
diff --git a/IncludeToolbox/GraphWindow/View/ToolWindowStyle.xaml b/IncludeToolboxShared/GraphWindow/View/ToolWindowStyle.xaml
similarity index 100%
rename from IncludeToolbox/GraphWindow/View/ToolWindowStyle.xaml
rename to IncludeToolboxShared/GraphWindow/View/ToolWindowStyle.xaml
diff --git a/IncludeToolbox/GraphWindow/ViewModel/FolderIncludeTreeItem.cs b/IncludeToolboxShared/GraphWindow/ViewModel/FolderIncludeTreeItem.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/ViewModel/FolderIncludeTreeItem.cs
rename to IncludeToolboxShared/GraphWindow/ViewModel/FolderIncludeTreeItem.cs
diff --git a/IncludeToolbox/GraphWindow/ViewModel/HierarchyIncludeTreeViewItem.cs b/IncludeToolboxShared/GraphWindow/ViewModel/HierarchyIncludeTreeViewItem.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/ViewModel/HierarchyIncludeTreeViewItem.cs
rename to IncludeToolboxShared/GraphWindow/ViewModel/HierarchyIncludeTreeViewItem.cs
diff --git a/IncludeToolbox/GraphWindow/ViewModel/IncludeGraphViewModel.cs b/IncludeToolboxShared/GraphWindow/ViewModel/IncludeGraphViewModel.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/ViewModel/IncludeGraphViewModel.cs
rename to IncludeToolboxShared/GraphWindow/ViewModel/IncludeGraphViewModel.cs
diff --git a/IncludeToolbox/GraphWindow/ViewModel/IncludeTreeViewItem.cs b/IncludeToolboxShared/GraphWindow/ViewModel/IncludeTreeViewItem.cs
similarity index 100%
rename from IncludeToolbox/GraphWindow/ViewModel/IncludeTreeViewItem.cs
rename to IncludeToolboxShared/GraphWindow/ViewModel/IncludeTreeViewItem.cs
diff --git a/IncludeToolboxShared/IWYU/IWYU.cs b/IncludeToolboxShared/IWYU/IWYU.cs
new file mode 100644
index 0000000..7b04f6c
--- /dev/null
+++ b/IncludeToolboxShared/IWYU/IWYU.cs
@@ -0,0 +1,160 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Shell;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.IncludeWhatYouUse
+{
+ internal class IWYU
+ {
+ readonly Process process = new();
+ string output = "";
+ string command_line = "";
+ readonly string support_path = "";
+ readonly string support_cpp_path = "";
+
+ public string ProcOutput { get => output; }
+
+ public IWYU()
+ {
+ process.EnableRaisingEvents = true;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+
+ process.OutputDataReceived += (s, args) =>
+ {
+ output += args.Data + "\n";
+ };
+ process.ErrorDataReceived += (s, args) =>
+ {
+ output += args.Data + "\n";
+ };
+
+ // initialize temp files (can be multithreaded, hence instance based)
+ support_cpp_path = Path.ChangeExtension(Path.GetTempFileName(), ".cpp");
+ support_path = Path.GetTempFileName();
+ }
+
+ public void BuildCommandLine(IWYUOptions settings)
+ {
+ process.StartInfo.FileName = settings.Executable;
+
+ List args = new()
+ {
+ string.Format("--verbose={0}", settings.Verbosity)
+ };
+
+ if (settings.Precompiled || settings.IgnoreHeader)
+ args.Add("--pch_in_code");
+ if (settings.Transitives)
+ args.Add("--transitive_includes_only");
+ if (settings.NoDefault)
+ args.Add("--no_default_mappings");
+ if (settings.UseProvided)
+ {
+ var path = IWYUDownload.GetDefaultMappingChecked();
+ if (!string.IsNullOrEmpty(path))
+ args.Add(string.Format("--mapping_file=\"{0}\"", path));
+ }
+ if (settings.MappingFile != "")
+ args.Add(string.Format("--mapping_file=\"{0}\"", settings.MappingFile));
+ args.Add("--max_line_length=256"); // output line for commentaries
+
+ command_line =
+ string.Join(" ", args.Select(x => " -Xiwyu " + x));
+
+ if (!settings.Warnings)
+ command_line += " -w";
+
+ command_line += " -Wno-invalid-token-paste -fms-compatibility -fms-extensions -fdelayed-template-parsing";
+ if (settings.ClangOptions != null && settings.ClangOptions?.Count() != 0)
+ command_line += " " + string.Join(" ", settings.ClangOptions);
+ if (settings.Options != null && settings.Options.Count() != 0)
+ command_line += " " + string.Join(" ", settings.Options.Select(x => " -Xiwyu " + x));
+ }
+
+ static public void MoveHeader(DocumentView view)
+ {
+ var buf = view.TextBuffer;
+ var str = buf.CurrentSnapshot.GetText();
+ var span = Utils.GetIncludeSpan(str);
+
+ Regex regex = new($"#include\\s[<\"]([\\w\\\\\\/\\.]+{Path.GetFileNameWithoutExtension(view.FilePath)}.h(?:pp|xx)?)[>\"]");
+ var match = regex.Match(str, span.Start, span.Length);
+ if (!match.Success) return;
+ var edit = buf.CreateEdit();
+ _ = edit.Delete(new(match.Index, match.Length));
+
+ edit.Insert(span.Start, match.Value + Utils.GetLineBreak(view.TextView));
+ edit.Apply();
+ }
+
+
+
+ public async Task StartAsync(string file, Project proj, bool rebuild)
+ {
+ var cmd = await VCUtil.GetCommandLineAsync(rebuild, proj);
+ if (string.IsNullOrEmpty(cmd))
+ {
+ Output.WriteLineAsync("Failed to gather command line for c++ project").FireAndForget();
+ return false;
+ }
+ return StartImpl(file, cmd);
+ }
+ public async Task StartAsync(string file, bool rebuild)
+ {
+ var cmd = await VCUtil.GetCommandLineAsync(rebuild);
+ if (string.IsNullOrEmpty(cmd))
+ {
+ Output.WriteLineAsync("Failed to gather command line for c++ project").FireAndForget();
+ return false;
+ }
+ return StartImpl(file, cmd);
+ }
+
+ bool StartImpl(string file, string cmd)
+ {
+ output = "";
+ File.WriteAllText(support_path, cmd);
+
+ var ext = Path.GetExtension(file);
+ if (ext == ".h" || ext == ".hpp" || ext == ".hxx")
+ {
+ File.WriteAllText(support_cpp_path, "#include \"" + file + "\"");
+ file = " -Xiwyu --check_also=" + "\"" + file + "\"";
+ file += " \"" + support_cpp_path.Replace("\\", "\\\\") + "\"";
+ }
+ else
+ {
+ file = "\"" + file + "\"";
+ }
+
+ process.StartInfo.Arguments = $"{command_line} \"@{support_path}\" {file}";
+
+ Output.WriteLineAsync(string.Format("Running command '{0}' with following arguments:\n{1}\n\n", process.StartInfo.FileName, process.StartInfo.Arguments)).FireAndForget();
+
+ process.Start();
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ process.WaitForExit();
+ process.CancelOutputRead();
+ process.CancelErrorRead();
+
+ Output.WriteLineAsync(output).FireAndForget();
+ return true;
+ }
+ public async Task CancelAsync()
+ {
+ await Task.Run(delegate { process.Kill(); });
+ Output.WriteLineAsync($"IWYU Process {process.ProcessName} was cancelled.").FireAndForget();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/IWYU/IWYUApply.cs b/IncludeToolboxShared/IWYU/IWYUApply.cs
new file mode 100644
index 0000000..5c4577e
--- /dev/null
+++ b/IncludeToolboxShared/IWYU/IWYUApply.cs
@@ -0,0 +1,147 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Text;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace IncludeToolbox
+{
+ internal static class IWYUApply
+ {
+ static readonly string match = "The full include-list for ";
+
+ public static void ClearNamespaces(DocumentView doc)
+ {
+ using var edit = doc.TextBuffer.CreateEdit();
+ var text = doc.TextBuffer.CurrentSnapshot.GetText();
+ var rem = Parser.ParseEmptyNamespaces(text);
+ foreach (var ns in rem)
+ edit.Delete(ns);
+ edit.Apply();
+ }
+
+ public static async Task FormatAsync(DocumentView doc)
+ {
+ var include_directories = await VCUtil.GetIncludeDirsAsync();
+ var settings = await FormatOptions.GetLiveInstanceAsync();
+ var text = doc.TextBuffer.CurrentSnapshot.GetText();
+ var span = Utils.GetIncludeSpan(text);
+ var slice = text.Substring(span.Start, span.Length);
+
+ var result = Formatter.IncludeFormatter.FormatIncludes(
+ slice.AsSpan(),
+ doc.FilePath,
+ include_directories, settings
+ );
+
+ Formatter.IncludeFormatter.ApplyChanges(result, doc, slice, span.Start);
+ }
+
+ public static void ApplyCheap(ITextEdit edit, string result, bool commentary, string lb)
+ {
+ if (!commentary)
+ {
+ result = string.Join(lb, result.Split('\n')
+ .Select(s =>
+ {
+ var str = s.Trim();
+ var idx = str.IndexOf("//");
+ if (idx >= 0)
+ return str.Substring(0, idx).Trim();
+ return str;
+ }).ToArray());
+ }
+ var span = Utils.GetIncludeSpan(edit.Snapshot.GetText());
+ edit.Replace(span, result);
+ }
+
+ public static async Task ApplyAsync(IWYUOptions settings, string output)
+ {
+ if (output == "") return;
+
+ int pos = output.IndexOf(match);
+ if (pos == -1) return;
+
+ pos += match.Length;
+ string part = output.Substring(pos);
+
+ int endp = part.IndexOf("---");
+ string path = part.Substring(0, part.IndexOf(':', 3));
+ var doc = await VS.Documents.OpenAsync(path);
+ var lb = Utils.GetLineBreak(doc.TextView);
+
+ using var edit = doc.TextBuffer.CreateEdit();
+
+ int endl = part.IndexOf("\n");
+ string result = part.Substring(endl, endp - endl);
+ ApplyCheap(edit,
+ result,
+ settings.Comms != Comment.No, lb);
+
+ edit.Apply();
+ }
+
+ public static async Task ApplyPreciseAsync(IWYUOptions settings, Parser.Output parsed, string output, Standard std)
+ {
+ if (output == "") return;
+
+ int pos = output.IndexOf(match);
+ if (pos == -1) return;
+
+ var retasks = Parser.Parse(output.AsSpan().Slice(0, pos), true, true);
+ int sep_index = output.IndexOf(" should remove these lines:"); //find middle ground
+
+ pos += match.Length;
+ string part = output.Substring(pos);
+
+ string path = part.Substring(0, part.IndexOf(':', 3));
+ var doc = await VS.Documents.OpenAsync(path);
+ using var edit = doc.TextBuffer.CreateEdit();
+ var lb = Utils.GetLineBreak(doc.TextView);
+
+ var add_f = retasks.Declarations.Where(s => s.span.Start < sep_index);
+ var rem_f = retasks.Declarations.Where(s => s.span.Start > sep_index);
+
+ var add_i = retasks.Includes.Where(s => s.span.Start < sep_index);
+ var rem_i = retasks.Includes.Where(s => s.span.Start > sep_index);
+
+ foreach (var task in rem_i)
+ {
+ var found = parsed.Includes.FindLast(s => s == task);
+ edit.Delete(found.span);
+ parsed.Includes.Remove(found);
+ }
+
+ DeclNode tree = new(Lexer.TType.Namespace)
+ {
+ LineBreak = lb
+ };
+ if (settings.MoveDecls)
+ {
+ tree.AddChildren(parsed.Declarations.Where(s => !rem_f.Contains(s)));
+ foreach (var task in parsed.Declarations)
+ edit.Delete(task.span);
+ }
+ else
+ {
+ foreach (var task in rem_f)
+ {
+ var found = parsed.Declarations.FindLast(s => s == task);
+ edit.Delete(found.span);
+ parsed.Declarations.Remove(found);
+ }
+ }
+ tree.AddChildren(add_f);
+
+ string addition = "";
+
+ foreach (var item in add_i.Select(s => s.Project(output)))
+ addition+=lb+item;
+
+ addition+= lb + lb + tree.ToString(std >= Standard.cpp17);
+
+ edit.Insert(parsed.InsertionPoint, addition);
+ edit.Apply();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/IWYU/IWYUDownload.cs b/IncludeToolboxShared/IWYU/IWYUDownload.cs
new file mode 100644
index 0000000..b220f0a
--- /dev/null
+++ b/IncludeToolboxShared/IWYU/IWYUDownload.cs
@@ -0,0 +1,249 @@
+using Community.VisualStudio.Toolkit;
+using IncludeToolbox.Commands;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Shell;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.IncludeWhatYouUse
+{
+ ///
+ /// Functions for downloading and versioning of the iwyu installation.
+ ///
+ public class IWYUDownload
+ {
+ public static readonly string DisplayRepositorURL = @"https://github.com/Agrael1/BuildIWYU";
+ private static readonly string DownloadRepositorURL = @"https://github.com/Agrael1/BuildIWYU/archive/main.zip";
+ private static readonly string LatestCommitQuery = @"https://api.github.com/repos/Agrael1/BuildIWYU/git/refs/heads/main";
+
+ public static string GetDefaultFolder()
+ {
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "iwyu");
+ }
+ public static string GetDefaultExecutablePath()
+ {
+ return Path.Combine(GetDefaultFolder(), "include-what-you-use.exe");
+ }
+ public static string GetDefaultMappingPath()
+ {
+ return Path.Combine(GetDefaultFolder(), "msvc.imp");
+ }
+ public static string GetDefaultMappingChecked()
+ {
+ var path = GetDefaultMappingPath();
+ return File.Exists(path) ? path : "";
+ }
+
+
+ static public string GetVersionFilePath()
+ {
+ return Path.Combine(GetDefaultFolder(), "version");
+ }
+
+ public delegate void OnChangeDelegate(string Section, string Status, float percent);
+
+
+
+ public event OnChangeDelegate OnProgress;
+ WebClient client;
+
+ protected void OnProgressEvent(string Section, string Status, float percent)
+ {
+ OnProgress?.Invoke(Section, Status, percent);
+ }
+
+
+
+ public static async Task DownloadAsync(IVsThreadedWaitDialogFactory dialogFactory, IWYUOptions settings)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ if (!await VS.MessageBox.ShowConfirmAsync($"Can't locate include-what-you-use. Do you want to download it from '{IWYUDownload.DisplayRepositorURL}'?"))
+ return false;
+
+ var downloader = new IWYUDownload();
+
+ dialogFactory.CreateInstance(out IVsThreadedWaitDialog2 xdialog);
+ IVsThreadedWaitDialog4 dialog = xdialog as IVsThreadedWaitDialog4;
+
+ downloader.OnProgress += (string section, string status, float percentage) =>
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+
+ dialog.UpdateProgress(
+ szUpdatedWaitMessage: section,
+ szProgressText: status,
+ szStatusBarText: $"Downloading include-what-you-use - {section} - {status}",
+ iCurrentStep: (int)(percentage * 100),
+ iTotalSteps: 100,
+ fDisableCancel: false,
+ pfCanceled: out bool canceled);
+ };
+
+ dialog.StartWaitDialogWithCallback(
+ szWaitCaption: "Include Toolbox - Downloading include-what-you-use",
+ szWaitMessage: "", // comes in later.
+ szProgressText: null,
+ varStatusBmpAnim: null,
+ szStatusBarText: "Downloading include-what-you-use",
+ fIsCancelable: true,
+ iDelayToShowDialog: 0,
+ fShowProgress: true,
+ iTotalSteps: 100,
+ iCurrentStep: 0,
+ new CancelCallback(() => { downloader.Cancel(); }));
+
+ await downloader.DownloadIWYUAsync();
+
+ if (dialog.EndWaitDialog()) return false;
+ settings.Executable = GetDefaultExecutablePath();
+
+ settings.Downloaded();
+ await settings.SaveAsync();
+
+ return true;
+ }
+
+ private static async Task GetVersionOnlineAsync()
+ {
+ using (var httpClient = new HttpClient())
+ {
+ // User agent is always required for github api.
+ // https://developer.github.com/v3/#user-agent-required
+ httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("IncludeToolbox");
+
+ string latestCommitResponse;
+ try
+ {
+ latestCommitResponse = await httpClient.GetStringAsync(LatestCommitQuery);
+ }
+ catch (HttpRequestException e)
+ {
+ _ = Output.WriteLineAsync($"Failed to query IWYU version from {DownloadRepositorURL}: {e}");
+ return "";
+ }
+
+ // Poor man's json parsing in lack of a json parser.
+ var shaRegex = new Regex(@"\""sha\""\w*:\w*\""([a-z0-9]+)\""");
+ return shaRegex.Match(latestCommitResponse).Groups[1].Value;
+ }
+ }
+
+ private static string GetCurrentVersionHarddrive()
+ {
+ // Read current version.
+ try
+ {
+ return File.ReadAllText(GetVersionFilePath());
+ }
+ catch
+ {
+ return "";
+ }
+ }
+
+ public static async Task IsNewerVersionAvailableOnlineAsync()
+ {
+ string currentVersion = GetCurrentVersionHarddrive();
+ string onlineVersion = await GetVersionOnlineAsync();
+ return currentVersion != onlineVersion;
+ }
+
+
+
+
+ async Task DownloadAsync(string targetZipFile)
+ {
+ using (client = new WebClient())
+ {
+ client.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) =>
+ {
+ int kbTodo = (int)System.Math.Ceiling((double)e.TotalBytesToReceive / 1024);
+ int kbDownloaded = (int)System.Math.Ceiling((double)e.BytesReceived / 1024);
+ OnProgressEvent("Downloading", kbTodo > 0 ? $"{kbTodo} / {kbDownloaded} kB" : $"{kbDownloaded} kB", e.ProgressPercentage * 0.01f);
+ };
+
+ await client.DownloadFileTaskAsync(DownloadRepositorURL, targetZipFile);
+ client = null;
+ }
+ }
+
+ ///
+ /// Downloads iwyu from default download repository.
+ ///
+ public async Task DownloadIWYUAsync()
+ {
+ string targetDirectory = GetDefaultFolder();
+ Directory.CreateDirectory(targetDirectory);
+ DirectoryInfo di = new DirectoryInfo(targetDirectory);
+
+ foreach (FileInfo file in di.EnumerateFiles())
+ {
+ file.Delete();
+ }
+ foreach (DirectoryInfo dir in di.EnumerateDirectories())
+ {
+ dir.Delete(true);
+ }
+
+ string targetZipFile = Path.Combine(targetDirectory, "download.zip");
+
+ // Download.
+ OnProgressEvent("Connecting...", "", -1.0f);
+
+ try
+ {
+ await DownloadAsync(targetZipFile);
+ }
+ catch (Exception e)
+ {
+ _ = Output.WriteLineAsync("Failed to download IWYU with error:" + e.Message);
+ return;
+ }
+
+ // Unpacking. Looks like there is no async api, so we're just moving this to a task.
+ OnProgressEvent("Unpacking...", "", -1.0f);
+
+ try
+ {
+ using (ZipArchive archive = ZipFile.OpenRead(targetZipFile))
+ {
+ foreach (ZipArchiveEntry entry in archive.Entries.Where(e =>
+ {
+ if (e.Name == "LICENSE") return true;
+ string a = Path.GetExtension(e.FullName);
+ return a == ".exe" || a == ".txt" || a == ".imp" || a == ".md";
+ }))
+ {
+ entry.ExtractToFile(Path.Combine(GetDefaultFolder(), entry.Name));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _ = Output.WriteLineAsync("Failed to unpack IWYU with error:" + e.Message);
+ File.Delete(targetZipFile);
+ return;
+ }
+
+ // Save version.
+ OnProgressEvent("Saving Version", "", -1.0f);
+ string version = await GetVersionOnlineAsync();
+ File.WriteAllText(GetVersionFilePath(), version);
+
+ OnProgressEvent("Clean Up", "", -1.0f);
+ File.Delete(targetZipFile);
+ }
+ public void Cancel()
+ {
+ client?.CancelAsync();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Implementation/IncludeFormatter.cs b/IncludeToolboxShared/Implementation/IncludeFormatter.cs
new file mode 100644
index 0000000..458fc57
--- /dev/null
+++ b/IncludeToolboxShared/Implementation/IncludeFormatter.cs
@@ -0,0 +1,247 @@
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.Text;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace IncludeToolbox.Formatter
+{
+ public static class IncludeFormatter
+ {
+ public static string FormatPath(string absoluteIncludeFilename, PathMode pathformat, IEnumerable includeDirectories)
+ {
+ // todo: Treat std library files special?
+
+ if (absoluteIncludeFilename == null) return null;
+
+ int bestLength = int.MaxValue;
+ string bestCandidate = null;
+
+ foreach (string includeDirectory in includeDirectories)
+ {
+ string proposal = Utils.MakeRelative(includeDirectory, absoluteIncludeFilename);
+
+ if (proposal.Length < bestLength)
+ {
+ if (pathformat == PathMode.Shortest ||
+ (proposal.IndexOf("../") < 0 && proposal.IndexOf("..\\") < 0))
+ {
+ bestCandidate = proposal;
+ bestLength = proposal.Length;
+ }
+ }
+ }
+ return bestCandidate;
+ }
+ private static void FormatPaths(IncludeLine[] lines, PathMode pathformat, IEnumerable includeDirectories)
+ {
+ for (int i = 0; i < lines.Length; i++)
+ {
+ string absoluteIncludeDir = lines[i].Resolve(includeDirectories);
+ if (string.IsNullOrEmpty(absoluteIncludeDir)) continue;
+ var formatted = FormatPath(absoluteIncludeDir, pathformat, includeDirectories);
+ if (string.IsNullOrEmpty(formatted)) continue;
+ lines[i].SetFile(formatted);
+ }
+ }
+
+ private static void FormatDelimiters(IncludeLine[] lines, DelimiterMode delimiterMode)
+ {
+ switch (delimiterMode)
+ {
+ case DelimiterMode.AngleBrackets:
+ for (int i = 0; i < lines.Length; i++)
+ lines[i].SetDelimiter(DelimiterMode.AngleBrackets);
+ break;
+ case DelimiterMode.Quotes:
+ for (int i = 0; i < lines.Length; i++)
+ lines[i].SetDelimiter(DelimiterMode.Quotes);
+ break;
+ }
+ }
+ private static void FormatSlashes(IncludeLine[] lines, SlashMode slashMode)
+ {
+ switch (slashMode)
+ {
+ case SlashMode.ForwardSlash:
+ for (int i = 0; i < lines.Length; i++)
+ lines[i].ToForward();
+ break;
+ case SlashMode.BackSlash:
+ for (int i = 0; i < lines.Length; i++)
+ lines[i].ToBackward();
+ break;
+ }
+ }
+
+ private static IncludeLine[] SortIncludes(IncludeLine[] lines, FormatOptions settings, string documentName)
+ {
+ string[] precedenceRegexes = RegexUtils.FixupRegexes(settings.PrecedenceRegexes, documentName);
+ List outSortedList = new(lines.Length);
+
+ while (lines.Length != 0)
+ {
+ int line_n = lines.First().line;
+
+ var pack = lines.TakeWhile(s =>
+ {
+ bool a = s.line - line_n <= 1;
+ line_n = s.line;
+ return a;
+ });
+ var e = pack.ToArray();
+ if (e.Count() > 1)
+ outSortedList.AddRange(SortIncludeBatch(settings, precedenceRegexes, e));
+ else
+ outSortedList.AddRange(e);
+ lines = lines.Skip(e.Count()).ToArray();
+ }
+
+ return outSortedList.ToArray();
+ }
+
+ private static void RemoveDuplicates(IncludeLine[] includes)
+ {
+ HashSet uniqueIncludes = new();
+ uniqueIncludes.UnionWith(includes.Where(s => s.Keep).Select(s => s.FullFile));
+
+ for (int i = 0; i < includes.Length; i++)
+ {
+ ref var r = ref includes[i];
+ if (!r.Keep && !uniqueIncludes.Add(r.FullFile))
+ r.SetFullContent("");
+ }
+ }
+ private static IncludeLine[] SortIncludeBatch(FormatOptions settings,
+ string[] precedenceRegexes,
+ IncludeLine[] includeBatch)
+ {
+ // Fetch settings.
+ TypeSorting typeSorting = settings.SortByType;
+ bool regexIncludeDelimiter = settings.RegexIncludeDelimiter;
+ bool blankAfterRegexGroupMatch = settings.BlankAfterRegexGroupMatch;
+
+ // Select only valid include lines and sort them. They'll stay in this relative sorted
+ // order when rearranged by regex precedence groups.
+ var includeLines = includeBatch
+ .OrderBy(x => { return x.Content; }).ToArray();
+
+ if (settings.RemoveDuplicates)
+ {
+ // store kept headers first, to remove all the duplicates
+ HashSet uniqueIncludes = new();
+ uniqueIncludes.UnionWith(includeLines.Where(s => s.Keep).Select(s => s.FullFile));
+
+ for (int i = 0; i < includeLines.Length; i++)
+ {
+ ref var r = ref includeLines[i];
+ if (!r.Keep && !uniqueIncludes.Add(r.FullFile))
+ r.SetFullContent("");
+ }
+ }
+
+ // Group the includes by the index of the precedence regex they match, or
+ // precedenceRegexes.Length for no match, and sort the groups by index.
+ var includeGroups = includeLines
+ .GroupBy(x =>
+ {
+ if (!x.Valid) return precedenceRegexes.Length;
+ var includeContent = regexIncludeDelimiter ? x.FullFile : x.Content;
+ for (int precedence = 0; precedence < precedenceRegexes.Count(); ++precedence)
+ {
+ if (Regex.Match(includeContent, precedenceRegexes[precedence]).Success)
+ return precedence;
+ }
+ return precedenceRegexes.Length;
+ }, x => x)
+ .OrderBy(x => x.Key);
+
+ // Optional newlines between regex match groups
+ var groupStarts = new HashSet();
+ if (blankAfterRegexGroupMatch && precedenceRegexes.Length > 0 && includeLines.Count() > 1)
+ {
+ // Set flag to prepend a newline to each group's first include
+ foreach (var grouping in includeGroups)
+ groupStarts.Add(grouping.First());
+ }
+
+ // Flatten the groups
+ var sortedIncludes = includeGroups.SelectMany(x => x.Select(y => y));
+
+ // Sort by angle or quoted delimiters if either of those options were selected
+ if (typeSorting == TypeSorting.AngleBracketsFirst)
+ sortedIncludes = sortedIncludes.OrderBy(x => x.delimiter == DelimiterMode.AngleBrackets ? 0 : 1);
+ else if (typeSorting == TypeSorting.QuotedFirst)
+ sortedIncludes = sortedIncludes.OrderBy(x => x.delimiter == DelimiterMode.Quotes ? 0 : 1);
+
+ return sortedIncludes.ToArray();
+ }
+
+
+
+ public static IncludeLine[] FormatIncludes(ReadOnlySpan text, string documentPath, IEnumerable includeDirectories, FormatOptions settings)
+ {
+ string documentDir = Path.GetDirectoryName(documentPath);
+ string documentName = Path.GetFileNameWithoutExtension(documentPath);
+
+ includeDirectories = new string[] { Microsoft.VisualStudio.PlatformUI.PathUtil.Normalize(documentDir) + Path.DirectorySeparatorChar }.Concat(includeDirectories);
+
+ var lines = Parser.ParseInclues(text, settings.IgnoreIfdefs);
+
+ // Format.
+ IEnumerable formatingDirs = includeDirectories;
+ if (settings.IgnoreFileRelative)
+ formatingDirs = formatingDirs.Skip(1);
+
+ if (settings.RemoveDuplicates)
+ RemoveDuplicates(lines);
+ if (settings.PathFormat != PathMode.Unchanged)
+ FormatPaths(lines, settings.PathFormat, formatingDirs);
+
+ FormatDelimiters(lines, settings.DelimiterFormatting);
+ FormatSlashes(lines, settings.SlashFormatting);
+
+
+ // Sorting. Ignores non-include lines.
+ return SortIncludes(lines, settings, documentName);
+ }
+
+
+ private static IEnumerable> RemoveWhitespaces(this IEnumerable> e, string text)
+ {
+ int start = 0;
+ foreach (var a in e)
+ {
+ var x = a;
+ if (start != 0 && a.Key.Start - start > 0)
+ {
+ ReadOnlySpan subspan = text.AsSpan(start, a.Key.Start - start);
+ if (subspan.IsWhiteSpace())
+ x = new(new(start, a.Key.Start - start + a.Key.Length), a.Value);
+ }
+ start = x.Key.End;
+ yield return x;
+ }
+ }
+ public static void ApplyChanges(IncludeLine[] includes, DocumentView doc, string text, int relative_pos, bool remove_empty = true)
+ {
+ var lb = Utils.GetLineBreak(doc.TextView);
+ var enumerator = includes
+ .OrderBy(s => s.line)
+ .Zip(includes,
+ (a, b) => { return new KeyValuePair(a.span, b.Project(text)); });
+
+
+ using var edit = doc.TextBuffer.CreateEdit();
+
+ if (remove_empty) enumerator = enumerator.RemoveWhitespaces(text);
+ foreach (var line in enumerator)
+ edit.Replace(line.Key.Move(relative_pos), line.Value);
+
+ edit.Apply();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/Implementation/TrialAndErrorRemoval.cs b/IncludeToolboxShared/Implementation/TrialAndErrorRemoval.cs
new file mode 100644
index 0000000..fd02680
--- /dev/null
+++ b/IncludeToolboxShared/Implementation/TrialAndErrorRemoval.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Linq;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using System.IO;
+using System.Threading.Tasks;
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio.VCProjectEngine;
+using Microsoft.VisualStudio.Threading;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox
+{
+ internal sealed class AsyncDispatcher : IDisposable
+ {
+ TaskCompletionSource tcs;
+ private bool disposedValue;
+
+ private void Dispatch(bool a)
+ {
+ tcs.SetResult(a);
+ }
+ public AsyncDispatcher()
+ {
+ VS.Events.BuildEvents.SolutionBuildDone += Dispatch;
+ }
+ ~AsyncDispatcher()
+ {
+ if (!disposedValue)
+ VS.Events.BuildEvents.SolutionBuildDone -= Dispatch;
+ }
+
+ public async Task CompileAsync(VCFileConfiguration config)
+ {
+ tcs = new();
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ config.Compile(true, false);
+ return await tcs.Task;
+ }
+ catch (Exception)
+ {
+ await Task.Delay(100);
+ }
+ }
+ return false;
+ }
+
+ void IDisposable.Dispose()
+ {
+ if (!disposedValue)
+ {
+ VS.Events.BuildEvents.SolutionBuildDone -= Dispatch;
+ disposedValue = true;
+ }
+ GC.SuppressFinalize(this);
+ }
+ }
+ internal sealed class DialogGuard : IDisposable
+ {
+ private bool disposedValue;
+ public IVsThreadedWaitDialog4 Dialog { get; private set; }
+
+ public DialogGuard(IVsThreadedWaitDialog4 dialog)
+ {
+ this.Dialog = dialog;
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: dispose managed state (managed objects)
+ }
+ Dialog.EndWaitDialog();
+ // TODO: free unmanaged resources (unmanaged objects) and override finalizer
+ // TODO: set large fields to null
+ disposedValue = true;
+ }
+ }
+
+ // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
+ ~DialogGuard()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: false);
+ }
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+ }
+ internal sealed class TrialAndErrorRemoval
+ {
+ public static bool WorkInProgress { get; private set; }
+
+ public int Removed { get; private set; } = 0;
+
+
+ //Makes less variable noise
+ struct Descriptor
+ {
+ public IncludeLine[] lines;
+ public ITextBuffer buffer;
+ public string text;
+ public string filename;
+ public VCFileConfiguration config;
+ public int offset;
+ public TrialAndErrorRemovalOptions settings;
+ }
+
+
+ private async Task TestCompileAsync(VCFileConfiguration config)
+ {
+ using AsyncDispatcher dispatcher = new();
+ return await dispatcher.CompileAsync(config);
+ }
+
+
+
+ public async Task StartGenericAsync(VCFileConfiguration config, DocumentView document, TrialAndErrorRemovalOptions settings)
+ {
+ var buffer = document.TextBuffer;
+ var snap = buffer.CurrentSnapshot;
+
+ var text = snap.GetText();
+ var span = Utils.GetIncludeSpan(text);
+ text = text.Substring(span.Start, span.Length);
+
+ var lines = Parser.ParseInclues(text.AsSpan(), settings.IgnoreIfdefs);
+ var iterator = lines.Where(s => !s.Keep);
+
+ // Filter regecies
+ string documentName = Path.GetFileNameWithoutExtension(document.FilePath);
+ string[] ignoreRegexList = RegexUtils.FixupRegexes(settings.IgnoreList, documentName);
+ iterator = iterator.Where(line => !ignoreRegexList.Any(regexPattern =>
+ new System.Text.RegularExpressions.Regex(regexPattern).Match(line.Content).Success));
+
+ iterator = settings.RemovalOrder == IncludeRemovalOrder.TopToBottom ? iterator : iterator.Reverse();
+
+ var array = iterator.ToArray();
+ try
+ {
+ Descriptor desc = new()
+ {
+ lines = array,
+ settings = settings,
+ config = config,
+ text = text,
+ filename = document.FilePath,
+ buffer = buffer,
+ offset = span.Start
+ };
+ await RemoveAsync(desc);
+ _ = Output.WriteLineAsync($"Successfully removed {Removed} headers from {desc.filename}");
+ }
+ catch (Exception e)
+ {
+ return $"Failed to create a dialog: {e.Message}";
+ }
+ return "";
+ }
+
+ private async Task RemoveAsync(Descriptor desc)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ using DialogGuard dialog = new(await StartProgressDialogAsync(desc.filename, desc.lines.Length + 1));
+ int delta = 0;
+ using AsyncDispatcher dispatcher = new();
+ int step = 0;
+
+ foreach (var line in desc.lines)
+ {
+ string waitMessage = $"Removing #includes from '{desc.filename}'";
+ string progressText = $"Trying to remove '{line.Content}' ...";
+ dialog.Dialog.UpdateProgress(
+ szUpdatedWaitMessage: waitMessage,
+ szProgressText: progressText,
+ szStatusBarText: "Running Trial & Error Removal - " + waitMessage + " - " + progressText,
+ iCurrentStep: ++step,
+ iTotalSteps: desc.lines.Length + 1,
+ fDisableCancel: false,
+ pfCanceled: out var canceled);
+
+ if (canceled)
+ {
+ _ = Output.WriteLineAsync("Operation was cancelled.");
+ return;
+ }
+
+ var rs = desc.settings.KeepLineBreaks ?
+ line.ReplaceSpanWithoutNewline(desc.offset) :
+ line.ReplaceSpan(desc.offset);
+
+ desc.buffer.Delete(rs);
+
+ bool b = await dispatcher.CompileAsync(desc.config);
+
+ if (b)
+ {
+ if (desc.settings.RemovalOrder == IncludeRemovalOrder.TopToBottom)
+ {
+ desc.offset -= rs.Length;
+ delta += rs.Length;
+ }
+ await Output.WriteLineAsync($"{line.FullFile} was successfully removed");
+ Removed++;
+ continue;
+ }
+ desc.buffer.Insert(rs.Start, desc.text.Substring(rs.Start - desc.offset + delta, rs.Length));
+ await Output.WriteLineAsync($"Unable to remove {line.FullFile}");
+ }
+ }
+ public async Task StartHeaderAsync(VCFile file, VCFile support, TrialAndErrorRemovalOptions settings)
+ {
+ var document = await VS.Documents.GetDocumentViewAsync(file.FullPath);
+ VCFileConfiguration config = VCUtil.GetVCFileConfig(support);
+ if (config == null) return $"{support.Name} has failed to yield a config.";
+
+ return !await TestCompileAsync(config)
+ ? $"{file.FullPath} failed to compile. Include removal stopped."
+ : await StartGenericAsync(config, document, settings);
+ }
+ //Expected: compilable file .cpp or other
+ public async Task StartAsync(VCFile file, TrialAndErrorRemovalOptions settings)
+ {
+ var document = await VS.Documents.GetDocumentViewAsync(file.FullPath);
+ VCFileConfiguration config = VCUtil.GetVCFileConfig(file);
+ if (config == null) return $"{file.Name} has failed to yield a config.";
+
+ return !await TestCompileAsync(config)
+ ? $"{document.FilePath} failed to compile. Include removal stopped."
+ : await StartGenericAsync(config, document, settings);
+ }
+
+ private async Task StartProgressDialogAsync(string documentName, int steps)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ var dialog_factory = (IVsThreadedWaitDialogFactory)await VS.Services.GetThreadedWaitDialogAsync();
+ var dialog = dialog_factory.CreateInstance();
+
+
+ string waitMessage = $"Parsing '{documentName}' ... ";
+ dialog.StartWaitDialogWithPercentageProgress(
+ szWaitCaption: "Include Toolbox - Running Trial & Error Include Removal",
+ szWaitMessage: waitMessage,
+ szProgressText: null,
+ varStatusBmpAnim: null,
+ szStatusBarText: "Running Trial & Error Removal - " + waitMessage,
+ fIsCancelable: true,
+ iDelayToShowDialog: 0,
+ iTotalSteps: steps,
+ iCurrentStep: 0);
+
+ return dialog;
+ }
+ }
+}
\ No newline at end of file
diff --git a/IncludeToolboxShared/IncludeGraphViewModel.cs b/IncludeToolboxShared/IncludeGraphViewModel.cs
new file mode 100644
index 0000000..f214e78
--- /dev/null
+++ b/IncludeToolboxShared/IncludeGraphViewModel.cs
@@ -0,0 +1,231 @@
+using IncludeToolbox.Formatter;
+using IncludeToolbox.Graph;
+using Microsoft.VisualStudio.PlatformUI;
+using Microsoft.VisualStudio.Shell;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using Task = System.Threading.Tasks.Task;
+
+namespace IncludeToolbox.GraphWindow
+{
+ public class IncludeGraphViewModel : PropertyChangedBase
+ {
+ public HierarchyIncludeTreeViewItem HierarchyIncludeTreeModel { get; set; } = new HierarchyIncludeTreeViewItem(new IncludeGraph.Include(), "");
+ public FolderIncludeTreeViewItem_Root FolderGroupedIncludeTreeModel { get; set; } = new FolderIncludeTreeViewItem_Root(null, null);
+
+ public IncludeGraph Graph { get; private set; }
+
+
+ public enum RefreshMode
+ {
+ DirectParsing,
+ ShowIncludes,
+ }
+
+ public static readonly string[] RefreshModeNames = new string[] { "Direct Parsing", "Compile /showIncludes" };
+
+ public RefreshMode ActiveRefreshMode
+ {
+ get => activeRefreshMode;
+ set
+ {
+ if (activeRefreshMode != value)
+ {
+ activeRefreshMode = value;
+ OnNotifyPropertyChanged();
+ QueueUpdatingRefreshStatus();
+ }
+ }
+ }
+ RefreshMode activeRefreshMode = RefreshMode.DirectParsing;
+
+ public IEnumerable PossibleRefreshModes => Enum.GetValues(typeof(RefreshMode)).Cast();
+
+ public bool CanRefresh
+ {
+ get => canRefresh;
+ private set { canRefresh = value; OnNotifyPropertyChanged(); }
+ }
+ private bool canRefresh = false;
+
+ public string RefreshTooltip
+ {
+ get => refreshTooltip;
+ set { refreshTooltip = value; OnNotifyPropertyChanged(); }
+ }
+ private string refreshTooltip = "";
+
+ public bool RefreshInProgress
+ {
+ get => refreshInProgress;
+ private set
+ {
+ refreshInProgress = value;
+ QueueUpdatingRefreshStatus();
+ OnNotifyPropertyChanged();
+ OnNotifyPropertyChanged(nameof(CanSave));
+ }
+ }
+ private bool refreshInProgress = false;
+
+ public string GraphRootFilename
+ {
+ get => graphRootFilename;
+ private set { graphRootFilename = value; OnNotifyPropertyChanged(); }
+ }
+ private string graphRootFilename = "";
+
+ public int NumIncludes
+ {
+ get => (Graph?.GraphItems.Count ?? 1) - 1;
+ }
+
+ public bool CanSave
+ {
+ get => !refreshInProgress && Graph != null && Graph.GraphItems.Count > 0;
+ }
+
+ // Need to keep these guys alive.
+ private EnvDTE.WindowEvents windowEvents;
+
+ public IncludeGraphViewModel()
+ {
+ ThreadHelper.ThrowIfNotOnUIThread();
+
+ // UI update on dte events.
+ var dte = VSUtils.GetDTE();
+ if (dte != null)
+ {
+ windowEvents = dte.Events.WindowEvents;
+ windowEvents.WindowActivated += (x, y) => QueueUpdatingRefreshStatus();
+ }
+
+ QueueUpdatingRefreshStatus();
+ }
+
+ private void QueueUpdatingRefreshStatus()
+ {
+ _ = Task.Run(async () =>
+ {
+ var currentDocument = VSUtils.GetDTE()?.ActiveDocument;
+
+ if (RefreshInProgress)
+ {
+ CanRefresh = false;
+ RefreshTooltip = "Refresh in progress";
+ }
+ // Limiting to C++ document is a bit harsh though for the general case as we might not have this information depending on the project type.
+ // This is why we just check for "having a document" here for now.
+ else if (currentDocument == null)
+ {
+ CanRefresh = false;
+ RefreshTooltip = "No open document";
+ }
+ else
+ {
+ if (activeRefreshMode == RefreshMode.ShowIncludes)
+ {
+ var canPerformShowIncludeCompilation = await CompilationBasedGraphParser.CanPerformShowIncludeCompilation(currentDocument);
+ CanRefresh = canPerformShowIncludeCompilation.Result;
+ RefreshTooltip = canPerformShowIncludeCompilation.Reason;
+ }
+ else
+ {
+ CanRefresh = true;
+ RefreshTooltip = null;
+ }
+ }
+ });
+ }
+
+ public async Task RefreshIncludeGraph()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ var currentDocument = VSUtils.GetDTE()?.ActiveDocument;
+ GraphRootFilename = currentDocument.Name ?? "";
+ if (currentDocument == null)
+ return;
+
+ var newGraph = new IncludeGraph();
+ RefreshInProgress = true;
+
+ try
+ {
+ switch (activeRefreshMode)
+ {
+ case RefreshMode.ShowIncludes:
+ if (await newGraph.AddIncludesRecursively_ShowIncludesCompilation(currentDocument, OnNewTreeComputed))
+ {
+ ResetIncludeTreeModel(null);
+ }
+ break;
+
+ case RefreshMode.DirectParsing:
+ ResetIncludeTreeModel(null);
+ var settings = (ViewerOptionsPage)IncludeToolboxPackage.Instance.GetDialogPage(typeof(ViewerOptionsPage));
+ var includeDirectories = VSUtils.GetProjectIncludeDirectories(currentDocument.ProjectItem.ContainingProject);
+ var uiThreadDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
+ await Task.Run(
+ async () =>
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ newGraph.AddIncludesRecursively_ManualParsing(currentDocument.FullName, includeDirectories, settings.NoParsePaths);
+ await OnNewTreeComputed(newGraph, currentDocument, true);
+ });
+ break;
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+ catch(Exception e)
+ {
+ Output.Instance.WriteLine("Unexpected error when refreshing Include Graph: {0}", e);
+ await OnNewTreeComputed(newGraph, currentDocument, false);
+ }
+ }
+
+ private void ResetIncludeTreeModel(IncludeGraph.Item root)
+ {
+ HierarchyIncludeTreeModel.Reset(new IncludeGraph.Include() { IncludedFile = root }, "");
+ OnNotifyPropertyChanged(nameof(HierarchyIncludeTreeModel));
+
+ FolderGroupedIncludeTreeModel.Reset(Graph?.GraphItems, root);
+ OnNotifyPropertyChanged(nameof(FolderGroupedIncludeTreeModel));
+
+ OnNotifyPropertyChanged(nameof(CanSave));
+ }
+
+ ///
+ /// Should be called after a tree was computed. Refreshes tree model.
+ ///
+ /// The include tree
+ /// This can be different from the active document at the time the refresh button was clicked.
+ /// Wheather the tree was created successfully
+ private async Task OnNewTreeComputed(IncludeGraph graph, EnvDTE.Document documentTreeComputedFor, bool success)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+
+ RefreshInProgress = false;
+
+ if (success)
+ {
+ this.Graph = graph;
+
+ var includeDirectories = VSUtils.GetProjectIncludeDirectories(documentTreeComputedFor.ProjectItem.ContainingProject);
+ includeDirectories.Insert(0, PathUtil.Normalize(documentTreeComputedFor.Path) + Path.DirectorySeparatorChar);
+
+ foreach (var item in Graph.GraphItems)
+ item.FormattedName = IncludeFormatter.FormatPath(item.AbsoluteFilename, FormatterOptionsPage.PathMode.Shortest_AvoidUpSteps, includeDirectories);
+
+ ResetIncludeTreeModel(Graph.CreateOrGetItem(documentTreeComputedFor.FullName, out _));
+ }
+
+ OnNotifyPropertyChanged(nameof(NumIncludes));
+ }
+ }
+}
diff --git a/IncludeToolboxShared/IncludeToolboxPackage.cs b/IncludeToolboxShared/IncludeToolboxPackage.cs
new file mode 100644
index 0000000..ecb2088
--- /dev/null
+++ b/IncludeToolboxShared/IncludeToolboxPackage.cs
@@ -0,0 +1,42 @@
+using Task = System.Threading.Tasks.Task;
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Community.VisualStudio.Toolkit;
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell;
+
+namespace IncludeToolbox
+{
+ [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
+ [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
+ [ProvideMenuResource("Menus.ctmenu", 1)]
+ [Guid(PackageGuids.IncludeToolboxString)]
+ [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionOpening_string, PackageAutoLoadFlags.BackgroundLoad)]
+ [ProvideToolWindow(typeof(IncludeGraphToolWindow.Pane), Style = VsDockStyle.Float)]
+ [ProvideToolWindowVisibility(typeof(IncludeGraphToolWindow.Pane), VSConstants.UICONTEXT.SolutionExists_string, Name = "Include Graph")]
+ [ProvideOptionPage(typeof(OptionsProvider.FormatterOptionsPage), "Include Toolbox", "Include Format", 0, 0, true, SupportsProfiles = true)]
+ [ProvideOptionPage(typeof(OptionsProvider.TrialAndErrorRemovalOptionsPage), "Include Toolbox", "Trial and Error", 0, 0, true, SupportsProfiles = true)]
+ [ProvideOptionPage(typeof(OptionsProvider.MapperOptionsPage), "Include Toolbox", "Mapper", 0, 0, true, SupportsProfiles = true)]
+ [ProvideOptionPage(typeof(OptionsProvider.IWYUOptionsPage), "Include Toolbox", "Include-What-You-Use", 0, 0, true, SupportsProfiles = true)]
+ [ProvideOptionPage(typeof(OptionsProvider.ViewerOptionsPage), "Include Toolbox", "Include Viewer", 0, 0, true, SupportsProfiles = true)]
+ [ProvideUIContextRule(PackageGuids.GOnlyVCString, "UIOnlyVC",
+ expression: "one & two",
+ termNames: new[] { "one", "two" },
+ termValues: new[] { @"ActiveProjectCapability:VisualC", @"HierSingleSelectionName:.(h|hpp|hxx|cpp|c|cxx)$" }
+)]
+ [ProvideUIContextRule(PackageGuids.GHeaderOnlyString, "UIOnlyHead",
+ expression: "one & two",
+ termNames: new[] { "one", "two" },
+ termValues: new[] { @"ActiveProjectCapability:VisualC", @"HierSingleSelectionName:.(h|hpp|hxx|inl)$" }
+)]
+ public sealed class IncludeToolboxPackage : ToolkitPackage
+ {
+ protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
+ {
+ await this.RegisterCommandsAsync();
+ await Output.InitializeAsync();
+ this.RegisterToolWindows();
+ }
+ }
+}
diff --git a/IncludeToolboxShared/IncludeToolboxShared.projitems b/IncludeToolboxShared/IncludeToolboxShared.projitems
new file mode 100644
index 0000000..ed53ba3
--- /dev/null
+++ b/IncludeToolboxShared/IncludeToolboxShared.projitems
@@ -0,0 +1,65 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ c50c4863-6200-4e51-837a-31febc09c8b2
+
+
+ IncludeToolboxShared
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IncludeGraphControl.xaml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+
\ No newline at end of file
diff --git a/IncludeToolboxShared/IncludeToolboxShared.shproj b/IncludeToolboxShared/IncludeToolboxShared.shproj
new file mode 100644
index 0000000..516a1a2
--- /dev/null
+++ b/IncludeToolboxShared/IncludeToolboxShared.shproj
@@ -0,0 +1,13 @@
+
+
+
+ c50c4863-6200-4e51-837a-31febc09c8b2
+ 14.0
+
+
+
+
+
+
+
+
diff --git a/IncludeToolboxShared/Options/FormatterOptions.cs b/IncludeToolboxShared/Options/FormatterOptions.cs
new file mode 100644
index 0000000..a26bb93
--- /dev/null
+++ b/IncludeToolboxShared/Options/FormatterOptions.cs
@@ -0,0 +1,109 @@
+using Community.VisualStudio.Toolkit;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace IncludeToolbox
+{
+ internal partial class OptionsProvider
+ {
+ [ComVisible(true)]
+ public class FormatterOptionsPage : BaseOptionPage { }
+ }
+
+ public class FormatOptions : BaseOptionModel
+ {
+ [Category("Path")]
+ [DisplayName("Mode")]
+ [Description("Changes the path mode to the given pattern.")]
+ public PathMode PathFormat { get; set; } = PathMode.Shortest_AvoidUpSteps;
+
+ [Category("Path")]
+ [DisplayName("Ignore File Relative")]
+ [Description("If true, include directives will not take the path of the file into account.")]
+ public bool IgnoreFileRelative { get; set; } = false;
+
+
+
+ [Category("Formatting")]
+ [DisplayName("Delimiter Mode")]
+ [Description("Optionally changes all delimiters to either angle brackets <...> or quotes \"...\".")]
+ public DelimiterMode DelimiterFormatting { get; set; } = DelimiterMode.Unchanged;
+
+
+ [Category("Formatting")]
+ [DisplayName("Slash Mode")]
+ [Description("Changes all slashes to the given type.")]
+ public SlashMode SlashFormatting { get; set; } = SlashMode.ForwardSlash;
+
+ [Category("Formatting")]
+ [DisplayName("Remove Empty Lines")]
+ [Description("If true, all empty lines of a include selection will be removed.")]
+ public bool RemoveEmptyLines { get; set; } = true;
+
+ [Category("Formatting")]
+ [DisplayName("Ignore #if blocks")]
+ [Description("If true, all empty lines of an #if block include selection will be ignored.")]
+ public bool IgnoreIfdefs { get; set; } = true;
+
+
+
+ [Category("Sorting")]
+ [DisplayName("Include delimiters in precedence regexes")]
+ [Description("If true, precedence regexes will consider delimiters (angle brackets or quotes.)")]
+ public bool RegexIncludeDelimiter { get; set; } = false;
+
+ [Category("Sorting")]
+ [DisplayName("Insert blank line between precedence regex match groups")]
+ [Description("If true, a blank line will be inserted after each group matching one of the precedence regexes.")]
+ public bool BlankAfterRegexGroupMatch { get; set; } = false;
+
+ [Category("Sorting")]
+ [DisplayName("Precedence Regexes")]
+ [Description("Earlier match means higher sorting priority.\n\"" + RegexUtils.CurrentFileNameKey + "\" will be replaced with the current file name without extension.")]
+ public string[] PrecedenceRegexes
+ {
+ get { return precedenceRegexes; }
+ set { precedenceRegexes = value.Where(x => x.Length > 0).ToArray(); } // Remove empty lines.
+ }
+ private string[] precedenceRegexes = new string[] { $"(?i){RegexUtils.CurrentFileNameKey}\\.(h|hpp|hxx|inl|c|cpp|cxx)(?-i)" };
+
+
+ [Category("Sorting")]
+ [DisplayName("Sort by Include Type")]
+ [Description("Optionally put either includes with angle brackets <...> or quotes \"...\" first.")]
+ public TypeSorting SortByType { get; set; } = TypeSorting.QuotedFirst;
+
+ [Category("Sorting")]
+ [DisplayName("Remove duplicates")]
+ [Description("If true, duplicate includes will be removed.")]
+ public bool RemoveDuplicates { get; set; } = true;
+ }
+
+
+ public enum PathMode
+ {
+ Unchanged,
+ Shortest,
+ Shortest_AvoidUpSteps,
+ Absolute,
+ }
+ public enum DelimiterMode
+ {
+ Unchanged,
+ AngleBrackets,
+ Quotes,
+ }
+ public enum SlashMode
+ {
+ Unchanged,
+ ForwardSlash,
+ BackSlash,
+ }
+ public enum TypeSorting
+ {
+ None,
+ AngleBracketsFirst,
+ QuotedFirst,
+ }
+}
diff --git a/IncludeToolboxShared/Options/IWYUOptions.cs b/IncludeToolboxShared/Options/IWYUOptions.cs
new file mode 100644
index 0000000..f64d38b
--- /dev/null
+++ b/IncludeToolboxShared/Options/IWYUOptions.cs
@@ -0,0 +1,187 @@
+using Community.VisualStudio.Toolkit;
+using IncludeToolbox.Commands;
+using IncludeToolbox.IncludeWhatYouUse;
+using System;
+using System.ComponentModel;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace IncludeToolbox
+{
+ internal partial class OptionsProvider
+ {
+ [ComVisible(true)]
+ public class IWYUOptionsPage : BaseOptionPage { }
+ }
+
+ public class IWYUOptions : BaseOptionModel
+ {
+ string exe = "";
+ Comment comm = Comment.Yes;
+ Substitution subs = Substitution.Precise;
+ uint verbose = 2;
+ bool? download_required = null;
+ bool pch = false;
+ bool nodefault = false;
+ bool provided = true;
+ bool transitives = false;
+ bool warn = false;
+ bool always = false;
+ bool header = false;
+ bool format = false;
+ string mapping = "";
+ string[] clang_options = new string[] { };
+ string[] iwyu_options = new string[] { };
+
+
+
+ [Category("General")]
+ [DisplayName("IWYU Executable")]
+ [Description("A path to IWYU executable.")]
+ [DefaultValue("")]
+ public string Executable
+ {
+ get { return exe; }
+ set
+ {
+ OnChangeEvent();
+ exe = value;
+ }
+ }
+
+ [Category("General")]
+ [DisplayName("Print Commentaries")]
+ [Description("Tells IWYU to show or hide individual commentaries to headers.")]
+ [DefaultValue(Comment.Yes)]
+ [TypeConverter(typeof(EnumConverter))]
+ public Comment Comms { get { return comm; } set { OnChangeEvent(); comm = value; } }
+
+ [Category("General")]
+ [DisplayName("Output Verbosity")]
+ [Description("Determines how much output needs to be printed. May help in case of error. Max value is 7.")]
+ [DefaultValue(2)]
+ public uint Verbosity{ get { return verbose; } set { OnChangeEvent(); verbose = Math.Min(value, 7); } }
+
+ [Category("General")]
+ [DisplayName("Precompiled Header")]
+ [Description("Sets if first file in .cpp is precompiled header. Blocks first file from being parsed.")]
+ [DefaultValue(false)]
+ public bool Precompiled { get { return pch; } set { OnChangeEvent(); pch = value; } }
+
+ [Category("General")]
+ [DisplayName("No Default Maps")]
+ [Description("If true, turns default gcc iwyu STL bindings off. Useful for STL map implementation.")]
+ [DefaultValue(false)]
+ public bool NoDefault { get { return nodefault; } set { OnChangeEvent(); nodefault = value; } }
+
+ [Category("General")]
+ [DisplayName("Use Provided Maps")]
+ [Description("If true, uses provided MSVC mappings from repository. Their location is C:/Users/$(USERNAME)/iwyu/msvc.imp")]
+ [DefaultValue(true)]
+ public bool UseProvided { get { return provided; } set { OnChangeEvent(); provided = value; } }
+
+ [Category("General")]
+ [DisplayName("Only Transitive")]
+ [Description("Do not suggest that a file add foo.h unless foo.h is already visible in the file's transitive includes.")]
+ [DefaultValue(false)]
+ public bool Transitives { get { return transitives; } set { OnChangeEvent(); transitives = value; } }
+
+ [Category("General")]
+ [DisplayName("Show Warnings")]
+ [Description("Shows warnings from IWYU compiler.")]
+ [DefaultValue(false)]
+ public bool Warnings { get { return warn; } set { OnChangeEvent(); warn = value; } }
+
+ [Category("General")]
+ [DisplayName("Always Rebuild")]
+ [Description("Rebuild the project command line on each call. Good for dynamic projects, that may change their options.")]
+ [DefaultValue(false)]
+ public bool AlwaysRebuid { get { return always; } set { OnChangeEvent(); always = value; } }
+
+ [Category("General")]
+ [DisplayName("Substitution Mode")]
+ [Description("Choose the model of substitution for headers. If includes are scattered across the file, the mode is precise. Cheap is used when includes are a block before any code. If used wrong, Cheap may remove code between first and the last include.")]
+ [TypeConverter(typeof(EnumConverter))]
+ public Substitution Sub { get { return subs; } set { subs = value; OnChangeEvent(); } }
+
+
+ [Category("Options")]
+ [DisplayName("IWYU options")]
+ [Description("IWYU launch options, that determine the flow of include-what-you-use.")]
+ public string[] Options { get { return iwyu_options; } set { iwyu_options = value; OnChangeEvent(); } }
+
+ [Category("Options")]
+ [DisplayName("Clang options")]
+ [Description("Clang launch options, that determine compilation stage flow.")]
+ public string[] ClangOptions { get { return clang_options; } set { clang_options = value; OnChangeEvent(); } }
+
+ [Category("Options")]
+ [DisplayName("Mapping File")]
+ [Description("Specifies the mapping file to use by iwyu.")]
+ [DefaultValue("")]
+ public string MappingFile { get { return mapping; } set { mapping = value; OnChangeEvent(); } }
+
+ [Category("Options")]
+ [DisplayName("Ignore Header")]
+ [Description("Ignores header of specified .cpp. Tries to find same-named .h in the includes. If it succeeds, the header is moved to the beginning and it is treated as precompiled. Useful when .h file is already refactored.")]
+ [DefaultValue(false)]
+ public bool IgnoreHeader { get { return header; } set { header = value; OnChangeEvent(); } }
+
+ [Category("Options")]
+ [DisplayName("Format Includes")]
+ [Description("Uses formatting tool after results of IWYU are applied.")]
+ [DefaultValue(false)]
+ public bool Format { get => format; set { format = value; OnChangeEvent(); } }
+
+ [Category("Options")]
+ [DisplayName("Format Document")]
+ [Description("Uses formatting command (Ctrl+K D) after results of IWYU are applied.")]
+ [DefaultValue(true)]
+ public bool FormatDoc { get; set; } = true;
+
+ [Category("Options")]
+ [DisplayName("Move Forward Declarations")]
+ [Description("Moves all forward declarations present in the document. !Does not work with Cheap IWYU mode.")]
+ [DefaultValue(true)]
+ public bool MoveDecls { get; set; } = true;
+
+ [Category("Options")]
+ [DisplayName("Remove Empty Namespaces")]
+ [Description("Removes empty namespaces from the file if finds any.")]
+ [DefaultValue(true)]
+ public bool RemoveENS { get; set; } = true;
+
+ public async Task DownloadRequiredAsync()
+ {
+ if (download_required != null) return download_required.Value;
+ if (Executable == IWYUDownload.GetDefaultExecutablePath())
+ download_required = await IWYUDownload.IsNewerVersionAvailableOnlineAsync();
+ return download_required.Value;
+ }
+ public void Downloaded()
+ {
+ download_required = false;
+ }
+
+
+ public delegate void OnChangeDelegate(IWYUOptions options);
+ public event OnChangeDelegate OnChange;
+
+ protected void OnChangeEvent()
+ {
+ OnChange?.Invoke(this);
+ }
+ }
+
+ public enum Comment
+ {
+ Yes,
+ No
+ }
+ public enum Substitution
+ {
+ Cheap,
+ Precise
+ }
+}
diff --git a/IncludeToolboxShared/Options/MapperOptions.cs b/IncludeToolboxShared/Options/MapperOptions.cs
new file mode 100644
index 0000000..767bb33
--- /dev/null
+++ b/IncludeToolboxShared/Options/MapperOptions.cs
@@ -0,0 +1,68 @@
+using Community.VisualStudio.Toolkit;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace IncludeToolbox
+{
+ internal partial class OptionsProvider
+ {
+ [ComVisible(true)]
+ public class MapperOptionsPage : BaseOptionPage { }
+ }
+
+ public class MapperOptions : BaseOptionModel
+ {
+ string map_path = "";
+ private MapManager map = new();
+
+ [Browsable(false)]
+ public MapManager Map { get { return map; } }
+
+ [Category("General")]
+ [DisplayName("Mapping file")]
+ [Description("File to write results to.")]
+ [DefaultValue("")]
+ public string MapFile { get => map_path; set
+ {
+ if (map_path == value) return;
+ map_path = value;
+ map.Load(map_path);
+ }
+ }
+
+
+
+ [Category("General")]
+ [DisplayName("Relative File Prefix")]
+ [Description("Prefix for relative file path stored into map. e.g. C:\\users\\map\\a.h with prefix C:\\users will write