From f8648365cbd5e22ba7277c43bb6bdd3312239c72 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 16:55:50 +0100 Subject: [PATCH 01/69] Update NuGet dependencies - Use VisualStudio.SDK Version 15.0.1, since the one we were using before is not visible on nuget.org, probably because it only offers compatibility with versions 15.9.X and up, and the 15.0.X covers all vs2017 versions - Replace FxCopAnalyzers by NetAnalyzers since the previous is deprecated - Update VSSDK.BuildTools and EmbedInteropTypes to latest --- NiftyPerforce/NiftyPerforce.csproj | 15 ++++++++++----- Shared/AuroraCore.csproj | 8 ++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index ccff033..77ac9c7 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -141,14 +141,19 @@ - - 2.9.8 + + 5.0.3 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + compile; build; native; contentfiles; analyzers; buildtransitive + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj index b71829a..16b1213 100644 --- a/Shared/AuroraCore.csproj +++ b/Shared/AuroraCore.csproj @@ -60,13 +60,13 @@ - - 2.9.8 + + 5.0.3 runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + + + + \ No newline at end of file From 27185d6e529bb65e69127e45d1a962a4c083c784 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 17:30:46 +0100 Subject: [PATCH 04/69] Fix analyzer warnings about main thread by adding checkers --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 8 ++++++++ .../EventHandlers/AutoCheckoutOnSave.cs | 12 ++++++++++-- .../EventHandlers/AutoCheckoutProject.cs | 4 +++- .../EventHandlers/AutoCheckoutTextEdit.cs | 8 ++++++-- NiftyPerforce/NiftyPerforce.csproj | 1 + NiftyPerforce/NiftyPerforcePackage.cs | 4 ++++ Shared/AsyncProcess.cs | 6 ++++++ Shared/AuroraCore.csproj | 1 + Shared/Feature.cs | 2 ++ Shared/Plugin.cs | 19 +++++++++++++++++-- Shared/VisualStudioLogHandler.cs | 8 +++----- 11 files changed, 61 insertions(+), 12 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 1d24189..c97cabf 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -84,6 +84,8 @@ private void RegisterEvents(object sender = null, EventArgs e = null) public void OnItemAdded(ProjectItem item) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); if (item.ProjectItems != null) @@ -103,6 +105,8 @@ public void OnItemAdded(ProjectItem item) public void OnItemRemoved(ProjectItem item) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); for (int i = 0; i < item.FileCount; i++) @@ -114,6 +118,8 @@ public void OnItemRemoved(ProjectItem item) private void OnProjectAdded(Project project) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); P4Operations.AddFile(m_plugin.OutputPane, project.FullName); // TODO: [jt] We should if the operation is not a add new project but rather a add existing project @@ -123,6 +129,8 @@ private void OnProjectAdded(Project project) private void OnProjectRemoved(Project project) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); P4Operations.DeleteFile(m_plugin.OutputPane, project.FullName); // TODO: [jt] Do we want to automatically delete the items from perforce here? diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index b2db2b3..a0e5d95 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -48,6 +48,7 @@ class AutoCheckoutOnSave : PreCommandFeature public AutoCheckoutOnSave(Plugin plugin) : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") { + ThreadHelper.ThrowIfNotOnUIThread(); ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; RegisterEvents(); } @@ -56,12 +57,17 @@ public AutoCheckoutOnSave(Plugin plugin) private void RegisterEvents(object sender = null, EventArgs e = null) { + ThreadHelper.ThrowIfNotOnUIThread(); + if (((Config)mPlugin.Options).AutoCheckoutOnSave) { if (!RDTAdvised) { Log.Info("Adding handlers for automatically checking out dirty files when you save"); - _sp = new Lazy(() => Package.GetGlobalService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); + _sp = new Lazy(() => { + ThreadHelper.ThrowIfNotOnUIThread(); + return Package.GetGlobalService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + }); _rdt = new Lazy(() => new RunningDocumentTable(new ServiceProvider(_sp.Value))); _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); } @@ -82,7 +88,9 @@ internal void OnBeforeSave(string filename) private void EditProjectRecursive(Project p) { - if(!p.Saved) + ThreadHelper.ThrowIfNotOnUIThread(); + + if (!p.Saved) P4Operations.EditFileImmediate(mPlugin.OutputPane, p.FullName); if(p.ProjectItems == null) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index 2de3776..93f12b7 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -58,8 +58,10 @@ private void RegisterEvents(object sender = null, EventArgs e = null) private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + // when I get Edit.Delete : - if(Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}" && ID == 17) + if (Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}" && ID == 17) { // see if the active window is SolutionExplorer : Window w = mPlugin.App.ActiveWindow; diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 8c26614..446fbef 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -76,7 +76,9 @@ private void RegisterEvents(object sender = null, EventArgs e = null) private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, bool InStatementCompletion, ref bool CancelKeypress) { - if(mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); } @@ -96,7 +98,9 @@ private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) { - if(mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); } diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 43a0d29..44e4ebe 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -16,6 +16,7 @@ true true false + true diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index 162919f..c32ccc3 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -188,6 +188,8 @@ public void Cleanup() private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) { + ThreadHelper.ThrowIfNotOnUIThread(); + try { Command cmd = m_plugin.Commands.Item(name, -1); @@ -241,6 +243,8 @@ private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) // Remove a command bar and contained controls private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCommands3) { + ThreadHelper.ThrowIfNotOnUIThread(); + DTE2 dte = GetGlobalService(typeof(DTE)) as DTE2; CommandBars commandBars = (CommandBars)dte.CommandBars; CommandBar existingCmdBar = null; diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index f25ec31..bbf9892 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -25,6 +25,8 @@ public static void Term() public static bool Run(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + int timeout = 1000; if (!RunCommand(output, executable, commandline, workingdir, timeout)) @@ -123,6 +125,8 @@ private class CommandThread public int timeout = 10000; public void Run() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + bool ok = false; try { @@ -140,6 +144,8 @@ public void Run() private static bool RunCommand(OutputWindowPane output, string executable, string commandline, string workingdir, int timeout) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + try { System.Diagnostics.Process process = new System.Diagnostics.Process(); diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj index 69f9bf2..b085d64 100644 --- a/Shared/AuroraCore.csproj +++ b/Shared/AuroraCore.csproj @@ -3,6 +3,7 @@ net472 Library false + true diff --git a/Shared/Feature.cs b/Shared/Feature.cs index 11a1393..c1b27bf 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -37,6 +37,7 @@ public PreCommandFeature(Plugin plugin, string name, string tooltip) protected bool RegisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); CommandEvents events = mPlugin.FindCommandEvents(commandName); if(null == events) return false; @@ -46,6 +47,7 @@ protected bool RegisterHandler(string commandName, _dispCommandEvents_BeforeExec protected void UnregisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); CommandEvents events = mPlugin.FindCommandEvents(commandName); if (null == events) return; diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index 61f3282..810b710 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -16,7 +16,14 @@ public class Plugin private Dictionary m_features = new Dictionary(); - public OutputWindowPane OutputPane { get { return GetOutputPane(); } } + public OutputWindowPane OutputPane + { + get + { + ThreadHelper.ThrowIfNotOnUIThread(); + return GetOutputPane(); + } + } public string Prefix { get; } public DTE2 App { get; } public Commands Commands { get { return App.Commands; }} @@ -41,6 +48,8 @@ public void AddFeature(Feature feature) public CommandEvents FindCommandEvents(string commandName) { + ThreadHelper.ThrowIfNotOnUIThread(); + CommandEvents events = null; try { @@ -56,6 +65,8 @@ public CommandEvents FindCommandEvents(string commandName) private OutputWindowPane GetOutputPane() { + ThreadHelper.ThrowIfNotOnUIThread(); + if (m_outputPane != null) return m_outputPane; try @@ -70,6 +81,8 @@ private OutputWindowPane GetOutputPane() private static OutputWindowPane AquireOutputPane(DTE2 app, string name) { + ThreadHelper.ThrowIfNotOnUIThread(); + if(string.IsNullOrEmpty(name) || app.Windows.Count == 0) return null; @@ -84,7 +97,9 @@ private static OutputWindowPane AquireOutputPane(DTE2 app, string name) public static OutputWindowPane FindOutputPane(DTE2 app, string name) { - if(string.IsNullOrEmpty(name) || app?.Windows.Count == 0) + ThreadHelper.ThrowIfNotOnUIThread(); + + if(string.IsNullOrEmpty(name) ||app?.Windows?.Count == null || app.Windows.Count == 0) return null; OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index f4c3504..da40e07 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -1,13 +1,9 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; using EnvDTE; -using EnvDTE80; -using System.Collections.Generic; -using System.Text; namespace Aurora { - public class VisualStudioLogHandler : Log.Handler + public class VisualStudioLogHandler : Log.Handler { private Plugin mPlugin; @@ -18,6 +14,8 @@ public VisualStudioLogHandler(Plugin plugin) public void OnMessage(Log.Level level, string message, string formattedLine) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + OutputWindowPane pane = mPlugin.OutputPane; if (null == pane) return; From ad4c24188e51f09bda1f8ad01fc1a695ed7edafa Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 17:40:58 +0100 Subject: [PATCH 05/69] Add the GitHub Actions yaml to the solution --- NiftyPlugins.sln | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/NiftyPlugins.sln b/NiftyPlugins.sln index 1c6131b..148b56e 100644 --- a/NiftyPlugins.sln +++ b/NiftyPlugins.sln @@ -3,9 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29728.190 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NiftyPerforce", "NiftyPerforce\NiftyPerforce.csproj", "{4633E0B5-D536-4FCC-988E-29D54DA113DF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NiftyPerforce", "NiftyPerforce\NiftyPerforce.csproj", "{4633E0B5-D536-4FCC-988E-29D54DA113DF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuroraCore", "Shared\AuroraCore.csproj", "{A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuroraCore", "Shared\AuroraCore.csproj", "{A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActions", "{EAFAD58B-C12C-4FCE-AC50-3B04143946E6}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-publish.yml = .github\workflows\build-and-publish.yml + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From a93a8b86dfef445cfd278cff37d838ec34c66a5d Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 17:41:53 +0100 Subject: [PATCH 06/69] Fix IntelliSense warning by making properties readonly --- NiftyPerforce/Commands/ItemCommandBase.cs | 4 ++-- NiftyPerforce/Commands/P4RevertItem.cs | 2 +- NiftyPerforce/Commands/P4RevisionGraphItem.cs | 2 +- NiftyPerforce/Commands/P4RevisionHistoryItem.cs | 2 +- NiftyPerforce/Commands/P4TimeLapseItem.cs | 2 +- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 6 +++--- NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs | 2 +- NiftyPerforce/P4Operations.cs | 4 ++-- Shared/AsyncProcess.cs | 8 ++++---- Shared/CommandRegistry.cs | 6 +++--- Shared/Feature.cs | 4 ++-- Shared/Log.cs | 2 +- Shared/Plugin.cs | 4 ++-- Shared/VisualStudioLogHandler.cs | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/NiftyPerforce/Commands/ItemCommandBase.cs b/NiftyPerforce/Commands/ItemCommandBase.cs index 841acf4..316693a 100644 --- a/NiftyPerforce/Commands/ItemCommandBase.cs +++ b/NiftyPerforce/Commands/ItemCommandBase.cs @@ -7,8 +7,8 @@ namespace NiftyPerforce // an item command is a command associated with selected items in solution explorer abstract class ItemCommandBase : CommandBase { - private bool m_executeForFileItems = true; - private bool m_executeForProjectItems = true; + private readonly bool m_executeForFileItems = true; + private readonly bool m_executeForProjectItems = true; protected ItemCommandBase(string name, string canonicalName, Plugin plugin, bool executeForFileItems, bool executeForProjectItems, int commandId) : base(name, canonicalName, plugin, commandId) diff --git a/NiftyPerforce/Commands/P4RevertItem.cs b/NiftyPerforce/Commands/P4RevertItem.cs index 02d6f57..b05fbe3 100644 --- a/NiftyPerforce/Commands/P4RevertItem.cs +++ b/NiftyPerforce/Commands/P4RevertItem.cs @@ -7,7 +7,7 @@ namespace NiftyPerforce { class P4RevertItem : ItemCommandBase { - private bool mOnlyUnchanged; + private readonly bool mOnlyUnchanged; public P4RevertItem(Plugin plugin, string canonicalName, bool onlyUnchanged) : base("RevertItem", canonicalName, plugin, true, true, onlyUnchanged ? PackageIds.NiftyRevertUnchanged : PackageIds.NiftyRevert) diff --git a/NiftyPerforce/Commands/P4RevisionGraphItem.cs b/NiftyPerforce/Commands/P4RevisionGraphItem.cs index 05c0c33..913f34c 100644 --- a/NiftyPerforce/Commands/P4RevisionGraphItem.cs +++ b/NiftyPerforce/Commands/P4RevisionGraphItem.cs @@ -7,7 +7,7 @@ namespace NiftyPerforce { class P4RevisionGraphItem : ItemCommandBase { - private bool mMainLine; + private readonly bool mMainLine; public P4RevisionGraphItem(Plugin plugin, string canonicalName, bool mainLine) : base("P4RevisionGraphItem", canonicalName, plugin, true, true, mainLine ? PackageIds.NiftyRevisionGraphMain : PackageIds.NiftyRevisionGraph) diff --git a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs index d581a3e..ee5d907 100644 --- a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs +++ b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs @@ -7,7 +7,7 @@ namespace NiftyPerforce { class P4RevisionHistoryItem : ItemCommandBase { - private bool mMainLine; + private readonly bool mMainLine; public P4RevisionHistoryItem(Plugin plugin, string canonicalName, bool mainLine) : base("RevisionHistoryItem", canonicalName, plugin, true, true, mainLine ? PackageIds.NiftyHistoryMain : PackageIds.NiftyHistory) diff --git a/NiftyPerforce/Commands/P4TimeLapseItem.cs b/NiftyPerforce/Commands/P4TimeLapseItem.cs index f42ca3d..d047d9c 100644 --- a/NiftyPerforce/Commands/P4TimeLapseItem.cs +++ b/NiftyPerforce/Commands/P4TimeLapseItem.cs @@ -7,7 +7,7 @@ namespace NiftyPerforce { class P4TimeLapseItem : ItemCommandBase { - private bool mMainLine; + private readonly bool mMainLine; public P4TimeLapseItem(Plugin plugin, string canonicalName, bool inMainLine) : base("TimeLapseItem", canonicalName, plugin, true, true, inMainLine ? PackageIds.NiftyTimeLapseMain : PackageIds.NiftyTimeLapse) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index c97cabf..95ffe01 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -10,15 +10,15 @@ namespace NiftyPerforce // Handles registration and events for add/delete files and projects. class AutoAddDelete : Feature { - private ProjectItemsEvents m_projectEvents; - private SolutionEvents m_solutionEvents; + private readonly ProjectItemsEvents m_projectEvents; + private readonly SolutionEvents m_solutionEvents; private _dispProjectItemsEvents_ItemAddedEventHandler _itemAddedEventHandler; private _dispSolutionEvents_ProjectAddedEventHandler _projectAddedEventHandler; private _dispProjectItemsEvents_ItemRemovedEventHandler _itemRemovedEventHandler; private _dispSolutionEvents_ProjectRemovedEventHandler _projectRemovedEventHandler; - private Plugin m_plugin; + private readonly Plugin m_plugin; public AutoAddDelete(Plugin plugin) : base("AutoAddDelete", "Automatically adds and deletes files matching project add/delete") diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index a0e5d95..f1c0e0c 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -14,7 +14,7 @@ namespace NiftyPerforce // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 internal class RunningDocTableEvents : IVsRunningDocTableEvents3 { - private AutoCheckoutOnSave autoCheckoutOnSave; + private readonly AutoCheckoutOnSave autoCheckoutOnSave; public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) { diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 3e9b455..29e1d7e 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -19,9 +19,9 @@ class P4Operations private static bool g_p4vc_history_supported = false; private static bool g_p4vc_diffhave_supported = false; - private static Dictionary g_opsInFlight = new Dictionary(); + private static readonly Dictionary g_opsInFlight = new Dictionary(); - private static HashSet g_alreadyNotified = new HashSet(); + private static readonly HashSet g_alreadyNotified = new HashSet(); private static bool LockOp(string token) { diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index bbf9892..654301d 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -8,7 +8,7 @@ namespace Aurora { public static class AsyncProcess { - private static int s_defaultTimeout = 30000; // in ms + private static readonly int s_defaultTimeout = 30000; // in ms public delegate void OnDone(bool ok, object arg0); @@ -80,9 +80,9 @@ public static bool Schedule(OutputWindowPane output, string executable, string c // // BEGIN INTERNALS // - private static Mutex m_queueLock = new Mutex(); - private static Semaphore m_startEvent = new Semaphore(0, 9999); - private static Queue m_commandQueue = new Queue(); + private static readonly Mutex m_queueLock = new Mutex(); + private static readonly Semaphore m_startEvent = new Semaphore(0, 9999); + private static readonly Queue m_commandQueue = new Queue(); private static System.Threading.Thread m_helperThread; private static void ThreadMain() diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index 36f3e5d..6c90498 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -11,9 +11,9 @@ namespace Aurora // the logic to execute and update the command itself. public class CommandRegistry { - private Dictionary mCommands; - private Dictionary mCommandsById; - private Plugin mPlugin; + private readonly Dictionary mCommands; + private readonly Dictionary mCommandsById; + private readonly Plugin mPlugin; private Guid mCmdGroupGuid; public CommandRegistry(Plugin plugin, Guid packageGuid, Guid cmdGroupGuid) diff --git a/Shared/Feature.cs b/Shared/Feature.cs index c1b27bf..ff5093b 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -8,8 +8,8 @@ namespace Aurora { public abstract class Feature { - private string mName; - private string mTooltip; + private readonly string mName; + private readonly string mTooltip; public string Name { get { return mName; } } diff --git a/Shared/Log.cs b/Shared/Log.cs index e6355a7..9044393 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -126,7 +126,7 @@ private static void OnMessage(Level level, string format, object[] args) } } - private static List mHandlers = new List(); + private static readonly List mHandlers = new List(); private static string mPrefix = ""; private static int mIndent = 0; } diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index 810b710..508e840 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -12,9 +12,9 @@ namespace Aurora public class Plugin { private OutputWindowPane m_outputPane; - private string m_panelName; + private readonly string m_panelName; - private Dictionary m_features = new Dictionary(); + private readonly Dictionary m_features = new Dictionary(); public OutputWindowPane OutputPane { diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index da40e07..de22c85 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -5,7 +5,7 @@ namespace Aurora { public class VisualStudioLogHandler : Log.Handler { - private Plugin mPlugin; + private readonly Plugin mPlugin; public VisualStudioLogHandler(Plugin plugin) { From 06822ee34cb835da88b150e21376b843bfb106e5 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 17:52:27 +0100 Subject: [PATCH 07/69] Fix most remaining IntelliSense warnings --- NiftyPerforce/NiftyPerforcePackage.cs | 3 +-- Shared/AsyncProcess.cs | 25 ++++++++++++------------- Shared/CommandRegistry.cs | 10 +++------- Shared/DebugLogHandler.cs | 2 +- Shared/Log.cs | 10 +++++----- Shared/VisualStudioLogHandler.cs | 2 +- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index c32ccc3..b0af503 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -48,7 +48,6 @@ namespace NiftyPerforce //[ProvideToolsOptionsPageVisibility("Source Control", "Nifty Perforce Settings", PackageGuids.guidNiftyPerforceSccProviderString)] // Declare the package guid [Guid(PackageGuids.guidNiftyPerforcePackageString)] - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] public sealed class NiftyPerforcePackage : AsyncPackage { private Plugin m_plugin = null; @@ -92,7 +91,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke m_plugin = new Plugin(application, oleMenuCommandService, "NiftyPerforce", "Aurora.NiftyPerforce.Connect", config); - m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageString), new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); + m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); // Initialize the logging system. if (Log.HandlerCount == 0) diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index 654301d..b51041c 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -36,10 +36,7 @@ public static bool Run(OutputWindowPane output, string executable, string comman } else { - if (null != callback) - { - callback(true, callbackArg); - } + callback?.Invoke(true, callbackArg); } return true; @@ -52,14 +49,16 @@ public static bool Schedule(OutputWindowPane output, string executable, string c public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) { - CommandThread cmd = new CommandThread(); - cmd.output = output; - cmd.executable = executable; - cmd.commandline = commandline; - cmd.workingdir = workingdir; - cmd.callback = callback; - cmd.callbackArg = callbackArg; - cmd.timeout = timeout; + CommandThread cmd = new CommandThread + { + output = output, + executable = executable, + commandline = commandline, + workingdir = workingdir, + callback = callback, + callbackArg = callbackArg, + timeout = timeout + }; try { @@ -127,7 +126,7 @@ public void Run() { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - bool ok = false; + bool ok; try { ok = RunCommand(output, executable, commandline, workingdir, timeout); diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index 6c90498..d3db152 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -16,7 +16,7 @@ public class CommandRegistry private readonly Plugin mPlugin; private Guid mCmdGroupGuid; - public CommandRegistry(Plugin plugin, Guid packageGuid, Guid cmdGroupGuid) + public CommandRegistry(Plugin plugin, Guid cmdGroupGuid) { mCommands = new Dictionary(); mCommandsById = new Dictionary(); @@ -56,9 +56,7 @@ private void OleMenuCommandBeforeQueryStatus(object sender, EventArgs e) try { - OleMenuCommand oleMenuCommand = sender as OleMenuCommand; - - if (oleMenuCommand != null) + if (sender is OleMenuCommand oleMenuCommand) { CommandID commandId = oleMenuCommand.CommandID; @@ -85,9 +83,7 @@ private void OleMenuCommandCallback(object sender, EventArgs e) { try { - OleMenuCommand oleMenuCommand = sender as OleMenuCommand; - - if (oleMenuCommand != null) + if (sender is OleMenuCommand oleMenuCommand) { CommandID commandId = oleMenuCommand.CommandID; if (commandId != null) diff --git a/Shared/DebugLogHandler.cs b/Shared/DebugLogHandler.cs index ac08e48..ba44329 100644 --- a/Shared/DebugLogHandler.cs +++ b/Shared/DebugLogHandler.cs @@ -6,7 +6,7 @@ namespace Aurora { - public class DebugLogHandler : Log.Handler + public class DebugLogHandler : Log.IHandler { public void OnMessage(Log.Level level, string message, string formattedLine) { diff --git a/Shared/Log.cs b/Shared/Log.cs index 9044393..2a50e53 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -17,7 +17,7 @@ public enum Level } // This needs to be implemented by all clients. - public interface Handler + public interface IHandler { void OnMessage(Level level, string message, string formattedLine); } @@ -37,7 +37,7 @@ public static int HandlerCount get { return mHandlers.Count; } } - public static void AddHandler(Handler handler) + public static void AddHandler(IHandler handler) { if(null == handler) return; @@ -48,7 +48,7 @@ public static void AddHandler(Handler handler) } } - public static void RemoveHandler(Handler handler) + public static void RemoveHandler(IHandler handler) { lock(mHandlers) { @@ -119,14 +119,14 @@ private static void OnMessage(Level level, string format, object[] args) lock(mHandlers) { - foreach(Handler handler in mHandlers) + foreach(IHandler handler in mHandlers) { handler.OnMessage(level, message, formattedLine); } } } - private static readonly List mHandlers = new List(); + private static readonly List mHandlers = new List(); private static string mPrefix = ""; private static int mIndent = 0; } diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index de22c85..0fb0c51 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -3,7 +3,7 @@ namespace Aurora { - public class VisualStudioLogHandler : Log.Handler + public class VisualStudioLogHandler : Log.IHandler { private readonly Plugin mPlugin; From ace3b0a442505fa3dd1fd5aae13d09aa00d93f93 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 18:47:41 +0100 Subject: [PATCH 08/69] Move product name and details from VSPackage.resx to Resources.resx and remove the first to reduce duplication. --- NiftyPerforce/Properties/Resources.resx | 6 ++ NiftyPerforce/VSPackage.resx | 126 ------------------------ 2 files changed, 6 insertions(+), 126 deletions(-) delete mode 100644 NiftyPerforce/VSPackage.resx diff --git a/NiftyPerforce/Properties/Resources.resx b/NiftyPerforce/Properties/Resources.resx index 0b14b0e..bdb7631 100644 --- a/NiftyPerforce/Properties/Resources.resx +++ b/NiftyPerforce/Properties/Resources.resx @@ -121,4 +121,10 @@ ..\Resources\Icons.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + Nifty Perforce Extension + + + Simple Perforce integration for Visual Studio + \ No newline at end of file diff --git a/NiftyPerforce/VSPackage.resx b/NiftyPerforce/VSPackage.resx deleted file mode 100644 index 51a4e27..0000000 --- a/NiftyPerforce/VSPackage.resx +++ /dev/null @@ -1,126 +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 - - - Nifty Perforce Extension - - - Simple Perforce integration for Visual Studio - - \ No newline at end of file From 95ba7aadad4ad96a5a2f34e7ca1a456d26544798 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 13 Mar 2021 19:50:17 +0100 Subject: [PATCH 09/69] Fix logo display, and remove the old unused icon file --- NiftyPerforce/NiftyPerforce.csproj | 6 +++++- NiftyPerforce/source.extension.ico | Bin 1933 -> 0 bytes NiftyPerforce/source.extension.vsixmanifest | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) delete mode 100644 NiftyPerforce/source.extension.ico diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 44e4ebe..9b2d103 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -37,6 +37,10 @@ Always true + + Always + true + Designer VsixManifestGenerator @@ -77,4 +81,4 @@ - \ No newline at end of file + diff --git a/NiftyPerforce/source.extension.ico b/NiftyPerforce/source.extension.ico deleted file mode 100644 index d35970758d705f0a403b3e0688ec912663170ab0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1933 zcmV;82XgoT0096203aX$0096P0Cxuf02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|AOHXW zAP5Ek0047(dh`GQ00DDSM?wIu&K&6g000DMK}|sb0I`n?{9y$E000SaNLh0L01m?d z01m?e$8V@)000LJNklN;XORAR14_PZ!aYB>;irp6NsTd=TY^wjh_DI-5F&s;FbL7_Pl%#;TBFe{ zmP4jmSGUgF)2qjH05wF9(Tx4=I&Ao<4H*QH_UReq>}f>S?nZprqDv}4>EK%6G_VQ^ zfIkpm2)Nzund?FgKq-KM05}awDijcXzZqIxKTcdS;QhuqvQg6-!-R5keU`lSG> z8b!R&=0epEXHnl{!pkQv!c1MV=9?Bg+Um!Gqh5%iQ~)Z(VmV~05r9h#z`Hk4zB6_= z-~(;9)bC}#3+VU7@Jxpv8(T!&rxEeJHG+pU0-meAfCqkV!+J`=4PR?_Zon#CI)@w) zG+Ykjt*#(m|4YOtXZ1L5cR`585u+QC1HII2$Cp%$CDf7ydz+;EFC?FH7mxyi0>Js_ zp64TX%)A?YjWq!n*yNx^CiT$pWEZX(V++me)ZzXb`cLY`|!lk^HLkW)?mOZl>60m z0Nwg+3+|&6a@kiJeSC^;ElG z`n$8!gLU7xV_CHp6$e}KQll9;zY5ZCt4<11aOECQ12`OxnP*6K5poUiHOyBp-*jB* z{>X&1cqZljc{+^v^GyD5^VQ(e?xz9JkTP=(^y&>(1%SJNi*SJMe;z(ulm=FzZa6Fj zsi^m&emI1W`vO>7FW}bGKI}CG;11I5rwXnH;2Q9WV$|t$X3HT{jdRm)w!BM+P((R~ zc?eNdP$nMTcm%mr58FtEu|POEc*w(t6nqu{+Wlz?KJy`@Htti4)mm*d4@7wq;wgw# zkZOTa!4N$4bb2EA#TJ4qAOlQ?xC$cB`Tw}Q{M^lAv8=RMZCZkum^30NwlHA(b+Pkeb8wuf*MlX2?P0$VZ zLtS0n$`vbCWK~pDWK>pGE}?f>`i0Ue|H62FU(5>?7Z>N{=jRvYR%F4PfGc&V1 zBO~KR`u=7D*)i;N3MBbhO-;?Vyu7?M{2TcydhYt{?CjFq+}x6-OP3ZhI7898bAnmO z5A#d>G6i3z4!&f`lAFnSzQ2uv+(FN*r_Z;OuOnYezcI*a6~r}#g@sF(FJI2}kWM~l zTgWn&mX>B06&2+(3@^rN@WKRD#$d9S@pt6cGBBx8OwSkaCRj;YYnK0-)6)J1e71o~ T?f|%|00000NkvXXu0mjf?_PXR diff --git a/NiftyPerforce/source.extension.vsixmanifest b/NiftyPerforce/source.extension.vsixmanifest index 8cf8663..bedb313 100644 --- a/NiftyPerforce/source.extension.vsixmanifest +++ b/NiftyPerforce/source.extension.vsixmanifest @@ -6,6 +6,8 @@ Simple Perforce integration for Visual Studio https://github.com/belkiss/niftyplugins COPYING + Resources\logo.png + Resources\logo.png perforce From 25774d3ba8c292c02c76134adaea5593a2c53713 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 11:56:08 +0200 Subject: [PATCH 10/69] Fix warning CA1012: Abstract type should not have public constructors, constructors didn't need to be public --- Shared/CommandBase.cs | 2 +- Shared/Feature.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Shared/CommandBase.cs b/Shared/CommandBase.cs index 529963f..f25672d 100644 --- a/Shared/CommandBase.cs +++ b/Shared/CommandBase.cs @@ -10,7 +10,7 @@ public abstract class CommandBase public string CanonicalName { get; } public int CommandId { get; } - public CommandBase(string name, string canonicalName, Plugin plugin, int commandId) + protected CommandBase(string name, string canonicalName, Plugin plugin, int commandId) { Name = name; CanonicalName = canonicalName; diff --git a/Shared/Feature.cs b/Shared/Feature.cs index ff5093b..bef781d 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -13,7 +13,7 @@ public abstract class Feature public string Name { get { return mName; } } - public Feature(string name, string tooltip) + protected Feature(string name, string tooltip) { mName = name; mTooltip = tooltip; @@ -29,7 +29,7 @@ public abstract class PreCommandFeature : Feature { protected Plugin mPlugin; - public PreCommandFeature(Plugin plugin, string name, string tooltip) + protected PreCommandFeature(Plugin plugin, string name, string tooltip) : base(name, tooltip) { mPlugin = plugin; From 066df313292582c9ef98b7380ea45af0871cfc86 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 12:48:42 +0200 Subject: [PATCH 11/69] Style cleanup Mostly converted tabs to space, and spaces around braces Used https://github.com/dotnet/format executable with this command "dotnet-format.exe -w .\NiftyPlugins.sln", without an .editorconfig --- NiftyPerforce/Commands/P4DiffItem.cs | 14 +- NiftyPerforce/Commands/P4EditItem.cs | 16 +- NiftyPerforce/Commands/P4EditModified.cs | 84 +- NiftyPerforce/Commands/P4RevertItem.cs | 42 +- NiftyPerforce/Commands/P4RevisionGraphItem.cs | 16 +- .../Commands/P4RevisionHistoryItem.cs | 38 +- NiftyPerforce/Commands/P4ShowItem.cs | 22 +- NiftyPerforce/Commands/P4TimeLapseItem.cs | 38 +- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 264 ++--- .../EventHandlers/AutoCheckoutOnSave.cs | 181 +-- .../EventHandlers/AutoCheckoutProject.cs | 132 +-- .../EventHandlers/AutoCheckoutTextEdit.cs | 170 +-- NiftyPerforce/NiftyPerforcePackage.cs | 488 ++++---- NiftyPerforce/OptionsDialogPage.cs | 88 +- NiftyPerforce/P4Operations.cs | 1030 ++++++++--------- Shared/AsyncProcess.cs | 464 ++++---- Shared/CommandBase.cs | 32 +- Shared/CommandRegistry.cs | 182 +-- Shared/DebugLogHandler.cs | 14 +- Shared/Feature.cs | 96 +- Shared/Help.cs | 32 +- Shared/Log.cs | 246 ++-- Shared/Plugin.cs | 218 ++-- Shared/Process.cs | 132 +-- Shared/Singleton.cs | 10 +- Shared/VisualStudioLogHandler.cs | 24 +- 26 files changed, 2038 insertions(+), 2035 deletions(-) diff --git a/NiftyPerforce/Commands/P4DiffItem.cs b/NiftyPerforce/Commands/P4DiffItem.cs index f326c45..9a5df6a 100644 --- a/NiftyPerforce/Commands/P4DiffItem.cs +++ b/NiftyPerforce/Commands/P4DiffItem.cs @@ -4,16 +4,16 @@ namespace NiftyPerforce { - class P4DiffItem : ItemCommandBase - { - public P4DiffItem(Plugin plugin, string canonicalName) - : base("DiffItem", canonicalName, plugin, true, true, PackageIds.NiftyDiff) - { - } + class P4DiffItem : ItemCommandBase + { + public P4DiffItem(Plugin plugin, string canonicalName) + : base("DiffItem", canonicalName, plugin, true, true, PackageIds.NiftyDiff) + { + } public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) { P4Operations.DiffFile(pane, fileName); } - } + } } diff --git a/NiftyPerforce/Commands/P4EditItem.cs b/NiftyPerforce/Commands/P4EditItem.cs index 1a5d5f7..d29ed1a 100644 --- a/NiftyPerforce/Commands/P4EditItem.cs +++ b/NiftyPerforce/Commands/P4EditItem.cs @@ -4,16 +4,16 @@ namespace NiftyPerforce { - class P4EditItem : ItemCommandBase - { - public P4EditItem(Plugin plugin, string canonicalName) - : base("EditItem", canonicalName, plugin, true, true, PackageIds.NiftyEdit) - { - } + class P4EditItem : ItemCommandBase + { + public P4EditItem(Plugin plugin, string canonicalName) + : base("EditItem", canonicalName, plugin, true, true, PackageIds.NiftyEdit) + { + } public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) { - P4Operations.EditFile(pane, fileName); + P4Operations.EditFile(pane, fileName); } - } + } } diff --git a/NiftyPerforce/Commands/P4EditModified.cs b/NiftyPerforce/Commands/P4EditModified.cs index f0a8f9a..530e0b4 100644 --- a/NiftyPerforce/Commands/P4EditModified.cs +++ b/NiftyPerforce/Commands/P4EditModified.cs @@ -4,49 +4,49 @@ namespace NiftyPerforce { - class P4EditModified : CommandBase - { - public P4EditModified(Plugin plugin, string canonicalName) - : base("EditModified", canonicalName, plugin, PackageIds.NiftyEditModified) - { - } + class P4EditModified : CommandBase + { + public P4EditModified(Plugin plugin, string canonicalName) + : base("EditModified", canonicalName, plugin, PackageIds.NiftyEditModified) + { + } public override bool OnCommand() - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - Log.Info("P4EditModified : looking for modified files"); - - if(!Plugin.App.Solution.Saved) - { - Log.Info($"P4EditModified : solution {Plugin.App.Solution.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, Plugin.App.Solution.FullName); - } - - foreach(Project p in Plugin.App.Solution.Projects) - { - if(!p.Saved) - { - Log.Info($"P4EditModified : project {p.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, p.FullName); - } - } - - foreach(Document doc in Plugin.App.Documents) - { - if(!doc.Saved) - { - Log.Info($"P4EditModified : document {doc.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, doc.FullName); - } - } - - return true; - } - - public override bool IsEnabled() - { - return true; - } - } + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + Log.Info("P4EditModified : looking for modified files"); + + if (!Plugin.App.Solution.Saved) + { + Log.Info($"P4EditModified : solution {Plugin.App.Solution.FullName} was dirty, checkout"); + P4Operations.EditFile(Plugin.OutputPane, Plugin.App.Solution.FullName); + } + + foreach (Project p in Plugin.App.Solution.Projects) + { + if (!p.Saved) + { + Log.Info($"P4EditModified : project {p.FullName} was dirty, checkout"); + P4Operations.EditFile(Plugin.OutputPane, p.FullName); + } + } + + foreach (Document doc in Plugin.App.Documents) + { + if (!doc.Saved) + { + Log.Info($"P4EditModified : document {doc.FullName} was dirty, checkout"); + P4Operations.EditFile(Plugin.OutputPane, doc.FullName); + } + } + + return true; + } + + public override bool IsEnabled() + { + return true; + } + } } diff --git a/NiftyPerforce/Commands/P4RevertItem.cs b/NiftyPerforce/Commands/P4RevertItem.cs index b05fbe3..1d913cb 100644 --- a/NiftyPerforce/Commands/P4RevertItem.cs +++ b/NiftyPerforce/Commands/P4RevertItem.cs @@ -5,27 +5,27 @@ namespace NiftyPerforce { - class P4RevertItem : ItemCommandBase - { - private readonly bool mOnlyUnchanged; + class P4RevertItem : ItemCommandBase + { + private readonly bool mOnlyUnchanged; - public P4RevertItem(Plugin plugin, string canonicalName, bool onlyUnchanged) - : base("RevertItem", canonicalName, plugin, true, true, onlyUnchanged ? PackageIds.NiftyRevertUnchanged : PackageIds.NiftyRevert) - { - mOnlyUnchanged = onlyUnchanged; - } + public P4RevertItem(Plugin plugin, string canonicalName, bool onlyUnchanged) + : base("RevertItem", canonicalName, plugin, true, true, onlyUnchanged ? PackageIds.NiftyRevertUnchanged : PackageIds.NiftyRevert) + { + mOnlyUnchanged = onlyUnchanged; + } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) - { - if (!mOnlyUnchanged) - { - string message = "You are about to revert the file '" + fileName + "'. Do you want to do this?"; - if(MessageBox.Show(message, "Revert File?", MessageBoxButtons.YesNo) != DialogResult.Yes) - { - return; - } - } - P4Operations.RevertFile(pane, fileName, mOnlyUnchanged); - } - } + public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + { + if (!mOnlyUnchanged) + { + string message = "You are about to revert the file '" + fileName + "'. Do you want to do this?"; + if (MessageBox.Show(message, "Revert File?", MessageBoxButtons.YesNo) != DialogResult.Yes) + { + return; + } + } + P4Operations.RevertFile(pane, fileName, mOnlyUnchanged); + } + } } diff --git a/NiftyPerforce/Commands/P4RevisionGraphItem.cs b/NiftyPerforce/Commands/P4RevisionGraphItem.cs index 913f34c..d282f0c 100644 --- a/NiftyPerforce/Commands/P4RevisionGraphItem.cs +++ b/NiftyPerforce/Commands/P4RevisionGraphItem.cs @@ -7,25 +7,25 @@ namespace NiftyPerforce { class P4RevisionGraphItem : ItemCommandBase { - private readonly bool mMainLine; + private readonly bool mMainLine; public P4RevisionGraphItem(Plugin plugin, string canonicalName, bool mainLine) : base("P4RevisionGraphItem", canonicalName, plugin, true, true, mainLine ? PackageIds.NiftyRevisionGraphMain : PackageIds.NiftyRevisionGraph) { - mMainLine = mainLine; + mMainLine = mainLine; } public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) { - string dirname = Path.GetDirectoryName(fileName); + string dirname = Path.GetDirectoryName(fileName); - if (mMainLine) - { + if (mMainLine) + { var options = (NiftyPerforce.Config)Plugin.Options; - fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); - } + fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); + } - P4Operations.RevisionGraph(pane, dirname, fileName); + P4Operations.RevisionGraph(pane, dirname, fileName); } } } diff --git a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs index ee5d907..e8915a1 100644 --- a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs +++ b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs @@ -5,27 +5,27 @@ namespace NiftyPerforce { - class P4RevisionHistoryItem : ItemCommandBase - { - private readonly bool mMainLine; + class P4RevisionHistoryItem : ItemCommandBase + { + private readonly bool mMainLine; - public P4RevisionHistoryItem(Plugin plugin, string canonicalName, bool mainLine) - : base("RevisionHistoryItem", canonicalName, plugin, true, true, mainLine ? PackageIds.NiftyHistoryMain : PackageIds.NiftyHistory) - { - mMainLine = mainLine; - } + public P4RevisionHistoryItem(Plugin plugin, string canonicalName, bool mainLine) + : base("RevisionHistoryItem", canonicalName, plugin, true, true, mainLine ? PackageIds.NiftyHistoryMain : PackageIds.NiftyHistory) + { + mMainLine = mainLine; + } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) - { - string dirname = Path.GetDirectoryName(fileName); + public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + { + string dirname = Path.GetDirectoryName(fileName); - if (mMainLine) - { - var options = (NiftyPerforce.Config)Plugin.Options; - fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); - } + if (mMainLine) + { + var options = (NiftyPerforce.Config)Plugin.Options; + fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); + } - P4Operations.RevisionHistoryFile(pane, dirname, fileName); - } - } + P4Operations.RevisionHistoryFile(pane, dirname, fileName); + } + } } diff --git a/NiftyPerforce/Commands/P4ShowItem.cs b/NiftyPerforce/Commands/P4ShowItem.cs index f7a387d..2e8bd72 100644 --- a/NiftyPerforce/Commands/P4ShowItem.cs +++ b/NiftyPerforce/Commands/P4ShowItem.cs @@ -4,16 +4,16 @@ namespace NiftyPerforce { - class P4ShowItem : ItemCommandBase - { - public P4ShowItem(Plugin plugin, string canonicalName) - : base("ShowItem", canonicalName, plugin, true, true, PackageIds.NiftyShow) - { - } + class P4ShowItem : ItemCommandBase + { + public P4ShowItem(Plugin plugin, string canonicalName) + : base("ShowItem", canonicalName, plugin, true, true, PackageIds.NiftyShow) + { + } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) - { - P4Operations.P4VShowFile(pane, fileName); - } - } + public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + { + P4Operations.P4VShowFile(pane, fileName); + } + } } diff --git a/NiftyPerforce/Commands/P4TimeLapseItem.cs b/NiftyPerforce/Commands/P4TimeLapseItem.cs index d047d9c..f643581 100644 --- a/NiftyPerforce/Commands/P4TimeLapseItem.cs +++ b/NiftyPerforce/Commands/P4TimeLapseItem.cs @@ -5,27 +5,27 @@ namespace NiftyPerforce { - class P4TimeLapseItem : ItemCommandBase - { - private readonly bool mMainLine; + class P4TimeLapseItem : ItemCommandBase + { + private readonly bool mMainLine; - public P4TimeLapseItem(Plugin plugin, string canonicalName, bool inMainLine) - : base("TimeLapseItem", canonicalName, plugin, true, true, inMainLine ? PackageIds.NiftyTimeLapseMain : PackageIds.NiftyTimeLapse) - { - mMainLine = inMainLine; - } + public P4TimeLapseItem(Plugin plugin, string canonicalName, bool inMainLine) + : base("TimeLapseItem", canonicalName, plugin, true, true, inMainLine ? PackageIds.NiftyTimeLapseMain : PackageIds.NiftyTimeLapse) + { + mMainLine = inMainLine; + } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) - { - string dirname = Path.GetDirectoryName(fileName); + public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + { + string dirname = Path.GetDirectoryName(fileName); - if( mMainLine ) - { - var options = (NiftyPerforce.Config)Plugin.Options; - fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); - } + if (mMainLine) + { + var options = (NiftyPerforce.Config)Plugin.Options; + fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); + } - P4Operations.TimeLapseView(pane, dirname, fileName); - } - } + P4Operations.TimeLapseView(pane, dirname, fileName); + } + } } diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 95ffe01..581abfb 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -5,136 +5,136 @@ namespace Aurora { - namespace NiftyPerforce - { - // Handles registration and events for add/delete files and projects. - class AutoAddDelete : Feature - { - private readonly ProjectItemsEvents m_projectEvents; - private readonly SolutionEvents m_solutionEvents; - - private _dispProjectItemsEvents_ItemAddedEventHandler _itemAddedEventHandler; - private _dispSolutionEvents_ProjectAddedEventHandler _projectAddedEventHandler; - private _dispProjectItemsEvents_ItemRemovedEventHandler _itemRemovedEventHandler; - private _dispSolutionEvents_ProjectRemovedEventHandler _projectRemovedEventHandler; - - private readonly Plugin m_plugin; - - public AutoAddDelete(Plugin plugin) - : base("AutoAddDelete", "Automatically adds and deletes files matching project add/delete") - { - m_plugin = plugin; - - m_projectEvents = ((EnvDTE80.Events2)m_plugin.App.Events).ProjectItemsEvents; - m_solutionEvents = ((EnvDTE80.Events2)m_plugin.App.Events).SolutionEvents; - - ((Config)m_plugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } - - private bool AddFilesHandlersInstalled { get { return _itemAddedEventHandler != null || _projectAddedEventHandler != null; } } // second conditional is useless but kept for clarity - private bool RemoveFilesHandlersInstalled { get { return _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; } } // second conditional is useless but kept for clarity - - private void RegisterEvents(object sender = null, EventArgs e = null) - { - if (((Config)m_plugin.Options).AutoAdd) - { - if (!AddFilesHandlersInstalled) - { - Log.Info("Adding handlers for automatically add files to perforce as you add them to the project"); - _itemAddedEventHandler = new _dispProjectItemsEvents_ItemAddedEventHandler(OnItemAdded); - m_projectEvents.ItemAdded += _itemAddedEventHandler; - - _projectAddedEventHandler = new _dispSolutionEvents_ProjectAddedEventHandler(OnProjectAdded); - m_solutionEvents.ProjectAdded += _projectAddedEventHandler; - } - } - else if (AddFilesHandlersInstalled) - { - Log.Info("Removing handlers for automatically add files to perforce as you add them to the project"); - m_projectEvents.ItemAdded -= _itemAddedEventHandler; - _itemAddedEventHandler = null; - - m_solutionEvents.ProjectAdded -= _projectAddedEventHandler; - _projectAddedEventHandler = null; - } - - if (((Config)m_plugin.Options).AutoDelete) - { - if (!RemoveFilesHandlersInstalled) - { - Log.Info("Adding handlers for automatically deleting files from perforce as you remove them from the project"); - _itemRemovedEventHandler = new _dispProjectItemsEvents_ItemRemovedEventHandler(OnItemRemoved); - m_projectEvents.ItemRemoved += _itemRemovedEventHandler; - - _projectRemovedEventHandler = new _dispSolutionEvents_ProjectRemovedEventHandler(OnProjectRemoved); - m_solutionEvents.ProjectRemoved += _projectRemovedEventHandler; - } - } - else if (RemoveFilesHandlersInstalled) - { - Log.Info("Removing handlers for automatically deleting files from perforce as you remove them from the project"); - m_projectEvents.ItemRemoved -= _itemRemovedEventHandler; - _itemRemovedEventHandler = null; - - m_solutionEvents.ProjectRemoved -= _projectRemovedEventHandler; - _projectRemovedEventHandler = null; - } - } - - public void OnItemAdded(ProjectItem item) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); - - if (item.ProjectItems != null) - { - for (int i = 0; i < item.FileCount; i++) - { - string name = item.get_FileNames((short)i); - P4Operations.AddFile(m_plugin.OutputPane, name); - } - } - else - { - if(System.IO.File.Exists(item.Name)) - P4Operations.AddFile(m_plugin.OutputPane, item.Name); - } - } - - public void OnItemRemoved(ProjectItem item) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); - - for (int i = 0; i < item.FileCount; i++) - { - string name = item.get_FileNames((short)i); - P4Operations.DeleteFile(m_plugin.OutputPane, name); - } - } - - private void OnProjectAdded(Project project) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); - P4Operations.AddFile(m_plugin.OutputPane, project.FullName); - // TODO: [jt] We should if the operation is not a add new project but rather a add existing project - // step through all the project items and add them to perforce. Or maybe we want the user - // to do this herself? - } - - private void OnProjectRemoved(Project project) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); - P4Operations.DeleteFile(m_plugin.OutputPane, project.FullName); - // TODO: [jt] Do we want to automatically delete the items from perforce here? - } - } - } + namespace NiftyPerforce + { + // Handles registration and events for add/delete files and projects. + class AutoAddDelete : Feature + { + private readonly ProjectItemsEvents m_projectEvents; + private readonly SolutionEvents m_solutionEvents; + + private _dispProjectItemsEvents_ItemAddedEventHandler _itemAddedEventHandler; + private _dispSolutionEvents_ProjectAddedEventHandler _projectAddedEventHandler; + private _dispProjectItemsEvents_ItemRemovedEventHandler _itemRemovedEventHandler; + private _dispSolutionEvents_ProjectRemovedEventHandler _projectRemovedEventHandler; + + private readonly Plugin m_plugin; + + public AutoAddDelete(Plugin plugin) + : base("AutoAddDelete", "Automatically adds and deletes files matching project add/delete") + { + m_plugin = plugin; + + m_projectEvents = ((EnvDTE80.Events2)m_plugin.App.Events).ProjectItemsEvents; + m_solutionEvents = ((EnvDTE80.Events2)m_plugin.App.Events).SolutionEvents; + + ((Config)m_plugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } + + private bool AddFilesHandlersInstalled { get { return _itemAddedEventHandler != null || _projectAddedEventHandler != null; } } // second conditional is useless but kept for clarity + private bool RemoveFilesHandlersInstalled { get { return _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; } } // second conditional is useless but kept for clarity + + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)m_plugin.Options).AutoAdd) + { + if (!AddFilesHandlersInstalled) + { + Log.Info("Adding handlers for automatically add files to perforce as you add them to the project"); + _itemAddedEventHandler = new _dispProjectItemsEvents_ItemAddedEventHandler(OnItemAdded); + m_projectEvents.ItemAdded += _itemAddedEventHandler; + + _projectAddedEventHandler = new _dispSolutionEvents_ProjectAddedEventHandler(OnProjectAdded); + m_solutionEvents.ProjectAdded += _projectAddedEventHandler; + } + } + else if (AddFilesHandlersInstalled) + { + Log.Info("Removing handlers for automatically add files to perforce as you add them to the project"); + m_projectEvents.ItemAdded -= _itemAddedEventHandler; + _itemAddedEventHandler = null; + + m_solutionEvents.ProjectAdded -= _projectAddedEventHandler; + _projectAddedEventHandler = null; + } + + if (((Config)m_plugin.Options).AutoDelete) + { + if (!RemoveFilesHandlersInstalled) + { + Log.Info("Adding handlers for automatically deleting files from perforce as you remove them from the project"); + _itemRemovedEventHandler = new _dispProjectItemsEvents_ItemRemovedEventHandler(OnItemRemoved); + m_projectEvents.ItemRemoved += _itemRemovedEventHandler; + + _projectRemovedEventHandler = new _dispSolutionEvents_ProjectRemovedEventHandler(OnProjectRemoved); + m_solutionEvents.ProjectRemoved += _projectRemovedEventHandler; + } + } + else if (RemoveFilesHandlersInstalled) + { + Log.Info("Removing handlers for automatically deleting files from perforce as you remove them from the project"); + m_projectEvents.ItemRemoved -= _itemRemovedEventHandler; + _itemRemovedEventHandler = null; + + m_solutionEvents.ProjectRemoved -= _projectRemovedEventHandler; + _projectRemovedEventHandler = null; + } + } + + public void OnItemAdded(ProjectItem item) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); + + if (item.ProjectItems != null) + { + for (int i = 0; i < item.FileCount; i++) + { + string name = item.get_FileNames((short)i); + P4Operations.AddFile(m_plugin.OutputPane, name); + } + } + else + { + if (System.IO.File.Exists(item.Name)) + P4Operations.AddFile(m_plugin.OutputPane, item.Name); + } + } + + public void OnItemRemoved(ProjectItem item) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); + + for (int i = 0; i < item.FileCount; i++) + { + string name = item.get_FileNames((short)i); + P4Operations.DeleteFile(m_plugin.OutputPane, name); + } + } + + private void OnProjectAdded(Project project) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); + P4Operations.AddFile(m_plugin.OutputPane, project.FullName); + // TODO: [jt] We should if the operation is not a add new project but rather a add existing project + // step through all the project items and add them to perforce. Or maybe we want the user + // to do this herself? + } + + private void OnProjectRemoved(Project project) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); + P4Operations.DeleteFile(m_plugin.OutputPane, project.FullName); + // TODO: [jt] Do we want to automatically delete the items from perforce here? + } + } + } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index f1c0e0c..5b303ad 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -8,107 +8,108 @@ namespace Aurora { - namespace NiftyPerforce - { - // Create a class to retrieve the OnBeforeSave event from VS - // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 - internal class RunningDocTableEvents : IVsRunningDocTableEvents3 - { - private readonly AutoCheckoutOnSave autoCheckoutOnSave; + namespace NiftyPerforce + { + // Create a class to retrieve the OnBeforeSave event from VS + // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 + internal class RunningDocTableEvents : IVsRunningDocTableEvents3 + { + private readonly AutoCheckoutOnSave autoCheckoutOnSave; - public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) - { - this.autoCheckoutOnSave = autoCheckoutOnSave; - } + public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) + { + this.autoCheckoutOnSave = autoCheckoutOnSave; + } - public int OnBeforeSave(uint docCookie) - { - RunningDocumentInfo runningDocumentInfo = autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); - autoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); - return VSConstants.S_OK; - } + public int OnBeforeSave(uint docCookie) + { + RunningDocumentInfo runningDocumentInfo = autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); + autoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); + return VSConstants.S_OK; + } - //////////////////////////////////////////////////////////////////// - // default implementation for the pure methods, return OK - public int OnAfterAttributeChange(uint docCookie, uint grfAttribs){return VSConstants.S_OK;} - public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew){return VSConstants.S_OK;} - public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame){return VSConstants.S_OK;} - public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining){return VSConstants.S_OK;} - public int OnAfterSave(uint docCookie){return VSConstants.S_OK;} - public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame){return VSConstants.S_OK;} - public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining){return VSConstants.S_OK;} - } + //////////////////////////////////////////////////////////////////// + // default implementation for the pure methods, return OK + public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) { return VSConstants.S_OK; } + public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew) { return VSConstants.S_OK; } + public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) { return VSConstants.S_OK; } + public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } + public int OnAfterSave(uint docCookie) { return VSConstants.S_OK; } + public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { return VSConstants.S_OK; } + public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } + } - class AutoCheckoutOnSave : PreCommandFeature - { - internal Lazy _rdt; - internal uint _rdte; - internal Lazy _sp; + class AutoCheckoutOnSave : PreCommandFeature + { + internal Lazy _rdt; + internal uint _rdte; + internal Lazy _sp; - public AutoCheckoutOnSave(Plugin plugin) - : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") - { - ThreadHelper.ThrowIfNotOnUIThread(); - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + public AutoCheckoutOnSave(Plugin plugin) + : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") + { + ThreadHelper.ThrowIfNotOnUIThread(); + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private bool RDTAdvised { get { return _sp != null || _rdt != null; } } + private bool RDTAdvised { get { return _sp != null || _rdt != null; } } - private void RegisterEvents(object sender = null, EventArgs e = null) - { - ThreadHelper.ThrowIfNotOnUIThread(); + private void RegisterEvents(object sender = null, EventArgs e = null) + { + ThreadHelper.ThrowIfNotOnUIThread(); - if (((Config)mPlugin.Options).AutoCheckoutOnSave) - { - if (!RDTAdvised) - { - Log.Info("Adding handlers for automatically checking out dirty files when you save"); - _sp = new Lazy(() => { - ThreadHelper.ThrowIfNotOnUIThread(); - return Package.GetGlobalService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider; - }); - _rdt = new Lazy(() => new RunningDocumentTable(new ServiceProvider(_sp.Value))); - _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); - } - } - else if (RDTAdvised) - { - Log.Info("Removing handlers for automatically checking out dirty files when you save"); - _rdt.Value.Unadvise(_rdte); - _rdt = null; - _sp = null; - } - } + if (((Config)mPlugin.Options).AutoCheckoutOnSave) + { + if (!RDTAdvised) + { + Log.Info("Adding handlers for automatically checking out dirty files when you save"); + _sp = new Lazy(() => + { + ThreadHelper.ThrowIfNotOnUIThread(); + return Package.GetGlobalService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + }); + _rdt = new Lazy(() => new RunningDocumentTable(new ServiceProvider(_sp.Value))); + _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); + } + } + else if (RDTAdvised) + { + Log.Info("Removing handlers for automatically checking out dirty files when you save"); + _rdt.Value.Unadvise(_rdte); + _rdt = null; + _sp = null; + } + } - internal void OnBeforeSave(string filename) - { - P4Operations.EditFileImmediate(mPlugin.OutputPane, filename); - } + internal void OnBeforeSave(string filename) + { + P4Operations.EditFileImmediate(mPlugin.OutputPane, filename); + } - private void EditProjectRecursive(Project p) - { - ThreadHelper.ThrowIfNotOnUIThread(); + private void EditProjectRecursive(Project p) + { + ThreadHelper.ThrowIfNotOnUIThread(); - if (!p.Saved) - P4Operations.EditFileImmediate(mPlugin.OutputPane, p.FullName); + if (!p.Saved) + P4Operations.EditFileImmediate(mPlugin.OutputPane, p.FullName); - if(p.ProjectItems == null) - return; + if (p.ProjectItems == null) + return; - foreach(ProjectItem pi in p.ProjectItems) - { - if(pi.SubProject != null) - { - EditProjectRecursive(pi.SubProject); - } - else if(!pi.Saved) - { - for(short i = 0; i <= pi.FileCount; i++) - P4Operations.EditFileImmediate(mPlugin.OutputPane, pi.get_FileNames(i)); - } - } - } - } - } + foreach (ProjectItem pi in p.ProjectItems) + { + if (pi.SubProject != null) + { + EditProjectRecursive(pi.SubProject); + } + else if (!pi.Saved) + { + for (short i = 0; i <= pi.FileCount; i++) + P4Operations.EditFileImmediate(mPlugin.OutputPane, pi.get_FileNames(i)); + } + } + } + } + } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index 93f12b7..813c6f5 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -6,77 +6,77 @@ namespace Aurora { - namespace NiftyPerforce - { - class AutoCheckoutProject : PreCommandFeature - { - public AutoCheckoutProject(Plugin plugin) - : base(plugin, "AutoCheckoutProject", "Automatically checks out the project files") - { - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + namespace NiftyPerforce + { + class AutoCheckoutProject : PreCommandFeature + { + public AutoCheckoutProject(Plugin plugin) + : base(plugin, "AutoCheckoutProject", "Automatically checks out the project files") + { + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private readonly string[] _commands = - { - "ClassViewContextMenus.ClassViewProject.Properties", - "ClassViewContextMenus.ClassViewMultiselectProjectreferencesItems.Properties", - "File.Properties", - "View.PropertiesWindow", - "Project.Properties", - "Project.AddNewItem", - "Project.AddExistingItem", - "Edit.Delete", // hmm : removing a file from Solution Explorer is just Edit.Delete !? + private readonly string[] _commands = + { + "ClassViewContextMenus.ClassViewProject.Properties", + "ClassViewContextMenus.ClassViewMultiselectProjectreferencesItems.Properties", + "File.Properties", + "View.PropertiesWindow", + "Project.Properties", + "Project.AddNewItem", + "Project.AddExistingItem", + "Edit.Delete", // hmm : removing a file from Solution Explorer is just Edit.Delete !? "File.Remove" // I don't think this actually does anything }; - private List _registeredCommands; + private List _registeredCommands; - private void RegisterEvents(object sender = null, EventArgs e = null) - { - if (((Config)mPlugin.Options).AutoCheckoutProject) - { - if (_registeredCommands == null) - { - Log.Info("Adding handlers for automatically checking out .vcproj files when you do changes to the project"); - foreach (string command in _commands) - { - if (RegisterHandler(command, OnCheckoutSelectedProjects)) - _registeredCommands.Add(command); - else - Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutSelectedProjects), command); - } - } - } - else if (_registeredCommands != null) - { - Log.Info("Removing handlers for automatically checking out .vcproj files when you do changes to the project"); - foreach (string command in _registeredCommands) - UnregisterHandler(command, OnCheckoutSelectedProjects); - _registeredCommands = null; - } - } + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)mPlugin.Options).AutoCheckoutProject) + { + if (_registeredCommands == null) + { + Log.Info("Adding handlers for automatically checking out .vcproj files when you do changes to the project"); + foreach (string command in _commands) + { + if (RegisterHandler(command, OnCheckoutSelectedProjects)) + _registeredCommands.Add(command); + else + Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutSelectedProjects), command); + } + } + } + else if (_registeredCommands != null) + { + Log.Info("Removing handlers for automatically checking out .vcproj files when you do changes to the project"); + foreach (string command in _registeredCommands) + UnregisterHandler(command, OnCheckoutSelectedProjects); + _registeredCommands = null; + } + } - private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - // when I get Edit.Delete : - if (Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}" && ID == 17) - { - // see if the active window is SolutionExplorer : - Window w = mPlugin.App.ActiveWindow; - if(w.Type != EnvDTE.vsWindowType.vsWindowTypeSolutionExplorer) - { - // it's just a delete in the text window, get out ! - return; - } - } + // when I get Edit.Delete : + if (Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}" && ID == 17) + { + // see if the active window is SolutionExplorer : + Window w = mPlugin.App.ActiveWindow; + if (w.Type != EnvDTE.vsWindowType.vsWindowTypeSolutionExplorer) + { + // it's just a delete in the text window, get out ! + return; + } + } - foreach(Project project in (Array)mPlugin.App.ActiveSolutionProjects) - { - P4Operations.EditFileImmediate(mPlugin.OutputPane, project.FullName); - } - } - } - } + foreach (Project project in (Array)mPlugin.App.ActiveSolutionProjects) + { + P4Operations.EditFileImmediate(mPlugin.OutputPane, project.FullName); + } + } + } + } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 446fbef..57a7b5f 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -8,102 +8,102 @@ namespace Aurora { - namespace NiftyPerforce - { - class AutoCheckoutTextEdit : PreCommandFeature - { - private EnvDTE80.TextDocumentKeyPressEvents mTextDocEvents; - private EnvDTE.TextEditorEvents mTextEditorEvents; + namespace NiftyPerforce + { + class AutoCheckoutTextEdit : PreCommandFeature + { + private EnvDTE80.TextDocumentKeyPressEvents mTextDocEvents; + private EnvDTE.TextEditorEvents mTextEditorEvents; - public AutoCheckoutTextEdit(Plugin plugin) - : base(plugin, "AutoCheckoutTextEdit", "Automatically checks out the text file on edits") - { - //((Config)mPlugin.Options).RegisterOnApplyAction(RegisterEvents); - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + public AutoCheckoutTextEdit(Plugin plugin) + : base(plugin, "AutoCheckoutTextEdit", "Automatically checks out the text file on edits") + { + //((Config)mPlugin.Options).RegisterOnApplyAction(RegisterEvents); + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private readonly string[] _commands = - { - "Edit.Delete", - "Edit.DeleteBackwards", - "Edit.Paste" - }; - private List _registeredCommands; - private _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler _beforeKeyPressEventHandler; - private _dispTextEditorEvents_LineChangedEventHandler _lineChangedEventHandler; + private readonly string[] _commands = + { + "Edit.Delete", + "Edit.DeleteBackwards", + "Edit.Paste" + }; + private List _registeredCommands; + private _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler _beforeKeyPressEventHandler; + private _dispTextEditorEvents_LineChangedEventHandler _lineChangedEventHandler; - private void RegisterEvents(object sender = null, EventArgs e = null) - { - if (((Config)mPlugin.Options).AutoCheckoutOnEdit) - { - if (_registeredCommands == null) - { - Log.Info("Adding handlers for automatically checking out text files as you edit them"); - _registeredCommands = new List(); - var events = (EnvDTE80.Events2)mPlugin.App.Events; - mTextDocEvents = events.get_TextDocumentKeyPressEvents(null); - _beforeKeyPressEventHandler = new _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler(OnBeforeKeyPress); - mTextDocEvents.BeforeKeyPress += _beforeKeyPressEventHandler; + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)mPlugin.Options).AutoCheckoutOnEdit) + { + if (_registeredCommands == null) + { + Log.Info("Adding handlers for automatically checking out text files as you edit them"); + _registeredCommands = new List(); + var events = (EnvDTE80.Events2)mPlugin.App.Events; + mTextDocEvents = events.get_TextDocumentKeyPressEvents(null); + _beforeKeyPressEventHandler = new _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler(OnBeforeKeyPress); + mTextDocEvents.BeforeKeyPress += _beforeKeyPressEventHandler; - mTextEditorEvents = events.get_TextEditorEvents(null); - _lineChangedEventHandler = new _dispTextEditorEvents_LineChangedEventHandler(OnLineChanged); - mTextEditorEvents.LineChanged += _lineChangedEventHandler; + mTextEditorEvents = events.get_TextEditorEvents(null); + _lineChangedEventHandler = new _dispTextEditorEvents_LineChangedEventHandler(OnLineChanged); + mTextEditorEvents.LineChanged += _lineChangedEventHandler; - foreach (string command in _commands) - { - if (RegisterHandler(command, OnCheckoutCurrentDocument)) - _registeredCommands.Add(command); - else - Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutCurrentDocument), command); - } - } - } - else if (_registeredCommands != null) - { - Log.Info("Removing handlers for automatically checking out text files as you edit them"); - foreach (string command in _registeredCommands) - UnregisterHandler(command, OnCheckoutCurrentDocument); - _registeredCommands = null; + foreach (string command in _commands) + { + if (RegisterHandler(command, OnCheckoutCurrentDocument)) + _registeredCommands.Add(command); + else + Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutCurrentDocument), command); + } + } + } + else if (_registeredCommands != null) + { + Log.Info("Removing handlers for automatically checking out text files as you edit them"); + foreach (string command in _registeredCommands) + UnregisterHandler(command, OnCheckoutCurrentDocument); + _registeredCommands = null; - mTextEditorEvents.LineChanged -= _lineChangedEventHandler; - mTextEditorEvents = null; + mTextEditorEvents.LineChanged -= _lineChangedEventHandler; + mTextEditorEvents = null; - mTextDocEvents.BeforeKeyPress -= _beforeKeyPressEventHandler; - mTextDocEvents = null; - } - } + mTextDocEvents.BeforeKeyPress -= _beforeKeyPressEventHandler; + mTextDocEvents = null; + } + } - private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, bool InStatementCompletion, ref bool CancelKeypress) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, bool InStatementCompletion, ref bool CancelKeypress) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); - } + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) + P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + } - // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but - // I can't get this one to fire reliably... Wonder how much these handlers will slow down the IDE? - private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if ((Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && - (Hint & (int)vsTextChanged.vsTextChangedMultiLine) == 0 && - (Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && - (Hint != 0)) - return; - if(mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); - } + // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but + // I can't get this one to fire reliably... Wonder how much these handlers will slow down the IDE? + private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if ((Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && + (Hint & (int)vsTextChanged.vsTextChangedMultiLine) == 0 && + (Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && + (Hint != 0)) + return; + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) + P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + } - private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); - } + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) + P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + } - } - } + } + } } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index b0af503..b5c648a 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -17,261 +17,263 @@ namespace NiftyPerforce { - /// - /// This is the class that implements the package exposed by this assembly. - /// - /// - /// - /// The minimum requirement for a class to be considered a valid package for Visual Studio - /// is to implement the IVsPackage interface and register itself with the shell. - /// This package uses the helper classes defined inside the Managed Package Framework (MPF) - /// to do it: it derives from the Package class that provides the implementation of the - /// IVsPackage interface and uses the registration attributes defined in the framework to - /// register itself and its components with the shell. These attributes tell the pkgdef creation - /// utility what data to put into .pkgdef file. - /// - /// - /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. - /// - /// - // Declare that resources for the package are to be found in the managed assembly resources, and not in a satellite dll - [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] - // Register the product to be listed in About box - [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] - [ProvideAutoLoad(Microsoft.VisualStudio.VSConstants.UICONTEXT.NoSolution_string, PackageAutoLoadFlags.BackgroundLoad)] // Note: the package must be loaded on startup to create and bind commands - // Register the resource ID of the CTMENU section (generated from compiling the VSCT file), so the IDE will know how to merge this package's menus with the rest of the IDE when "devenv /setup" is run - // The menu resource ID needs to match the ResourceName number defined in the csproj project file in the VSCTCompile section - // Everytime the version number changes VS will automatically update the menus on startup; if the version doesn't change, you will need to run manually "devenv /setup /rootsuffix:Exp" to see VSCT changes reflected in IDE - [ProvideMenuResource("Menus.ctmenu", 1)] - // Register a sample options page visible as Tools/Options/SourceControl/NiftyPerforceSettings when the provider is active - [ProvideOptionPage(typeof(Config), "Source Control", Vsix.Name, 106, 107, false)] - //[ProvideToolsOptionsPageVisibility("Source Control", "Nifty Perforce Settings", PackageGuids.guidNiftyPerforceSccProviderString)] - // Declare the package guid - [Guid(PackageGuids.guidNiftyPerforcePackageString)] - public sealed class NiftyPerforcePackage : AsyncPackage - { - private Plugin m_plugin = null; - private CommandRegistry m_commandRegistry = null; - - public NiftyPerforcePackage() - { - // 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. - } - - #region Package Members - - /// - /// Initialization of the package; this method is called right after the package is sited, so this is the place - /// where you can put all the initialization code that rely on services provided by VisualStudio. - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) - { - await base.InitializeAsync(cancellationToken, progress); - - // Every plugin needs a command bar. - DTE2 application = await GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2; - OleMenuCommandService oleMenuCommandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; - - // Switches to the UI thread in order to consume some services used in command initialization - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var config = (Config)GetDialogPage(typeof(Config)); - Singleton.Instance = config; - - config.OnApplyEvent += (object sender, EventArgs e) => { - if (config.CleanLegacyNiftyCommands) - { - Cleanup(); - config.CleanLegacyNiftyCommands = false; - } - }; - - m_plugin = new Plugin(application, oleMenuCommandService, "NiftyPerforce", "Aurora.NiftyPerforce.Connect", config); - - m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); - - // Initialize the logging system. - if (Log.HandlerCount == 0) - { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. These attributes tell the pkgdef creation + /// utility what data to put into .pkgdef file. + /// + /// + /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. + /// + /// + // Declare that resources for the package are to be found in the managed assembly resources, and not in a satellite dll + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + // Register the product to be listed in About box + [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] + [ProvideAutoLoad(Microsoft.VisualStudio.VSConstants.UICONTEXT.NoSolution_string, PackageAutoLoadFlags.BackgroundLoad)] // Note: the package must be loaded on startup to create and bind commands + // Register the resource ID of the CTMENU section (generated from compiling the VSCT file), so the IDE will know how to merge this package's menus with the rest of the IDE when "devenv /setup" is run + // The menu resource ID needs to match the ResourceName number defined in the csproj project file in the VSCTCompile section + // Everytime the version number changes VS will automatically update the menus on startup; if the version doesn't change, you will need to run manually "devenv /setup /rootsuffix:Exp" to see VSCT changes reflected in IDE + [ProvideMenuResource("Menus.ctmenu", 1)] + // Register a sample options page visible as Tools/Options/SourceControl/NiftyPerforceSettings when the provider is active + [ProvideOptionPage(typeof(Config), "Source Control", Vsix.Name, 106, 107, false)] + //[ProvideToolsOptionsPageVisibility("Source Control", "Nifty Perforce Settings", PackageGuids.guidNiftyPerforceSccProviderString)] + // Declare the package guid + [Guid(PackageGuids.guidNiftyPerforcePackageString)] + public sealed class NiftyPerforcePackage : AsyncPackage + { + private Plugin m_plugin = null; + private CommandRegistry m_commandRegistry = null; + + public NiftyPerforcePackage() + { + // 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. + } + + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + await base.InitializeAsync(cancellationToken, progress); + + // Every plugin needs a command bar. + DTE2 application = await GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2; + OleMenuCommandService oleMenuCommandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + + // Switches to the UI thread in order to consume some services used in command initialization + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var config = (Config)GetDialogPage(typeof(Config)); + Singleton.Instance = config; + + config.OnApplyEvent += (object sender, EventArgs e) => + { + if (config.CleanLegacyNiftyCommands) + { + Cleanup(); + config.CleanLegacyNiftyCommands = false; + } + }; + + m_plugin = new Plugin(application, oleMenuCommandService, "NiftyPerforce", "Aurora.NiftyPerforce.Connect", config); + + m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); + + // Initialize the logging system. + if (Log.HandlerCount == 0) + { #if DEBUG - Log.AddHandler(new DebugLogHandler()); + Log.AddHandler(new DebugLogHandler()); #endif - Log.AddHandler(new VisualStudioLogHandler(m_plugin)); - Log.Prefix = "NiftyPerforce"; - } + Log.AddHandler(new VisualStudioLogHandler(m_plugin)); + Log.Prefix = "NiftyPerforce"; + } - // Now we can take care of registering ourselves and all our commands and hooks. - Log.Debug("Booting up..."); - Log.IncIndent(); + // Now we can take care of registering ourselves and all our commands and hooks. + Log.Debug("Booting up..."); + Log.IncIndent(); - // Add our command handlers for menu (commands must exist in the .vsct file) - m_commandRegistry.RegisterCommand(new P4EditModified (m_plugin, "NiftyEditModified")); - m_commandRegistry.RegisterCommand(new P4EditItem (m_plugin, "NiftyEdit")); - m_commandRegistry.RegisterCommand(new P4DiffItem (m_plugin, "NiftyDiff")); - m_commandRegistry.RegisterCommand(new P4RevisionHistoryItem(m_plugin, "NiftyHistory", false)); - m_commandRegistry.RegisterCommand(new P4RevisionHistoryItem(m_plugin, "NiftyHistoryMain", true)); - m_commandRegistry.RegisterCommand(new P4TimeLapseItem (m_plugin, "NiftyTimeLapse", false)); - m_commandRegistry.RegisterCommand(new P4TimeLapseItem (m_plugin, "NiftyTimeLapseMain", true)); - m_commandRegistry.RegisterCommand(new P4RevisionGraphItem (m_plugin, "NiftyRevisionGraph", false)); - m_commandRegistry.RegisterCommand(new P4RevisionGraphItem (m_plugin, "NiftyRevisionGraphMain", true)); - m_commandRegistry.RegisterCommand(new P4RevertItem (m_plugin, "NiftyRevert", false)); - m_commandRegistry.RegisterCommand(new P4RevertItem (m_plugin, "NiftyRevertUnchanged", true)); - m_commandRegistry.RegisterCommand(new P4ShowItem (m_plugin, "NiftyShow")); + // Add our command handlers for menu (commands must exist in the .vsct file) + m_commandRegistry.RegisterCommand(new P4EditModified(m_plugin, "NiftyEditModified")); + m_commandRegistry.RegisterCommand(new P4EditItem(m_plugin, "NiftyEdit")); + m_commandRegistry.RegisterCommand(new P4DiffItem(m_plugin, "NiftyDiff")); + m_commandRegistry.RegisterCommand(new P4RevisionHistoryItem(m_plugin, "NiftyHistory", false)); + m_commandRegistry.RegisterCommand(new P4RevisionHistoryItem(m_plugin, "NiftyHistoryMain", true)); + m_commandRegistry.RegisterCommand(new P4TimeLapseItem(m_plugin, "NiftyTimeLapse", false)); + m_commandRegistry.RegisterCommand(new P4TimeLapseItem(m_plugin, "NiftyTimeLapseMain", true)); + m_commandRegistry.RegisterCommand(new P4RevisionGraphItem(m_plugin, "NiftyRevisionGraph", false)); + m_commandRegistry.RegisterCommand(new P4RevisionGraphItem(m_plugin, "NiftyRevisionGraphMain", true)); + m_commandRegistry.RegisterCommand(new P4RevertItem(m_plugin, "NiftyRevert", false)); + m_commandRegistry.RegisterCommand(new P4RevertItem(m_plugin, "NiftyRevertUnchanged", true)); + m_commandRegistry.RegisterCommand(new P4ShowItem(m_plugin, "NiftyShow")); - m_plugin.AddFeature(new AutoAddDelete(m_plugin)); - m_plugin.AddFeature(new AutoCheckoutProject(m_plugin)); - m_plugin.AddFeature(new AutoCheckoutTextEdit(m_plugin)); - m_plugin.AddFeature(new AutoCheckoutOnSave(m_plugin)); + m_plugin.AddFeature(new AutoAddDelete(m_plugin)); + m_plugin.AddFeature(new AutoCheckoutProject(m_plugin)); + m_plugin.AddFeature(new AutoCheckoutTextEdit(m_plugin)); + m_plugin.AddFeature(new AutoCheckoutOnSave(m_plugin)); - P4Operations.CheckInstalledFiles(); + P4Operations.CheckInstalledFiles(); - AsyncProcess.Init(); + AsyncProcess.Init(); - Log.DecIndent(); - Log.Debug("Initialized..."); + Log.DecIndent(); + Log.Debug("Initialized..."); #if DEBUG - Log.Info("NiftyPerforce (Debug)"); + Log.Info("NiftyPerforce (Debug)"); #else //Log.Info("NiftyPerforce (Release)"); #endif - // Show where we are and when we were compiled... - Log.Info("I'm running {0} compiled on {1}", Assembly.GetExecutingAssembly().Location, System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location)); - } - - /// - /// Removes all installed legacy commands and controls. - /// - public void Cleanup() - { - Log.Info("Cleaning up all legacy nifty commands"); - - ThreadHelper.ThrowIfNotOnUIThread(); - - IVsProfferCommands3 profferCommands3 = base.GetService(typeof(SVsProfferCommands)) as IVsProfferCommands3; - RemoveCommandBar("NiftyPerforceCmdBar", profferCommands3); - RemoveCommandBar("NiftyPerforce", profferCommands3); - - RemoveCommand("NiftyConfig", profferCommands3); - RemoveCommand("NiftyEditModified", profferCommands3); - RemoveCommand("NiftyEdit", profferCommands3); - RemoveCommand("NiftyEditItem", profferCommands3); - RemoveCommand("NiftyEditSolution", profferCommands3); - RemoveCommand("NiftyDiff", profferCommands3); - RemoveCommand("NiftyDiffItem", profferCommands3); - RemoveCommand("NiftyDiffSolution", profferCommands3); - RemoveCommand("NiftyHistory", profferCommands3); - RemoveCommand("NiftyHistoryMain", profferCommands3); - RemoveCommand("NiftyHistoryItem", profferCommands3); - RemoveCommand("NiftyHistoryItemMain", profferCommands3); - RemoveCommand("NiftyHistorySolution", profferCommands3); - RemoveCommand("NiftyTimeLapse", profferCommands3); - RemoveCommand("NiftyTimeLapseMain", profferCommands3); - RemoveCommand("NiftyTimeLapseItem", profferCommands3); - RemoveCommand("NiftyTimeLapseItemMain", profferCommands3); - RemoveCommand("NiftyRevisionGraph", profferCommands3); - RemoveCommand("NiftyRevisionGraphMain", profferCommands3); - RemoveCommand("NiftyRevisionGraphItem", profferCommands3); - RemoveCommand("NiftyRevisionGraphItemMain", profferCommands3); - RemoveCommand("NiftyRevert", profferCommands3); - RemoveCommand("NiftyRevertItem", profferCommands3); - RemoveCommand("NiftyRevertUnchanged", profferCommands3); - RemoveCommand("NiftyRevertUnchangedItem", profferCommands3); - RemoveCommand("NiftyShow", profferCommands3); - RemoveCommand("NiftyShowItem", profferCommands3); - } - - private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - try - { - Command cmd = m_plugin.Commands.Item(name, -1); - if (cmd != null) - { - profferCommands3.RemoveNamedCommand(name); - } - } catch (Exception) { } - - string[] bars = { - "Project", - "Item", - "Easy MDI Document Window", - "Cross Project Multi Item", - "Cross Project Multi Project" - }; - - string Absname = m_plugin.Prefix + "." + name; - - - foreach (string bar in bars) - { - CommandBar b = ((CommandBars)m_plugin.App.CommandBars)[bar]; - if (null != b) - { - bool done = false; - while (!done) - { - bool found = false; - foreach (CommandBarControl ctrl in b.Controls) - { - if (ctrl.Caption == name || ctrl.Caption == Absname) - { - found = true; - try - { - profferCommands3.RemoveCommandBarControl(ctrl); - } - catch (Exception) - { - } - break; - } - } - done = !found; - } - } - } - } - - // Remove a command bar and contained controls - private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCommands3) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - DTE2 dte = GetGlobalService(typeof(DTE)) as DTE2; - CommandBars commandBars = (CommandBars)dte.CommandBars; - CommandBar existingCmdBar = null; - - try - { - existingCmdBar = commandBars[name]; - } - catch (Exception) - { - } - - if (existingCmdBar != null) - { - // Remove all buttons - - while (existingCmdBar.Controls.Count > 0) - { - foreach (CommandBarControl ctrl in existingCmdBar.Controls) - { - profferCommands3.RemoveCommandBarControl(ctrl); - break; - } - } - } - profferCommands3.RemoveCommandBar(existingCmdBar); - } - - #endregion - } + // Show where we are and when we were compiled... + Log.Info("I'm running {0} compiled on {1}", Assembly.GetExecutingAssembly().Location, System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location)); + } + + /// + /// Removes all installed legacy commands and controls. + /// + public void Cleanup() + { + Log.Info("Cleaning up all legacy nifty commands"); + + ThreadHelper.ThrowIfNotOnUIThread(); + + IVsProfferCommands3 profferCommands3 = base.GetService(typeof(SVsProfferCommands)) as IVsProfferCommands3; + RemoveCommandBar("NiftyPerforceCmdBar", profferCommands3); + RemoveCommandBar("NiftyPerforce", profferCommands3); + + RemoveCommand("NiftyConfig", profferCommands3); + RemoveCommand("NiftyEditModified", profferCommands3); + RemoveCommand("NiftyEdit", profferCommands3); + RemoveCommand("NiftyEditItem", profferCommands3); + RemoveCommand("NiftyEditSolution", profferCommands3); + RemoveCommand("NiftyDiff", profferCommands3); + RemoveCommand("NiftyDiffItem", profferCommands3); + RemoveCommand("NiftyDiffSolution", profferCommands3); + RemoveCommand("NiftyHistory", profferCommands3); + RemoveCommand("NiftyHistoryMain", profferCommands3); + RemoveCommand("NiftyHistoryItem", profferCommands3); + RemoveCommand("NiftyHistoryItemMain", profferCommands3); + RemoveCommand("NiftyHistorySolution", profferCommands3); + RemoveCommand("NiftyTimeLapse", profferCommands3); + RemoveCommand("NiftyTimeLapseMain", profferCommands3); + RemoveCommand("NiftyTimeLapseItem", profferCommands3); + RemoveCommand("NiftyTimeLapseItemMain", profferCommands3); + RemoveCommand("NiftyRevisionGraph", profferCommands3); + RemoveCommand("NiftyRevisionGraphMain", profferCommands3); + RemoveCommand("NiftyRevisionGraphItem", profferCommands3); + RemoveCommand("NiftyRevisionGraphItemMain", profferCommands3); + RemoveCommand("NiftyRevert", profferCommands3); + RemoveCommand("NiftyRevertItem", profferCommands3); + RemoveCommand("NiftyRevertUnchanged", profferCommands3); + RemoveCommand("NiftyRevertUnchangedItem", profferCommands3); + RemoveCommand("NiftyShow", profferCommands3); + RemoveCommand("NiftyShowItem", profferCommands3); + } + + private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + try + { + Command cmd = m_plugin.Commands.Item(name, -1); + if (cmd != null) + { + profferCommands3.RemoveNamedCommand(name); + } + } + catch (Exception) { } + + string[] bars = { + "Project", + "Item", + "Easy MDI Document Window", + "Cross Project Multi Item", + "Cross Project Multi Project" + }; + + string Absname = m_plugin.Prefix + "." + name; + + + foreach (string bar in bars) + { + CommandBar b = ((CommandBars)m_plugin.App.CommandBars)[bar]; + if (null != b) + { + bool done = false; + while (!done) + { + bool found = false; + foreach (CommandBarControl ctrl in b.Controls) + { + if (ctrl.Caption == name || ctrl.Caption == Absname) + { + found = true; + try + { + profferCommands3.RemoveCommandBarControl(ctrl); + } + catch (Exception) + { + } + break; + } + } + done = !found; + } + } + } + } + + // Remove a command bar and contained controls + private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCommands3) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + DTE2 dte = GetGlobalService(typeof(DTE)) as DTE2; + CommandBars commandBars = (CommandBars)dte.CommandBars; + CommandBar existingCmdBar = null; + + try + { + existingCmdBar = commandBars[name]; + } + catch (Exception) + { + } + + if (existingCmdBar != null) + { + // Remove all buttons + + while (existingCmdBar.Controls.Count > 0) + { + foreach (CommandBarControl ctrl in existingCmdBar.Controls) + { + profferCommands3.RemoveCommandBarControl(ctrl); + break; + } + } + } + profferCommands3.RemoveCommandBar(existingCmdBar); + } + + #endregion + } } diff --git a/NiftyPerforce/OptionsDialogPage.cs b/NiftyPerforce/OptionsDialogPage.cs index ada830f..2799678 100644 --- a/NiftyPerforce/OptionsDialogPage.cs +++ b/NiftyPerforce/OptionsDialogPage.cs @@ -14,63 +14,63 @@ IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR namespace NiftyPerforce { - /// - /// Summary description for SccProviderOptions. - /// - //[Guid(PackageGuids.guidNiftyPerforceSccProviderOptionsString)] - public class Config : DialogPage - { - [Category("Operation")] - [Description("Controls if we automagically check out files from perforce upon keypress (loose some performance in editor)")] - public bool AutoCheckoutOnEdit { get; set; } = false; + /// + /// Summary description for SccProviderOptions. + /// + //[Guid(PackageGuids.guidNiftyPerforceSccProviderOptionsString)] + public class Config : DialogPage + { + [Category("Operation")] + [Description("Controls if we automagically check out files from perforce upon keypress (loose some performance in editor)")] + public bool AutoCheckoutOnEdit { get; set; } = false; - [Category("Operation"), Description("Automatically check out projects on edit properties (loose some performance in editor)")] - public bool AutoCheckoutProject { get; set; } = false; + [Category("Operation"), Description("Automatically check out projects on edit properties (loose some performance in editor)")] + public bool AutoCheckoutProject { get; set; } = false; - [Category("Operation"), Description("Controls if we automagically check out files from perforce before saving")] - public bool AutoCheckoutOnSave { get; set; } = true; + [Category("Operation"), Description("Controls if we automagically check out files from perforce before saving")] + public bool AutoCheckoutOnSave { get; set; } = true; - [Category("Operation"), Description("Automagically add files to perforce")] - public bool AutoAdd { get; set; } = false; + [Category("Operation"), Description("Automagically add files to perforce")] + public bool AutoAdd { get; set; } = false; - [Category("Operation"), Description("Automagically delete files from perforce when we're deleting files from visual studio (fairly dangerous)")] - public bool AutoDelete { get; set; } = false; + [Category("Operation"), Description("Automagically delete files from perforce when we're deleting files from visual studio (fairly dangerous)")] + public bool AutoDelete { get; set; } = false; - [Category("Operation"), Description("Try to do a p4 edit even though the file is writable. Useful if you have a git repository above your p4 workspace. Costly!")] - public bool IgnoreReadOnlyOnEdit { get; set; } = false; + [Category("Operation"), Description("Try to do a p4 edit even though the file is writable. Useful if you have a git repository above your p4 workspace. Costly!")] + public bool IgnoreReadOnlyOnEdit { get; set; } = false; - [Category("Connection"), Description("Use config from system. Effectivly disables the settings inside this dialog for the client etc and picks up the settings from the registry/p4config environment.")] - public bool UseSystemEnv { get; set; } = true; + [Category("Connection"), Description("Use config from system. Effectivly disables the settings inside this dialog for the client etc and picks up the settings from the registry/p4config environment.")] + public bool UseSystemEnv { get; set; } = true; - [Category("Connection"), Description("Perforce port number")] - public string Port { get; set; } = ""; + [Category("Connection"), Description("Perforce port number")] + public string Port { get; set; } = ""; - [Category("Connection"), Description("Perforce client")] - public string Client { get; set; } = ""; + [Category("Connection"), Description("Perforce client")] + public string Client { get; set; } = ""; - [Category("Connection"), Description("Perforce username")] - public string Username { get; set; } = ""; + [Category("Connection"), Description("Perforce username")] + public string Username { get; set; } = ""; - [Category("Branching"), Description("Where we can find the mainline version of this file")] - public string MainLinePath { get; set; } = ""; + [Category("Branching"), Description("Where we can find the mainline version of this file")] + public string MainLinePath { get; set; } = ""; - [Category("VSIX Legacy"), Description("Clean the legacy nifty perforce commands from the IDE when clicking ok. Will not be persisted.")] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool CleanLegacyNiftyCommands { get; set; } = false; + [Category("VSIX Legacy"), Description("Clean the legacy nifty perforce commands from the IDE when clicking ok. Will not be persisted.")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool CleanLegacyNiftyCommands { get; set; } = false; - public event EventHandler OnApplyEvent; + public event EventHandler OnApplyEvent; - protected override void OnApply(PageApplyEventArgs e) - { - base.OnApply(e); - if (e?.ApplyBehavior == ApplyKind.Apply) - OnApplyEvent?.Invoke(this, EventArgs.Empty); - } + protected override void OnApply(PageApplyEventArgs e) + { + base.OnApply(e); + if (e?.ApplyBehavior == ApplyKind.Apply) + OnApplyEvent?.Invoke(this, EventArgs.Empty); + } - protected override void SaveSetting(PropertyDescriptor property) - { - base.SaveSetting(property); - } - } + protected override void SaveSetting(PropertyDescriptor property) + { + base.SaveSetting(property); + } + } } diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 29e1d7e..730853f 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -8,520 +8,520 @@ namespace NiftyPerforce { - // Simplification wrapper around running perforce commands. - class P4Operations - { - private static bool g_p4installed = false; - private static bool g_p4vinstalled = false; - private static bool g_p4customdiff = false; - private static string g_p4vc_exename = null; - - private static bool g_p4vc_history_supported = false; - private static bool g_p4vc_diffhave_supported = false; - - private static readonly Dictionary g_opsInFlight = new Dictionary(); - - private static readonly HashSet g_alreadyNotified = new HashSet(); - - private static bool LockOp(string token) - { - try - { - lock (g_opsInFlight) - { - g_opsInFlight.Add(token, true); - } - Log.Debug("## Locked \"" + token + "\"" ); - return true; - } - catch(ArgumentException) - { - //Log.Debug("!! Failed to lock \"" + token + "\""); - Log.Error(token + " already in progress"); - return false; - } - } - - private static void UnlockOp(bool ok, object token_) - { - string token = (string)token_; - try - { - lock (g_opsInFlight) - { - if (g_opsInFlight.Remove(token)) - { - Log.Debug("## Unlocked \"" + token + "\""); - } - else - { - Log.Debug("!! Failed to unlock \"" + token + "\""); - } - } - } - catch (ArgumentNullException) - { - } - } - - private static string FormatToken(string operation, string filename) - { - string token = operation + " " + Path.GetFullPath(filename).ToLowerInvariant(); - return token; - } - - public delegate bool CheckoutCallback(OutputWindowPane output, string filename); - - public static bool DeleteFile(OutputWindowPane output, string filename) - { - if(filename.Length == 0) - return false; - if(!g_p4installed) - return NotifyUser("could not find p4 exe installed in perforce directory"); - - string token = FormatToken("delete", filename); - if (!LockOp(token)) - return false; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "delete \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - } - - public static bool AddFile(OutputWindowPane output, string filename) - { - if(filename.Length == 0) - return false; - if(!g_p4installed) - return NotifyUser("could not find p4 exe installed in perforce directory"); - - string token = FormatToken("add", filename); - if (!LockOp(token)) - return false; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - } - - public static bool EditFile(OutputWindowPane output, string filename) - { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFile), output, filename); - } - - public static bool EditFileImmediate(OutputWindowPane output, string filename) - { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFileImmediate), output, filename); - } - - private static bool Internal_CheckEditFile(CheckoutCallback callback, OutputWindowPane output, string filename) - { - Log.Debug($"Edit '{filename}'"); - bool result = callback(output, filename); - - string ext = Path.GetExtension(filename).ToLowerInvariant(); - if (ext == ".vcxproj") - { - callback(output, filename + ".filters"); - } - - if (ext == ".settings" || ext == ".resx") - { - callback(output, Path.ChangeExtension(filename, ".Designer.cs")); - } - - if (ext == ".cs") - { - callback(output, Path.ChangeExtension(filename, ".Designer.cs")); - callback(output, Path.ChangeExtension(filename, ".resx")); - } - - return result; - } - - private static bool Internal_EditFile(OutputWindowPane output, string filename) - { - return Internal_EditFile(output, filename, false); - } - - private static bool Internal_EditFileImmediate(OutputWindowPane output, string filename) - { - return Internal_EditFile(output, filename, true); - } - - private static bool Internal_EditFile(OutputWindowPane output, string filename, bool immediate) - { - if (filename.Length == 0) - { - Log.Debug("EditFile failed due to empty filename"); - return false; - } - if (!File.Exists(filename)) - { - Log.Debug($"EditFile '{filename}' failed due to not existing file"); - return false; - } - if (!Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) - { - Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle IgnoreReadOnlyOnEdit in the options."); - return false; - } - if (!g_p4installed) - { - Log.Debug($"EditFile '{filename}' failed because p4 exe was not found"); - return NotifyUser("could not find p4 exe installed in perforce directory"); - } - - Log.Debug("EditFile" + (immediate ? "Immediate " : " ") + filename); - string token = FormatToken("edit", filename); - if (!LockOp(token)) - return false; - - if (immediate) - return AsyncProcess.Run(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - } - - public static bool RevertFile(OutputWindowPane output, string filename, bool onlyUnchanged) - { - if(filename.Length == 0) - return false; - if(!g_p4installed) - return NotifyUser("could not find p4 exe installed in perforce directory"); - - string token = FormatToken("revert", filename); - if (!LockOp(token)) - return false; - - string revertArguments = onlyUnchanged ? "-a " : string.Empty; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "revert " + revertArguments + "\"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - } - - public static bool DiffFile(OutputWindowPane output, string filename) - { - if(filename.Length == 0) - return false; - - if (!g_p4installed) - return NotifyUser("could not find p4.exe installed in perforce directory"); - - string token = FormatToken("diff", filename); - if (!LockOp(token)) - return false; - - string dirname = Path.GetDirectoryName(filename); - - // Let's figure out if the user has some custom diff tool installed. Then we just send whatever we have without any fancy options. - if (g_p4customdiff) - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); - - if (g_p4vc_diffhave_supported) - return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " diffhave \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - - // Otherwise let's show a unified diff in the outputpane. - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff -du \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); - } - - public static bool RevisionHistoryFile(OutputWindowPane output, string dirname, string filename) - { - if(filename.Length == 0) - return false; - string token = FormatToken("history", filename); - if (!LockOp(token)) - return false; - if(g_p4vc_history_supported) - return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - if(g_p4vinstalled) - return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - return NotifyUser("could not find a supported p4vc.exe or p4v.exe installed in perforce directory"); - } - - public static bool P4VShowFile(OutputWindowPane output, string filename) - { - if(filename.Length == 0) - return false; - if (g_p4vinstalled) // note that the cmd line also accepts -t to open P4V with a specific tab shown - return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, Path.GetDirectoryName(filename)) + " -s \"" + filename + "\"", Path.GetDirectoryName(filename), null, null, 0); - return NotifyUser("could not find p4v.exe installed in perforce directory"); - } - - private static string GetUserInfoString() - { - return GetUserInfoStringFull(false, ""); - } - - private static string GetUserInfoStringFull(bool lookup, string dir) - { - // NOTE: This to allow the user to have a P4CONFIG variable and connect to multiple perforce servers seamlessly. - if (Singleton.Instance.UseSystemEnv) - { - if(lookup) - { - try - { - string output = Aurora.Process.Execute("p4", dir, $"-s -L \"{dir}\" info"); - Regex userpattern = new Regex(@"User name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex portpattern = new Regex(@"Server address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex brokerpattern = new Regex(@"Broker address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex clientpattern = new Regex(@"Client name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - - Match usermatch = userpattern.Match(output); - Match portmatch = portpattern.Match(output); - Match brokermatch = brokerpattern.Match(output); - Match clientmatch = clientpattern.Match(output); - - string port = portmatch.Groups["port"].Value.Trim(); - string broker = brokermatch.Groups["port"].Value.Trim(); - string username = usermatch.Groups["user"].Value.Trim(); - string client = clientmatch.Groups["client"].Value.Trim(); - - string server = broker; - if (string.IsNullOrEmpty(server)) - server = port; - - string ret = $" -p {server} -u {username} -c {client} "; - - Log.Debug("GetUserInfoStringFull : " + ret); - - return ret; - } - catch (Aurora.Process.Error e) - { - Log.Error("Failed to execute info string discovery: {0}", e.info); - } - } - - return ""; - } - - var config = Singleton.Instance; - string arguments = ""; - arguments += " -p " + config.Port; - arguments += " -u " + config.Username; - arguments += " -c " + config.Client; - arguments += " "; - - Log.Debug("GetUserInfoStringFull : " + arguments); - - return arguments; - } - - public static bool TimeLapseView(OutputWindowPane output, string dirname, string filename) - { - if(string.IsNullOrEmpty(g_p4vc_exename)) - return NotifyUser("could not find p4vc in perforce directory"); - - string arguments = GetUserInfoStringFull(true, dirname); - arguments += " tlv \"" + filename + "\""; - - - string token = FormatToken("timelapse", filename); - if (!LockOp(token)) - return false; - return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - } - - public static bool RevisionGraph(OutputWindowPane output, string dirname, string filename) - { - if (string.IsNullOrEmpty(g_p4vc_exename)) - return NotifyUser("could not find p4vc in perforce directory"); - - string arguments = GetUserInfoStringFull(true, dirname); - arguments += " revisiongraph \"" + filename + "\""; - - string token = FormatToken("revisiongraph", filename); - if (!LockOp(token)) - return false; - return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - } - - public static string GetRegistryValue(string key, string value, bool global) - { - Microsoft.Win32.RegistryKey hklm = Microsoft.Win32.Registry.LocalMachine; - if(!global) - hklm = Microsoft.Win32.Registry.CurrentUser; - hklm = hklm.OpenSubKey(key); - if(null == hklm) - { - Log.Debug("Could not find registry key " + (global ? "HKLM\\" : "HKCU\\") + key); - return null; - } - Object regValue = hklm.GetValue(value); - if(null == regValue) - { - Log.Debug("Could not find registry value " + value + " in " + (global ? "HKLM\\" : "HKCU\\") + key); - return null; - } - - return (string)regValue; - } - - private static bool LookupP4VC(Func check) + // Simplification wrapper around running perforce commands. + class P4Operations + { + private static bool g_p4installed = false; + private static bool g_p4vinstalled = false; + private static bool g_p4customdiff = false; + private static string g_p4vc_exename = null; + + private static bool g_p4vc_history_supported = false; + private static bool g_p4vc_diffhave_supported = false; + + private static readonly Dictionary g_opsInFlight = new Dictionary(); + + private static readonly HashSet g_alreadyNotified = new HashSet(); + + private static bool LockOp(string token) + { + try + { + lock (g_opsInFlight) + { + g_opsInFlight.Add(token, true); + } + Log.Debug("## Locked \"" + token + "\""); + return true; + } + catch (ArgumentException) + { + //Log.Debug("!! Failed to lock \"" + token + "\""); + Log.Error(token + " already in progress"); + return false; + } + } + + private static void UnlockOp(bool ok, object token_) + { + string token = (string)token_; + try + { + lock (g_opsInFlight) + { + if (g_opsInFlight.Remove(token)) + { + Log.Debug("## Unlocked \"" + token + "\""); + } + else + { + Log.Debug("!! Failed to unlock \"" + token + "\""); + } + } + } + catch (ArgumentNullException) + { + } + } + + private static string FormatToken(string operation, string filename) + { + string token = operation + " " + Path.GetFullPath(filename).ToLowerInvariant(); + return token; + } + + public delegate bool CheckoutCallback(OutputWindowPane output, string filename); + + public static bool DeleteFile(OutputWindowPane output, string filename) + { + if (filename.Length == 0) + return false; + if (!g_p4installed) + return NotifyUser("could not find p4 exe installed in perforce directory"); + + string token = FormatToken("delete", filename); + if (!LockOp(token)) + return false; + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "delete \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + } + + public static bool AddFile(OutputWindowPane output, string filename) + { + if (filename.Length == 0) + return false; + if (!g_p4installed) + return NotifyUser("could not find p4 exe installed in perforce directory"); + + string token = FormatToken("add", filename); + if (!LockOp(token)) + return false; + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + } + + public static bool EditFile(OutputWindowPane output, string filename) + { + return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFile), output, filename); + } + + public static bool EditFileImmediate(OutputWindowPane output, string filename) + { + return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFileImmediate), output, filename); + } + + private static bool Internal_CheckEditFile(CheckoutCallback callback, OutputWindowPane output, string filename) + { + Log.Debug($"Edit '{filename}'"); + bool result = callback(output, filename); + + string ext = Path.GetExtension(filename).ToLowerInvariant(); + if (ext == ".vcxproj") + { + callback(output, filename + ".filters"); + } + + if (ext == ".settings" || ext == ".resx") + { + callback(output, Path.ChangeExtension(filename, ".Designer.cs")); + } + + if (ext == ".cs") + { + callback(output, Path.ChangeExtension(filename, ".Designer.cs")); + callback(output, Path.ChangeExtension(filename, ".resx")); + } + + return result; + } + + private static bool Internal_EditFile(OutputWindowPane output, string filename) + { + return Internal_EditFile(output, filename, false); + } + + private static bool Internal_EditFileImmediate(OutputWindowPane output, string filename) + { + return Internal_EditFile(output, filename, true); + } + + private static bool Internal_EditFile(OutputWindowPane output, string filename, bool immediate) + { + if (filename.Length == 0) + { + Log.Debug("EditFile failed due to empty filename"); + return false; + } + if (!File.Exists(filename)) + { + Log.Debug($"EditFile '{filename}' failed due to not existing file"); + return false; + } + if (!Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) + { + Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle IgnoreReadOnlyOnEdit in the options."); + return false; + } + if (!g_p4installed) + { + Log.Debug($"EditFile '{filename}' failed because p4 exe was not found"); + return NotifyUser("could not find p4 exe installed in perforce directory"); + } + + Log.Debug("EditFile" + (immediate ? "Immediate " : " ") + filename); + string token = FormatToken("edit", filename); + if (!LockOp(token)) + return false; + + if (immediate) + return AsyncProcess.Run(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + } + + public static bool RevertFile(OutputWindowPane output, string filename, bool onlyUnchanged) + { + if (filename.Length == 0) + return false; + if (!g_p4installed) + return NotifyUser("could not find p4 exe installed in perforce directory"); + + string token = FormatToken("revert", filename); + if (!LockOp(token)) + return false; + + string revertArguments = onlyUnchanged ? "-a " : string.Empty; + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "revert " + revertArguments + "\"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + } + + public static bool DiffFile(OutputWindowPane output, string filename) + { + if (filename.Length == 0) + return false; + + if (!g_p4installed) + return NotifyUser("could not find p4.exe installed in perforce directory"); + + string token = FormatToken("diff", filename); + if (!LockOp(token)) + return false; + + string dirname = Path.GetDirectoryName(filename); + + // Let's figure out if the user has some custom diff tool installed. Then we just send whatever we have without any fancy options. + if (g_p4customdiff) + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); + + if (g_p4vc_diffhave_supported) + return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " diffhave \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + + // Otherwise let's show a unified diff in the outputpane. + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff -du \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); + } + + public static bool RevisionHistoryFile(OutputWindowPane output, string dirname, string filename) { - // starting with 2021.1/2075061 - // #105247 (Change #2069769) - // The p4vc.exe executable has been removed from the Windows installers. - // To start P4VC, use the p4vc.bat script. - foreach (var candidateName in new[] { "p4vc.bat", "p4vc.exe" }) - { - if (check(candidateName)) - { - g_p4vc_exename = candidateName; - return true; - } - } - return false; - } - - public static void CheckInstalledFiles() - { - Log.Debug("Looking for installed files..."); - g_p4installed = false; - g_p4vinstalled = false; - g_p4customdiff = false; - g_p4vc_exename = null; - string p4diff = null; - - // Let's try the default 64 bit installation. Since we are in a 32 bit exe this is tricky - // to ask the registry... - string installRoot = null; - string candidate = @"C:\Program Files\Perforce"; - if (Directory.Exists(candidate) && File.Exists(Path.Combine(candidate, "p4.exe"))) - { - installRoot = candidate; - } - - if( null == installRoot ) - { - installRoot = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4INSTROOT", true); - - if (null == installRoot) - { - // Perhaps it's an older installation? - // http://code.google.com/p/niftyplugins/issues/detail?id=47&can=1&q=path - installRoot = GetRegistryValue("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths", "p4.exe", true); - } - } - - if(null != installRoot) - { - Log.Info("Found perforce installation at {0}", installRoot); - - g_p4installed = File.Exists(Path.Combine(installRoot, "p4.exe")); - g_p4vinstalled = File.Exists(Path.Combine(installRoot, "p4v.exe")); - LookupP4VC((candidateName) => File.Exists(Path.Combine(installRoot, candidateName))); - - Log.Info("[{0}] p4.exe", g_p4installed ? "X" : " "); - Log.Info("[{0}] p4v.exe", g_p4vinstalled ? "X" : " "); - Log.Info("[{0}] {1}", g_p4vc_exename != null ? "X" : " ", g_p4vc_exename ?? "p4vc(.bat|.exe)"); - - p4diff = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4DIFF", true); - if(!string.IsNullOrEmpty(p4diff)) - { - Log.Info("[X] p4 custom diff '{0}' from HKLM", p4diff); - g_p4customdiff = true; - } - p4diff = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4DIFF", false); - if(!string.IsNullOrEmpty(p4diff)) - { - Log.Info("[X] p4 custom diff '{0}' from HKCU", p4diff); - g_p4customdiff = true; - } - p4diff = Environment.GetEnvironmentVariable("P4DIFF"); - if(null != p4diff) - { - Log.Info("[X] p4 custom diff '{0}' from P4DIFF env var", p4diff); - g_p4customdiff = true; - } - - if (!g_p4customdiff) - Log.Info("[ ] p4 custom diff"); - } - else - { - // Let's try to find the executables through the path variable instead. - if(null != Help.FindFileInPath("p4.exe")) - { - g_p4installed = true; - Log.Info("Found p4 in path"); - } - - if(null != Help.FindFileInPath("p4v.exe")) - { - g_p4vinstalled = true; - Log.Info("Found p4v in path"); - } - - if(LookupP4VC((candidateName) => Help.FindFileInPath(candidateName) != null)) - { - Log.Info("Found {0} in path", g_p4vc_exename); - } - - Log.Warning("Could not find any peforce installation in the registry!!!"); - - p4diff = Environment.GetEnvironmentVariable("P4DIFF"); - if(null != p4diff) - { - Log.Info("Found p4 custom diff"); - g_p4customdiff = true; - } - } - - DetermineSupportedFeatures(); - } - - private static void DetermineSupportedFeatures() - { - // history was added in p4v 2019.2 update1/1883366 - g_p4vc_history_supported = P4VCHasCommand("history"); - Log.Info("[{0}] p4vc history", g_p4vc_history_supported ? "X" : " "); - - // diffhave was added in p4v 2020.1/1946989 - g_p4vc_diffhave_supported = P4VCHasCommand("diffhave"); - Log.Info("[{0}] p4vc diffhave", g_p4vc_diffhave_supported ? "X" : " "); - } - - private static bool P4VCHasCommand(string command) - { - if (string.IsNullOrEmpty(g_p4vc_exename)) - return false; - - string result = Aurora.Process.Execute(g_p4vc_exename, "", $"help {command}", throwIfNonZeroExitCode: false); - - return result.IndexOf("Invalid help command request...", StringComparison.Ordinal) == -1; - - } - - private static bool NotifyUser(string message) - { - if(!g_alreadyNotified.Contains(message)) - { - System.Windows.Forms.MessageBox.Show(message, "NiftyPerforce Notice!", System.Windows.Forms.MessageBoxButtons.OK); - g_alreadyNotified.Add(message); - } - return false; - } - - public static string RemapToMain(string filename, string mainline) - { - Log.Debug("RemapToMain : {0} {1}", filename, mainline); - - if (mainline.Length == 0) - { - Log.Error( "Tried to find the mainline version of {0}, but the mainline path spec is empty", filename); - throw new Exception( string.Format("Tried to find the mainline version of {0}, but the mainline path spec is empty", filename) ); - } - - string result = Aurora.Process.Execute("p4.exe", Path.GetDirectoryName(filename), GetUserInfoString() + "integrated \"" + filename + "\""); - - Regex pattern = new Regex(@"//(.*)#\d+ - .*//([^#]+)#\d+", RegexOptions.Compiled); - - string mainline_ = mainline.ToLowerInvariant(); - - foreach( Match m in pattern.Matches(result) ) - { - string candidate = "//" + m.Groups[2].ToString().ToLowerInvariant(); - - if( candidate.StartsWith(mainline_, StringComparison.Ordinal) ) - return candidate; - } - - return filename; - } - } + if (filename.Length == 0) + return false; + string token = FormatToken("history", filename); + if (!LockOp(token)) + return false; + if (g_p4vc_history_supported) + return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + if (g_p4vinstalled) + return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return NotifyUser("could not find a supported p4vc.exe or p4v.exe installed in perforce directory"); + } + + public static bool P4VShowFile(OutputWindowPane output, string filename) + { + if (filename.Length == 0) + return false; + if (g_p4vinstalled) // note that the cmd line also accepts -t to open P4V with a specific tab shown + return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, Path.GetDirectoryName(filename)) + " -s \"" + filename + "\"", Path.GetDirectoryName(filename), null, null, 0); + return NotifyUser("could not find p4v.exe installed in perforce directory"); + } + + private static string GetUserInfoString() + { + return GetUserInfoStringFull(false, ""); + } + + private static string GetUserInfoStringFull(bool lookup, string dir) + { + // NOTE: This to allow the user to have a P4CONFIG variable and connect to multiple perforce servers seamlessly. + if (Singleton.Instance.UseSystemEnv) + { + if (lookup) + { + try + { + string output = Aurora.Process.Execute("p4", dir, $"-s -L \"{dir}\" info"); + Regex userpattern = new Regex(@"User name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + Regex portpattern = new Regex(@"Server address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + Regex brokerpattern = new Regex(@"Broker address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + Regex clientpattern = new Regex(@"Client name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + + Match usermatch = userpattern.Match(output); + Match portmatch = portpattern.Match(output); + Match brokermatch = brokerpattern.Match(output); + Match clientmatch = clientpattern.Match(output); + + string port = portmatch.Groups["port"].Value.Trim(); + string broker = brokermatch.Groups["port"].Value.Trim(); + string username = usermatch.Groups["user"].Value.Trim(); + string client = clientmatch.Groups["client"].Value.Trim(); + + string server = broker; + if (string.IsNullOrEmpty(server)) + server = port; + + string ret = $" -p {server} -u {username} -c {client} "; + + Log.Debug("GetUserInfoStringFull : " + ret); + + return ret; + } + catch (Aurora.Process.Error e) + { + Log.Error("Failed to execute info string discovery: {0}", e.info); + } + } + + return ""; + } + + var config = Singleton.Instance; + string arguments = ""; + arguments += " -p " + config.Port; + arguments += " -u " + config.Username; + arguments += " -c " + config.Client; + arguments += " "; + + Log.Debug("GetUserInfoStringFull : " + arguments); + + return arguments; + } + + public static bool TimeLapseView(OutputWindowPane output, string dirname, string filename) + { + if (string.IsNullOrEmpty(g_p4vc_exename)) + return NotifyUser("could not find p4vc in perforce directory"); + + string arguments = GetUserInfoStringFull(true, dirname); + arguments += " tlv \"" + filename + "\""; + + + string token = FormatToken("timelapse", filename); + if (!LockOp(token)) + return false; + return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + } + + public static bool RevisionGraph(OutputWindowPane output, string dirname, string filename) + { + if (string.IsNullOrEmpty(g_p4vc_exename)) + return NotifyUser("could not find p4vc in perforce directory"); + + string arguments = GetUserInfoStringFull(true, dirname); + arguments += " revisiongraph \"" + filename + "\""; + + string token = FormatToken("revisiongraph", filename); + if (!LockOp(token)) + return false; + return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + } + + public static string GetRegistryValue(string key, string value, bool global) + { + Microsoft.Win32.RegistryKey hklm = Microsoft.Win32.Registry.LocalMachine; + if (!global) + hklm = Microsoft.Win32.Registry.CurrentUser; + hklm = hklm.OpenSubKey(key); + if (null == hklm) + { + Log.Debug("Could not find registry key " + (global ? "HKLM\\" : "HKCU\\") + key); + return null; + } + Object regValue = hklm.GetValue(value); + if (null == regValue) + { + Log.Debug("Could not find registry value " + value + " in " + (global ? "HKLM\\" : "HKCU\\") + key); + return null; + } + + return (string)regValue; + } + + private static bool LookupP4VC(Func check) + { + // starting with 2021.1/2075061 + // #105247 (Change #2069769) + // The p4vc.exe executable has been removed from the Windows installers. + // To start P4VC, use the p4vc.bat script. + foreach (var candidateName in new[] { "p4vc.bat", "p4vc.exe" }) + { + if (check(candidateName)) + { + g_p4vc_exename = candidateName; + return true; + } + } + return false; + } + + public static void CheckInstalledFiles() + { + Log.Debug("Looking for installed files..."); + g_p4installed = false; + g_p4vinstalled = false; + g_p4customdiff = false; + g_p4vc_exename = null; + string p4diff = null; + + // Let's try the default 64 bit installation. Since we are in a 32 bit exe this is tricky + // to ask the registry... + string installRoot = null; + string candidate = @"C:\Program Files\Perforce"; + if (Directory.Exists(candidate) && File.Exists(Path.Combine(candidate, "p4.exe"))) + { + installRoot = candidate; + } + + if (null == installRoot) + { + installRoot = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4INSTROOT", true); + + if (null == installRoot) + { + // Perhaps it's an older installation? + // http://code.google.com/p/niftyplugins/issues/detail?id=47&can=1&q=path + installRoot = GetRegistryValue("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths", "p4.exe", true); + } + } + + if (null != installRoot) + { + Log.Info("Found perforce installation at {0}", installRoot); + + g_p4installed = File.Exists(Path.Combine(installRoot, "p4.exe")); + g_p4vinstalled = File.Exists(Path.Combine(installRoot, "p4v.exe")); + LookupP4VC((candidateName) => File.Exists(Path.Combine(installRoot, candidateName))); + + Log.Info("[{0}] p4.exe", g_p4installed ? "X" : " "); + Log.Info("[{0}] p4v.exe", g_p4vinstalled ? "X" : " "); + Log.Info("[{0}] {1}", g_p4vc_exename != null ? "X" : " ", g_p4vc_exename ?? "p4vc(.bat|.exe)"); + + p4diff = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4DIFF", true); + if (!string.IsNullOrEmpty(p4diff)) + { + Log.Info("[X] p4 custom diff '{0}' from HKLM", p4diff); + g_p4customdiff = true; + } + p4diff = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4DIFF", false); + if (!string.IsNullOrEmpty(p4diff)) + { + Log.Info("[X] p4 custom diff '{0}' from HKCU", p4diff); + g_p4customdiff = true; + } + p4diff = Environment.GetEnvironmentVariable("P4DIFF"); + if (null != p4diff) + { + Log.Info("[X] p4 custom diff '{0}' from P4DIFF env var", p4diff); + g_p4customdiff = true; + } + + if (!g_p4customdiff) + Log.Info("[ ] p4 custom diff"); + } + else + { + // Let's try to find the executables through the path variable instead. + if (null != Help.FindFileInPath("p4.exe")) + { + g_p4installed = true; + Log.Info("Found p4 in path"); + } + + if (null != Help.FindFileInPath("p4v.exe")) + { + g_p4vinstalled = true; + Log.Info("Found p4v in path"); + } + + if (LookupP4VC((candidateName) => Help.FindFileInPath(candidateName) != null)) + { + Log.Info("Found {0} in path", g_p4vc_exename); + } + + Log.Warning("Could not find any peforce installation in the registry!!!"); + + p4diff = Environment.GetEnvironmentVariable("P4DIFF"); + if (null != p4diff) + { + Log.Info("Found p4 custom diff"); + g_p4customdiff = true; + } + } + + DetermineSupportedFeatures(); + } + + private static void DetermineSupportedFeatures() + { + // history was added in p4v 2019.2 update1/1883366 + g_p4vc_history_supported = P4VCHasCommand("history"); + Log.Info("[{0}] p4vc history", g_p4vc_history_supported ? "X" : " "); + + // diffhave was added in p4v 2020.1/1946989 + g_p4vc_diffhave_supported = P4VCHasCommand("diffhave"); + Log.Info("[{0}] p4vc diffhave", g_p4vc_diffhave_supported ? "X" : " "); + } + + private static bool P4VCHasCommand(string command) + { + if (string.IsNullOrEmpty(g_p4vc_exename)) + return false; + + string result = Aurora.Process.Execute(g_p4vc_exename, "", $"help {command}", throwIfNonZeroExitCode: false); + + return result.IndexOf("Invalid help command request...", StringComparison.Ordinal) == -1; + + } + + private static bool NotifyUser(string message) + { + if (!g_alreadyNotified.Contains(message)) + { + System.Windows.Forms.MessageBox.Show(message, "NiftyPerforce Notice!", System.Windows.Forms.MessageBoxButtons.OK); + g_alreadyNotified.Add(message); + } + return false; + } + + public static string RemapToMain(string filename, string mainline) + { + Log.Debug("RemapToMain : {0} {1}", filename, mainline); + + if (mainline.Length == 0) + { + Log.Error("Tried to find the mainline version of {0}, but the mainline path spec is empty", filename); + throw new Exception(string.Format("Tried to find the mainline version of {0}, but the mainline path spec is empty", filename)); + } + + string result = Aurora.Process.Execute("p4.exe", Path.GetDirectoryName(filename), GetUserInfoString() + "integrated \"" + filename + "\""); + + Regex pattern = new Regex(@"//(.*)#\d+ - .*//([^#]+)#\d+", RegexOptions.Compiled); + + string mainline_ = mainline.ToLowerInvariant(); + + foreach (Match m in pattern.Matches(result)) + { + string candidate = "//" + m.Groups[2].ToString().ToLowerInvariant(); + + if (candidate.StartsWith(mainline_, StringComparison.Ordinal)) + return candidate; + } + + return filename; + } + } } diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index b51041c..a0dc6cd 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -6,195 +6,195 @@ namespace Aurora { - public static class AsyncProcess - { - private static readonly int s_defaultTimeout = 30000; // in ms - - public delegate void OnDone(bool ok, object arg0); - - public static void Init() - { - m_helperThread = new System.Threading.Thread(new ThreadStart(ThreadMain)); - m_helperThread.Start(); - } - - public static void Term() - { - m_helperThread.Abort(); - } - - public static bool Run(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - int timeout = 1000; - - if (!RunCommand(output, executable, commandline, workingdir, timeout)) - { - Log.Debug("Failed to run immediate (process hung?), trying again on a remote thread: " + commandline); - return Schedule(output, executable, commandline, workingdir, callback, callbackArg); - } - else - { - callback?.Invoke(true, callbackArg); - } - - return true; - } - - public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) - { - return Schedule(output, executable, commandline, workingdir, callback, callbackArg, s_defaultTimeout); - } - - public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) - { - CommandThread cmd = new CommandThread - { - output = output, - executable = executable, - commandline = commandline, - workingdir = workingdir, - callback = callback, - callbackArg = callbackArg, - timeout = timeout - }; - - try - { - m_queueLock.WaitOne(); - m_commandQueue.Enqueue(cmd); - } - finally - { - m_queueLock.ReleaseMutex(); - } - - m_startEvent.Release(); - Log.Info("Scheduled {0} {1}\n", cmd.executable, cmd.commandline); - return true; - } - - // --------------------------------------------------------------------------------------------------------------------------------------------- - // - // BEGIN INTERNALS - // - private static readonly Mutex m_queueLock = new Mutex(); - private static readonly Semaphore m_startEvent = new Semaphore(0, 9999); - private static readonly Queue m_commandQueue = new Queue(); - private static System.Threading.Thread m_helperThread; - - private static void ThreadMain() - { - while(true) - { - m_startEvent.WaitOne(); - CommandThread cmd = null; - - try - { - m_queueLock.WaitOne(); - cmd = m_commandQueue.Dequeue(); - } - finally - { - m_queueLock.ReleaseMutex(); - } - - try - { - System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(cmd.Run)); - thread.Start(); - } - catch - { - } - } - } - - - private class CommandThread - { - public string executable = ""; - public string commandline = ""; - public string workingdir = ""; - public OutputWindowPane output = null; - public OnDone callback = null; - public object callbackArg = null; - public int timeout = 10000; - public void Run() - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - bool ok; - try - { - ok = RunCommand(output, executable, commandline, workingdir, timeout); - } - catch - { - ok = false; - Log.Error("Caught unhandled exception in async process -- supressing so that we don't bring down Visual Studio"); - } - - callback?.Invoke(ok, callbackArg); - } - } - - private static bool RunCommand(OutputWindowPane output, string executable, string commandline, string workingdir, int timeout) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - try - { - System.Diagnostics.Process process = new System.Diagnostics.Process(); - process.StartInfo.UseShellExecute = false; - process.StartInfo.FileName = executable; - if( 0 == timeout ) - { - // We are not for these processes reading the stdout and thus they could if they wrote more - // data on the output line hang. - process.StartInfo.RedirectStandardOutput = false; - process.StartInfo.RedirectStandardError = false; - } - else - { - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - } - process.StartInfo.CreateNoWindow = true; - process.StartInfo.WorkingDirectory = workingdir; - process.StartInfo.Arguments = commandline; - - Log.Debug("executableName : " + executable); - Log.Debug("workingDirectory : " + workingdir); - Log.Debug("command : " + commandline); - - if(!process.Start()) - { - Log.Error("{0}: {1} Failed to start. Is Perforce installed and in the path?\n", executable, commandline); - return false; - } - - if (0 == timeout) - { - // Fire and forget task. - return true; - } - - bool exited = false; - string alloutput = ""; - using (Process.Handler stderr = new Process.Handler(), stdout = new Process.Handler()) - { - process.OutputDataReceived += stdout.OnOutput; - process.BeginOutputReadLine(); - - process.ErrorDataReceived += stderr.OnOutput; - process.BeginErrorReadLine(); - - exited = process.WaitForExit(timeout); - - /* + public static class AsyncProcess + { + private static readonly int s_defaultTimeout = 30000; // in ms + + public delegate void OnDone(bool ok, object arg0); + + public static void Init() + { + m_helperThread = new System.Threading.Thread(new ThreadStart(ThreadMain)); + m_helperThread.Start(); + } + + public static void Term() + { + m_helperThread.Abort(); + } + + public static bool Run(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + int timeout = 1000; + + if (!RunCommand(output, executable, commandline, workingdir, timeout)) + { + Log.Debug("Failed to run immediate (process hung?), trying again on a remote thread: " + commandline); + return Schedule(output, executable, commandline, workingdir, callback, callbackArg); + } + else + { + callback?.Invoke(true, callbackArg); + } + + return true; + } + + public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) + { + return Schedule(output, executable, commandline, workingdir, callback, callbackArg, s_defaultTimeout); + } + + public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) + { + CommandThread cmd = new CommandThread + { + output = output, + executable = executable, + commandline = commandline, + workingdir = workingdir, + callback = callback, + callbackArg = callbackArg, + timeout = timeout + }; + + try + { + m_queueLock.WaitOne(); + m_commandQueue.Enqueue(cmd); + } + finally + { + m_queueLock.ReleaseMutex(); + } + + m_startEvent.Release(); + Log.Info("Scheduled {0} {1}\n", cmd.executable, cmd.commandline); + return true; + } + + // --------------------------------------------------------------------------------------------------------------------------------------------- + // + // BEGIN INTERNALS + // + private static readonly Mutex m_queueLock = new Mutex(); + private static readonly Semaphore m_startEvent = new Semaphore(0, 9999); + private static readonly Queue m_commandQueue = new Queue(); + private static System.Threading.Thread m_helperThread; + + private static void ThreadMain() + { + while (true) + { + m_startEvent.WaitOne(); + CommandThread cmd = null; + + try + { + m_queueLock.WaitOne(); + cmd = m_commandQueue.Dequeue(); + } + finally + { + m_queueLock.ReleaseMutex(); + } + + try + { + System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(cmd.Run)); + thread.Start(); + } + catch + { + } + } + } + + + private class CommandThread + { + public string executable = ""; + public string commandline = ""; + public string workingdir = ""; + public OutputWindowPane output = null; + public OnDone callback = null; + public object callbackArg = null; + public int timeout = 10000; + public void Run() + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + bool ok; + try + { + ok = RunCommand(output, executable, commandline, workingdir, timeout); + } + catch + { + ok = false; + Log.Error("Caught unhandled exception in async process -- supressing so that we don't bring down Visual Studio"); + } + + callback?.Invoke(ok, callbackArg); + } + } + + private static bool RunCommand(OutputWindowPane output, string executable, string commandline, string workingdir, int timeout) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + try + { + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = executable; + if (0 == timeout) + { + // We are not for these processes reading the stdout and thus they could if they wrote more + // data on the output line hang. + process.StartInfo.RedirectStandardOutput = false; + process.StartInfo.RedirectStandardError = false; + } + else + { + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + } + process.StartInfo.CreateNoWindow = true; + process.StartInfo.WorkingDirectory = workingdir; + process.StartInfo.Arguments = commandline; + + Log.Debug("executableName : " + executable); + Log.Debug("workingDirectory : " + workingdir); + Log.Debug("command : " + commandline); + + if (!process.Start()) + { + Log.Error("{0}: {1} Failed to start. Is Perforce installed and in the path?\n", executable, commandline); + return false; + } + + if (0 == timeout) + { + // Fire and forget task. + return true; + } + + bool exited = false; + string alloutput = ""; + using (Process.Handler stderr = new Process.Handler(), stdout = new Process.Handler()) + { + process.OutputDataReceived += stdout.OnOutput; + process.BeginOutputReadLine(); + + process.ErrorDataReceived += stderr.OnOutput; + process.BeginErrorReadLine(); + + exited = process.WaitForExit(timeout); + + /* * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? * @@ -203,47 +203,47 @@ private static bool RunCommand(OutputWindowPane output, string executable, strin throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); }*/ - stderr.sentinel.WaitOne(); - stdout.sentinel.WaitOne(); - alloutput = stdout.buffer + "\n" + stderr.buffer; - } - - if(!exited) - { - Log.Info("{0}: {1} timed out ({2} ms)", executable, commandline, timeout); - process.Kill(); - return false; - } - else - { - if(null != output) - { - output.OutputString(executable + ": " + commandline + "\n"); - output.OutputString(alloutput); - } - - System.Diagnostics.Debug.WriteLine(commandline + "\n"); - System.Diagnostics.Debug.WriteLine(alloutput); - - if(0 != process.ExitCode) - { - Log.Debug("{0}: {1} exit code {2}", executable, commandline, process.ExitCode); - return false; - } - } - - return true; - } - catch(System.ComponentModel.Win32Exception e) - { - Log.Error("{0}: {1} failed to spawn: {2}", executable, commandline, e.ToString()); - return false; - } - } - - // - // END INTERNALS - // - // --------------------------------------------------------------------------------------------------------------------------------------------- - } + stderr.sentinel.WaitOne(); + stdout.sentinel.WaitOne(); + alloutput = stdout.buffer + "\n" + stderr.buffer; + } + + if (!exited) + { + Log.Info("{0}: {1} timed out ({2} ms)", executable, commandline, timeout); + process.Kill(); + return false; + } + else + { + if (null != output) + { + output.OutputString(executable + ": " + commandline + "\n"); + output.OutputString(alloutput); + } + + System.Diagnostics.Debug.WriteLine(commandline + "\n"); + System.Diagnostics.Debug.WriteLine(alloutput); + + if (0 != process.ExitCode) + { + Log.Debug("{0}: {1} exit code {2}", executable, commandline, process.ExitCode); + return false; + } + } + + return true; + } + catch (System.ComponentModel.Win32Exception e) + { + Log.Error("{0}: {1} failed to spawn: {2}", executable, commandline, e.ToString()); + return false; + } + } + + // + // END INTERNALS + // + // --------------------------------------------------------------------------------------------------------------------------------------------- + } } diff --git a/Shared/CommandBase.cs b/Shared/CommandBase.cs index f25672d..012f2fe 100644 --- a/Shared/CommandBase.cs +++ b/Shared/CommandBase.cs @@ -3,22 +3,22 @@ namespace Aurora { - public abstract class CommandBase - { - public Plugin Plugin { get; } - public string Name { get; } - public string CanonicalName { get; } - public int CommandId { get; } + public abstract class CommandBase + { + public Plugin Plugin { get; } + public string Name { get; } + public string CanonicalName { get; } + public int CommandId { get; } - protected CommandBase(string name, string canonicalName, Plugin plugin, int commandId) - { - Name = name; - CanonicalName = canonicalName; - Plugin = plugin; - CommandId = commandId; - } + protected CommandBase(string name, string canonicalName, Plugin plugin, int commandId) + { + Name = name; + CanonicalName = canonicalName; + Plugin = plugin; + CommandId = commandId; + } - public abstract bool OnCommand(); // returns if the command was dispatched or not. - public abstract bool IsEnabled(); // is the command active? - } + public abstract bool OnCommand(); // returns if the command was dispatched or not. + public abstract bool IsEnabled(); // is the command active? + } } diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index d3db152..829b230 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -7,105 +7,105 @@ namespace Aurora { - // Holds a dictionary between local command names and the instance that holds - // the logic to execute and update the command itself. - public class CommandRegistry - { - private readonly Dictionary mCommands; - private readonly Dictionary mCommandsById; - private readonly Plugin mPlugin; - private Guid mCmdGroupGuid; + // Holds a dictionary between local command names and the instance that holds + // the logic to execute and update the command itself. + public class CommandRegistry + { + private readonly Dictionary mCommands; + private readonly Dictionary mCommandsById; + private readonly Plugin mPlugin; + private Guid mCmdGroupGuid; - public CommandRegistry(Plugin plugin, Guid cmdGroupGuid) - { - mCommands = new Dictionary(); - mCommandsById = new Dictionary(); - mPlugin = plugin; - mCmdGroupGuid = cmdGroupGuid; - } + public CommandRegistry(Plugin plugin, Guid cmdGroupGuid) + { + mCommands = new Dictionary(); + mCommandsById = new Dictionary(); + mPlugin = plugin; + mCmdGroupGuid = cmdGroupGuid; + } - public void RegisterCommand(CommandBase commandHandler) - { - var command = RegisterCommandPrivate(commandHandler); - if (command != null) - mCommands.Add(commandHandler.CanonicalName, commandHandler); - } + public void RegisterCommand(CommandBase commandHandler) + { + var command = RegisterCommandPrivate(commandHandler); + if (command != null) + mCommands.Add(commandHandler.CanonicalName, commandHandler); + } - private OleMenuCommand RegisterCommandPrivate(CommandBase commandHandler) - { - OleMenuCommand vscommand = null; - //if (cmdId == 0) - { - OleMenuCommandService menuCommandService = mPlugin.MenuCommandService; - CommandID commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); + private OleMenuCommand RegisterCommandPrivate(CommandBase commandHandler) + { + OleMenuCommand vscommand = null; + //if (cmdId == 0) + { + OleMenuCommandService menuCommandService = mPlugin.MenuCommandService; + CommandID commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); - vscommand = new OleMenuCommand(OleMenuCommandCallback, commandID); - vscommand.BeforeQueryStatus += this.OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong - menuCommandService.AddCommand(vscommand); - mCommandsById[commandID.ID] = commandHandler; - } - // Register the graphics controls for this command as well. - // First let the command itself have a stab at register whatever it needs. - // Then by default we always register ourselves in the main toolbar of the application. - //commandHandler.RegisterGUI(vscommand, mCommandBar, toolbarOnly); + vscommand = new OleMenuCommand(OleMenuCommandCallback, commandID); + vscommand.BeforeQueryStatus += this.OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong + menuCommandService.AddCommand(vscommand); + mCommandsById[commandID.ID] = commandHandler; + } + // Register the graphics controls for this command as well. + // First let the command itself have a stab at register whatever it needs. + // Then by default we always register ourselves in the main toolbar of the application. + //commandHandler.RegisterGUI(vscommand, mCommandBar, toolbarOnly); - return vscommand; - } - private void OleMenuCommandBeforeQueryStatus(object sender, EventArgs e) - { + return vscommand; + } + private void OleMenuCommandBeforeQueryStatus(object sender, EventArgs e) + { - try - { - if (sender is OleMenuCommand oleMenuCommand) - { - CommandID commandId = oleMenuCommand.CommandID; + try + { + if (sender is OleMenuCommand oleMenuCommand) + { + CommandID commandId = oleMenuCommand.CommandID; - if (commandId != null) - { - if (mCommandsById.ContainsKey(commandId.ID)) - { - var bc = mCommandsById[commandId.ID]; + if (commandId != null) + { + if (mCommandsById.ContainsKey(commandId.ID)) + { + var bc = mCommandsById[commandId.ID]; - oleMenuCommand.Supported = true; - oleMenuCommand.Enabled = mCommandsById[commandId.ID].IsEnabled(); - oleMenuCommand.Visible = true; - } - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex.ToString()); - } - } + oleMenuCommand.Supported = true; + oleMenuCommand.Enabled = mCommandsById[commandId.ID].IsEnabled(); + oleMenuCommand.Visible = true; + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.ToString()); + } + } - private void OleMenuCommandCallback(object sender, EventArgs e) - { - try - { - if (sender is OleMenuCommand oleMenuCommand) - { - CommandID commandId = oleMenuCommand.CommandID; - if (commandId != null) - { - if (mCommandsById.ContainsKey(commandId.ID)) - { - var command = mCommandsById[commandId.ID]; + private void OleMenuCommandCallback(object sender, EventArgs e) + { + try + { + if (sender is OleMenuCommand oleMenuCommand) + { + CommandID commandId = oleMenuCommand.CommandID; + if (commandId != null) + { + if (mCommandsById.ContainsKey(commandId.ID)) + { + var command = mCommandsById[commandId.ID]; - bool dispatched = command.OnCommand(); - Log.Debug($"{command.Name} (0x{commandId.ID:X}) " + (dispatched ? "was dispatched" : "fail")); - } - else - { - Log.Debug($"Couldn't find command with id 0x{commandId.ID:X}"); - } - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex.ToString()); - } - } - } + bool dispatched = command.OnCommand(); + Log.Debug($"{command.Name} (0x{commandId.ID:X}) " + (dispatched ? "was dispatched" : "fail")); + } + else + { + Log.Debug($"Couldn't find command with id 0x{commandId.ID:X}"); + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.ToString()); + } + } + } } diff --git a/Shared/DebugLogHandler.cs b/Shared/DebugLogHandler.cs index ba44329..3b7b2ad 100644 --- a/Shared/DebugLogHandler.cs +++ b/Shared/DebugLogHandler.cs @@ -6,11 +6,11 @@ namespace Aurora { - public class DebugLogHandler : Log.IHandler - { - public void OnMessage(Log.Level level, string message, string formattedLine) - { - Debug.Write(formattedLine); - } - } + public class DebugLogHandler : Log.IHandler + { + public void OnMessage(Log.Level level, string message, string formattedLine) + { + Debug.Write(formattedLine); + } + } } diff --git a/Shared/Feature.cs b/Shared/Feature.cs index bef781d..c6ecd3d 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -6,52 +6,52 @@ namespace Aurora { - public abstract class Feature - { - private readonly string mName; - private readonly string mTooltip; - - public string Name { get { return mName; } } - - protected Feature(string name, string tooltip) - { - mName = name; - mTooltip = tooltip; - } - - public virtual bool Execute() - { - return true; - } - }; - - public abstract class PreCommandFeature : Feature - { - protected Plugin mPlugin; - - protected PreCommandFeature(Plugin plugin, string name, string tooltip) - : base(name, tooltip) - { - mPlugin = plugin; - } - - protected bool RegisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - CommandEvents events = mPlugin.FindCommandEvents(commandName); - if(null == events) - return false; - events.BeforeExecute += handler; - return true; - } - - protected void UnregisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - CommandEvents events = mPlugin.FindCommandEvents(commandName); - if (null == events) - return; - events.BeforeExecute -= handler; - } - }; + public abstract class Feature + { + private readonly string mName; + private readonly string mTooltip; + + public string Name { get { return mName; } } + + protected Feature(string name, string tooltip) + { + mName = name; + mTooltip = tooltip; + } + + public virtual bool Execute() + { + return true; + } + }; + + public abstract class PreCommandFeature : Feature + { + protected Plugin mPlugin; + + protected PreCommandFeature(Plugin plugin, string name, string tooltip) + : base(name, tooltip) + { + mPlugin = plugin; + } + + protected bool RegisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + CommandEvents events = mPlugin.FindCommandEvents(commandName); + if (null == events) + return false; + events.BeforeExecute += handler; + return true; + } + + protected void UnregisterHandler(string commandName, _dispCommandEvents_BeforeExecuteEventHandler handler) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + CommandEvents events = mPlugin.FindCommandEvents(commandName); + if (null == events) + return; + events.BeforeExecute -= handler; + } + }; } diff --git a/Shared/Help.cs b/Shared/Help.cs index d5d6e7f..f8421eb 100644 --- a/Shared/Help.cs +++ b/Shared/Help.cs @@ -3,21 +3,21 @@ namespace Aurora { - public static class Help - { - public static string FindFileInPath(string filename) - { - string pathenv = Environment.GetEnvironmentVariable("PATH"); - string[] items = pathenv.Split(';'); + public static class Help + { + public static string FindFileInPath(string filename) + { + string pathenv = Environment.GetEnvironmentVariable("PATH"); + string[] items = pathenv.Split(';'); - foreach(string item in items) - { - string candidate = Path.Combine(item, filename); - if(System.IO.File.Exists(candidate)) - return candidate; - } - - return null; - } - } + foreach (string item in items) + { + string candidate = Path.Combine(item, filename); + if (System.IO.File.Exists(candidate)) + return candidate; + } + + return null; + } + } } diff --git a/Shared/Log.cs b/Shared/Log.cs index 2a50e53..cc10889 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -5,129 +5,129 @@ namespace Aurora { - public static class Log - { - // Internal enumeration. Only used in handlers to identify the type of message - public enum Level - { - Debug, - Info, - Warn, - Error, - } - - // This needs to be implemented by all clients. - public interface IHandler - { - void OnMessage(Level level, string message, string formattedLine); - } - - // Helper class to keep the indent levels balanced (with the help of the using statement) - - - // Log class implement below - public static string Prefix - { - get { return mPrefix; } - set { mPrefix = value; } - } - - public static int HandlerCount - { - get { return mHandlers.Count; } - } - - public static void AddHandler(IHandler handler) - { - if(null == handler) - return; - - lock(mHandlers) - { - mHandlers.Add(handler); - } - } - - public static void RemoveHandler(IHandler handler) - { - lock(mHandlers) - { - mHandlers.Remove(handler); - } - } - - public static void ClearHandlers() - { - lock(mHandlers) - { - mHandlers.Clear(); - } - } - - public static void IncIndent() - { - mIndent++; - } - - public static void DecIndent() - { - mIndent--; - } - - public static void Debug(string message, params object[] args) - { + public static class Log + { + // Internal enumeration. Only used in handlers to identify the type of message + public enum Level + { + Debug, + Info, + Warn, + Error, + } + + // This needs to be implemented by all clients. + public interface IHandler + { + void OnMessage(Level level, string message, string formattedLine); + } + + // Helper class to keep the indent levels balanced (with the help of the using statement) + + + // Log class implement below + public static string Prefix + { + get { return mPrefix; } + set { mPrefix = value; } + } + + public static int HandlerCount + { + get { return mHandlers.Count; } + } + + public static void AddHandler(IHandler handler) + { + if (null == handler) + return; + + lock (mHandlers) + { + mHandlers.Add(handler); + } + } + + public static void RemoveHandler(IHandler handler) + { + lock (mHandlers) + { + mHandlers.Remove(handler); + } + } + + public static void ClearHandlers() + { + lock (mHandlers) + { + mHandlers.Clear(); + } + } + + public static void IncIndent() + { + mIndent++; + } + + public static void DecIndent() + { + mIndent--; + } + + public static void Debug(string message, params object[] args) + { #if DEBUG - OnMessage(Level.Debug, message, args); + OnMessage(Level.Debug, message, args); #endif - } - - public static void Info(string message, params object[] args) - { - OnMessage(Level.Info, message, args); - } - - public static void Warning(string message, params object[] args) - { - OnMessage(Level.Warn, message, args); - } - - public static void Error(string message, params object[] args) - { - OnMessage(Level.Error, message, args); - } - - private static void OnMessage(Level level, string format, object[] args) - { - string message = args.Length > 0 ? string.Format(format, args) : format; - string formattedLine; - string indent = ""; - string levelName = level.ToString().PadLeft(5, ' '); - - for(int i = 0; i < mIndent; i++) - { - indent += " "; - } - - if(mPrefix.Length > 0) - { - formattedLine = mPrefix + " (" + levelName + "): " + indent + message + "\n"; - } - else - { - formattedLine = levelName + ": " + indent + message + "\n"; - } - - lock(mHandlers) - { - foreach(IHandler handler in mHandlers) - { - handler.OnMessage(level, message, formattedLine); - } - } - } - - private static readonly List mHandlers = new List(); - private static string mPrefix = ""; - private static int mIndent = 0; - } + } + + public static void Info(string message, params object[] args) + { + OnMessage(Level.Info, message, args); + } + + public static void Warning(string message, params object[] args) + { + OnMessage(Level.Warn, message, args); + } + + public static void Error(string message, params object[] args) + { + OnMessage(Level.Error, message, args); + } + + private static void OnMessage(Level level, string format, object[] args) + { + string message = args.Length > 0 ? string.Format(format, args) : format; + string formattedLine; + string indent = ""; + string levelName = level.ToString().PadLeft(5, ' '); + + for (int i = 0; i < mIndent; i++) + { + indent += " "; + } + + if (mPrefix.Length > 0) + { + formattedLine = mPrefix + " (" + levelName + "): " + indent + message + "\n"; + } + else + { + formattedLine = levelName + ": " + indent + message + "\n"; + } + + lock (mHandlers) + { + foreach (IHandler handler in mHandlers) + { + handler.OnMessage(level, message, formattedLine); + } + } + } + + private static readonly List mHandlers = new List(); + private static string mPrefix = ""; + private static int mIndent = 0; + } } diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index 508e840..8a2e4aa 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -7,113 +7,113 @@ namespace Aurora { - // Wrapper class around registering other classes to handle the actual commands. - // Interfaces with visual studio and handles the dispatch. - public class Plugin - { - private OutputWindowPane m_outputPane; - private readonly string m_panelName; - - private readonly Dictionary m_features = new Dictionary(); - - public OutputWindowPane OutputPane - { - get - { - ThreadHelper.ThrowIfNotOnUIThread(); - return GetOutputPane(); - } - } - public string Prefix { get; } - public DTE2 App { get; } - public Commands Commands { get { return App.Commands; }} - public OleMenuCommandService MenuCommandService { get; } - public object Options { get; } - - public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, string panelName, string connectPath, object options) - { - // TODO: This can be figured out from traversing the assembly and locating the Connect class... - Prefix = connectPath; - - App = application; - m_panelName = panelName; - MenuCommandService = oleMenuCommandService; - Options = options; - } - - public void AddFeature(Feature feature) - { - m_features.Add(feature.Name, feature); - } - - public CommandEvents FindCommandEvents(string commandName) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - CommandEvents events = null; - try - { - Command command = App.DTE.Commands.Item(commandName, -1); - if(command != null) - events = App.DTE.Events.get_CommandEvents(command.Guid, command.ID); - } - catch - { - } - return events; - } - - private OutputWindowPane GetOutputPane() - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (m_outputPane != null) - return m_outputPane; - try - { - m_outputPane = AquireOutputPane(App, m_panelName); - } - catch (Exception) - { - } - return m_outputPane; - } - - private static OutputWindowPane AquireOutputPane(DTE2 app, string name) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if(string.IsNullOrEmpty(name) || app.Windows.Count == 0) - return null; - - OutputWindowPane result = FindOutputPane(app, name); - if(null != result) - return result; - - OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; - OutputWindowPanes panes = outputWindow.OutputWindowPanes; - return panes.Add(name); - } - - public static OutputWindowPane FindOutputPane(DTE2 app, string name) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if(string.IsNullOrEmpty(name) ||app?.Windows?.Count == null || app.Windows.Count == 0) - return null; - - OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; - OutputWindowPanes panes = outputWindow.OutputWindowPanes; - - foreach(OutputWindowPane pane in panes) - { - if(name != pane.Name) - continue; - - return pane; - } - - return null; - } - } + // Wrapper class around registering other classes to handle the actual commands. + // Interfaces with visual studio and handles the dispatch. + public class Plugin + { + private OutputWindowPane m_outputPane; + private readonly string m_panelName; + + private readonly Dictionary m_features = new Dictionary(); + + public OutputWindowPane OutputPane + { + get + { + ThreadHelper.ThrowIfNotOnUIThread(); + return GetOutputPane(); + } + } + public string Prefix { get; } + public DTE2 App { get; } + public Commands Commands { get { return App.Commands; } } + public OleMenuCommandService MenuCommandService { get; } + public object Options { get; } + + public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, string panelName, string connectPath, object options) + { + // TODO: This can be figured out from traversing the assembly and locating the Connect class... + Prefix = connectPath; + + App = application; + m_panelName = panelName; + MenuCommandService = oleMenuCommandService; + Options = options; + } + + public void AddFeature(Feature feature) + { + m_features.Add(feature.Name, feature); + } + + public CommandEvents FindCommandEvents(string commandName) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + CommandEvents events = null; + try + { + Command command = App.DTE.Commands.Item(commandName, -1); + if (command != null) + events = App.DTE.Events.get_CommandEvents(command.Guid, command.ID); + } + catch + { + } + return events; + } + + private OutputWindowPane GetOutputPane() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (m_outputPane != null) + return m_outputPane; + try + { + m_outputPane = AquireOutputPane(App, m_panelName); + } + catch (Exception) + { + } + return m_outputPane; + } + + private static OutputWindowPane AquireOutputPane(DTE2 app, string name) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (string.IsNullOrEmpty(name) || app.Windows.Count == 0) + return null; + + OutputWindowPane result = FindOutputPane(app, name); + if (null != result) + return result; + + OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; + OutputWindowPanes panes = outputWindow.OutputWindowPanes; + return panes.Add(name); + } + + public static OutputWindowPane FindOutputPane(DTE2 app, string name) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (string.IsNullOrEmpty(name) || app?.Windows?.Count == null || app.Windows.Count == 0) + return null; + + OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; + OutputWindowPanes panes = outputWindow.OutputWindowPanes; + + foreach (OutputWindowPane pane in panes) + { + if (name != pane.Name) + continue; + + return pane; + } + + return null; + } + } } diff --git a/Shared/Process.cs b/Shared/Process.cs index 1b72b47..2eff99f 100644 --- a/Shared/Process.cs +++ b/Shared/Process.cs @@ -4,83 +4,83 @@ namespace Aurora { - public static class Process - { - public class Error : System.Exception - { - public string info; - public Error(string info_, params object[] vaargs_) { info = string.Format(info_, vaargs_); } - }; + public static class Process + { + public class Error : System.Exception + { + public string info; + public Error(string info_, params object[] vaargs_) { info = string.Format(info_, vaargs_); } + }; - // Helper class to capture output correctly and send an event once we've reached the end of the file. - public class Handler : IDisposable - { - public string buffer; - public ManualResetEvent sentinel; + // Helper class to capture output correctly and send an event once we've reached the end of the file. + public class Handler : IDisposable + { + public string buffer; + public ManualResetEvent sentinel; - public Handler() - { - buffer = ""; - sentinel = new ManualResetEvent(false); - } + public Handler() + { + buffer = ""; + sentinel = new ManualResetEvent(false); + } - public void Dispose() - { - sentinel.Close(); - } + public void Dispose() + { + sentinel.Close(); + } - public void OnOutput(object sender, System.Diagnostics.DataReceivedEventArgs e) - { - if( e?.Data == null ) - { - sentinel.Set(); - } - else - { - buffer = buffer + e.Data + "\n"; - } - } - }; + public void OnOutput(object sender, System.Diagnostics.DataReceivedEventArgs e) + { + if (e?.Data == null) + { + sentinel.Set(); + } + else + { + buffer = buffer + e.Data + "\n"; + } + } + }; - public static string Execute(string executable, string workingdir, string arguments, bool throwIfNonZeroExitCode = true) - { - using( System.Diagnostics.Process process = new System.Diagnostics.Process() ) - { - process.StartInfo.UseShellExecute = false; - process.StartInfo.FileName = executable; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.WorkingDirectory = workingdir; - process.StartInfo.Arguments = arguments; + public static string Execute(string executable, string workingdir, string arguments, bool throwIfNonZeroExitCode = true) + { + using (System.Diagnostics.Process process = new System.Diagnostics.Process()) + { + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = executable; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.WorkingDirectory = workingdir; + process.StartInfo.Arguments = arguments; - if(!process.Start()) - { - throw new Error("{0}: Failed to start {1}.", executable, process.StartInfo.Arguments); - } + if (!process.Start()) + { + throw new Error("{0}: Failed to start {1}.", executable, process.StartInfo.Arguments); + } - using( Handler stderr = new Handler(), stdout = new Handler() ) - { - process.OutputDataReceived += stdout.OnOutput; - process.BeginOutputReadLine(); + using (Handler stderr = new Handler(), stdout = new Handler()) + { + process.OutputDataReceived += stdout.OnOutput; + process.BeginOutputReadLine(); - process.ErrorDataReceived += stderr.OnOutput; - process.BeginErrorReadLine(); + process.ErrorDataReceived += stderr.OnOutput; + process.BeginErrorReadLine(); - process.WaitForExit(); + process.WaitForExit(); - if(throwIfNonZeroExitCode && 0 != process.ExitCode) - { - throw new Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); - } + if (throwIfNonZeroExitCode && 0 != process.ExitCode) + { + throw new Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); + } - stderr.sentinel.WaitOne(); - stdout.sentinel.WaitOne(); + stderr.sentinel.WaitOne(); + stdout.sentinel.WaitOne(); - return stdout.buffer + "\n" + stderr.buffer; - } - } - } - } + return stdout.buffer + "\n" + stderr.buffer; + } + } + } + } } diff --git a/Shared/Singleton.cs b/Shared/Singleton.cs index a03a0b9..7450c9c 100644 --- a/Shared/Singleton.cs +++ b/Shared/Singleton.cs @@ -3,9 +3,9 @@ namespace Aurora { - public sealed class Singleton where T : class, new() - { - private Singleton() {} - public static T Instance = new T(); - } + public sealed class Singleton where T : class, new() + { + private Singleton() { } + public static T Instance = new T(); + } } diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index 0fb0c51..9b06928 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -4,23 +4,23 @@ namespace Aurora { public class VisualStudioLogHandler : Log.IHandler - { + { private readonly Plugin mPlugin; - public VisualStudioLogHandler(Plugin plugin) - { + public VisualStudioLogHandler(Plugin plugin) + { mPlugin = plugin; - } + } - public void OnMessage(Log.Level level, string message, string formattedLine) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + public void OnMessage(Log.Level level, string message, string formattedLine) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); OutputWindowPane pane = mPlugin.OutputPane; - if (null == pane) - return; + if (null == pane) + return; - pane.OutputString(formattedLine); - } - } + pane.OutputString(formattedLine); + } + } } From dcf71ab52f07a151b22313674552706d1bf8bd4f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 12:55:08 +0200 Subject: [PATCH 12/69] Add an .editorconfig file at the root Reformat the codebase with it, using https://github.com/dotnet/format, with cmd: "dotnet-format.exe .\NiftyPlugins.sln -w -s info -a info --no-restore" --- .editorconfig | 267 ++++++++++++++++++ NiftyPerforce/Commands/ItemCommandBase.cs | 6 +- NiftyPerforce/Commands/P4DiffItem.cs | 6 +- NiftyPerforce/Commands/P4EditItem.cs | 6 +- NiftyPerforce/Commands/P4EditModified.cs | 7 +- NiftyPerforce/Commands/P4RevertItem.cs | 7 +- NiftyPerforce/Commands/P4RevisionGraphItem.cs | 6 +- .../Commands/P4RevisionHistoryItem.cs | 6 +- NiftyPerforce/Commands/P4ShowItem.cs | 6 +- NiftyPerforce/Commands/P4TimeLapseItem.cs | 6 +- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 10 +- .../EventHandlers/AutoCheckoutOnSave.cs | 6 +- .../EventHandlers/AutoCheckoutProject.cs | 4 +- .../EventHandlers/AutoCheckoutTextEdit.cs | 10 +- NiftyPerforce/NiftyPerforcePackage.cs | 19 +- NiftyPerforce/OptionsDialogPage.cs | 3 +- NiftyPerforce/P4Operations.cs | 28 +- NiftyPlugins.sln | 5 + Shared/AsyncProcess.cs | 15 +- Shared/CommandBase.cs | 2 +- Shared/CommandRegistry.cs | 7 +- Shared/DebugLogHandler.cs | 5 +- Shared/Feature.cs | 8 +- Shared/Help.cs | 2 +- Shared/Log.cs | 14 +- Shared/Plugin.cs | 10 +- Shared/Process.cs | 4 +- Shared/Properties/AssemblyInfo.cs | 1 - Shared/Singleton.cs | 3 +- Shared/VisualStudioLogHandler.cs | 2 +- 30 files changed, 371 insertions(+), 110 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..038aaef --- /dev/null +++ b/.editorconfig @@ -0,0 +1,267 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Don't use tabs for indentation. +[*] +indent_style = space +# (Please don't specify an indent_size here; that has too many unintended consequences.) + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +[*.{sh}] +end_of_line = lf +indent_size = 2 + +# Dotnet code style settings: +[*.{cs,vb}] + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:error +dotnet_style_qualification_for_property = false:error +dotnet_style_qualification_for_method = false:error +dotnet_style_qualification_for_event = false:error + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:error + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Whitespace options +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = false + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Non-private readonly fields are PascalCase +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style + +dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly + +dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase and start with s_ +dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case +dotnet_naming_style.static_field_style.required_prefix = s_ + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# IDE0035: Remove unreachable code +dotnet_diagnostic.IDE0035.severity = warning + +# IDE0036: Order modifiers +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0043: Format string contains invalid placeholder +dotnet_diagnostic.IDE0043.severity = warning + +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = warning + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = warning + +# CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? +# IDE0051: Remove unused private member +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0059: Unnecessary assignment to a value +dotnet_diagnostic.IDE0059.severity = warning + +# IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0060.severity = warning + +# CA1012: Abstract types should not have public constructors +dotnet_diagnostic.CA1012.severity = warning + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = warning + +# IDE0005: Using directive is unnecessary +dotnet_diagnostic.IDE0005.severity = warning + +# dotnet_style_allow_multiple_blank_lines_experimental +dotnet_diagnostic.IDE2000.severity = warning + +# csharp_style_allow_embedded_statements_on_same_line_experimental +dotnet_diagnostic.IDE2001.severity = warning + +# csharp_style_allow_blank_lines_between_consecutive_braces_experimental +dotnet_diagnostic.IDE2002.severity = warning + +# dotnet_style_allow_statement_immediately_after_block_experimental +dotnet_diagnostic.IDE2003.severity = warning + +# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental +dotnet_diagnostic.IDE2004.severity = warning + +# CSharp code style settings: +[*.cs] +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Whitespace options +csharp_style_allow_embedded_statements_on_same_line_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false + +# Prefer "var" when type is not visible +dotnet_diagnostic.IDE0007.severity = error +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = true:error +csharp_style_var_elsewhere = false:silent + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:error +csharp_style_expression_bodied_indexers = true:error +csharp_style_expression_bodied_accessors = true:error + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:error +csharp_style_conditional_delegate_call = true:suggestion + +# Spacing +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Blocks are allowed +csharp_prefer_braces = when_multiline:silent +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false diff --git a/NiftyPerforce/Commands/ItemCommandBase.cs b/NiftyPerforce/Commands/ItemCommandBase.cs index 316693a..1d297a7 100644 --- a/NiftyPerforce/Commands/ItemCommandBase.cs +++ b/NiftyPerforce/Commands/ItemCommandBase.cs @@ -1,11 +1,11 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using Aurora; +using EnvDTE; namespace NiftyPerforce { // an item command is a command associated with selected items in solution explorer - abstract class ItemCommandBase : CommandBase + internal abstract class ItemCommandBase : CommandBase { private readonly bool m_executeForFileItems = true; private readonly bool m_executeForProjectItems = true; diff --git a/NiftyPerforce/Commands/P4DiffItem.cs b/NiftyPerforce/Commands/P4DiffItem.cs index 9a5df6a..c92f1e0 100644 --- a/NiftyPerforce/Commands/P4DiffItem.cs +++ b/NiftyPerforce/Commands/P4DiffItem.cs @@ -1,10 +1,10 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4DiffItem : ItemCommandBase + internal class P4DiffItem : ItemCommandBase { public P4DiffItem(Plugin plugin, string canonicalName) : base("DiffItem", canonicalName, plugin, true, true, PackageIds.NiftyDiff) diff --git a/NiftyPerforce/Commands/P4EditItem.cs b/NiftyPerforce/Commands/P4EditItem.cs index d29ed1a..31064f4 100644 --- a/NiftyPerforce/Commands/P4EditItem.cs +++ b/NiftyPerforce/Commands/P4EditItem.cs @@ -1,10 +1,10 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4EditItem : ItemCommandBase + internal class P4EditItem : ItemCommandBase { public P4EditItem(Plugin plugin, string canonicalName) : base("EditItem", canonicalName, plugin, true, true, PackageIds.NiftyEdit) diff --git a/NiftyPerforce/Commands/P4EditModified.cs b/NiftyPerforce/Commands/P4EditModified.cs index 530e0b4..0cee5dd 100644 --- a/NiftyPerforce/Commands/P4EditModified.cs +++ b/NiftyPerforce/Commands/P4EditModified.cs @@ -1,17 +1,16 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4EditModified : CommandBase + internal class P4EditModified : CommandBase { public P4EditModified(Plugin plugin, string canonicalName) : base("EditModified", canonicalName, plugin, PackageIds.NiftyEditModified) { } - public override bool OnCommand() { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); diff --git a/NiftyPerforce/Commands/P4RevertItem.cs b/NiftyPerforce/Commands/P4RevertItem.cs index 1d913cb..885fc69 100644 --- a/NiftyPerforce/Commands/P4RevertItem.cs +++ b/NiftyPerforce/Commands/P4RevertItem.cs @@ -1,11 +1,11 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.Windows.Forms; -using EnvDTE; using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4RevertItem : ItemCommandBase + internal class P4RevertItem : ItemCommandBase { private readonly bool mOnlyUnchanged; @@ -25,6 +25,7 @@ public override void OnExecute(SelectedItem item, string fileName, OutputWindowP return; } } + P4Operations.RevertFile(pane, fileName, mOnlyUnchanged); } } diff --git a/NiftyPerforce/Commands/P4RevisionGraphItem.cs b/NiftyPerforce/Commands/P4RevisionGraphItem.cs index d282f0c..cd17868 100644 --- a/NiftyPerforce/Commands/P4RevisionGraphItem.cs +++ b/NiftyPerforce/Commands/P4RevisionGraphItem.cs @@ -1,11 +1,11 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.IO; using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4RevisionGraphItem : ItemCommandBase + internal class P4RevisionGraphItem : ItemCommandBase { private readonly bool mMainLine; diff --git a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs index e8915a1..1b0e326 100644 --- a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs +++ b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs @@ -1,11 +1,11 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.IO; using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4RevisionHistoryItem : ItemCommandBase + internal class P4RevisionHistoryItem : ItemCommandBase { private readonly bool mMainLine; diff --git a/NiftyPerforce/Commands/P4ShowItem.cs b/NiftyPerforce/Commands/P4ShowItem.cs index 2e8bd72..0275edd 100644 --- a/NiftyPerforce/Commands/P4ShowItem.cs +++ b/NiftyPerforce/Commands/P4ShowItem.cs @@ -1,10 +1,10 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4ShowItem : ItemCommandBase + internal class P4ShowItem : ItemCommandBase { public P4ShowItem(Plugin plugin, string canonicalName) : base("ShowItem", canonicalName, plugin, true, true, PackageIds.NiftyShow) diff --git a/NiftyPerforce/Commands/P4TimeLapseItem.cs b/NiftyPerforce/Commands/P4TimeLapseItem.cs index f643581..02af51e 100644 --- a/NiftyPerforce/Commands/P4TimeLapseItem.cs +++ b/NiftyPerforce/Commands/P4TimeLapseItem.cs @@ -1,11 +1,11 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.IO; using Aurora; +using EnvDTE; namespace NiftyPerforce { - class P4TimeLapseItem : ItemCommandBase + internal class P4TimeLapseItem : ItemCommandBase { private readonly bool mMainLine; diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 581abfb..6a42f2b 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -1,14 +1,14 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +using System; using EnvDTE; using NiftyPerforce; -using System; namespace Aurora { namespace NiftyPerforce { // Handles registration and events for add/delete files and projects. - class AutoAddDelete : Feature + internal class AutoAddDelete : Feature { private readonly ProjectItemsEvents m_projectEvents; private readonly SolutionEvents m_solutionEvents; @@ -32,8 +32,8 @@ public AutoAddDelete(Plugin plugin) RegisterEvents(); } - private bool AddFilesHandlersInstalled { get { return _itemAddedEventHandler != null || _projectAddedEventHandler != null; } } // second conditional is useless but kept for clarity - private bool RemoveFilesHandlersInstalled { get { return _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; } } // second conditional is useless but kept for clarity + private bool AddFilesHandlersInstalled => _itemAddedEventHandler != null || _projectAddedEventHandler != null; // second conditional is useless but kept for clarity + private bool RemoveFilesHandlersInstalled => _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; // second conditional is useless but kept for clarity private void RegisterEvents(object sender = null, EventArgs e = null) { diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 5b303ad..f9ba4eb 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using EnvDTE; using Microsoft.VisualStudio; @@ -39,7 +39,7 @@ public int OnBeforeSave(uint docCookie) public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } } - class AutoCheckoutOnSave : PreCommandFeature + internal class AutoCheckoutOnSave : PreCommandFeature { internal Lazy _rdt; internal uint _rdte; @@ -53,7 +53,7 @@ public AutoCheckoutOnSave(Plugin plugin) RegisterEvents(); } - private bool RDTAdvised { get { return _sp != null || _rdt != null; } } + private bool RDTAdvised => _sp != null || _rdt != null; private void RegisterEvents(object sender = null, EventArgs e = null) { diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index 813c6f5..238671a 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using System.Collections.Generic; using EnvDTE; @@ -8,7 +8,7 @@ namespace Aurora { namespace NiftyPerforce { - class AutoCheckoutProject : PreCommandFeature + internal class AutoCheckoutProject : PreCommandFeature { public AutoCheckoutProject(Plugin plugin) : base(plugin, "AutoCheckoutProject", "Automatically checks out the project files") diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 57a7b5f..b08bb2f 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -1,16 +1,15 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +using System; +using System.Collections.Generic; using EnvDTE; using EnvDTE80; -using Microsoft.VisualStudio.Shell.Interop; using NiftyPerforce; -using System; -using System.Collections.Generic; namespace Aurora { namespace NiftyPerforce { - class AutoCheckoutTextEdit : PreCommandFeature + internal class AutoCheckoutTextEdit : PreCommandFeature { private EnvDTE80.TextDocumentKeyPressEvents mTextDocEvents; private EnvDTE.TextEditorEvents mTextEditorEvents; @@ -103,7 +102,6 @@ private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, obj if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); } - } } } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index b5c648a..a349b94 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -1,9 +1,9 @@ // Copyright (C) 2006-2015 Jim Tilander. See COPYING for and README for more details. using System; using System.ComponentModel.Design; -using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; +using System.Threading; using Aurora; using Aurora.NiftyPerforce; using EnvDTE; @@ -11,8 +11,6 @@ using Microsoft.VisualStudio.CommandBars; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; - -using System.Threading; using Task = System.Threading.Tasks.Task; namespace NiftyPerforce @@ -72,8 +70,8 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await base.InitializeAsync(cancellationToken, progress); // Every plugin needs a command bar. - DTE2 application = await GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2; - OleMenuCommandService oleMenuCommandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + var application = await GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2; + var oleMenuCommandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; // Switches to the UI thread in order to consume some services used in command initialization await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -122,7 +120,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke m_commandRegistry.RegisterCommand(new P4RevertItem(m_plugin, "NiftyRevertUnchanged", true)); m_commandRegistry.RegisterCommand(new P4ShowItem(m_plugin, "NiftyShow")); - m_plugin.AddFeature(new AutoAddDelete(m_plugin)); m_plugin.AddFeature(new AutoCheckoutProject(m_plugin)); m_plugin.AddFeature(new AutoCheckoutTextEdit(m_plugin)); @@ -153,7 +150,7 @@ public void Cleanup() ThreadHelper.ThrowIfNotOnUIThread(); - IVsProfferCommands3 profferCommands3 = base.GetService(typeof(SVsProfferCommands)) as IVsProfferCommands3; + var profferCommands3 = base.GetService(typeof(SVsProfferCommands)) as IVsProfferCommands3; RemoveCommandBar("NiftyPerforceCmdBar", profferCommands3); RemoveCommandBar("NiftyPerforce", profferCommands3); @@ -210,7 +207,6 @@ private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) string Absname = m_plugin.Prefix + "." + name; - foreach (string bar in bars) { CommandBar b = ((CommandBars)m_plugin.App.CommandBars)[bar]; @@ -232,9 +228,11 @@ private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) catch (Exception) { } + break; } } + done = !found; } } @@ -246,8 +244,8 @@ private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCom { ThreadHelper.ThrowIfNotOnUIThread(); - DTE2 dte = GetGlobalService(typeof(DTE)) as DTE2; - CommandBars commandBars = (CommandBars)dte.CommandBars; + var dte = GetGlobalService(typeof(DTE)) as DTE2; + var commandBars = (CommandBars)dte.CommandBars; CommandBar existingCmdBar = null; try @@ -271,6 +269,7 @@ private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCom } } } + profferCommands3.RemoveCommandBar(existingCmdBar); } diff --git a/NiftyPerforce/OptionsDialogPage.cs b/NiftyPerforce/OptionsDialogPage.cs index 2799678..82f94f3 100644 --- a/NiftyPerforce/OptionsDialogPage.cs +++ b/NiftyPerforce/OptionsDialogPage.cs @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF @@ -39,7 +39,6 @@ public class Config : DialogPage [Category("Operation"), Description("Try to do a p4 edit even though the file is writable. Useful if you have a git repository above your p4 workspace. Costly!")] public bool IgnoreReadOnlyOnEdit { get; set; } = false; - [Category("Connection"), Description("Use config from system. Effectivly disables the settings inside this dialog for the client etc and picks up the settings from the registry/p4config environment.")] public bool UseSystemEnv { get; set; } = true; diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 730853f..3218327 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -1,15 +1,15 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; -using EnvDTE; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using Aurora; +using EnvDTE; namespace NiftyPerforce { // Simplification wrapper around running perforce commands. - class P4Operations + internal class P4Operations { private static bool g_p4installed = false; private static bool g_p4vinstalled = false; @@ -31,6 +31,7 @@ private static bool LockOp(string token) { g_opsInFlight.Add(token, true); } + Log.Debug("## Locked \"" + token + "\""); return true; } @@ -150,16 +151,19 @@ private static bool Internal_EditFile(OutputWindowPane output, string filename, Log.Debug("EditFile failed due to empty filename"); return false; } + if (!File.Exists(filename)) { Log.Debug($"EditFile '{filename}' failed due to not existing file"); return false; } + if (!Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) { Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle IgnoreReadOnlyOnEdit in the options."); return false; } + if (!g_p4installed) { Log.Debug($"EditFile '{filename}' failed because p4 exe was not found"); @@ -255,10 +259,10 @@ private static string GetUserInfoStringFull(bool lookup, string dir) try { string output = Aurora.Process.Execute("p4", dir, $"-s -L \"{dir}\" info"); - Regex userpattern = new Regex(@"User name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex portpattern = new Regex(@"Server address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex brokerpattern = new Regex(@"Broker address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); - Regex clientpattern = new Regex(@"Client name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + var userpattern = new Regex(@"User name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + var portpattern = new Regex(@"Server address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + var brokerpattern = new Regex(@"Broker address: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); + var clientpattern = new Regex(@"Client name: (?.*)$", RegexOptions.Compiled | RegexOptions.Multiline); Match usermatch = userpattern.Match(output); Match portmatch = portpattern.Match(output); @@ -309,7 +313,6 @@ public static bool TimeLapseView(OutputWindowPane output, string dirname, string string arguments = GetUserInfoStringFull(true, dirname); arguments += " tlv \"" + filename + "\""; - string token = FormatToken("timelapse", filename); if (!LockOp(token)) return false; @@ -341,7 +344,8 @@ public static string GetRegistryValue(string key, string value, bool global) Log.Debug("Could not find registry key " + (global ? "HKLM\\" : "HKCU\\") + key); return null; } - Object regValue = hklm.GetValue(value); + + object regValue = hklm.GetValue(value); if (null == regValue) { Log.Debug("Could not find registry value " + value + " in " + (global ? "HKLM\\" : "HKCU\\") + key); @@ -365,6 +369,7 @@ private static bool LookupP4VC(Func check) return true; } } + return false; } @@ -416,12 +421,14 @@ public static void CheckInstalledFiles() Log.Info("[X] p4 custom diff '{0}' from HKLM", p4diff); g_p4customdiff = true; } + p4diff = GetRegistryValue("SOFTWARE\\Perforce\\Environment", "P4DIFF", false); if (!string.IsNullOrEmpty(p4diff)) { Log.Info("[X] p4 custom diff '{0}' from HKCU", p4diff); g_p4customdiff = true; } + p4diff = Environment.GetEnvironmentVariable("P4DIFF"); if (null != p4diff) { @@ -494,6 +501,7 @@ private static bool NotifyUser(string message) System.Windows.Forms.MessageBox.Show(message, "NiftyPerforce Notice!", System.Windows.Forms.MessageBoxButtons.OK); g_alreadyNotified.Add(message); } + return false; } @@ -509,7 +517,7 @@ public static string RemapToMain(string filename, string mainline) string result = Aurora.Process.Execute("p4.exe", Path.GetDirectoryName(filename), GetUserInfoString() + "integrated \"" + filename + "\""); - Regex pattern = new Regex(@"//(.*)#\d+ - .*//([^#]+)#\d+", RegexOptions.Compiled); + var pattern = new Regex(@"//(.*)#\d+ - .*//([^#]+)#\d+", RegexOptions.Compiled); string mainline_ = mainline.ToLowerInvariant(); diff --git a/NiftyPlugins.sln b/NiftyPlugins.sln index 148b56e..cd8dce5 100644 --- a/NiftyPlugins.sln +++ b/NiftyPlugins.sln @@ -12,6 +12,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActi .github\workflows\build-and-publish.yml = .github\workflows\build-and-publish.yml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D9887FB7-0D0E-4B27-BC6C-2E59CE41AF8D}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index a0dc6cd..443d87b 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -1,8 +1,7 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; -using EnvDTE; -using System.Threading; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.Collections.Generic; +using System.Threading; +using EnvDTE; namespace Aurora { @@ -49,7 +48,7 @@ public static bool Schedule(OutputWindowPane output, string executable, string c public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) { - CommandThread cmd = new CommandThread + var cmd = new CommandThread { output = output, executable = executable, @@ -103,7 +102,7 @@ private static void ThreadMain() try { - System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(cmd.Run)); + var thread = new System.Threading.Thread(new ThreadStart(cmd.Run)); thread.Start(); } catch @@ -112,7 +111,6 @@ private static void ThreadMain() } } - private class CommandThread { public string executable = ""; @@ -147,7 +145,7 @@ private static bool RunCommand(OutputWindowPane output, string executable, strin try { - System.Diagnostics.Process process = new System.Diagnostics.Process(); + var process = new System.Diagnostics.Process(); process.StartInfo.UseShellExecute = false; process.StartInfo.FileName = executable; if (0 == timeout) @@ -162,6 +160,7 @@ private static bool RunCommand(OutputWindowPane output, string executable, strin process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; } + process.StartInfo.CreateNoWindow = true; process.StartInfo.WorkingDirectory = workingdir; process.StartInfo.Arguments = commandline; diff --git a/Shared/CommandBase.cs b/Shared/CommandBase.cs index 012f2fe..9c69af9 100644 --- a/Shared/CommandBase.cs +++ b/Shared/CommandBase.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. namespace Aurora { diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index 829b230..d4c93e0 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -1,6 +1,5 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; -using EnvDTE; using System.Collections.Generic; using System.ComponentModel.Design; using Microsoft.VisualStudio.Shell; @@ -37,10 +36,10 @@ private OleMenuCommand RegisterCommandPrivate(CommandBase commandHandler) //if (cmdId == 0) { OleMenuCommandService menuCommandService = mPlugin.MenuCommandService; - CommandID commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); + var commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); vscommand = new OleMenuCommand(OleMenuCommandCallback, commandID); - vscommand.BeforeQueryStatus += this.OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong + vscommand.BeforeQueryStatus += OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong menuCommandService.AddCommand(vscommand); mCommandsById[commandID.ID] = commandHandler; } diff --git a/Shared/DebugLogHandler.cs b/Shared/DebugLogHandler.cs index 3b7b2ad..1eba848 100644 --- a/Shared/DebugLogHandler.cs +++ b/Shared/DebugLogHandler.cs @@ -1,7 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.Diagnostics; namespace Aurora diff --git a/Shared/Feature.cs b/Shared/Feature.cs index c6ecd3d..c3f9998 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using EnvDTE; -using EnvDTE80; +using EnvDTE; namespace Aurora { @@ -11,7 +7,7 @@ public abstract class Feature private readonly string mName; private readonly string mTooltip; - public string Name { get { return mName; } } + public string Name => mName; protected Feature(string name, string tooltip) { diff --git a/Shared/Help.cs b/Shared/Help.cs index f8421eb..8fe3240 100644 --- a/Shared/Help.cs +++ b/Shared/Help.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Aurora diff --git a/Shared/Log.cs b/Shared/Log.cs index cc10889..409c584 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -1,7 +1,5 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.Collections.Generic; -using System.Text; namespace Aurora { @@ -24,18 +22,14 @@ public interface IHandler // Helper class to keep the indent levels balanced (with the help of the using statement) - // Log class implement below public static string Prefix { - get { return mPrefix; } - set { mPrefix = value; } + get => mPrefix; + set => mPrefix = value; } - public static int HandlerCount - { - get { return mHandlers.Count; } - } + public static int HandlerCount => mHandlers.Count; public static void AddHandler(IHandler handler) { diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index 8a2e4aa..a7e6765 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using System.Collections.Generic; using EnvDTE; @@ -26,7 +26,7 @@ public OutputWindowPane OutputPane } public string Prefix { get; } public DTE2 App { get; } - public Commands Commands { get { return App.Commands; } } + public Commands Commands => App.Commands; public OleMenuCommandService MenuCommandService { get; } public object Options { get; } @@ -60,6 +60,7 @@ public CommandEvents FindCommandEvents(string commandName) catch { } + return events; } @@ -76,6 +77,7 @@ private OutputWindowPane GetOutputPane() catch (Exception) { } + return m_outputPane; } @@ -90,7 +92,7 @@ private static OutputWindowPane AquireOutputPane(DTE2 app, string name) if (null != result) return result; - OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; + var outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; OutputWindowPanes panes = outputWindow.OutputWindowPanes; return panes.Add(name); } @@ -102,7 +104,7 @@ public static OutputWindowPane FindOutputPane(DTE2 app, string name) if (string.IsNullOrEmpty(name) || app?.Windows?.Count == null || app.Windows.Count == 0) return null; - OutputWindow outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; + var outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; OutputWindowPanes panes = outputWindow.OutputWindowPanes; foreach (OutputWindowPane pane in panes) diff --git a/Shared/Process.cs b/Shared/Process.cs index 2eff99f..8d0d5a9 100644 --- a/Shared/Process.cs +++ b/Shared/Process.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using System.Threading; @@ -44,7 +44,7 @@ public void OnOutput(object sender, System.Diagnostics.DataReceivedEventArgs e) public static string Execute(string executable, string workingdir, string arguments, bool throwIfNonZeroExitCode = true) { - using (System.Diagnostics.Process process = new System.Diagnostics.Process()) + using (var process = new System.Diagnostics.Process()) { process.StartInfo.UseShellExecute = false; process.StartInfo.FileName = executable; diff --git a/Shared/Properties/AssemblyInfo.cs b/Shared/Properties/AssemblyInfo.cs index df5fd26..fd58d8f 100644 --- a/Shared/Properties/AssemblyInfo.cs +++ b/Shared/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/Shared/Singleton.cs b/Shared/Singleton.cs index 7450c9c..7c98442 100644 --- a/Shared/Singleton.cs +++ b/Shared/Singleton.cs @@ -1,5 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. namespace Aurora { diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index 9b06928..6430925 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. +// Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using EnvDTE; namespace Aurora From 2d12bc3960491dc12bec36cf1794383d0a952499 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 13:12:55 +0200 Subject: [PATCH 13/69] Add dependabot yaml --- .github/dependabot.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c3f84a6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +--- +version: 2 +updates: + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + # Maintain dependencies for NuGet + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: daily From e043683949465ad7c82ef752d03d409d15863944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Jun 2021 11:18:10 +0000 Subject: [PATCH 14/69] Bump actions/cache from 1 to 2.1.6 Bumps [actions/cache](https://github.com/actions/cache) from 1 to 2.1.6. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v1...v2.1.6) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 7f2189c..5d38fe5 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -67,7 +67,7 @@ jobs: uses: microsoft/setup-msbuild@v1.0.2 - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v2.1.6 with: path: ~\sonar\cache key: ${{ runner.os }}-sonar @@ -75,7 +75,7 @@ jobs: - name: Cache SonarCloud scanner id: cache-sonar-scanner - uses: actions/cache@v1 + uses: actions/cache@v2.1.6 with: path: .\.sonar\scanner key: ${{ runner.os }}-sonar-scanner From 20c709923990c3e5befa64e2db7762a029b4adb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Jun 2021 11:18:13 +0000 Subject: [PATCH 15/69] Bump actions/setup-java from 1 to 2.1.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 1 to 2.1.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1...v2.1.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 5d38fe5..67df281 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -59,7 +59,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2.1.0 with: java-version: 1.11 From 63ddff08ac799f49a318fffdf2357eaf52bae926 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 13:33:57 +0200 Subject: [PATCH 16/69] Update build-and-publish.yml Migrate java installation step to v2 format --- .github/workflows/build-and-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 67df281..e5eacc2 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -61,7 +61,8 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v2.1.0 with: - java-version: 1.11 + distribution: 'zulu' + java-version: '11' - name: Setup MSBuild uses: microsoft/setup-msbuild@v1.0.2 From 898b3abbffab4ac106596c7b98323ba0b0273666 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Jun 2021 11:18:12 +0000 Subject: [PATCH 17/69] Bump Microsoft.VSSDK.BuildTools from 16.9.1050 to 16.10.1055 Bumps Microsoft.VSSDK.BuildTools from 16.9.1050 to 16.10.1055. --- updated-dependencies: - dependency-name: Microsoft.VSSDK.BuildTools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- NiftyPerforce/NiftyPerforce.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 9b2d103..690d256 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -67,7 +67,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From a57ec63d6f411a8be5e369373bf081d3573753c6 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 15:12:11 +0200 Subject: [PATCH 18/69] Update .gitignore to ignore Visual Studio tmp files properly. --- .gitignore | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 387 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 015c7b7..34c8dee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,388 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser *.suo -NiftyPerforce/bin/ -NiftyPerforce/obj/ -NiftySolution/bin/ -NiftySolution/obj/ -Shared/bin/ -Shared/obj/ -*.csproj.user -packages -.vs -Build \ No newline at end of file +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Nuget personal access tokens and Credentials +nuget.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml From 23b9940e3ed58b2f256a74a241c15691a3a72578 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 15:14:33 +0200 Subject: [PATCH 19/69] Add dependapot yaml to the solution --- NiftyPlugins.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/NiftyPlugins.sln b/NiftyPlugins.sln index cd8dce5..660be46 100644 --- a/NiftyPlugins.sln +++ b/NiftyPlugins.sln @@ -10,6 +10,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActions", "{EAFAD58B-C12C-4FCE-AC50-3B04143946E6}" ProjectSection(SolutionItems) = preProject .github\workflows\build-and-publish.yml = .github\workflows\build-and-publish.yml + .github\dependabot.yml = .github\dependabot.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D9887FB7-0D0E-4B27-BC6C-2E59CE41AF8D}" From 8e36bd25232e15a7d8b91482584ffb4a0981425f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:19:00 +0200 Subject: [PATCH 20/69] Add support for vs2022 --- NiftyPerforce/NiftyPerforce.cs | 4 ++-- NiftyPerforce/NiftyPerforce.csproj | 6 +----- NiftyPerforce/source.extension.vsixmanifest | 12 ++++++------ Shared/AuroraCore.csproj | 7 +------ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.cs b/NiftyPerforce/NiftyPerforce.cs index f7e690e..19741df 100644 --- a/NiftyPerforce/NiftyPerforce.cs +++ b/NiftyPerforce/NiftyPerforce.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // This file was generated by VSIX Synchronizer // @@ -72,4 +72,4 @@ internal sealed partial class PackageIds public const int tb_timelapse_hover = 0x0013; public const int tb_timelapse = 0x0014; } -} \ No newline at end of file +} diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 690d256..b9f672c 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -63,11 +63,7 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/NiftyPerforce/source.extension.vsixmanifest b/NiftyPerforce/source.extension.vsixmanifest index bedb313..9958b8c 100644 --- a/NiftyPerforce/source.extension.vsixmanifest +++ b/NiftyPerforce/source.extension.vsixmanifest @@ -11,18 +11,18 @@ perforce - + + + amd64 + - + - + - - |%CurrentProject%| - diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj index b085d64..39e248f 100644 --- a/Shared/AuroraCore.csproj +++ b/Shared/AuroraCore.csproj @@ -6,12 +6,7 @@ true - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - + From eef0d01e1a938c75dac21c05c19a10e0304d2af5 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:19:32 +0200 Subject: [PATCH 21/69] Use info from the vsixmanifest code-behind in the AssemblyInfo.cs --- NiftyPerforce/Properties/AssemblyInfo.cs | 31 ++++++------------------ 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/NiftyPerforce/Properties/AssemblyInfo.cs b/NiftyPerforce/Properties/AssemblyInfo.cs index 5299fae..fbc4565 100644 --- a/NiftyPerforce/Properties/AssemblyInfo.cs +++ b/NiftyPerforce/Properties/AssemblyInfo.cs @@ -1,32 +1,17 @@ using System.Reflection; using System.Runtime.InteropServices; +using NiftyPerforce; -// 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("NiftyPerforce")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyTitle(Vsix.Name)] +[assembly: AssemblyDescription(Vsix.Description)] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NiftyPerforce")] -[assembly: AssemblyCopyright("")] +[assembly: AssemblyCompany(Vsix.Author)] +[assembly: AssemblyProduct(Vsix.Name)] +[assembly: AssemblyCopyright(Vsix.Author)] [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")] +[assembly: AssemblyVersion(Vsix.Version)] +[assembly: AssemblyFileVersion(Vsix.Version)] From 9684f6cb43e4756b4bc3667b950b474797f32aa7 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:19:59 +0200 Subject: [PATCH 22/69] Remove debug names from menu entries --- NiftyPerforce/NiftyPerforce.vsct | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.vsct b/NiftyPerforce/NiftyPerforce.vsct index a74a509..f56758f 100644 --- a/NiftyPerforce/NiftyPerforce.vsct +++ b/NiftyPerforce/NiftyPerforce.vsct @@ -46,12 +46,13 @@ DefaultDocked - Nifty Perforce ToolBar CommandName + Nifty Perforce Nifty Perforce ToolBar ButtonText Nifty Perforce ToolBar MenuText Nifty Perforce ToolBar ToolTipText + @@ -63,7 +64,7 @@ - Nifty Perforce FileTabSubMenu CommandName + Nifty Perforce Nifty Perforce FileTabSubMenu ButtonText Nifty Perforce FileTabSubMenu MenuText Nifty Perforce FileTabSubMenu ToolTipText @@ -73,7 +74,7 @@ - Nifty Perforce Item CommandName + Nifty Perforce Nifty Perforce Item ButtonText Nifty Perforce Item MenuText Nifty Perforce Item ToolTipText @@ -83,7 +84,7 @@ - Nifty Perforce Project CommandName + Nifty Perforce Nifty Perforce Project ButtonText Nifty Perforce Project MenuText Nifty Perforce Project ToolTipText @@ -93,7 +94,7 @@ - Nifty Perforce Solution CommandName + Nifty Perforce Nifty Perforce Solution ButtonText Nifty Perforce Solution MenuText Nifty Perforce Solution ToolTipText From aa2c217eb7f96860392cc3ab7d86a29563ba9a7b Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:20:17 +0200 Subject: [PATCH 23/69] Hide "mainline" buttons by default --- NiftyPerforce/NiftyPerforce.vsct | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.vsct b/NiftyPerforce/NiftyPerforce.vsct index f56758f..f1b381d 100644 --- a/NiftyPerforce/NiftyPerforce.vsct +++ b/NiftyPerforce/NiftyPerforce.vsct @@ -91,7 +91,7 @@ - + Nifty Perforce @@ -137,11 +137,11 @@ - + - + - + From ad32d29b640ef2921527271c977b2f1c8049cabc Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:32:20 +0200 Subject: [PATCH 24/69] Remove unused reference to System.Design --- NiftyPerforce/NiftyPerforce.csproj | 3 --- Shared/AuroraCore.csproj | 3 --- 2 files changed, 6 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index b9f672c..09a940d 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -56,9 +56,6 @@ - - - diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj index 39e248f..6736ceb 100644 --- a/Shared/AuroraCore.csproj +++ b/Shared/AuroraCore.csproj @@ -8,7 +8,4 @@ - - - \ No newline at end of file From e15835fd285f2f13a3934952a84acb910e29f52c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:32:37 +0200 Subject: [PATCH 25/69] Update copyright info in the root COPYING file --- COPYING | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/COPYING b/COPYING index 81c11ac..b90b3bd 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,7 @@ The MIT License -Copyright (c) 2006-2015 Jim Tilander +Copyright (c) 2006-2017 Jim Tilander +Copyright (c) 2017-2021 Lambert Clara 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: From 732a35529ed78e21f031ee868c99da53f3a0f944 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:32:56 +0200 Subject: [PATCH 26/69] Remove unused or duplicated files --- NiftyPerforce/AssemblyInfo.cs | 51 --- NiftyPerforce/COPYING | 10 - NiftyPerforce/CommandBar.resx | 424 ------------------------- NiftyPerforce/Commands/P4Edit.cs | 53 ---- NiftyPerforce/NiftyPerforce.csproj | 9 +- NiftyPerforce/ToolbarIcons.Designer.cs | 163 ---------- NiftyPerforce/ToolbarIcons.resx | 151 --------- 7 files changed, 1 insertion(+), 860 deletions(-) delete mode 100644 NiftyPerforce/AssemblyInfo.cs delete mode 100644 NiftyPerforce/COPYING delete mode 100644 NiftyPerforce/CommandBar.resx delete mode 100644 NiftyPerforce/Commands/P4Edit.cs delete mode 100644 NiftyPerforce/ToolbarIcons.Designer.cs delete mode 100644 NiftyPerforce/ToolbarIcons.resx diff --git a/NiftyPerforce/AssemblyInfo.cs b/NiftyPerforce/AssemblyInfo.cs deleted file mode 100644 index 3a95ad5..0000000 --- a/NiftyPerforce/AssemblyInfo.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// -// 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("")] -[assembly: AssemblyDescription("Perforce plugin helper for Visual Studio")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Aurora")] -[assembly: AssemblyProduct("NiftyPerforce")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Revision -// Build Number -// -// You can specify all the value or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("2.0.2")] - -// -// In order to sign your assembly you must specify a key to use. Refer to the -// Microsoft .NET Framework documentation for more information on assembly signing. -// -// Use the attributes below to control which key is used for signing. -// -// Notes: -// (*) If no key is specified - the assembly cannot be signed. -// (*) KeyName refers to a key that has been installed in the Crypto Service -// Provider (CSP) on your machine. -// (*) If the key file and a key name attributes are both specified, the -// following processing occurs: -// (1) If the KeyName can be found in the CSP - that key is used. -// (2) If the KeyName does not exist and the KeyFile does exist, the key -// in the file is installed into the CSP and used. -// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework -// documentation for more information on this. -// -[assembly: AssemblyDelaySign(false)] -[assembly: AssemblyKeyFile("")] -[assembly: AssemblyKeyName("")] diff --git a/NiftyPerforce/COPYING b/NiftyPerforce/COPYING deleted file mode 100644 index 3946072..0000000 --- a/NiftyPerforce/COPYING +++ /dev/null @@ -1,10 +0,0 @@ -The MIT License - -Copyright (c) 2006-2019 Jim Tilander, Lambert Clara - -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/NiftyPerforce/CommandBar.resx b/NiftyPerforce/CommandBar.resx deleted file mode 100644 index ad98b19..0000000 --- a/NiftyPerforce/CommandBar.resx +++ /dev/null @@ -1,424 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Action - Addins - Build - Class Diagram - Community - Data - Database - Database Diagram - Debug - Diagram - Edit - File - Format - Frames - Help - Image - Layout - Macros - Project - Query - Query Designer - Refactor - Resources - Schema - Styles - Team - Table - Table Designer - Test - Tools - View - Window - XML - - 操作 - アドイン - ビルド - クラス ダイアグラム - コミュニティ - データ - データベース - データベース ダイアグラム - デバッグ - ダイアグラム - 編集 - ファイル - 書式 - フレーム - ヘルプ - イメージ - レイアウト - マクロ - プロジェクト - クエリ - クエリ デザイナ - リファクタ - リソース - スキーマ - スタイル - チーム - テーブル - テーブル デザイナ - テスト - ツール - 表示 - ウィンドウ - XML - - Aktion - Add-Ins - Erstellen - Klassendiagramm - Community - Daten - Datenbank - Datenbankdiagramm - Debuggen - Diagramm - Bearbeiten - Datei - Format - Rahmen - Hilfe - Bild - Layout - Makros - Projekt - Query - Abfrage-Designer - Umgestalten - Ressourcen - Schema - Formate - Team - Tabelle - Tabellen-Designer - Testen - Extras - Ansicht - Fenster - XML - - Acción - Complementos - Generar - Diagrama de clase - Comunidad - Datos - Base de datos - Diagrama de base de datos - Depurar - Diagrama - Editar - Archivo - Formato - Marcos - Ayuda - Imagen - Diseño - Macros - Proyecto - Consulta - Diseñador de consultas - Refactorizar - Recursos - Esquema - Estilos - Equipo - Tabla - Diseñador de tablas - Prueba - Herramientas - Ver - Ventana - XML - - Action - Compléments - Générer - Diagramme de classes - Communauté - Données - Base de données - Schéma de base de données - Déboguer - Schéma - Modifier - Fichier - Format - Frames - ? - Image - Disposition - Macros - Projet - Requête - Concepteur de requêtes - Refactoriser - Ressources - Schéma - Styles - équipe - Tableau - Concepteur de tables - Test - Outils - Affichage - Fenêtre - XML - - Azione - Componenti aggiuntivi - Genera - Diagramma classi - Comunità - Dati - Database - Diagramma database - Debug - Diagramma - Modifica - File - Formato - Frame - ? - Immagine - Layout - Macro - Progetto - Query - Progettazione query - Effettua refactoring - Risorse - Schema - Stili - Team - Tabella - Progettazione tabelle - Prova - Strumenti - Visualizza - Finestra - XML - - 작업 - 추가 기능 - 빌드 - 클래스 다이어그램 - 커뮤니티 - 데이터 - 데이터베이스 - 데이터베이스 다이어그램 - 디버그 - 다이어그램 - 편집 - 파일 - 서식 - 프레임 - 도움말 - 이미지 - 레이아웃 - 매크로 - 프로젝트 - 쿼리 - 쿼리 디자이너 - 리팩터링 - 리소스 - 스키마 - 스타일 - - 테이블 - 테이블 디자이너 - 테스트 - 도구 - - - XML - - 操作 - 外接程序 - 生成 - 类关系图 - 社区 - 数据 - 数据库 - 数据库关系图 - 调试 - 关系图 - 编辑 - 文件 - 格式 - 框架 - 帮助 - 图像 - 布局 - - 项目 - 查询 - 查询设计器 - 重构 - 资源 - 架构 - 样式 - 工作组 - - 表设计器 - 测试 - 工具 - 视图 - 窗口 - XML - - 動作 - 增益集 - 建置 - 類別圖表 - 社群 - 資料 - 資料庫 - 資料庫圖表 - 偵錯 - 圖表 - 編輯 - 檔案 - 格式 - 框架 - 說明 - 影像 - 配置 - 巨集 - 專案 - 查詢 - 查詢設計工具 - 重整 - 資源 - 結構描述 - 樣式 - 小組 - 資料表 - 資料表設計工具 - 測試 - 工具 - 檢視 - 視窗 - XML - - \ No newline at end of file diff --git a/NiftyPerforce/Commands/P4Edit.cs b/NiftyPerforce/Commands/P4Edit.cs deleted file mode 100644 index 97ccc8b..0000000 --- a/NiftyPerforce/Commands/P4Edit.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using EnvDTE; -using EnvDTE80; - -namespace Aurora -{ - namespace NiftyPerforce - { - class P4Edit - { - public void OnCommand(DTE2 application, OutputWindowPane pane) - { - // Check if we've selected stuff in the solution explorer and we currently have this as the active window. - if ("Tool" == application.ActiveWindow.Kind && application.ActiveWindow.Caption.StartsWith("Solution Explorer")) - { - int checkoutItems = 0; - foreach (SelectedItem sel in application.SelectedItems) - { - if (null != sel.ProjectItem) - { - if (P4EditItem.EditItem(sel.ProjectItem, pane)) - checkoutItems++; - } - - if (null != sel.Project) - { - if (P4EditItem.EditProject(sel.Project, pane)) - checkoutItems++; - } - } - - if (checkoutItems > 0) - return; - } - - // Finally, let's just see if the text editor is active - if ("Document" == application.ActiveWindow.Kind && application.ActiveDocument != null) - { - string fullName = application.ActiveDocument.FullName; - - if( !application.ActiveDocument.ReadOnly ) - { - pane.OutputString( fullName + " is already opened for edit (is writeable on disk at least)\n" ); - } - else - { - P4Operations.EditFile(pane, fullName); - } - } - } - } - } -} diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 09a940d..7756c10 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -33,7 +33,7 @@ - + Always true @@ -65,13 +65,6 @@ all - - - - - - - diff --git a/NiftyPerforce/ToolbarIcons.Designer.cs b/NiftyPerforce/ToolbarIcons.Designer.cs deleted file mode 100644 index 933b820..0000000 --- a/NiftyPerforce/ToolbarIcons.Designer.cs +++ /dev/null @@ -1,163 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace NiftyPerforce { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class ToolbarIcons { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal ToolbarIcons() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NiftyPerforce.ToolbarIcons", typeof(ToolbarIcons).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _1 { - get { - object obj = ResourceManager.GetObject("1", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _2 { - get { - object obj = ResourceManager.GetObject("2", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _3 { - get { - object obj = ResourceManager.GetObject("3", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _4 { - get { - object obj = ResourceManager.GetObject("4", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _5 { - get { - object obj = ResourceManager.GetObject("5", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _6 { - get { - object obj = ResourceManager.GetObject("6", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _7 { - get { - object obj = ResourceManager.GetObject("7", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _8 { - get { - object obj = ResourceManager.GetObject("8", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap _9 { - get { - object obj = ResourceManager.GetObject("9", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap Icons { - get { - object obj = ResourceManager.GetObject("Icons", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - } -} diff --git a/NiftyPerforce/ToolbarIcons.resx b/NiftyPerforce/ToolbarIcons.resx deleted file mode 100644 index 8ab7813..0000000 --- a/NiftyPerforce/ToolbarIcons.resx +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Resources\Edit.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\Configure.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\Diff.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\Revert.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\EditAll.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\RevisionHistory.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\TimeLapseView.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\Show.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\RevisionGraph.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - Resources\Icons.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - \ No newline at end of file From a08b6b5ef9279113c49802e9c3ef3f2602d60d50 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:43:56 +0200 Subject: [PATCH 27/69] Reduce png sizes using ImageOptimizer vs plugin --- NiftyPerforce/Resources/Icons.png | Bin 9240 -> 2962 bytes NiftyPerforce/Resources/logo.png | Bin 9897 -> 3543 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/NiftyPerforce/Resources/Icons.png b/NiftyPerforce/Resources/Icons.png index 9bb61bcfe3abdf05ba259d588f64bddb29a2b06b..76fc52de98bc8653ca92940e855bb7a699a92613 100644 GIT binary patch literal 2962 zcmZuzc{J2*8=fJ{OuS6S@Mf1Jk|ha|8ChExTjG^LSwe&{8at&hBtx==EU&HXYnG8M zyooWEEH&A(lfodw_nfbD-t+zO-RC*?x$f(^?(4pvbAEsPqOryX-0J~%i?rBbn2ER9CPSl$W<4D1{jx#i(!YHA9| z-|;i!*wE0BM&=Lz;0z250A4@q9cTyT?JHM6xt@fy!eWKuD@R91Ws?VgYks#Yr%%p5 z7P#=TA9w;i$Y7Sl(^jAXCBS0jDzFB?W@ct&G8x#aq)dPVFd&DAhXtNhCHxmFr+-#X z83zs)78XDT{2SzD#1cASv1LFE3_(^*83zC?3>-M4Ujqij?e(rKM#OnGEIv z9%LYsNv;Dvzyx@L0REsoo}37-00}Y}1RTH>j8w`}Fbf)Cq%tjX5qK=v5cC3#AU=jO zrW77q1{ff)wY5E(+zK#fY?)unp!~}(U?@=jdw>97kTtTJ{&-gX+Xpb9ci2f(xaqr9 zNtDsN3ar4YVBz1(A08h59t50) zDVy5a&G9{Bb3Yjz)jw`mf7sh+o^9W>>f35r+R5wq$WJ6MFE8^)Wc}RUuC1->ng9NJ zcDs0x-qGG3^JSOR^093En=c+87!;HnHXH}Op&GoIu{mU$ZDz}BbuY-_Pn2>XjRksI>lQ|yXQ=2 zE%j2D>D9zvUKQS^y}l;Pzm{J&4CK&LNW)ax-autrRVjG?|BC|>`O+)|!gBJbid?WrwGL|iMQ?wTrR(PY{{HK4Th^P8Ot1Cqc^=mM%3w_m_oFi{ zq|f5MrTVh(Yc$8dVc(C`Wj`j7j1Z?P?o7D;EG{vhIpSedP4_5z{Q_roM?{@$E{p!A z>Am~I_mrAT1z(1uBIGRdQ)CHEL2eLp1d1N&sjU2>Yna334zd`9IxYF zHKIa>FeujEN~Epr472E^(a&_(Rre~+mLuoP%g#Xyh=g7dzBy{ zHMIXKA|hFkY1en%+P{_*%I1cTK#$|$#1TBv(zlc|dhfsMTo5as0_UG7*X>K*L5nJx(wd^Ba=(@6O z8A)`2B+LFnHEH_;As80l(}>oQ;%$Pe>llu$=U$^^|JR1SgE+{o6Ya4EcV1s3F?p zFQZ!uS6Y0NNez+yhJl^~Dv-*4Snd%#&l{RuK zruIjI-E|hf9x$!p-LaNUVmva8MAqV)+MV&ht z@{PS>r8gc55}p$I1N`AJQYQX#jlBlKrajDSuWXCU=&31+YiVV;H06kYFmJ3m(89{< zu{kHz`1SPILtbiIAx_>rDn}iE<5I>WOH)b9ixqCby@;C5blhRdY}(oDic2-21w&^j zw$?w`HHqPIo?`AvUlaqrOZ83*psZWB`Z^=Z5WTe(s_HsVwbX?2&cqcTPjEE(g7B6D9!!HLulNFjmcZwXs$bRMOQ zXEb_Wn}yEIcm*|AcNnNK9a)LCJU+pj_GJoRt<24FMAl+@T2od`6GK|;MzpxgI#zvZ zyEz)RWQhE=@PV%qKIyrK2r+22ICt3Eo{(&4UVd3uPvUv5Q{|lVKjAM&bZ7B_hIPuP zPaCCD|Ay+;j5tK|<%Lz$PM$k?Is%E!HOo8s93BF_YsK$2h=IG7+e|E0B(N+ck|mP@ zAa{}2wfDO$gh@eMXw{`ljJL5Iw#;woWfZ&J40*|@D^hMZY!o8Xbu|PEwcck7uw*_9=kER$K(P(UF_A#Rs zM^r+7_RBiSQ;xjEiM07ob*vP*Y=TU~h*Jz}0S&tN{KocGpXg{TmvnVN(#6)r ztth-w0@0Z+Nmg&WV<*dymoyU9>1<>bKuFsoB%mZt2U66e$scOZwS!_*ZciNUhf6K|BZkb z9{qWPI-)i_5cY-SszL5;m&Vq%N>AQF2`)SyPi zXpEJBq9oR+*iCFCDzRYH*q<%N7#lWJ5G+_gkTw*V`u*7J$`hP0AcVZ{vHvXYaPL|7 z+;Yx3d+&3uK&7;0%a$!$wv3e6)Q%rNe*F0Hq$zDV|7~W`dRT|4J%v#P9cxebacmNy z*x>l7OUkrIPR;Ao_fC>-(toC(VDJsEleP2wj^mFn)Xd)S^_GFV4Zf9q-c?p*k%W;)9F@O`Qzf^;^N}UUVkqyFE1~zve&=Ls_cK!xZNRF-g5kvu}K=E z*AI+x1LKoS#!GV<6VGzHqZ?ON{&(J4y*fU=?EDul1P0ctS9boGn3$NDn6mTx`1ttv z_!#GxmF=|boEWFIU_t0{h_?j~#&dy;YX-7F%(XYQWG>I?C*JBbfpP!WT#oAV(w-@o z=Zy;^?aSrdIArSa*7nmMu9^FWyY8Ga$GYk8RLOSvM1|K#8-0Cxi)wqE6EftJlx8%U z-j@6zb8B`{*}mn`EFSWG#HnDBy)|_tM zr(^r2reud%ivs~_xXsDMW~M5?_8GhWRtIjiUp}jwQ`2)fU%f7GmHj=&4Xvy*Z<%G_ zX9yh~giccx+^FtD^{@(G@YM?-|O>pIV)DN5G>9wl=jB3pprvT zet?F$^;X9Bt24em279k#3_A@0_v>GmYkm~_!*do`jd3VL6Jv~B0VLfcqg!@@ksMJqRZto+f(Oyes(t*9-i zq#;(Gwmw9zvIZ-gG$+ypNZZS!h}6#7pY(SO{WT%#0oof^Gc*p*cRo*9x?3Hd@qXIe zol6P^e&W^0EqjK@$HLBg0dIAOl8;!%D9dH+F@wAIguP1&8VUEKCad`TE%mCNV<3bO z)n&L81GY_!`Ea1r4q$AJ{ah0m&nXzkYWR9ze9PGRvtX+@einkp`T6=)96w+0LVgLA z(pWgDmox6~$T$-t?d3ZdlXF+ClplEsgyUdZdMx$XKgNEKRlW{7J>;FAM z&!5tOD*!aD&lvx^M;iuToTa)3!-@cx4iYNcy;^(rJUb3;E zG0xBbFUnsN1E%*p8Q(>y;a!yDIF!+lL4R%F(cfnL077hCcW&V88WBM5A2Ir(J?H@8 z=PN$nzk&awzos{|0)Hru$NRRn5qFSYcV2y5#n+#&dtH+SAYU)DSVnm#Q;#bz;||f; z3K;!&^K*x27R&he*WXmbeZqYogY!K3HsfgwHN%7$X~_~aG|k~lENdGg(modJ`c}Ngy9fb2}2M>ov3@NQ-X!8N%=z4bZK8hHYJx`!? zNkfU%q+W(6bXZu@UF+K4@&m;L$_PBr#JA2kBC8q78|DfkiLdO8nTp74qH>7EVIoI~ z+f;KmB5ET9N`i^>^>oK{GNPRQnmxMdM01WN^bxMhyWY&w%{24sLS!wuAbUt35`DP; zv-FEBkWU&AVsb?NzJq`>gQAyx^6IPq93fWKc}5F7K$h@y(kuoaY%bZ$La)i$(o5h+2$jV9Wck;- z>k*2*i&PQ=Jil*1NRIk9cbLH)*#nYy;_t3M->0R`*&|rI{>Xh=$Xg?pGp4R(+);-r z*tnxoAh$6Tp7L8O7?+BGTNB{12}UH# z%3s{=)kizJye>aK$CbwqVO~!B9iR`!AjK>h4vkTJxScp&>#xs7g9w=tkGP4BT(=~CexA#ZpWmP9^YNaP$j?Hr{P_94O`l(80SLhbB{vKqrcWUJ zcDH8qL+(x;bjaUi@Nq{G+Fgi-F9yksy%93`Y$s#rRmSOPNcSK_Dz?YuB>wT<1nB_P zX?pv5fPTO7%^1Ly~u@7Ig}Sa3qL73s?ivD zOosqfLU^d~JTJg~JYQ}d84HTaDqBzmVL0Vt-bm>99J zZp3Jh5ONx>gA~8lhhTXF@6VkR|IGTgM!Po)xrqm+a>sQQ9flX$Ll+1oJp}RtcnGe0 zGp4LXM^|F%%*)mn5r^UMfF9R!$CdFE^+1Q?iUF(Mo$;;@?{^aCdG;Q6bh+b8$6vhu z+;Nq@{@j60uUZQTAcY8`*Z;jUdmg zN%jSOHjI4JY!uC;>Lkz&QjnA+MAVgPkPT7xOtrPP7tw{MsXwSSI=AM9+Q>3xIC?mF@6?wh5YO(D$bADsio%U@{IGpo+&Zeq0?;qF#F~I zKfA{`A0b_;S-$+{2!XWxxX3>+iQ^eONj9P^u;1$NldF5A|Z`h@EH$e?+^dMJmZ@ z3|RgZ#Ml&$08xOM&d<+tH~)ISrU(gla$}))Ad`il#!lRWx1X!||7-A~_CyX`VV*|V+Pa&N02>CRX;t3Rijn{^i=Cv%HCiu%RR#4y4zf<_YMX5?n%N{BMgXP(b| zLS*)~*?qIqL{2W26O`?V>c3UZ$+`>C%~@$rvV*Og#nw*^&OT;+(pU{y(AAp9Ti-yq zooUX$M=WRIrY|zB-4D5=-@+3h>ft=hDh)-B#M*rjehZB8v!H8+>%2Ui@sbn&J|&lh zTV9b6q(9!^aO8;DLU^ft80UL3E;|U}7ix^31zmA|{(VX=3%9cKL--lxud>Q9CyGSt@NQ#q<;Q*z^K8)||Wu8faECf?miwAOtW(N=HEVM^n zMOI6Y>@Qudtk5q60KE<{&#)g@2wr}X+JYRY3a>x&ObTQnSXh74?vEAtWVEt@Ql-d>8U_ImP)) z6oByfVflnlF&i!%FQ^+^Cc`_TZLMu;Fwjr9Y2QmJk3fv7cZ{vKCs zpIOE(Up4j9*2C)ucRt?hhKH}?BI=y@?e+Mpmx+9aw)(uOlXPCXL4T^g8PUR_=XAH< zY|wwxglpdSx{VH`GjpX;-9io%U7;gHg|jj9NlSVnA%MK7I+1+1bd6*qkxP8P^ z>P@wUKAh-GdP=nFB9Y5*%Yg<@qD+-`hG8mE_5O;P!e}DpD2b23k%$%u?xZK8P+DNT zPMPS?f5RjiFEo5os3K-kJ}%& z`-$j}<$o+crzYBVf7|`!%|s14dw2GZBx-ZJL+1_#A{s!KP4Vxrz_Y!?TZ?eM{&=q5 z$1J9l5XSA;zas`Bi;}ud;uF^`68yW*DQy z*gB#Z&}Cur*nK<3$MD4DAmk%XQm%yoMc+M)>5<$aPBG(-QjMJHNr)8qouR)pF$L!; zJQkObf^rwR+bXnm76=V`BgN$dgka}3k{rY#;pF6}UX53M#HORR{N{9j; z69sZ+m58)_i3YS51oC7ouNCiu`ik!tQkrxR0uaIvlP^a2F@6K3AxFm)h8wK(@S{;^2nM5(iP77l}DMimi-VgyMJ`U2=`8%yhkPbw5olP=$R%p zFx8dlaQe}KNB0tW)vDjLelk(bJm=}oPl={~)jHYRS-LeYI(>)AefZTwl<@3{|3Ma{ z7yr2Xg7E|eP)njrTXmpz8%xijHZnnah)Dk(g&VFB z^*!vOmV^_%Kf(5d;VF@9r{Ndf0HU-J>hDQIB-m0TA|eCP)#BBBgb~qj2p^f~2|O!4 z2|x$|141ZyMq{X0|1X0*feGSlPu$M}G}xLa;{FYHJ5{#{Rz11VC$)N37uChvl(tjV z+M!iyx4ex7w~4Bg^*E(>VfoGPHox0^9+A&SJ|Fqm5gEoAJ~xabGCN_`OE!Qg<#g_} z=QoIM&q`WRrCU3r=R(s0I)k&E|aIFi|eik#?17J3451nVI* zI0d0)UkJ@z|B(9o0>41JD~6aY@IKYU`fCpmu^J<$lpsgz8>BATV|`YGk!6C!5a3DU6csxn<7He04XqPL{=Uc zUf#PnKR?&!+wpT;dVW5x?EEa~&bMQMUV45$&NzQD0SMuTWaSa)P{QGfQOsnVgWQhA zdI&$G>)Mpj@B&j9)ggfjEL?{CK0kV8fKAk~>dHj3nVlzeGt}*j;J?y<1~;$hb*OFR`YbHn<;)Uu3A!^pLGDF58hrZQtC8cE)?IE{B_7Dw zmpkasW_P~I_(B!mm7>)69O^47lnL@xJ>eD2nBzEP?2Xe z<9B#L$(gAr@+eIQH=Uh@2y5OeUFw(2^~3wx)?4X1kY5*^AKX z^f6ps)@4)qS*RJ-9^@e0~;$r~^4KNAb@ujt6=3s2BTqCkQJz!NDI4qEOYpL91?BmTbuxyNzn z5GU>BZ8>lQ zaXF%iRs2LmC(zCfMmydUk&Y__Pj!)yaoXmW(U>*HWViN6ExY#}<0W{o=|D;R{2W&v zKRngt&i^7tj~qX2kHE+(9P7|2FiUq1T)vBKA^gIrLd<|1FZoc)Ap=RnXEtuAsHZ!4 z*16_GNj=TJ_Z~YuRB9E!6doaqkzJNuCQ>&l@F?&giuh#3qZN;cR&`i?XLSNm?poDk z)tf}syp{fn2T}{kJ>j0P!Qq~!URteWLHixuArFHtaS!Fv7Fweh^h(1!hS@}qFXtu} z6c9O%Fdr-PCF*tCqqoCiBG*LAPVyy0t}^+1Q>D4D{5v)-X?@vZbvgdoh9xe&dPN; zW#z9pv*MX7T5?m;KE372g%{qsV?(se_fNa{Jw%U_bG)8A6KQ<(4fNVV6n_`@6OR+i z5z8%U_Ad*!=jeFmBFFScF}V~uJ93P82%f(q@Q_V?AIrMDL1i=+ZaR#VA`lW;iSl*0 zZW3|GicjvGL&rJ!Q$9Yq%)*O>TiN;f__Fi=!>!Z{*Z;Q|a`HMc9zX{g2m$m#d&aTY zPQ)q;b!V53MdZ1LP}!9i>tRBuAtd`aopCEt3I<|8FMIa@}!o9ToTy9oR#?%*{ zdF14EVyw9ObNSa0Dl4o%n`}=hi$gnQ5VQDSGvxWd&c7j(@#%MrqucU;<=wlbEs2kF z#6V#hJjSkQH+SN^6C1yLRB?VDu#}#kk9&Q79Ol2s^=cxSLMD|7M2amI+b#Tv;=M9X-EXAcr8%k}pm)8~U+who zMtke&n_719I?y=o>z4Yrg}Sob*u#bZ$!&SmxPg*|vf~umF!a}~2aVIMgt`Z#k^}N< z5lMftd~8#L$R*Z0#9=p){-X4Pye(1A5Y0ftTq5ZmVLYuPKV8d$&-2sbEe)}$Q=bH` z6%6KAHlH3A8AkIj_djCD|Gw;(s5IsvegX!cj5%q?A!2n}(LG0*H|W`^5+vDT)R^5N_{qs*Jusq@vrh!aG=lzhIT5X|br9>HlL{HrsPSjF|EuOLVA z3k%-=k%^(+l8uGj^BU|C48yv4E)Z4&{TLJReoXAi`^POSFMbws&ug$puw;JTzx?=% z7}ocqOrW%k<&@PPGZ;jlahboZ`QK81e8Y4RZ;6hQJ999b7YpecCRg-%!1ICGgE8pX zVsJkSXJeGN;TxvK>W|sH6;%HSL{_X2d8md-F9xJsh^aVIEChZZ>(1t3Rf{-07in0? z+>mF42qEZaDOLRcX{V4}x)LtiV61z$b}~QrAxM$ghX`aAJm9n*vw{)vc?*5abPO1J zW4*z%7&xpsjN^8h%FoYzaekyetjDZillgI+$^87>7w5Z>y?R>v)6RC`*dQ$g6|Fn(@r5mmN@E5ZFi}kV&-r=O;gPg-4eGA(tK@4 zy(LlJWnE|5NTlmUb*L7RMxwS>t5PQzG#O{pGJpC?9};)+O3s>%>Y>5m0bkt0%IAi1 zUUs4+bBE9VJ{EpH5^HSF0c{%n;;~SBFyiL<@8a*R$k@?4c3{$hgtjRYa{nms{XFr( zfb1T*CtA$88IblQ`#(Pg7b$m>p2%5aJ3+BQ?)&E~7lUOh#eoQ$0QZJJy7xU8J)l9O zAdfP93XX-{;}8~tUm!I=3(we!Px=4X&z81aQw__%XjlkN!O^Ts-pO@ z{ELQ#U~zu_f5q{$2^c_R;yF5nj@cZ`Z5WOh*}nm!CXP{!4nn|wSwW21EluN>AlEeq zxkrDZTyu0d25~tRao~ve>p46;1;~{Y;Hk*O!t2>4ew(wh>tATxapg8F?Q!L0=(xoC z9)GRE%3$1aRaE_dMZa@ebbyGS_M($wkgF_CHp@dUN>6c?CPK{ggkEhgK804r&xQfd z5*BhxhVidem73T=I{PoeSt`QUr;DYT7@u6W5k zQoHDS7-|un&3P2~YzWcH85eHGd1node~{*s_0zmIL^HPbiHtWID$M+TNu4i)?$5UL z>bHH|`dLl&8|zAauI-AZ-WDyTACG@RiG~CU_D`jnx?F9BbK4okZ>k!%|M+%q5bfAUb-wKv+evZ{V-$MQwpoSORN#Gc@;vYZAE zH*wxWFe|#^zojc03%#W6ECfeD2;?F3O7kk3pM_r1b{2va#xI6)#2j2)Uj-%+^}vWS zTNE_n%Jf5DE=G!w-5wmj03Rnez?EY_a=%7gEntZeeyr0ZPh#wfdT06J|DuNh9hLx( zlms4#gBU4)gY`fRSdz)mkx?zK6Cy5eQrY#dvPv=*1_rEam@WSNqmkX$7C~64BcS5* zM*KyZ%HwBYPJF--qmt_)f3@y|+GC zBcpe-YovFOz3Y{&>6z(#kdRB&u2@W$(3WmQ&X#SP2zAY|+a2S5{~E_q>Yf*kMtlT5}-5I~7%;aMzm u$_PGrSZ02B7R$_!<$21?56_}e{{I89VZx)^#PHGp0000FVkQ1qBll z5)={=3=0d<)70+o?-dgh?e6aF?(XR6>hkmR>+I~&)6~+`)%W-J?CtH+)YbCy^YZia z^7Hf3)YbF!^zH8M`}_O$_xIuBFNNj?)LWf_4W1s{{Hjx^8k>$@$&N0)YaJ9+t%0E z{Qdpc*xB>+^yKB{)YjM2)z;tO;rI9V`uqF&`uh0z`QYK>`~3XZ+1uje<>BJv+1uRP z+}+&W-re8e1ChJh+uQ@L?)mxo=I7`RmAwtI?i-rG2$Q@Mv+op_z8kggBAmgng&|*_?6rCkJtdD8U5;OeyA>?61EF}(1y)81gq z^zQHQ>h<|cs>oKy^3Uq@Wz6%h&)CZA_>9QfP{He{)#IYk;Jn!3N2?MST2 z)5yt}0000^bW%=J03s;^4+$Ot6AK*%6B`^Q1TGmB7vKj4BMm^!G9cU0V&v`JDjV`u z>*J_|Oq=-S_u+8O!S>tu>-4X_-k*Xw@t4s0kT33bHe6G>xF_zy@dX6utcAXr#C~4uGf2Jl~*kMA6dv!`_kJD z(DjfxYG(=lBeK01UZOJ4z11od%=|eyog=r#bCWGe4@i173#k5OELGJc04H4jICr zs1=i$DPXq4%#28D2D^XkxOhiqc%BjE$YQEAXFRIf z(xz?D4LaRyv3w4D|DoXj`Si^jH##~9eHOdFf2`o~G88^vTwo#Ib!L;;Cf(d@dGGD7 zUaY1cd+o-9G}wUuJUgG7iO3gq(KFkn+g9s&Y?HQ3z5K6-=x5Jq(g?vp@0l&67C|>$ zisv!rXJ-4_M!KOA5}TAwXv^ERX~Q=0;r3ZIzaJ#H800W!igRtdB=+@bu*ov);j!6* z>hbcr&98VwjLWy%TER9OxiU>`lZIg#*6*pTm*QOrP*|LA>ojANvVm@{L7TSOH`J}> zJ)XQ;>RE^YE*C}_%rB&&?rPd{EqY811S=y#aWjSaGT5a8hna1Q)e5og573|2rz>|7 z;1y9vVbd_Yeu46Qk;mTU5{H9x4n1}`HEL6NYTMDBbWJa&(Prqe07X>sPHKDoaDDw| z(Tl0TvaKX@3(UT4S*C8g6eTPPelzu3?_B;2F=edwXNV_WZU-8(4i^wUdzXhLyyIvp-oMv|gv$$O{EJ5vsHML1H3yZ&vg z^wAUH)xo~rR(DL8o@S$d7VS)@;g4g)Y3*J^2ei+_&D~kqk93t~f87ar>;bwY&%ld2 zZ4IL}=h3Dnpu}!l@A7uMcf?`U=uj+l%ESYaKzS$_S<>lHvhHpK+UP?qmu!8#7Uww1 zWaw-HvF@&}d@#SOP(b4SkG`4s`XkIj7kz*N&jY z&?G#Q_?eN;28+|G&@tqL!IM4SnG!x7*XgyZJXA^53(GdW4-G}N&4V?BwD~wkt!J_> zht+YUQ}=_*c$7({@P@u)Xr&hY>I3xb0Q9kI^`qQcEH{o~cZ($~jvQ@i&b?d=taW~I zlvnX_nKSO9;J)~7tVK`s{5%A7t2KwRJ_Lg&tS`=E=3au9d?B4WO5sBA&LUT$@`(#o zKZ<_yoQ5~;;1E=82YP7}DFPC1mrE4B=S<^Md2hCWPMK|U5`p#w#p zNntE+A7;a=ho=ma=Yrq{MdK!-aDz!(u1II-5R0M*2ptsp`H22vSoGWxF&ALu1<|{L zZyA#(xydKrHRxZhEXXceYQ6S=|)HWtr`_|AgJMQ|}{G@T=%uS46djs(pDE5Mz* zMTzRDY}k}n&GVv+^I>qw%ggLz>MAr0U-v4C!KmrsqNfy&sPm)wygHxfFvIhwYlbYG zkkEv_N@`=vbWaO3LaZJe9o^)h4A~nLDF}|s8wo>>t?IH#PhE*l!w21@u_IRXLD>1rcX!u`&k43j3(cLj zQ$_2I{APUhD`3-7D531RSEIhrW@!8%cbK#jxtf0O+y6XtqzA_pCOur4kzK29YZ9d1 zN<_1VdkuCV$w5UGP0D`j+n1iZ?6~l|pU7ath*TP>{J*(1+P{2WYwGdg5PieDviP zCf$2>Isv%dHSM0>OI{KjBEJulQD^=6iC6w#nq8zj62m|MZnhsYGcz+YQ?ebV3uQlt zedSWxVXkH1mp%nsp4{EyM5OK1KiX(qmT#F@dwpWNxw`e|&?wfPG0k_N-C_aH)(L-^ zxgD)`-K?B;EGRSff3b<@>mTc8@u_NAF%M4e9K|a#yiJm^orhzix=`a1TPeKDvC2o= z)-J^N_Rs86vKG9vd12=$-8St!{O8^b;JSADO7)Mf9O4Xu_7`gl{%3FEs{SJs;H?YG zw0V*wwwx0zPTUGye>(40b}$lSe`IU}w>JVSPN9IM`ns ze(dh;?d|E&S_g7xKDum;nEXeMBq;ukN`0J4ZX+wJ zrUr9zb8>|_IWs88$}%{+I9b`)TLQq_o=7c27_AnWWTUG!^YdX1dw~$Qd?`W}!W3yY0KjLVEAX>u!z(g{k0}W7iCt9g7mEqK2DJOL-|>IizY`rOd}X^ ziCiI;$>d2cwLTcMVEriDWP0F#ns%7(dXN<71M)5%$Sq)d0}n|wSLiCf96Ww%HiGwj z8MGB5y_G;dH>g<@2k#u~=#B;IPMd|%4d$l0T2Nah8v4@07Ysr>wUzKoG9bW+mkaxJ zSKj|N+uVD4Aak(;gH-BpIX4k=;8AFKT6*We#+gFtk5_( z)IM2i%#BMRkwS}^-yfdh`*Oj=Em+JfxcF{V9i{$d5t2159_fj_Ql{^kP++p}-ZgAG zWFoxM$id}v21Z+YJ;Xk8X;^KJJqdD&Lo@h!Z!R`fUcC3Y!&S7T9;31-K> zj@B%Cj(HhcP({}@f0@0mC}=zq-LIXwb+cAft%ZMk>hM#g7upUNO(lo5Q-5KQCat`* z3kpfZf+3h?|0>32UQ*beSGWreGM7oG%-E+EDQ}lW+zKW|rzjUe9k()V;t!R6;#Y#Iah)ReZl*vUzK&B5lI zh_^|O6z)He2k?l~k^EQA?+lA7MYf3K6I~}=lU>egTFq)kKREod?Rp8RF&w%wzDl{} z8-96fo4pjUrgQ9M*=ZE4Da81e;QsY&DZIF#M@G%1KXcQ5Ay<67uF;tzP>u#7Xr03|DRJ;r+F4yo>>&0*U92U~ooAHVv0++>KZ1@;vhJ4lTMKQz%!aR(cf3DS$avFLEBrrG=t zj_g^?zlwL6_-B5-aj>ni@gr;U8Iu}yQreSL-m9bjIIIDL-T$dNZ2Tg&jK(}Lmwu_s??sY)i zI}d320>0V|F03+-WPKZ$G@Pv5KAE~$0?wv3j&9tJ zmTn9J+^@KKg5kLNK7pSeuAknvP7YeaQ5 z*h-Nd99qk-tc0)(jYiAS`1wkp8j3xeNirGFqQF>;tIJrl`h0lD(1>NA@;T25w{E4q z*I2-Cz4wTJB8$E@4q^4h4Ux9R_BLu!>~Q{}`A|0k5|@%UAy!!5B~e;YU}K}9aau-A zld^Mre-3qWI{a($>N0r2#F;yuz~=DMj^_PobuF6nz*cweMm=Gq%3?{8)7!EFcfp38 zokkD@2MGoou$QD(js?}I<8f^CA7|9Iw|@mvwHJ{`!tR8ThniCjg@tyuVwol-MXHbF ztO+zwBK$lJ2ucpNw{?kfpt!TS_p?kj6u-qlq_OHc#sSiPva4f&9hRvSS#enIe=*eg7k*Xa_u8==kDoFEhXe zSH=W2fSnzX_=YHy@y^apG8&7_EZYO7poKHqim7eMfICa|!a;H69C#QY!?4Kw73gSp ziVo96q9M_Go7G-IDS@NTG(*Ep*{7MU5x0)EtV^M|g4|Q}@bjNpg@xIp2e4r8mta*8 z3{#XErb)MQc1?yfg=$E{4?tV2!0^`IUK2POJ2J3Z;yP6@-Zl&HB$#gm%Kb6mJq{l7 z;U%LB!5Px<#1GPqC^}3&tpC37n6lC`vS46h>de)dXbom|MczUCLD6ehwF*kUv&2__1Q4Ag``(C$j3 z(7b^?f20UAnl$tvbO+@5&PTq-VSj($HPhnlZXeYw*-}HpT48ds7vM_M2F9AVQmU`% zF)P7YsUQF(WOT%EsYeYXsh$HKVGQy?U?RQXl0XV*ZV&(lU~GWX(eK~J_$tBQ8A`0J z-{Ol-Cwwr};77Nd)!9?sWOc2t!P*H50=NJ!$kqo#FuwioJgs&Cxwo%%h@*(X0>cqR zpe+XI4e*=Hd-U+acy!|;PsUm?l^2oNc!O1k0a#?MLzpJv?{|{1pXG5fivHsi; zlJ*7FC{F4|sNOTUot*hEUP=Z>tYWt|2TpeW3#hdC<1NY7Luk+6q8vV>f(E;9pL6WJ zEhP$n++OaLKWL3%98~84D;e2r?A|@ik^UMZwR@kgzpr?)LF4O*f zln(^M#g=HB0BDICiR*~Of*Qjx9ltyo-^bfQG|>%D+#Q=@lwkBWlT2TKJI79g+2h2R|LG{vKj-p+5+U|rJTCI@hE@qrbF@Wq*OOmn z_v*1*rGT-~qoxovfix(U=}46!fST%@L8k{R84t~km_b-J3GT~QO+t-tldYPWiT*YJ z`Eby{0amp={EUp^$3GdF-oH?J_ZpB;#sJUDA0_oHCr9Z9^~GDWYGq)N#dQ;Iy zd_8iQD2_YGk*k`jfmAVAbqFQmkheVUk84t-@}>)=784VOArVn@)>EHD;U!h{_FyK7 z!F;or2iW4ziQ-GVKCRST>jGAm<6a~H9G~`HK{8M%>C(S#uzAPWt^)UMM7@pPy zcBSPVcIMBLH|yvwo5S`BJhh>Z09v*W(n1iHGdJjkM?aI^-3hJCtP-#|t_kY+g&6}@ zf0Wn3&HeM`7Yu_iBYPt0fD4O?TdRQd`Ihz1w_pU%l{wamdB*eC@Bwt*gL*$ctkDef zd0+Ztm-1>E+K4AyRk)sNpI<*C3me?-MQraW!U97%TPTFLOieUm;Tj?EmR5waQNKW6 zGri@2a4d?l#b2#noYs>`fRL)?Hd2-^U_Y>BGq0)6*uS2~c<`J9v$1Pm8xcv$*ESKm z!YqW0ztcbmb4i8$x6}ts|sO^r16}|bmyNQYjn4W@~lAZQ4TUCuYAQmF0LOJz&)E-rDh@xc=$!V z{hg_Jii7)tgXdj@D8hOn7WLges z6|VJ-1RRDxl>b&n^}Y-xTIn*UP>+6nRcf#6S_k!D*26oV6^^$Xq2|s z(N&e&0xDE7geU+%VSt>5sA!S&Tom5SyvTWI@uT0MDQe{DwMU$mp@15KF{_At?UlTI zk$4AGOo|YY0M6cKCw)6fz7$ZHnr1;QFqmyZmxb8`m}&0vs?1M!zKRLodKU}+GA zP49}`$G}MwVtlIEK;31PL;ASBS$0)uDmiyZRx81ga3|_zY)|Wa3fl-Rcl?EGzlCw; z8==j_z6Ux1(+(HfVB-b|Low7bq;pZkD7)=saq)hsE?!HspS3AncW9)}&xfdxBpMAh zfkN*^tq{8@DEZ`B2v&zrpd?{XS}Zrh>JPFM#$sRu?oapAF{&#Up&biUQWnULmLd9$ zp}kwtdyM`Ub;ozBwn+Yjx#uHhae2f^YrovX#&3q@+3S{HSEPnrzU|)ig4VsN*?1dU zSbsHtE~EcK$lJSXYKAL4io4((bFJV>Mw2?xPq!M78~t7{!Vvj{P}kSt1yXLhGf+ro zxO4m_&)Ei#l_}OFH2lbu}V#yi7+j*5 zWcpw3yWG@ro-GtDK$s4+%RC`I*_qAyJ9CW|twpl;j8$4K-bm&|iaLk~@(d|TU3p0j zjD*=UqY>8mAv)(%HOyiIy0=wzw%a-c3G*%q55Yesu){5sDhyR2J1mV2x>4GK1OEEW zP5Nsw*kj>j7A@)Ao5?-Lm3Zs(hOFV7-ANtoCt8{;?9VhGNUec@AC|R!Ot(>oGddeF z+VFb>2g95l#KQf3%HZ5~qRpJ1gD)zS#1rV}IW58@}f{I6`H-j#|? z7uIH!7rZ$bv`wgGgod>{25}?*qexFW3o!eZbMK(VWV>81hA-X8ztS|(6OYn2w53=T zUXZn(83(?PO8K2`(-%=*>%48AlPf>i6z^Pa6fz8|r^ANu}}OeE9w+2g?0Yb zQhp#))21m_rITUl7>(BYLVo-nR`XJ4e29hA{zg%Q?{P!}r@HkH zP715Wzg4X_Eac@nzY??1iZxEFEzs9ryK-^ETc-g1s1sY>5W3pQ)E52pL!ScFhDZCk z(Yeq`w)BYzvlWMnphF5x`EjxiaeqQUKbdQe?8*BCXXmB>ohXBd!nM#((FN#7rs=wA z@1rfq1~T@*eAF2Z7(@FBf;|MY=!V6QQrRH5m_ZJ1RRzVg8f(mcG4)5Mq*z5}DY9Ju zF5ka z)vP-$XtOazNc1_m*B5$-e#ZOg+Hk(s6tCoYXIR1hqD*t;`Bv@_o8`-4+U|H#H$1a! z#NPlb_#`7ST8TW?KcDe_?~vn~PtpB>W;sh6Zak(XWA~+dcJa8Ok%4dWgIlO z66^`piJ~a*^WrZe)7@7a`!^tD(Vw1t4vJxqBK5N7+%|Vbd|RGYI6c<~6hWR1-h~ zgUpVT@x!eCv7tfCvm37-maNh{@Lz!!zA0O5da8_Q>6A@AyC+9Z7d&8Sa_=FE?K=ChrJbZry%KCeq8@~^5i6iox)GZM+A{Wdg& zt$XvirUe+a-v~p*8f9KO|CD>^APeeJk2$bP^Sd}e(}mmDGO)HBFYpRVI`8-sb`u0y zp@vx|gdKvVM`r#q)m+X$lNOfED{I2Xc8G|KK2=mJqEibO$9#6>MT~_q&{+h8FZ#366*qsN<+9w+=|b;MS0U16w5pD+f?@^}C~CX@=${Zs zJ(-_WAD;*i(Jf6>c71Q^OYV42VQ{DNC0YeOVi);P$tACC6}HlyA0Vp{4*Nd#zhS`?u z6_*nP_2xc_20?n)lf>@|3m|Nq?`V0mxSha2(zR3EC*U!JhQPSv{F#bAy)P?&-ESRa z|l!!Pe4agIi=<&9ISE&SE~u-cV~g0MB~-(!wxN7tdZ*aLioS zV^BL>o!Bz1MwT$>T4f9L`fr*j`nv~XrYfh)rO)5Oo?4|Or;2|?A1UIDKH!OwAH&xa z{8v$E?Y?mdCKA|L4s!B?NMOw z$$3^{^2>Pq0G-KDg|G4wBWL)ohT+AE!hJ**e1Xmdal20pu$Pm{%UU#UzaSdU>Abo} zOI>uaySClr666iPUg;=n`}-iXC+jn$)U)AEGMIlXN4@ezgft1FB9lZK_tJ5$2uNKp zkV3GQ$7{lB3uoALMw|u4Tp5^EC@Oe`G`x%-GnB}v&%zk;{sA^)eMrN)aA-817%0qo zF~;Y>4ed|-?J|fb@B^|R=_m;03*KCXJHl zL25oZr@tPIxo%sr-QpK=;^3hCYNvbf-Zv_91;L3lcm%Hsx*<6}OpwHuQqL=|1C>ZW$SK>=L(e7bU(6aJdvP?#$rn8;eT`+eC zn`1X2_~Cv(>%>mE^UB|d*2I3k@ukkh-{M2j0^CIk>@7la2U()eq|hJGsz(o95HKRp z3JYdkgCV@=u@1d9?uO)$-l&^OK{D{CP3IEP@pj_WeZO5w+VMvRsQ@3)UzRM@AQ7-z zd7@LLCuP;N<^1A4Mevf*C-@sisgqQS?(?S^i)@Rs3O#2;!$hJQ%H3_(tI?+kr;&3A z5u@?fdNnlE)wsz8kM^w#V>V{wFLak2TiTU0EN$i2Uor=?j%T`|&yZqGeFqw2tB0#@ zQ`p?!o<@|gsPYP4TM{yD#WJ-vnVl)ArsSR#0~JWo9qt|jiP?j?($T%1n=Fh2Qra^ zXe#^1-E_lLDsu1d&uQydv&64veb_8GXa_g9tyEP--s^rtKbt*%qQw(n+G$!(Cpz@q z(J)ZLOra;C74HJt8*9dlwnYm|{$Dr;qv42>{!7{bE~?K`vSMVdyY18B^Z3qRp;LUv zLcMC9F_u*tgiN9x7YPySnCy~{CT)i$UAZ(|3u8+ev1Ei?oPMXsL;3E@Qgzunym_EC z9Nc+$8R?Sd?1|J>`~M}jaHvX%L(_f=o!asnmWU-M{5eQQqO~ks6%kkW>$5ge4}MtS zHKreV5zpdWNGf31pHcjrJIs}e{Nj)Z^_KtX;h6CQza~{HH#I@h^$BPMp`^pxjphCX zA`7i*8avon>qqU@BH{TX7zJWg%_kbKzuY^0qg2TdHGMD4Ia)BqAd`;Xd&Z`;{*~Y< ze4~%&d}DzA)lo^O>!8)$1{INiagJ9;?_rewnyH3GZ`CV?jQk7vPp5ep5Hl}I{H-I1 zUaQy_gxy>N3HVtI0XVnuVxqQayN>iv+D~amPPcrVzlO^2RPEEsXV@YTBOY{I-2mUK zjHuq)6NtOR-#QgTapW$+y1{E4;SF%1dW&Wyhg|cd-zPfFxRypX4+-q~{NZKtinlaFR z8Eu)^r#RmE+kQw6^ZU!zxANIPX?JWdgRdl$vJ6GOJXf93q4eq!rd^wcZp!jMW)uVo z|CQT;yIIs1prux!#o`1AQ3jX4i(5Nv%*B|eZvKnQKN56}FePs<$TBu_N@$3D)*E}8 zTvkcTd_kodLAsXNQ#4({H%&8B0l~`8)J1nLFE*ekKMJ-hFfnQPQ$b0tVhL+Boi!rn z9IuaB#?@1%W9#}wvsxDa0)sXV?PJ4%-MJ*XnrDmClZ*-Qdo0Du48%c;P$%<%N8$Dqt)z)C9LRRUzi&q|U*o0Tp>YP^}iB*$koo;0ojqG5mTl?TGhH z{&hzA8_Z&Ss5x3mI}y4{Z&OBaOj%$z>@oJQad*$6)zVr8?HT-Oa{|UN+UW9i0zc@O4f{%ALmYv_cU~xnq#S(Cvc`2DaapV%guP2{}o8 z!I+!-`*p0f#Hl$swxfu*mE)fx7W7mb=$IfUbm2sp2NAZZ@i0uu@SyKaAT74y9)?-= z=d8a2zF2TSlx>v*XVr%j(C?=R9}q-@LGtIAo-cumpo!}?nOGrIK4@s7g~KUKTyIO` z!uROjGSND9aIg48DXWa$!vMh!L}JYW$%~J63nxZL1|RYKEwgJfb?ZA^(Ub_BpM)@} z&vsJ@`X%JO#{l$#sDB%$l-0f=ne^9Uis_&ov;F;*bDR_dJ>|E#T_nTQJ_*;Ly_|;|;pbIZ6m9IAwM4bF64E}0Mo?Wsr*9=$c!0yu z%XKXc#*)o|+)$UuitiCG>Ig^<;daFBeKqj;IZFC##Ip_CX1wKySa_%DZd+ve#FV6( zURNeYK02KpHJ&fo^lfN4z(5JT03FC|4PJ4mz^T^@i?ENn`{2LPyvc%dapBF*Zpdf! z%cWN29Zeg;{5!$s##Y43j?70OWF*@B7bHlKK&17wlqQA?c?cvKzq;JKX=z-hZk-@4 z$4xU%xgu}#rH5awmr3{qn?2XDE5EV~glsua|Aj}Bd1EZ4!S~=s!KLV11!`#7XFCLr zDnmoG0!fRTpe04P1a4aifO^pC2Ee!;c17A>UtcEjFz;z#&|z$;=Hnq2F_sRY{F(fR zVhZLqj7w*6cD8mpnmSXrq;N2RDuL1%ID8&b{2Y)&PU;cSc7s5-e|BV){}kbX#DceA zCgZcn&Kd=T(!km3cXlYyR!X7@cjDlCAwiFuHF9-+ZhyB+=Qe!|2#bj0U;_1I-h|ge z(2=li=&X@{l%Bc9sVDT7slTHKG=+&o0TN3A2&m3(`Ati@^|NWYB|HxtkycPMN zRy&ca@WVTDNBD4HI_4e?$cy5?TGElIEo4xa-|EFss-)j|#P--zE9De97U`@^Uf0b* zs+*+|`czF#&08*g%k`AUdf=AygFK@u>|f&za~si{t&S&vjwusn&;)^M5ZJr z3i;2D7d#!!RaH?);bU>%S?h~Ku)|}|SLyFyM*e}@bmQ~ezybzR8Z|L!H#vPfy8>GD zr@J;5cJFLfad2?(u{A8ciKdlkSP9TBNftaTuDYZef9<>Q+Min>{ieakdeD3}63dl8fIK*o-ySlHSV2m{?pu6w7(aY1?`MtUPbchtjv zxfPBjMe1isFD22l3$G#8c4h=~qkc6esIp8K?EMT%PKE@7r!rMjUoCUO7Cs-ZQby1V zJ1*CKT&0Ml5%VBT7rPl%;%PQH+nJc!SlcL(rutx{Ww;+!6r4Jk&$D{4pA#e}Nhjv9H)Gxt zp$lOZy-HnMTXT(*kCt@$xAk{3FFTvFaph#7^=Mq<-C}B7Tq6;B9(w7<#i1}WYrlig zT!W6W-`V7DS?hhcMy|5%@P(SMlpA-gJ5g;t*_=_w$da`UHnd$B&h}4tUT57KBDv;) z_RexKqHX3ApNK=bpz!Vt4ekkmJ;tk+;X}R@8UJbf=SuHz|L+%RV~iTdn~i{ zYkYiM>49QDGmP%~APOu-H`Dm(klAjkwD~Sk-?83fp~*{UN$|(>=g$oWr%oh%d49J$ zupdneNd;%*pl?cUIc~Omd<4_H_0aNXi3C4tG>;COud$?X?JFAeEf!h0c1&hCE@=bC zVKV{MeNH%c%VnXc>xRMCDT8mD?BMF4p$j6Aw?YI$_Wj8w8sYZecKZ$3&|fi(6s^}k zi+D*$g|dRnH-DKATF7CWGh@)^)q*^Y-6B*KM>Bbp6qU0(-H#cz^oAF=%T|5J47#yv zC$OJow2!P4;BxSwg}yWxO^c|v zq89GG%eD;Jc5YkA1MWxORu^60`nBl*!_O27vxirDXLJ(LH`rZY(3zH9GOlT9(P-II z$W6}4{c`)rg5Tw^PctszK|Gi=7cx1`910PgnA0SE6+7TY|Yg1;nb7Lf+FTgou0|`ZiF_YUW$!hteQ1J&D?5qd-SKlm|?99%_j+zQ72$K69*h6ZE zT;i||GTOG{DAT{$5oWlVzp528Kw_W%F@ From a57b6c917b531651c3b429818432f61b21d133de Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 17:45:15 +0200 Subject: [PATCH 28/69] Change extension name and hash and make it obvious we are a fork of the original NiftyPerforce --- NiftyPerforce/source.extension.cs | 6 +++--- NiftyPerforce/source.extension.vsixmanifest | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NiftyPerforce/source.extension.cs b/NiftyPerforce/source.extension.cs index b4c979a..c2c1dec 100644 --- a/NiftyPerforce/source.extension.cs +++ b/NiftyPerforce/source.extension.cs @@ -7,11 +7,11 @@ namespace NiftyPerforce { internal sealed partial class Vsix { - public const string Id = "NiftyPerforce.Tilander.eb29cef1-a8b5-41b1-922d-6ba8cec91ef4"; - public const string Name = "NiftyPerforce"; + public const string Id = "NiftyPerforce.belkiss.3cc12a76-88f6-4e50-9ede-377ba823db79"; + public const string Name = "Nifty Perforce - belkiss' fork"; public const string Description = @"Simple Perforce integration for Visual Studio"; public const string Language = "en-US"; - public const string Version = "2.0.12"; + public const string Version = "3.0.0"; public const string Author = "Lambert Clara"; public const string Tags = "perforce"; } diff --git a/NiftyPerforce/source.extension.vsixmanifest b/NiftyPerforce/source.extension.vsixmanifest index 9958b8c..63adb44 100644 --- a/NiftyPerforce/source.extension.vsixmanifest +++ b/NiftyPerforce/source.extension.vsixmanifest @@ -1,8 +1,8 @@  - - NiftyPerforce + + Nifty Perforce - belkiss' fork Simple Perforce integration for Visual Studio https://github.com/belkiss/niftyplugins COPYING From 4224f33c7c186ad2e7fad22da907f89faa072089 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:08:36 +0200 Subject: [PATCH 29/69] Remove config button from menu, useless now that the settings are found in the VS options --- NiftyPerforce/NiftyPerforce.cs | 25 ++++++++++++------------- NiftyPerforce/NiftyPerforce.vsct | 28 +++++++++++++--------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.cs b/NiftyPerforce/NiftyPerforce.cs index 19741df..c8813c2 100644 --- a/NiftyPerforce/NiftyPerforce.cs +++ b/NiftyPerforce/NiftyPerforce.cs @@ -38,19 +38,18 @@ internal sealed partial class PackageIds public const int NiftyFileStatusCommandGroup = 0x3003; public const int NiftyFileTabGroup = 0x3004; public const int NiftyFileTabSubMenuGroup = 0x3005; - public const int NiftyConfig = 0x0100; - public const int NiftyEditModified = 0x0200; - public const int NiftyEdit = 0x0300; - public const int NiftyDiff = 0x0400; - public const int NiftyHistory = 0x0500; - public const int NiftyHistoryMain = 0x0501; - public const int NiftyTimeLapse = 0x0600; - public const int NiftyTimeLapseMain = 0x0601; - public const int NiftyRevisionGraph = 0x0700; - public const int NiftyRevisionGraphMain = 0x0701; - public const int NiftyRevert = 0x0800; - public const int NiftyRevertUnchanged = 0x0801; - public const int NiftyShow = 0x0900; + public const int NiftyEditModified = 0x0100; + public const int NiftyEdit = 0x0200; + public const int NiftyDiff = 0x0300; + public const int NiftyHistory = 0x0400; + public const int NiftyHistoryMain = 0x0401; + public const int NiftyTimeLapse = 0x0500; + public const int NiftyTimeLapseMain = 0x0501; + public const int NiftyRevisionGraph = 0x0600; + public const int NiftyRevisionGraphMain = 0x0601; + public const int NiftyRevert = 0x0700; + public const int NiftyRevertUnchanged = 0x0701; + public const int NiftyShow = 0x0800; public const int hammer = 0x0001; public const int history_mode_icon = 0x0002; public const int p4vimage = 0x0003; diff --git a/NiftyPerforce/NiftyPerforce.vsct b/NiftyPerforce/NiftyPerforce.vsct index f1b381d..7d4a279 100644 --- a/NiftyPerforce/NiftyPerforce.vsct +++ b/NiftyPerforce/NiftyPerforce.vsct @@ -132,8 +132,7 @@ DefaultInvisible DynamicVisibility If you do not want an image next to your command, remove the Icon node /> --> - - + @@ -215,19 +214,18 @@ - - - - - - - - - - - - - + + + + + + + + + + + + From 1bfd60cb23d50c6440ce1fdb665a19e2953dd750 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:09:24 +0200 Subject: [PATCH 30/69] Fix subsequent press of revision graph button when the operation first failes --- NiftyPerforce/P4Operations.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 3218327..ffe03f6 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -225,13 +225,20 @@ public static bool RevisionHistoryFile(OutputWindowPane output, string dirname, { if (filename.Length == 0) return false; - string token = FormatToken("history", filename); - if (!LockOp(token)) - return false; - if (g_p4vc_history_supported) - return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); - if (g_p4vinstalled) - return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + + if (g_p4vc_history_supported || g_p4vinstalled) + { + string token = FormatToken("history", filename); + if (!LockOp(token)) + return false; + + if (g_p4vc_history_supported) + return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + + if (g_p4vinstalled) + return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + } + return NotifyUser("could not find a supported p4vc.exe or p4v.exe installed in perforce directory"); } From da90429258cc1c85e85323471213593efc0dc092 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:37:14 +0200 Subject: [PATCH 31/69] Fix grammar in some log messages --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 6a42f2b..14ac861 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -41,7 +41,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) { if (!AddFilesHandlersInstalled) { - Log.Info("Adding handlers for automatically add files to perforce as you add them to the project"); + Log.Info("Adding handlers to automatically add files to perforce as you add them to the project"); _itemAddedEventHandler = new _dispProjectItemsEvents_ItemAddedEventHandler(OnItemAdded); m_projectEvents.ItemAdded += _itemAddedEventHandler; @@ -51,7 +51,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) } else if (AddFilesHandlersInstalled) { - Log.Info("Removing handlers for automatically add files to perforce as you add them to the project"); + Log.Info("Removing handlers to automatically add files to perforce as you add them to the project"); m_projectEvents.ItemAdded -= _itemAddedEventHandler; _itemAddedEventHandler = null; @@ -63,7 +63,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) { if (!RemoveFilesHandlersInstalled) { - Log.Info("Adding handlers for automatically deleting files from perforce as you remove them from the project"); + Log.Info("Adding handlers to automatically delete files from perforce as you remove them from the project"); _itemRemovedEventHandler = new _dispProjectItemsEvents_ItemRemovedEventHandler(OnItemRemoved); m_projectEvents.ItemRemoved += _itemRemovedEventHandler; @@ -73,7 +73,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) } else if (RemoveFilesHandlersInstalled) { - Log.Info("Removing handlers for automatically deleting files from perforce as you remove them from the project"); + Log.Info("Removing handlers to automatically deleting files from perforce as you remove them from the project"); m_projectEvents.ItemRemoved -= _itemRemovedEventHandler; _itemRemovedEventHandler = null; From c2dc5f94f2aa5caa884961619cdc0736bf2917e3 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:37:33 +0200 Subject: [PATCH 32/69] Add some debugspam when adding/removing files --- NiftyPerforce/P4Operations.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index ffe03f6..528eb69 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -77,6 +77,9 @@ public static bool DeleteFile(OutputWindowPane output, string filename) { if (filename.Length == 0) return false; + + Log.Debug($"Delete '{filename}'"); + if (!g_p4installed) return NotifyUser("could not find p4 exe installed in perforce directory"); @@ -90,12 +93,16 @@ public static bool AddFile(OutputWindowPane output, string filename) { if (filename.Length == 0) return false; + + Log.Debug($"Add '{filename}'"); + if (!g_p4installed) return NotifyUser("could not find p4 exe installed in perforce directory"); string token = FormatToken("add", filename); if (!LockOp(token)) return false; + return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } From 4188ea0c0f5ec7155a0168ec538d697a71efbcdd Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:39:04 +0200 Subject: [PATCH 33/69] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index cfb6d50..ba8aaf2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,3 +6,4 @@ NiftyPerforce: Tim Burris Charles Bloom Hakon Steino + Lambert Clara From f2891c66bf2604357b50dddfc2889696e6a056d5 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sat, 26 Jun 2021 18:46:36 +0200 Subject: [PATCH 34/69] Update README.md --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4311e64..a5af277 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ Introduction ============ -This is a small collection of C# plugins for Visual Studio. +This repository holds a forked version of Nifty Plugins, orginally written by Jim Tilander (and found at https://github.com/jtilander/niftyplugins) -NiftyPerforce -============= +Note that NiftySolution was removed from this repository. -Adds some automation and IDE support for common perforce operations. -The goal of the plugin is to be very lightweight and unobtrusive. +NiftyPerforce, belkiss' fork +============================ +[![Build](https://github.com/belkiss/niftyplugins/actions/workflows/build-and-publish.yml/badge.svg)](https://github.com/belkiss/niftyplugins/actions/workflows/build-and-publish.yml) -Jim Tilander -San Francisco, 2006 +The biggest changes from the original NiftyPerforce are: + * support for recent Visual Studio versions, from vs2017 to vs2022 + * proper registration of NiftyPerforce in the UI, so it can be removed without leaving anything behind + * CI using GithubActions From 2aa413f17c05fbece0d1b3679f9ebf7b62d259c3 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 12:57:59 +0200 Subject: [PATCH 35/69] - Revert the Microsoft.VisualStudio.SDK nuget package to 15.0.1 to bring back vs2017 and vs2019 support - Bring back the System.Design dependency in AuroraCore.csproj since it became necessary again --- NiftyPerforce/NiftyPerforce.csproj | 2 +- Shared/AuroraCore.csproj | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 7756c10..416ef28 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -60,7 +60,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj index 6736ceb..d3d39d9 100644 --- a/Shared/AuroraCore.csproj +++ b/Shared/AuroraCore.csproj @@ -6,6 +6,9 @@ true - + + + + \ No newline at end of file From e3b7ac8545f7b7dae75f10666bccc4279ce72a9f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 13:18:44 +0200 Subject: [PATCH 36/69] Use VSConstants entry instead of hardcoded Guid to check that we got a file deletion message --- NiftyPerforce/EventHandlers/AutoCheckoutProject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index 238671a..a71c7a6 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -61,7 +61,7 @@ private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, ob Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); // when I get Edit.Delete : - if (Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}" && ID == 17) + if (Guid == Microsoft.VisualStudio.VSConstants.CMDSETID.StandardCommandSet97_string && ID == 17) { // see if the active window is SolutionExplorer : Window w = mPlugin.App.ActiveWindow; From cbb7a7ce87cab12a4e216ccb880b4c4aa4ebb0a2 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 13:27:27 +0200 Subject: [PATCH 37/69] Remove unused method EditProjectRecursive in AutoCheckoutOnSave --- .../EventHandlers/AutoCheckoutOnSave.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index f9ba4eb..9d0db9e 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -86,30 +86,6 @@ internal void OnBeforeSave(string filename) { P4Operations.EditFileImmediate(mPlugin.OutputPane, filename); } - - private void EditProjectRecursive(Project p) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (!p.Saved) - P4Operations.EditFileImmediate(mPlugin.OutputPane, p.FullName); - - if (p.ProjectItems == null) - return; - - foreach (ProjectItem pi in p.ProjectItems) - { - if (pi.SubProject != null) - { - EditProjectRecursive(pi.SubProject); - } - else if (!pi.Saved) - { - for (short i = 0; i <= pi.FileCount; i++) - P4Operations.EditFileImmediate(mPlugin.OutputPane, pi.get_FileNames(i)); - } - } - } } } } From 7d264b4de39f6ddeff68add3275aa2c4db4e685f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 13:53:11 +0200 Subject: [PATCH 38/69] Change the implementation of the output pane logger, we now don't need to pass it around, and this removes enforcing the main thread in a bunch of methods --- NiftyPerforce/Commands/ItemCommandBase.cs | 8 +- NiftyPerforce/Commands/P4DiffItem.cs | 4 +- NiftyPerforce/Commands/P4EditItem.cs | 4 +- NiftyPerforce/Commands/P4EditModified.cs | 6 +- NiftyPerforce/Commands/P4RevertItem.cs | 4 +- NiftyPerforce/Commands/P4RevisionGraphItem.cs | 4 +- .../Commands/P4RevisionHistoryItem.cs | 4 +- NiftyPerforce/Commands/P4ShowItem.cs | 4 +- NiftyPerforce/Commands/P4TimeLapseItem.cs | 4 +- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 18 ++--- .../EventHandlers/AutoCheckoutOnSave.cs | 4 +- .../EventHandlers/AutoCheckoutProject.cs | 7 +- .../EventHandlers/AutoCheckoutTextEdit.cs | 6 +- NiftyPerforce/NiftyPerforcePackage.cs | 49 ++++++------ NiftyPerforce/P4Operations.cs | 74 +++++++++---------- Shared/AsyncProcess.cs | 46 ++++-------- Shared/CommandRegistry.cs | 1 + Shared/Plugin.cs | 68 +---------------- Shared/VisualStudioLogHandler.cs | 62 +++++++++++++--- 19 files changed, 172 insertions(+), 205 deletions(-) diff --git a/NiftyPerforce/Commands/ItemCommandBase.cs b/NiftyPerforce/Commands/ItemCommandBase.cs index 1d297a7..f930a0e 100644 --- a/NiftyPerforce/Commands/ItemCommandBase.cs +++ b/NiftyPerforce/Commands/ItemCommandBase.cs @@ -26,15 +26,15 @@ public override bool OnCommand() { if (m_executeForFileItems && Plugin.App.ActiveWindow.Type == vsWindowType.vsWindowTypeDocument) { - OnExecute(sel, Plugin.App.ActiveDocument.FullName, Plugin.OutputPane); + OnExecute(sel, Plugin.App.ActiveDocument.FullName); } else if (m_executeForFileItems && sel.ProjectItem != null && m_fileItemGUID == sel.ProjectItem.Kind.ToUpperInvariant()) { - OnExecute(sel, sel.ProjectItem.get_FileNames(0), Plugin.OutputPane); + OnExecute(sel, sel.ProjectItem.get_FileNames(0)); } else if (m_executeForProjectItems && sel.Project != null) { - OnExecute(sel, sel.Project.FullName, Plugin.OutputPane); + OnExecute(sel, sel.Project.FullName); } } @@ -47,6 +47,6 @@ public override bool IsEnabled() return Plugin.App.SelectedItems.Count > 0; } - public abstract void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane); + public abstract void OnExecute(SelectedItem item, string fileName); } } diff --git a/NiftyPerforce/Commands/P4DiffItem.cs b/NiftyPerforce/Commands/P4DiffItem.cs index c92f1e0..5e37a3a 100644 --- a/NiftyPerforce/Commands/P4DiffItem.cs +++ b/NiftyPerforce/Commands/P4DiffItem.cs @@ -11,9 +11,9 @@ public P4DiffItem(Plugin plugin, string canonicalName) { } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { - P4Operations.DiffFile(pane, fileName); + P4Operations.DiffFile(fileName); } } } diff --git a/NiftyPerforce/Commands/P4EditItem.cs b/NiftyPerforce/Commands/P4EditItem.cs index 31064f4..1833a0f 100644 --- a/NiftyPerforce/Commands/P4EditItem.cs +++ b/NiftyPerforce/Commands/P4EditItem.cs @@ -11,9 +11,9 @@ public P4EditItem(Plugin plugin, string canonicalName) { } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { - P4Operations.EditFile(pane, fileName); + P4Operations.EditFile(fileName); } } } diff --git a/NiftyPerforce/Commands/P4EditModified.cs b/NiftyPerforce/Commands/P4EditModified.cs index 0cee5dd..5041b79 100644 --- a/NiftyPerforce/Commands/P4EditModified.cs +++ b/NiftyPerforce/Commands/P4EditModified.cs @@ -19,7 +19,7 @@ public override bool OnCommand() if (!Plugin.App.Solution.Saved) { Log.Info($"P4EditModified : solution {Plugin.App.Solution.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, Plugin.App.Solution.FullName); + P4Operations.EditFile(Plugin.App.Solution.FullName); } foreach (Project p in Plugin.App.Solution.Projects) @@ -27,7 +27,7 @@ public override bool OnCommand() if (!p.Saved) { Log.Info($"P4EditModified : project {p.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, p.FullName); + P4Operations.EditFile(p.FullName); } } @@ -36,7 +36,7 @@ public override bool OnCommand() if (!doc.Saved) { Log.Info($"P4EditModified : document {doc.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.OutputPane, doc.FullName); + P4Operations.EditFile(doc.FullName); } } diff --git a/NiftyPerforce/Commands/P4RevertItem.cs b/NiftyPerforce/Commands/P4RevertItem.cs index 885fc69..5794b1f 100644 --- a/NiftyPerforce/Commands/P4RevertItem.cs +++ b/NiftyPerforce/Commands/P4RevertItem.cs @@ -15,7 +15,7 @@ public P4RevertItem(Plugin plugin, string canonicalName, bool onlyUnchanged) mOnlyUnchanged = onlyUnchanged; } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { if (!mOnlyUnchanged) { @@ -26,7 +26,7 @@ public override void OnExecute(SelectedItem item, string fileName, OutputWindowP } } - P4Operations.RevertFile(pane, fileName, mOnlyUnchanged); + P4Operations.RevertFile(fileName, mOnlyUnchanged); } } } diff --git a/NiftyPerforce/Commands/P4RevisionGraphItem.cs b/NiftyPerforce/Commands/P4RevisionGraphItem.cs index cd17868..39fbf4f 100644 --- a/NiftyPerforce/Commands/P4RevisionGraphItem.cs +++ b/NiftyPerforce/Commands/P4RevisionGraphItem.cs @@ -15,7 +15,7 @@ public P4RevisionGraphItem(Plugin plugin, string canonicalName, bool mainLine) mMainLine = mainLine; } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { string dirname = Path.GetDirectoryName(fileName); @@ -25,7 +25,7 @@ public override void OnExecute(SelectedItem item, string fileName, OutputWindowP fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); } - P4Operations.RevisionGraph(pane, dirname, fileName); + P4Operations.RevisionGraph(dirname, fileName); } } } diff --git a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs index 1b0e326..466bfff 100644 --- a/NiftyPerforce/Commands/P4RevisionHistoryItem.cs +++ b/NiftyPerforce/Commands/P4RevisionHistoryItem.cs @@ -15,7 +15,7 @@ public P4RevisionHistoryItem(Plugin plugin, string canonicalName, bool mainLine) mMainLine = mainLine; } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { string dirname = Path.GetDirectoryName(fileName); @@ -25,7 +25,7 @@ public override void OnExecute(SelectedItem item, string fileName, OutputWindowP fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); } - P4Operations.RevisionHistoryFile(pane, dirname, fileName); + P4Operations.RevisionHistoryFile(dirname, fileName); } } } diff --git a/NiftyPerforce/Commands/P4ShowItem.cs b/NiftyPerforce/Commands/P4ShowItem.cs index 0275edd..cd7cb61 100644 --- a/NiftyPerforce/Commands/P4ShowItem.cs +++ b/NiftyPerforce/Commands/P4ShowItem.cs @@ -11,9 +11,9 @@ public P4ShowItem(Plugin plugin, string canonicalName) { } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { - P4Operations.P4VShowFile(pane, fileName); + P4Operations.P4VShowFile(fileName); } } } diff --git a/NiftyPerforce/Commands/P4TimeLapseItem.cs b/NiftyPerforce/Commands/P4TimeLapseItem.cs index 02af51e..bf6b875 100644 --- a/NiftyPerforce/Commands/P4TimeLapseItem.cs +++ b/NiftyPerforce/Commands/P4TimeLapseItem.cs @@ -15,7 +15,7 @@ public P4TimeLapseItem(Plugin plugin, string canonicalName, bool inMainLine) mMainLine = inMainLine; } - public override void OnExecute(SelectedItem item, string fileName, OutputWindowPane pane) + public override void OnExecute(SelectedItem item, string fileName) { string dirname = Path.GetDirectoryName(fileName); @@ -25,7 +25,7 @@ public override void OnExecute(SelectedItem item, string fileName, OutputWindowP fileName = P4Operations.RemapToMain(fileName, options.MainLinePath); } - P4Operations.TimeLapseView(pane, dirname, fileName); + P4Operations.TimeLapseView(dirname, fileName); } } } diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 14ac861..8204f58 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -86,20 +86,20 @@ public void OnItemAdded(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); + P4Operations.EditFile(item.ContainingProject.FullName); if (item.ProjectItems != null) { for (int i = 0; i < item.FileCount; i++) { string name = item.get_FileNames((short)i); - P4Operations.AddFile(m_plugin.OutputPane, name); + P4Operations.AddFile(name); } } else { if (System.IO.File.Exists(item.Name)) - P4Operations.AddFile(m_plugin.OutputPane, item.Name); + P4Operations.AddFile(item.Name); } } @@ -107,12 +107,12 @@ public void OnItemRemoved(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.OutputPane, item.ContainingProject.FullName); + P4Operations.EditFile(item.ContainingProject.FullName); for (int i = 0; i < item.FileCount; i++) { string name = item.get_FileNames((short)i); - P4Operations.DeleteFile(m_plugin.OutputPane, name); + P4Operations.DeleteFile(name); } } @@ -120,8 +120,8 @@ private void OnProjectAdded(Project project) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); - P4Operations.AddFile(m_plugin.OutputPane, project.FullName); + P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.AddFile(project.FullName); // TODO: [jt] We should if the operation is not a add new project but rather a add existing project // step through all the project items and add them to perforce. Or maybe we want the user // to do this herself? @@ -131,8 +131,8 @@ private void OnProjectRemoved(Project project) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.OutputPane, m_plugin.App.Solution.FullName); - P4Operations.DeleteFile(m_plugin.OutputPane, project.FullName); + P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.DeleteFile(project.FullName); // TODO: [jt] Do we want to automatically delete the items from perforce here? } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 9d0db9e..17a2215 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -57,8 +57,6 @@ public AutoCheckoutOnSave(Plugin plugin) private void RegisterEvents(object sender = null, EventArgs e = null) { - ThreadHelper.ThrowIfNotOnUIThread(); - if (((Config)mPlugin.Options).AutoCheckoutOnSave) { if (!RDTAdvised) @@ -84,7 +82,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) internal void OnBeforeSave(string filename) { - P4Operations.EditFileImmediate(mPlugin.OutputPane, filename); + P4Operations.EditFileImmediate(filename); } } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index a71c7a6..a411f45 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -27,8 +27,9 @@ public AutoCheckoutProject(Plugin plugin) "Project.AddNewItem", "Project.AddExistingItem", "Edit.Delete", // hmm : removing a file from Solution Explorer is just Edit.Delete !? - "File.Remove" // I don't think this actually does anything - }; + "File.Remove" // I don't think this actually does anything + }; + private List _registeredCommands; private void RegisterEvents(object sender = null, EventArgs e = null) @@ -74,7 +75,7 @@ private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, ob foreach (Project project in (Array)mPlugin.App.ActiveSolutionProjects) { - P4Operations.EditFileImmediate(mPlugin.OutputPane, project.FullName); + P4Operations.EditFileImmediate(project.FullName); } } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index b08bb2f..77be374 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -78,7 +78,7 @@ private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, b Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); } // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but @@ -92,7 +92,7 @@ private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) (Hint != 0)) return; if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); } private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) @@ -100,7 +100,7 @@ private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, obj Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.OutputPane, mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); } } } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index a349b94..f6945be 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -76,6 +76,31 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke // Switches to the UI thread in order to consume some services used in command initialization await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + // Initialize the logging system. + if (Log.HandlerCount == 0) + { +#if DEBUG + Log.AddHandler(new DebugLogHandler()); +#endif + Log.AddHandler(new VisualStudioLogHandler("NiftyPerforce", this)); + Log.Prefix = "NiftyPerforce"; + } + +#if DEBUG + Log.Info("NiftyPerforce (Debug)"); +#else + //Log.Info("NiftyPerforce (Release)"); +#endif + + // Show where we are and when we were compiled... + var niftyAssembly = Assembly.GetExecutingAssembly(); + + Log.Info("I'm running {0} compiled on {1}", niftyAssembly.Location, System.IO.File.GetLastWriteTime(niftyAssembly.Location)); + + // Now we can take care of registering ourselves and all our commands and hooks. + Log.Debug("Booting up..."); + Log.IncIndent(); + var config = (Config)GetDialogPage(typeof(Config)); Singleton.Instance = config; @@ -88,24 +113,10 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke } }; - m_plugin = new Plugin(application, oleMenuCommandService, "NiftyPerforce", "Aurora.NiftyPerforce.Connect", config); + m_plugin = new Plugin(application, oleMenuCommandService, "Aurora.NiftyPerforce.Connect", config); m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); - // Initialize the logging system. - if (Log.HandlerCount == 0) - { -#if DEBUG - Log.AddHandler(new DebugLogHandler()); -#endif - Log.AddHandler(new VisualStudioLogHandler(m_plugin)); - Log.Prefix = "NiftyPerforce"; - } - - // Now we can take care of registering ourselves and all our commands and hooks. - Log.Debug("Booting up..."); - Log.IncIndent(); - // Add our command handlers for menu (commands must exist in the .vsct file) m_commandRegistry.RegisterCommand(new P4EditModified(m_plugin, "NiftyEditModified")); m_commandRegistry.RegisterCommand(new P4EditItem(m_plugin, "NiftyEdit")); @@ -131,14 +142,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke Log.DecIndent(); Log.Debug("Initialized..."); - -#if DEBUG - Log.Info("NiftyPerforce (Debug)"); -#else - //Log.Info("NiftyPerforce (Release)"); -#endif - // Show where we are and when we were compiled... - Log.Info("I'm running {0} compiled on {1}", Assembly.GetExecutingAssembly().Location, System.IO.File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location)); } /// diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 528eb69..69a5ebe 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -71,9 +71,9 @@ private static string FormatToken(string operation, string filename) return token; } - public delegate bool CheckoutCallback(OutputWindowPane output, string filename); + public delegate bool CheckoutCallback(string filename); - public static bool DeleteFile(OutputWindowPane output, string filename) + public static bool DeleteFile(string filename) { if (filename.Length == 0) return false; @@ -86,10 +86,10 @@ public static bool DeleteFile(OutputWindowPane output, string filename) string token = FormatToken("delete", filename); if (!LockOp(token)) return false; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "delete \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + "delete \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } - public static bool AddFile(OutputWindowPane output, string filename) + public static bool AddFile(string filename) { if (filename.Length == 0) return false; @@ -103,55 +103,55 @@ public static bool AddFile(OutputWindowPane output, string filename) if (!LockOp(token)) return false; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } - public static bool EditFile(OutputWindowPane output, string filename) + public static bool EditFile(string filename) { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFile), output, filename); + return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFile), filename); } - public static bool EditFileImmediate(OutputWindowPane output, string filename) + public static bool EditFileImmediate(string filename) { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFileImmediate), output, filename); + return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFileImmediate), filename); } - private static bool Internal_CheckEditFile(CheckoutCallback callback, OutputWindowPane output, string filename) + private static bool Internal_CheckEditFile(CheckoutCallback callback, string filename) { Log.Debug($"Edit '{filename}'"); - bool result = callback(output, filename); + bool result = callback(filename); string ext = Path.GetExtension(filename).ToLowerInvariant(); if (ext == ".vcxproj") { - callback(output, filename + ".filters"); + callback(filename + ".filters"); } if (ext == ".settings" || ext == ".resx") { - callback(output, Path.ChangeExtension(filename, ".Designer.cs")); + callback(Path.ChangeExtension(filename, ".Designer.cs")); } if (ext == ".cs") { - callback(output, Path.ChangeExtension(filename, ".Designer.cs")); - callback(output, Path.ChangeExtension(filename, ".resx")); + callback(Path.ChangeExtension(filename, ".Designer.cs")); + callback(Path.ChangeExtension(filename, ".resx")); } return result; } - private static bool Internal_EditFile(OutputWindowPane output, string filename) + private static bool Internal_EditFile(string filename) { - return Internal_EditFile(output, filename, false); + return Internal_EditFile(filename, false); } - private static bool Internal_EditFileImmediate(OutputWindowPane output, string filename) + private static bool Internal_EditFileImmediate(string filename) { - return Internal_EditFile(output, filename, true); + return Internal_EditFile(filename, true); } - private static bool Internal_EditFile(OutputWindowPane output, string filename, bool immediate) + private static bool Internal_EditFile(string filename, bool immediate) { if (filename.Length == 0) { @@ -183,12 +183,12 @@ private static bool Internal_EditFile(OutputWindowPane output, string filename, return false; if (immediate) - return AsyncProcess.Run(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Run("p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + "edit \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } - public static bool RevertFile(OutputWindowPane output, string filename, bool onlyUnchanged) + public static bool RevertFile(string filename, bool onlyUnchanged) { if (filename.Length == 0) return false; @@ -200,10 +200,10 @@ public static bool RevertFile(OutputWindowPane output, string filename, bool onl return false; string revertArguments = onlyUnchanged ? "-a " : string.Empty; - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + "revert " + revertArguments + "\"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + "revert " + revertArguments + "\"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } - public static bool DiffFile(OutputWindowPane output, string filename) + public static bool DiffFile(string filename) { if (filename.Length == 0) return false; @@ -219,16 +219,16 @@ public static bool DiffFile(OutputWindowPane output, string filename) // Let's figure out if the user has some custom diff tool installed. Then we just send whatever we have without any fancy options. if (g_p4customdiff) - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + " diff \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); if (g_p4vc_diffhave_supported) - return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " diffhave \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return AsyncProcess.Schedule(g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " diffhave \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); // Otherwise let's show a unified diff in the outputpane. - return AsyncProcess.Schedule(output, "p4.exe", GetUserInfoString() + " diff -du \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); + return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + " diff -du \"" + filename + "#have\"", dirname, new AsyncProcess.OnDone(UnlockOp), token); } - public static bool RevisionHistoryFile(OutputWindowPane output, string dirname, string filename) + public static bool RevisionHistoryFile(string dirname, string filename) { if (filename.Length == 0) return false; @@ -240,21 +240,21 @@ public static bool RevisionHistoryFile(OutputWindowPane output, string dirname, return false; if (g_p4vc_history_supported) - return AsyncProcess.Schedule(output, g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return AsyncProcess.Schedule(g_p4vc_exename, GetUserInfoStringFull(true, dirname) + " history \"" + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); if (g_p4vinstalled) - return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return AsyncProcess.Schedule("p4v.exe", " -win 0 " + GetUserInfoStringFull(true, dirname) + " -cmd \"history " + filename + "\"", dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); } return NotifyUser("could not find a supported p4vc.exe or p4v.exe installed in perforce directory"); } - public static bool P4VShowFile(OutputWindowPane output, string filename) + public static bool P4VShowFile(string filename) { if (filename.Length == 0) return false; if (g_p4vinstalled) // note that the cmd line also accepts -t to open P4V with a specific tab shown - return AsyncProcess.Schedule(output, "p4v.exe", " -win 0 " + GetUserInfoStringFull(true, Path.GetDirectoryName(filename)) + " -s \"" + filename + "\"", Path.GetDirectoryName(filename), null, null, 0); + return AsyncProcess.Schedule("p4v.exe", " -win 0 " + GetUserInfoStringFull(true, Path.GetDirectoryName(filename)) + " -s \"" + filename + "\"", Path.GetDirectoryName(filename), null, null, 0); return NotifyUser("could not find p4v.exe installed in perforce directory"); } @@ -319,7 +319,7 @@ private static string GetUserInfoStringFull(bool lookup, string dir) return arguments; } - public static bool TimeLapseView(OutputWindowPane output, string dirname, string filename) + public static bool TimeLapseView(string dirname, string filename) { if (string.IsNullOrEmpty(g_p4vc_exename)) return NotifyUser("could not find p4vc in perforce directory"); @@ -330,10 +330,10 @@ public static bool TimeLapseView(OutputWindowPane output, string dirname, string string token = FormatToken("timelapse", filename); if (!LockOp(token)) return false; - return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return AsyncProcess.Schedule(g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); } - public static bool RevisionGraph(OutputWindowPane output, string dirname, string filename) + public static bool RevisionGraph(string dirname, string filename) { if (string.IsNullOrEmpty(g_p4vc_exename)) return NotifyUser("could not find p4vc in perforce directory"); @@ -344,7 +344,7 @@ public static bool RevisionGraph(OutputWindowPane output, string dirname, string string token = FormatToken("revisiongraph", filename); if (!LockOp(token)) return false; - return AsyncProcess.Schedule(output, g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); + return AsyncProcess.Schedule(g_p4vc_exename, arguments, dirname, new AsyncProcess.OnDone(UnlockOp), token, 0); } public static string GetRegistryValue(string key, string value, bool global) diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index 443d87b..d6d34fd 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -22,16 +22,14 @@ public static void Term() m_helperThread.Abort(); } - public static bool Run(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) + public static bool Run(string executable, string commandline, string workingdir, OnDone callback, object callbackArg) { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - int timeout = 1000; - if (!RunCommand(output, executable, commandline, workingdir, timeout)) + if (!RunCommand(executable, commandline, workingdir, timeout)) { Log.Debug("Failed to run immediate (process hung?), trying again on a remote thread: " + commandline); - return Schedule(output, executable, commandline, workingdir, callback, callbackArg); + return Schedule(executable, commandline, workingdir, callback, callbackArg); } else { @@ -41,16 +39,15 @@ public static bool Run(OutputWindowPane output, string executable, string comman return true; } - public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg) + public static bool Schedule(string executable, string commandline, string workingdir, OnDone callback, object callbackArg) { - return Schedule(output, executable, commandline, workingdir, callback, callbackArg, s_defaultTimeout); + return Schedule(executable, commandline, workingdir, callback, callbackArg, s_defaultTimeout); } - public static bool Schedule(OutputWindowPane output, string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) + public static bool Schedule(string executable, string commandline, string workingdir, OnDone callback, object callbackArg, int timeout) { var cmd = new CommandThread { - output = output, executable = executable, commandline = commandline, workingdir = workingdir, @@ -116,18 +113,15 @@ private class CommandThread public string executable = ""; public string commandline = ""; public string workingdir = ""; - public OutputWindowPane output = null; public OnDone callback = null; public object callbackArg = null; public int timeout = 10000; public void Run() { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - bool ok; try { - ok = RunCommand(output, executable, commandline, workingdir, timeout); + ok = RunCommand(executable, commandline, workingdir, timeout); } catch { @@ -139,10 +133,8 @@ public void Run() } } - private static bool RunCommand(OutputWindowPane output, string executable, string commandline, string workingdir, int timeout) + private static bool RunCommand(string executable, string commandline, string workingdir, int timeout) { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - try { var process = new System.Diagnostics.Process(); @@ -194,13 +186,13 @@ private static bool RunCommand(OutputWindowPane output, string executable, strin exited = process.WaitForExit(timeout); /* - * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? - * + * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? + * - if (0 != process.ExitCode) - { - throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); - }*/ + if (0 != process.ExitCode) + { + throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); + }*/ stderr.sentinel.WaitOne(); stdout.sentinel.WaitOne(); @@ -215,14 +207,8 @@ private static bool RunCommand(OutputWindowPane output, string executable, strin } else { - if (null != output) - { - output.OutputString(executable + ": " + commandline + "\n"); - output.OutputString(alloutput); - } - - System.Diagnostics.Debug.WriteLine(commandline + "\n"); - System.Diagnostics.Debug.WriteLine(alloutput); + Log.Info(executable + ": " + commandline); + Log.Info(alloutput); if (0 != process.ExitCode) { diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index d4c93e0..b8ef01c 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -50,6 +50,7 @@ private OleMenuCommand RegisterCommandPrivate(CommandBase commandHandler) return vscommand; } + private void OleMenuCommandBeforeQueryStatus(object sender, EventArgs e) { diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index a7e6765..70c05e3 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -11,32 +11,20 @@ namespace Aurora // Interfaces with visual studio and handles the dispatch. public class Plugin { - private OutputWindowPane m_outputPane; - private readonly string m_panelName; - private readonly Dictionary m_features = new Dictionary(); - public OutputWindowPane OutputPane - { - get - { - ThreadHelper.ThrowIfNotOnUIThread(); - return GetOutputPane(); - } - } public string Prefix { get; } public DTE2 App { get; } public Commands Commands => App.Commands; public OleMenuCommandService MenuCommandService { get; } public object Options { get; } - public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, string panelName, string connectPath, object options) + public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, string connectPath, object options) { // TODO: This can be figured out from traversing the assembly and locating the Connect class... Prefix = connectPath; App = application; - m_panelName = panelName; MenuCommandService = oleMenuCommandService; Options = options; } @@ -63,59 +51,5 @@ public CommandEvents FindCommandEvents(string commandName) return events; } - - private OutputWindowPane GetOutputPane() - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (m_outputPane != null) - return m_outputPane; - try - { - m_outputPane = AquireOutputPane(App, m_panelName); - } - catch (Exception) - { - } - - return m_outputPane; - } - - private static OutputWindowPane AquireOutputPane(DTE2 app, string name) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (string.IsNullOrEmpty(name) || app.Windows.Count == 0) - return null; - - OutputWindowPane result = FindOutputPane(app, name); - if (null != result) - return result; - - var outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; - OutputWindowPanes panes = outputWindow.OutputWindowPanes; - return panes.Add(name); - } - - public static OutputWindowPane FindOutputPane(DTE2 app, string name) - { - ThreadHelper.ThrowIfNotOnUIThread(); - - if (string.IsNullOrEmpty(name) || app?.Windows?.Count == null || app.Windows.Count == 0) - return null; - - var outputWindow = (OutputWindow)app.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object; - OutputWindowPanes panes = outputWindow.OutputWindowPanes; - - foreach (OutputWindowPane pane in panes) - { - if (name != pane.Name) - continue; - - return pane; - } - - return null; - } } } diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index 6430925..df948a7 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -1,26 +1,70 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using EnvDTE; +using System; +using Microsoft; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; namespace Aurora { public class VisualStudioLogHandler : Log.IHandler { - private readonly Plugin mPlugin; + private readonly IServiceProvider _serviceProvider; + private IVsOutputWindowPane _pane; + private readonly object _lock = new object(); + private readonly string _name; - public VisualStudioLogHandler(Plugin plugin) + public VisualStudioLogHandler(string name, IServiceProvider serviceProvider) { - mPlugin = plugin; + _name = name; + _serviceProvider = serviceProvider; } public void OnMessage(Log.Level level, string message, string formattedLine) { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - OutputWindowPane pane = mPlugin.OutputPane; - if (null == pane) + if (string.IsNullOrEmpty(message)) return; - pane.OutputString(formattedLine); + ThreadHelper.JoinableTaskFactory.Run(() => LogAsync(formattedLine)); + } + + private async Task LogAsync(string message) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + try + { + if (EnsurePane()) + { + _pane.OutputStringThreadSafe(message); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.Write(ex); + } + } + + private bool EnsurePane() + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_pane == null) + { + lock (_lock) + { + if (_pane == null) + { + var guid = Guid.NewGuid(); + IVsOutputWindow output = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow)); + Assumes.Present(output); + output.CreatePane(ref guid, _name, 1, 0); + output.GetPane(ref guid, out _pane); + } + } + } + + return _pane != null; } } } From 23f715c9d0d2e2af6da21021db580ac669ed7bf3 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 13:53:54 +0200 Subject: [PATCH 39/69] Make OnItemAdded and OnItemRemoved private in AutoAddDelete --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 8204f58..1fc40d2 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -82,7 +82,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) } } - public void OnItemAdded(ProjectItem item) + private void OnItemAdded(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); @@ -103,7 +103,7 @@ public void OnItemAdded(ProjectItem item) } } - public void OnItemRemoved(ProjectItem item) + private void OnItemRemoved(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); From 91726e7e7a4c90018cf00b76730a62fd30b0c8d6 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:02:23 +0200 Subject: [PATCH 40/69] Pass the main serviceprovider to the AutoCheckoutOnSave class instead of creating a new one --- NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs | 15 +++++---------- NiftyPerforce/NiftyPerforcePackage.cs | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 17a2215..283a534 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -43,17 +43,18 @@ internal class AutoCheckoutOnSave : PreCommandFeature { internal Lazy _rdt; internal uint _rdte; - internal Lazy _sp; + private IServiceProvider _serviceProvider; - public AutoCheckoutOnSave(Plugin plugin) + public AutoCheckoutOnSave(Plugin plugin, IServiceProvider serviceProvider) : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") { ThreadHelper.ThrowIfNotOnUIThread(); + _serviceProvider = serviceProvider; ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; RegisterEvents(); } - private bool RDTAdvised => _sp != null || _rdt != null; + private bool RDTAdvised => _rdt != null; private void RegisterEvents(object sender = null, EventArgs e = null) { @@ -62,12 +63,7 @@ private void RegisterEvents(object sender = null, EventArgs e = null) if (!RDTAdvised) { Log.Info("Adding handlers for automatically checking out dirty files when you save"); - _sp = new Lazy(() => - { - ThreadHelper.ThrowIfNotOnUIThread(); - return Package.GetGlobalService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider; - }); - _rdt = new Lazy(() => new RunningDocumentTable(new ServiceProvider(_sp.Value))); + _rdt = new Lazy(() => new RunningDocumentTable(_serviceProvider)); _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); } } @@ -76,7 +72,6 @@ private void RegisterEvents(object sender = null, EventArgs e = null) Log.Info("Removing handlers for automatically checking out dirty files when you save"); _rdt.Value.Unadvise(_rdte); _rdt = null; - _sp = null; } } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index f6945be..0ca22df 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -134,7 +134,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke m_plugin.AddFeature(new AutoAddDelete(m_plugin)); m_plugin.AddFeature(new AutoCheckoutProject(m_plugin)); m_plugin.AddFeature(new AutoCheckoutTextEdit(m_plugin)); - m_plugin.AddFeature(new AutoCheckoutOnSave(m_plugin)); + m_plugin.AddFeature(new AutoCheckoutOnSave(m_plugin, this)); P4Operations.CheckInstalledFiles(); From 9255a36d66a111a6b0ef49dbf735bfa63a67b706 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:03:17 +0200 Subject: [PATCH 41/69] Add an existence check before calling checkout on the optional associated files (like vcxproj.filters, .resx, etc) --- NiftyPerforce/P4Operations.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 69a5ebe..8a6ebec 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -119,23 +119,30 @@ public static bool EditFileImmediate(string filename) private static bool Internal_CheckEditFile(CheckoutCallback callback, string filename) { Log.Debug($"Edit '{filename}'"); + bool result = callback(filename); + void CheckoutAdditionalIfExists(string f) + { + if (File.Exists(f)) + callback(f); + } + string ext = Path.GetExtension(filename).ToLowerInvariant(); if (ext == ".vcxproj") { - callback(filename + ".filters"); + CheckoutAdditionalIfExists(filename + ".filters"); } if (ext == ".settings" || ext == ".resx") { - callback(Path.ChangeExtension(filename, ".Designer.cs")); + CheckoutAdditionalIfExists(Path.ChangeExtension(filename, ".Designer.cs")); } if (ext == ".cs") { - callback(Path.ChangeExtension(filename, ".Designer.cs")); - callback(Path.ChangeExtension(filename, ".resx")); + CheckoutAdditionalIfExists(Path.ChangeExtension(filename, ".Designer.cs")); + CheckoutAdditionalIfExists(Path.ChangeExtension(filename, ".resx")); } return result; From 2c15560c30f40805006a57bd17224e1f27954681 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:08:00 +0200 Subject: [PATCH 42/69] Add version to the log message when nifty initializes --- NiftyPerforce/NiftyPerforcePackage.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index 0ca22df..a6f7b33 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -94,8 +94,13 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke // Show where we are and when we were compiled... var niftyAssembly = Assembly.GetExecutingAssembly(); + Version version = niftyAssembly.GetName().Version; + string versionString = string.Join(".", version.Major, version.Minor, version.Build); + string informationalVersion = niftyAssembly.GetCustomAttribute()?.InformationalVersion; + if (informationalVersion != null) + versionString += " " + informationalVersion; - Log.Info("I'm running {0} compiled on {1}", niftyAssembly.Location, System.IO.File.GetLastWriteTime(niftyAssembly.Location)); + Log.Info("I'm running {0} v{1} compiled on {2}", niftyAssembly.Location, versionString, System.IO.File.GetLastWriteTime(niftyAssembly.Location)); // Now we can take care of registering ourselves and all our commands and hooks. Log.Debug("Booting up..."); From 9a8aa3a81733d1339f844a80188595f31e105fce Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:10:30 +0200 Subject: [PATCH 43/69] Remove the prefix from Plugin, it's only used by the legacy cleanup command --- NiftyPerforce/NiftyPerforcePackage.cs | 5 +++-- Shared/Plugin.cs | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index a6f7b33..f37894f 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -118,7 +118,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke } }; - m_plugin = new Plugin(application, oleMenuCommandService, "Aurora.NiftyPerforce.Connect", config); + m_plugin = new Plugin(application, oleMenuCommandService, config); m_commandRegistry = new CommandRegistry(m_plugin, new Guid(PackageGuids.guidNiftyPerforcePackageCmdSetString)); @@ -213,7 +213,8 @@ private void RemoveCommand(string name, IVsProfferCommands3 profferCommands3) "Cross Project Multi Project" }; - string Absname = m_plugin.Prefix + "." + name; + const string prefix = "Aurora.NiftyPerforce.Connect"; + string Absname = prefix + "." + name; foreach (string bar in bars) { diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index 70c05e3..c56daa7 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -13,17 +13,13 @@ public class Plugin { private readonly Dictionary m_features = new Dictionary(); - public string Prefix { get; } public DTE2 App { get; } public Commands Commands => App.Commands; public OleMenuCommandService MenuCommandService { get; } public object Options { get; } - public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, string connectPath, object options) + public Plugin(DTE2 application, OleMenuCommandService oleMenuCommandService, object options) { - // TODO: This can be figured out from traversing the assembly and locating the Connect class... - Prefix = connectPath; - App = application; MenuCommandService = oleMenuCommandService; Options = options; From 204987c371822ddcfd62c16c72fd20e55a15e703 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:15:04 +0200 Subject: [PATCH 44/69] Speed up initialization by skipping some p4 supported features checks when we know for sure they are available - If p4vc.bat is found, we know that diffhave and history are supported since it was added after those two commands - Same for diffhave, if we have it, we know we have history --- NiftyPerforce/P4Operations.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 8a6ebec..cc32f60 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -11,6 +11,7 @@ namespace NiftyPerforce // Simplification wrapper around running perforce commands. internal class P4Operations { + private const string _p4vcBatFileName = "p4vc.bat"; private static bool g_p4installed = false; private static bool g_p4vinstalled = false; private static bool g_p4customdiff = false; @@ -380,9 +381,9 @@ private static bool LookupP4VC(Func check) { // starting with 2021.1/2075061 // #105247 (Change #2069769) - // The p4vc.exe executable has been removed from the Windows installers. - // To start P4VC, use the p4vc.bat script. - foreach (var candidateName in new[] { "p4vc.bat", "p4vc.exe" }) + // The p4vc.exe executable has been removed from the Windows installers. + // To start P4VC, use the p4vc.bat script. + foreach (var candidateName in new[] { _p4vcBatFileName, "p4vc.exe" }) { if (check(candidateName)) { @@ -495,13 +496,17 @@ public static void CheckInstalledFiles() private static void DetermineSupportedFeatures() { - // history was added in p4v 2019.2 update1/1883366 - g_p4vc_history_supported = P4VCHasCommand("history"); - Log.Info("[{0}] p4vc history", g_p4vc_history_supported ? "X" : " "); + bool p4vcBat = g_p4vc_exename == _p4vcBatFileName; + // since p4vc.bat was introduced with 2021.1/2075061, if we have it we know we have diffhave, hence history // diffhave was added in p4v 2020.1/1946989 - g_p4vc_diffhave_supported = P4VCHasCommand("diffhave"); + g_p4vc_diffhave_supported = p4vcBat || P4VCHasCommand("diffhave"); Log.Info("[{0}] p4vc diffhave", g_p4vc_diffhave_supported ? "X" : " "); + + // history was added in p4v 2019.2 update1/1883366 + // so if we have diffhave we know we have history and can skip the test + g_p4vc_history_supported = g_p4vc_diffhave_supported || P4VCHasCommand("history"); + Log.Info("[{0}] p4vc history", g_p4vc_history_supported ? "X" : " "); } private static bool P4VCHasCommand(string command) From fc605ee7a2390e26930fd6e764bb77bb7bb38782 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:19:10 +0200 Subject: [PATCH 45/69] Wrap the process in using to make sure it's disposed properly --- Shared/AsyncProcess.cs | 128 +++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index d6d34fd..405b505 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -137,84 +137,86 @@ private static bool RunCommand(string executable, string commandline, string wor { try { - var process = new System.Diagnostics.Process(); - process.StartInfo.UseShellExecute = false; - process.StartInfo.FileName = executable; - if (0 == timeout) + using (var process = new System.Diagnostics.Process()) { - // We are not for these processes reading the stdout and thus they could if they wrote more - // data on the output line hang. - process.StartInfo.RedirectStandardOutput = false; - process.StartInfo.RedirectStandardError = false; - } - else - { - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - } - - process.StartInfo.CreateNoWindow = true; - process.StartInfo.WorkingDirectory = workingdir; - process.StartInfo.Arguments = commandline; + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = executable; + if (0 == timeout) + { + // We are not for these processes reading the stdout and thus they could if they wrote more + // data on the output line hang. + process.StartInfo.RedirectStandardOutput = false; + process.StartInfo.RedirectStandardError = false; + } + else + { + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + } - Log.Debug("executableName : " + executable); - Log.Debug("workingDirectory : " + workingdir); - Log.Debug("command : " + commandline); + process.StartInfo.CreateNoWindow = true; + process.StartInfo.WorkingDirectory = workingdir; + process.StartInfo.Arguments = commandline; - if (!process.Start()) - { - Log.Error("{0}: {1} Failed to start. Is Perforce installed and in the path?\n", executable, commandline); - return false; - } + Log.Debug("executableName : " + executable); + Log.Debug("workingDirectory : " + workingdir); + Log.Debug("command : " + commandline); - if (0 == timeout) - { - // Fire and forget task. - return true; - } + if (!process.Start()) + { + Log.Error("{0}: {1} Failed to start. Is Perforce installed and in the path?\n", executable, commandline); + return false; + } - bool exited = false; - string alloutput = ""; - using (Process.Handler stderr = new Process.Handler(), stdout = new Process.Handler()) - { - process.OutputDataReceived += stdout.OnOutput; - process.BeginOutputReadLine(); + if (0 == timeout) + { + // Fire and forget task. + return true; + } - process.ErrorDataReceived += stderr.OnOutput; - process.BeginErrorReadLine(); + bool exited = false; + string alloutput = ""; + using (Process.Handler stderr = new Process.Handler(), stdout = new Process.Handler()) + { + process.OutputDataReceived += stdout.OnOutput; + process.BeginOutputReadLine(); - exited = process.WaitForExit(timeout); + process.ErrorDataReceived += stderr.OnOutput; + process.BeginErrorReadLine(); - /* - * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? - * + exited = process.WaitForExit(timeout); - if (0 != process.ExitCode) - { - throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); - }*/ + /* + * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? + * - stderr.sentinel.WaitOne(); - stdout.sentinel.WaitOne(); - alloutput = stdout.buffer + "\n" + stderr.buffer; - } + if (0 != process.ExitCode) + { + throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); + }*/ - if (!exited) - { - Log.Info("{0}: {1} timed out ({2} ms)", executable, commandline, timeout); - process.Kill(); - return false; - } - else - { - Log.Info(executable + ": " + commandline); - Log.Info(alloutput); + stderr.sentinel.WaitOne(); + stdout.sentinel.WaitOne(); + alloutput = stdout.buffer + "\n" + stderr.buffer; + } - if (0 != process.ExitCode) + if (!exited) { - Log.Debug("{0}: {1} exit code {2}", executable, commandline, process.ExitCode); + Log.Info("{0}: {1} timed out ({2} ms)", executable, commandline, timeout); + process.Kill(); return false; } + else + { + Log.Info(executable + ": " + commandline); + Log.Info(alloutput); + + if (0 != process.ExitCode) + { + Log.Debug("{0}: {1} exit code {2}", executable, commandline, process.ExitCode); + return false; + } + } } return true; From ed2144c65b4fd185e8c78ed3ed7f2e6175ea190c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:27:34 +0200 Subject: [PATCH 46/69] Fix warning CA1802: Field 's_defaultTimeout' is declared as 'readonly' but is initialized with a constant value. Mark this field as 'const' instead. --- Shared/AsyncProcess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index 405b505..369d218 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -7,7 +7,7 @@ namespace Aurora { public static class AsyncProcess { - private static readonly int s_defaultTimeout = 30000; // in ms + private const int s_defaultTimeout = 30000; // in ms public delegate void OnDone(bool ok, object arg0); From 6694512da14319d7dbbf4be265188d7deba28e3d Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:37:50 +0200 Subject: [PATCH 47/69] Fix some analyser warnings related to custom process exception type - Renamed to ProcessException - Remove useless info field and use base class - Un-nest it - Create additional constructors --- NiftyPerforce/P4Operations.cs | 4 ++-- Shared/AsyncProcess.cs | 2 +- Shared/Process.cs | 38 +++++++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index cc32f60..091317f 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -306,9 +306,9 @@ private static string GetUserInfoStringFull(bool lookup, string dir) return ret; } - catch (Aurora.Process.Error e) + catch (Aurora.ProcessException e) { - Log.Error("Failed to execute info string discovery: {0}", e.info); + Log.Error("Failed to execute info string discovery: {0}", e.Message); } } diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index 369d218..ad0f6c2 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -192,7 +192,7 @@ private static bool RunCommand(string executable, string commandline, string wor if (0 != process.ExitCode) { - throw new Process.Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); + throw new Process.ProcessException("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); }*/ stderr.sentinel.WaitOne(); diff --git a/Shared/Process.cs b/Shared/Process.cs index 8d0d5a9..8e65eab 100644 --- a/Shared/Process.cs +++ b/Shared/Process.cs @@ -1,17 +1,12 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; +using System.Runtime.Serialization; using System.Threading; namespace Aurora { public static class Process { - public class Error : System.Exception - { - public string info; - public Error(string info_, params object[] vaargs_) { info = string.Format(info_, vaargs_); } - }; - // Helper class to capture output correctly and send an event once we've reached the end of the file. public class Handler : IDisposable { @@ -56,7 +51,7 @@ public static string Execute(string executable, string workingdir, string argume if (!process.Start()) { - throw new Error("{0}: Failed to start {1}.", executable, process.StartInfo.Arguments); + throw new ProcessException("{0}: Failed to start {1}.", executable, process.StartInfo.Arguments); } using (Handler stderr = new Handler(), stdout = new Handler()) @@ -71,7 +66,7 @@ public static string Execute(string executable, string workingdir, string argume if (throwIfNonZeroExitCode && 0 != process.ExitCode) { - throw new Error("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); + throw new ProcessException("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); } stderr.sentinel.WaitOne(); @@ -82,5 +77,32 @@ public static string Execute(string executable, string workingdir, string argume } } } + + public class ProcessException : Exception + { + public ProcessException() + { + } + + public ProcessException(string message) + : base(message) + { + } + + public ProcessException(string info, params object[] vaargs) + : this(string.Format(info, vaargs)) + { + } + + public ProcessException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected ProcessException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } } From 76de2a0edc9e63e593b4f45415e2e4eb8bfb6bb4 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:47:46 +0200 Subject: [PATCH 48/69] Small cleanup in AutoCheckoutOnSave - OnBeforeSave was made static - _serviceProvider is now readonly --- NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 283a534..7a7b8eb 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -14,17 +14,17 @@ namespace NiftyPerforce // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 internal class RunningDocTableEvents : IVsRunningDocTableEvents3 { - private readonly AutoCheckoutOnSave autoCheckoutOnSave; + private readonly AutoCheckoutOnSave _autoCheckoutOnSave; public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) { - this.autoCheckoutOnSave = autoCheckoutOnSave; + _autoCheckoutOnSave = autoCheckoutOnSave; } public int OnBeforeSave(uint docCookie) { - RunningDocumentInfo runningDocumentInfo = autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); - autoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); + RunningDocumentInfo runningDocumentInfo = _autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); + AutoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); return VSConstants.S_OK; } @@ -43,7 +43,7 @@ internal class AutoCheckoutOnSave : PreCommandFeature { internal Lazy _rdt; internal uint _rdte; - private IServiceProvider _serviceProvider; + private readonly IServiceProvider _serviceProvider; public AutoCheckoutOnSave(Plugin plugin, IServiceProvider serviceProvider) : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") @@ -75,9 +75,9 @@ private void RegisterEvents(object sender = null, EventArgs e = null) } } - internal void OnBeforeSave(string filename) + internal static bool OnBeforeSave(string filename) { - P4Operations.EditFileImmediate(filename); + return P4Operations.EditFileImmediate(filename); } } } From 6845e1db099e0b4fd2804dd5cbf5343063bcea2a Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:51:27 +0200 Subject: [PATCH 49/69] Fix SonarQube warning S2292 on Log.Prefix: Make this an auto-implemented property and remove its backing field --- Shared/Log.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Shared/Log.cs b/Shared/Log.cs index 409c584..cc26eb8 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -23,11 +23,7 @@ public interface IHandler // Helper class to keep the indent levels balanced (with the help of the using statement) // Log class implement below - public static string Prefix - { - get => mPrefix; - set => mPrefix = value; - } + public static string Prefix { get; set; } = ""; public static int HandlerCount => mHandlers.Count; @@ -102,9 +98,9 @@ private static void OnMessage(Level level, string format, object[] args) indent += " "; } - if (mPrefix.Length > 0) + if (Prefix.Length > 0) { - formattedLine = mPrefix + " (" + levelName + "): " + indent + message + "\n"; + formattedLine = Prefix + " (" + levelName + "): " + indent + message + "\n"; } else { @@ -121,7 +117,6 @@ private static void OnMessage(Level level, string format, object[] args) } private static readonly List mHandlers = new List(); - private static string mPrefix = ""; private static int mIndent = 0; } } From ff8524bc73193df58ac35e0fd23c1c7c4cd9a799 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:53:20 +0200 Subject: [PATCH 50/69] Fix SonarQube warning S4487, tooltip was never used so remove it --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 2 +- NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs | 2 +- NiftyPerforce/EventHandlers/AutoCheckoutProject.cs | 2 +- NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs | 2 +- Shared/Feature.cs | 8 +++----- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 1fc40d2..f5c0570 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -21,7 +21,7 @@ internal class AutoAddDelete : Feature private readonly Plugin m_plugin; public AutoAddDelete(Plugin plugin) - : base("AutoAddDelete", "Automatically adds and deletes files matching project add/delete") + : base("AutoAddDelete") { m_plugin = plugin; diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 7a7b8eb..923ee06 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -46,7 +46,7 @@ internal class AutoCheckoutOnSave : PreCommandFeature private readonly IServiceProvider _serviceProvider; public AutoCheckoutOnSave(Plugin plugin, IServiceProvider serviceProvider) - : base(plugin, "AutoCheckoutOnSave", "Automatically checks out files on save") + : base(plugin, "AutoCheckoutOnSave") { ThreadHelper.ThrowIfNotOnUIThread(); _serviceProvider = serviceProvider; diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index a411f45..c51fc8f 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -11,7 +11,7 @@ namespace NiftyPerforce internal class AutoCheckoutProject : PreCommandFeature { public AutoCheckoutProject(Plugin plugin) - : base(plugin, "AutoCheckoutProject", "Automatically checks out the project files") + : base(plugin, "AutoCheckoutProject") { ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; RegisterEvents(); diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 77be374..163c91d 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -15,7 +15,7 @@ internal class AutoCheckoutTextEdit : PreCommandFeature private EnvDTE.TextEditorEvents mTextEditorEvents; public AutoCheckoutTextEdit(Plugin plugin) - : base(plugin, "AutoCheckoutTextEdit", "Automatically checks out the text file on edits") + : base(plugin, "AutoCheckoutTextEdit") { //((Config)mPlugin.Options).RegisterOnApplyAction(RegisterEvents); ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; diff --git a/Shared/Feature.cs b/Shared/Feature.cs index c3f9998..ba8799d 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -5,14 +5,12 @@ namespace Aurora public abstract class Feature { private readonly string mName; - private readonly string mTooltip; public string Name => mName; - protected Feature(string name, string tooltip) + protected Feature(string name) { mName = name; - mTooltip = tooltip; } public virtual bool Execute() @@ -25,8 +23,8 @@ public abstract class PreCommandFeature : Feature { protected Plugin mPlugin; - protected PreCommandFeature(Plugin plugin, string name, string tooltip) - : base(name, tooltip) + protected PreCommandFeature(Plugin plugin, string name) + : base(name) { mPlugin = plugin; } From 176aca80388493a14c76750b74c2a690ecb59566 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 14:58:44 +0200 Subject: [PATCH 51/69] Fix SonarQube warning S125: remove commented out code --- .../EventHandlers/AutoCheckoutTextEdit.cs | 1 - NiftyPerforce/NiftyPerforcePackage.cs | 2 +- NiftyPerforce/P4Operations.cs | 1 - Shared/AsyncProcess.cs | 9 --------- Shared/CommandRegistry.cs | 20 ++++++------------- 5 files changed, 7 insertions(+), 26 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 163c91d..4bc0f4f 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -17,7 +17,6 @@ internal class AutoCheckoutTextEdit : PreCommandFeature public AutoCheckoutTextEdit(Plugin plugin) : base(plugin, "AutoCheckoutTextEdit") { - //((Config)mPlugin.Options).RegisterOnApplyAction(RegisterEvents); ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; RegisterEvents(); } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index f37894f..33da693 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -89,7 +89,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke #if DEBUG Log.Info("NiftyPerforce (Debug)"); #else - //Log.Info("NiftyPerforce (Release)"); + Log.Info("NiftyPerforce (Release)"); #endif // Show where we are and when we were compiled... diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 091317f..ba49b04 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -38,7 +38,6 @@ private static bool LockOp(string token) } catch (ArgumentException) { - //Log.Debug("!! Failed to lock \"" + token + "\""); Log.Error(token + " already in progress"); return false; } diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index ad0f6c2..f9d814a 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -186,15 +186,6 @@ private static bool RunCommand(string executable, string commandline, string wor exited = process.WaitForExit(timeout); - /* - * This causes the plugin to unexpectedly crash, since it brings the entire thread down, and thus the entire environment?!? - * - - if (0 != process.ExitCode) - { - throw new Process.ProcessException("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); - }*/ - stderr.sentinel.WaitOne(); stdout.sentinel.WaitOne(); alloutput = stdout.buffer + "\n" + stderr.buffer; diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index b8ef01c..e731455 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -32,21 +32,13 @@ public void RegisterCommand(CommandBase commandHandler) private OleMenuCommand RegisterCommandPrivate(CommandBase commandHandler) { - OleMenuCommand vscommand = null; - //if (cmdId == 0) - { - OleMenuCommandService menuCommandService = mPlugin.MenuCommandService; - var commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); + OleMenuCommandService menuCommandService = mPlugin.MenuCommandService; + var commandID = new CommandID(mCmdGroupGuid, commandHandler.CommandId); - vscommand = new OleMenuCommand(OleMenuCommandCallback, commandID); - vscommand.BeforeQueryStatus += OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong - menuCommandService.AddCommand(vscommand); - mCommandsById[commandID.ID] = commandHandler; - } - // Register the graphics controls for this command as well. - // First let the command itself have a stab at register whatever it needs. - // Then by default we always register ourselves in the main toolbar of the application. - //commandHandler.RegisterGUI(vscommand, mCommandBar, toolbarOnly); + var vscommand = new OleMenuCommand(OleMenuCommandCallback, commandID); + vscommand.BeforeQueryStatus += OleMenuCommandBeforeQueryStatus; // LCTODO: this spams too much, figure out what's wrong + menuCommandService.AddCommand(vscommand); + mCommandsById[commandID.ID] = commandHandler; return vscommand; } From 7cf0ee3d9bb58facb2c360de3d8caa543fe79c6f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 15:07:05 +0200 Subject: [PATCH 52/69] Fix SonarQube warnings in Process --- Shared/AsyncProcess.cs | 6 +++--- Shared/Process.cs | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index f9d814a..7995bbc 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -186,9 +186,9 @@ private static bool RunCommand(string executable, string commandline, string wor exited = process.WaitForExit(timeout); - stderr.sentinel.WaitOne(); - stdout.sentinel.WaitOne(); - alloutput = stdout.buffer + "\n" + stderr.buffer; + stderr.Sentinel.WaitOne(); + stdout.Sentinel.WaitOne(); + alloutput = stdout.Buffer + "\n" + stderr.Buffer; } if (!exited) diff --git a/Shared/Process.cs b/Shared/Process.cs index 8e65eab..33f24e5 100644 --- a/Shared/Process.cs +++ b/Shared/Process.cs @@ -10,29 +10,29 @@ public static class Process // Helper class to capture output correctly and send an event once we've reached the end of the file. public class Handler : IDisposable { - public string buffer; - public ManualResetEvent sentinel; + public string Buffer { get; private set; } + public ManualResetEvent Sentinel { get; set; } public Handler() { - buffer = ""; - sentinel = new ManualResetEvent(false); + Buffer = ""; + Sentinel = new ManualResetEvent(false); } public void Dispose() { - sentinel.Close(); + Sentinel.Close(); } public void OnOutput(object sender, System.Diagnostics.DataReceivedEventArgs e) { if (e?.Data == null) { - sentinel.Set(); + Sentinel.Set(); } else { - buffer = buffer + e.Data + "\n"; + Buffer = Buffer + e.Data + "\n"; } } }; @@ -69,10 +69,10 @@ public static string Execute(string executable, string workingdir, string argume throw new ProcessException("Failed to execute {0} {1}, exit code was {2}", executable, process.StartInfo.Arguments, process.ExitCode); } - stderr.sentinel.WaitOne(); - stdout.sentinel.WaitOne(); + stderr.Sentinel.WaitOne(); + stdout.Sentinel.WaitOne(); - return stdout.buffer + "\n" + stderr.buffer; + return stdout.Buffer + "\n" + stderr.Buffer; } } } From 4bbc1dbd877511e1cb0fe498277f0d611dc606a5 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 15:09:33 +0200 Subject: [PATCH 53/69] Fix SonarQube warning by merging ifs --- Shared/CommandRegistry.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Shared/CommandRegistry.cs b/Shared/CommandRegistry.cs index e731455..a351fac 100644 --- a/Shared/CommandRegistry.cs +++ b/Shared/CommandRegistry.cs @@ -52,16 +52,13 @@ private void OleMenuCommandBeforeQueryStatus(object sender, EventArgs e) { CommandID commandId = oleMenuCommand.CommandID; - if (commandId != null) + if (commandId != null && mCommandsById.ContainsKey(commandId.ID)) { - if (mCommandsById.ContainsKey(commandId.ID)) - { - var bc = mCommandsById[commandId.ID]; + var bc = mCommandsById[commandId.ID]; - oleMenuCommand.Supported = true; - oleMenuCommand.Enabled = mCommandsById[commandId.ID].IsEnabled(); - oleMenuCommand.Visible = true; - } + oleMenuCommand.Supported = true; + oleMenuCommand.Enabled = bc.IsEnabled(); + oleMenuCommand.Visible = true; } } } From c89cc0c1ea38935f8e0a449be27efd4f4b22d8a8 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 15:09:57 +0200 Subject: [PATCH 54/69] Use new string instead of a for loop to create indentation in logger --- Shared/Log.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Shared/Log.cs b/Shared/Log.cs index cc26eb8..6897bc4 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -90,14 +90,9 @@ private static void OnMessage(Level level, string format, object[] args) { string message = args.Length > 0 ? string.Format(format, args) : format; string formattedLine; - string indent = ""; + string indent = new string(' ', mIndent * 4); string levelName = level.ToString().PadLeft(5, ' '); - for (int i = 0; i < mIndent; i++) - { - indent += " "; - } - if (Prefix.Length > 0) { formattedLine = Prefix + " (" + levelName + "): " + indent + message + "\n"; From e8a03ec19160a89d7c96129aec0d9891b63d79ee Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 19:37:41 +0200 Subject: [PATCH 55/69] Remove Execute() method from the Feature class, since it's unused --- Shared/Feature.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Shared/Feature.cs b/Shared/Feature.cs index ba8799d..5b5d227 100644 --- a/Shared/Feature.cs +++ b/Shared/Feature.cs @@ -12,11 +12,6 @@ protected Feature(string name) { mName = name; } - - public virtual bool Execute() - { - return true; - } }; public abstract class PreCommandFeature : Feature From 8651b44f116e72c4b8f4d784314e0683c35e803a Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 19:39:52 +0200 Subject: [PATCH 56/69] Remove unused usings --- NiftyPerforce/P4Operations.cs | 1 - Shared/AsyncProcess.cs | 1 - Shared/Plugin.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index ba49b04..e53bc58 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -4,7 +4,6 @@ using System.IO; using System.Text.RegularExpressions; using Aurora; -using EnvDTE; namespace NiftyPerforce { diff --git a/Shared/AsyncProcess.cs b/Shared/AsyncProcess.cs index 7995bbc..b0ef59e 100644 --- a/Shared/AsyncProcess.cs +++ b/Shared/AsyncProcess.cs @@ -1,7 +1,6 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System.Collections.Generic; using System.Threading; -using EnvDTE; namespace Aurora { diff --git a/Shared/Plugin.cs b/Shared/Plugin.cs index c56daa7..72d3ea6 100644 --- a/Shared/Plugin.cs +++ b/Shared/Plugin.cs @@ -1,5 +1,4 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. -using System; using System.Collections.Generic; using EnvDTE; using EnvDTE80; From 80f4c37773059c3dcdc169340cff369cc40bb51f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 19:40:30 +0200 Subject: [PATCH 57/69] Remove namespace Aurora from NiftyPerforce --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 195 +++++++++--------- .../EventHandlers/AutoCheckoutOnSave.cs | 114 +++++----- .../EventHandlers/AutoCheckoutProject.cs | 91 ++++---- .../EventHandlers/AutoCheckoutTextEdit.cs | 145 +++++++------ NiftyPerforce/NiftyPerforcePackage.cs | 1 - 5 files changed, 266 insertions(+), 280 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index f5c0570..9abb5ac 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -1,140 +1,137 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; +using Aurora; using EnvDTE; -using NiftyPerforce; -namespace Aurora +namespace NiftyPerforce { - namespace NiftyPerforce + // Handles registration and events for add/delete files and projects. + internal class AutoAddDelete : Feature { - // Handles registration and events for add/delete files and projects. - internal class AutoAddDelete : Feature - { - private readonly ProjectItemsEvents m_projectEvents; - private readonly SolutionEvents m_solutionEvents; + private readonly ProjectItemsEvents m_projectEvents; + private readonly SolutionEvents m_solutionEvents; - private _dispProjectItemsEvents_ItemAddedEventHandler _itemAddedEventHandler; - private _dispSolutionEvents_ProjectAddedEventHandler _projectAddedEventHandler; - private _dispProjectItemsEvents_ItemRemovedEventHandler _itemRemovedEventHandler; - private _dispSolutionEvents_ProjectRemovedEventHandler _projectRemovedEventHandler; + private _dispProjectItemsEvents_ItemAddedEventHandler _itemAddedEventHandler; + private _dispSolutionEvents_ProjectAddedEventHandler _projectAddedEventHandler; + private _dispProjectItemsEvents_ItemRemovedEventHandler _itemRemovedEventHandler; + private _dispSolutionEvents_ProjectRemovedEventHandler _projectRemovedEventHandler; - private readonly Plugin m_plugin; + private readonly Plugin m_plugin; - public AutoAddDelete(Plugin plugin) - : base("AutoAddDelete") - { - m_plugin = plugin; + public AutoAddDelete(Plugin plugin) + : base("AutoAddDelete") + { + m_plugin = plugin; - m_projectEvents = ((EnvDTE80.Events2)m_plugin.App.Events).ProjectItemsEvents; - m_solutionEvents = ((EnvDTE80.Events2)m_plugin.App.Events).SolutionEvents; + m_projectEvents = ((EnvDTE80.Events2)m_plugin.App.Events).ProjectItemsEvents; + m_solutionEvents = ((EnvDTE80.Events2)m_plugin.App.Events).SolutionEvents; - ((Config)m_plugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + ((Config)m_plugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private bool AddFilesHandlersInstalled => _itemAddedEventHandler != null || _projectAddedEventHandler != null; // second conditional is useless but kept for clarity - private bool RemoveFilesHandlersInstalled => _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; // second conditional is useless but kept for clarity + private bool AddFilesHandlersInstalled => _itemAddedEventHandler != null || _projectAddedEventHandler != null; // second conditional is useless but kept for clarity + private bool RemoveFilesHandlersInstalled => _itemRemovedEventHandler != null || _projectRemovedEventHandler != null; // second conditional is useless but kept for clarity - private void RegisterEvents(object sender = null, EventArgs e = null) + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)m_plugin.Options).AutoAdd) { - if (((Config)m_plugin.Options).AutoAdd) - { - if (!AddFilesHandlersInstalled) - { - Log.Info("Adding handlers to automatically add files to perforce as you add them to the project"); - _itemAddedEventHandler = new _dispProjectItemsEvents_ItemAddedEventHandler(OnItemAdded); - m_projectEvents.ItemAdded += _itemAddedEventHandler; - - _projectAddedEventHandler = new _dispSolutionEvents_ProjectAddedEventHandler(OnProjectAdded); - m_solutionEvents.ProjectAdded += _projectAddedEventHandler; - } - } - else if (AddFilesHandlersInstalled) + if (!AddFilesHandlersInstalled) { - Log.Info("Removing handlers to automatically add files to perforce as you add them to the project"); - m_projectEvents.ItemAdded -= _itemAddedEventHandler; - _itemAddedEventHandler = null; + Log.Info("Adding handlers to automatically add files to perforce as you add them to the project"); + _itemAddedEventHandler = new _dispProjectItemsEvents_ItemAddedEventHandler(OnItemAdded); + m_projectEvents.ItemAdded += _itemAddedEventHandler; - m_solutionEvents.ProjectAdded -= _projectAddedEventHandler; - _projectAddedEventHandler = null; + _projectAddedEventHandler = new _dispSolutionEvents_ProjectAddedEventHandler(OnProjectAdded); + m_solutionEvents.ProjectAdded += _projectAddedEventHandler; } + } + else if (AddFilesHandlersInstalled) + { + Log.Info("Removing handlers to automatically add files to perforce as you add them to the project"); + m_projectEvents.ItemAdded -= _itemAddedEventHandler; + _itemAddedEventHandler = null; - if (((Config)m_plugin.Options).AutoDelete) - { - if (!RemoveFilesHandlersInstalled) - { - Log.Info("Adding handlers to automatically delete files from perforce as you remove them from the project"); - _itemRemovedEventHandler = new _dispProjectItemsEvents_ItemRemovedEventHandler(OnItemRemoved); - m_projectEvents.ItemRemoved += _itemRemovedEventHandler; - - _projectRemovedEventHandler = new _dispSolutionEvents_ProjectRemovedEventHandler(OnProjectRemoved); - m_solutionEvents.ProjectRemoved += _projectRemovedEventHandler; - } - } - else if (RemoveFilesHandlersInstalled) + m_solutionEvents.ProjectAdded -= _projectAddedEventHandler; + _projectAddedEventHandler = null; + } + + if (((Config)m_plugin.Options).AutoDelete) + { + if (!RemoveFilesHandlersInstalled) { - Log.Info("Removing handlers to automatically deleting files from perforce as you remove them from the project"); - m_projectEvents.ItemRemoved -= _itemRemovedEventHandler; - _itemRemovedEventHandler = null; + Log.Info("Adding handlers to automatically delete files from perforce as you remove them from the project"); + _itemRemovedEventHandler = new _dispProjectItemsEvents_ItemRemovedEventHandler(OnItemRemoved); + m_projectEvents.ItemRemoved += _itemRemovedEventHandler; - m_solutionEvents.ProjectRemoved -= _projectRemovedEventHandler; - _projectRemovedEventHandler = null; + _projectRemovedEventHandler = new _dispSolutionEvents_ProjectRemovedEventHandler(OnProjectRemoved); + m_solutionEvents.ProjectRemoved += _projectRemovedEventHandler; } } - - private void OnItemAdded(ProjectItem item) + else if (RemoveFilesHandlersInstalled) { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(item.ContainingProject.FullName); + Log.Info("Removing handlers to automatically deleting files from perforce as you remove them from the project"); + m_projectEvents.ItemRemoved -= _itemRemovedEventHandler; + _itemRemovedEventHandler = null; - if (item.ProjectItems != null) - { - for (int i = 0; i < item.FileCount; i++) - { - string name = item.get_FileNames((short)i); - P4Operations.AddFile(name); - } - } - else - { - if (System.IO.File.Exists(item.Name)) - P4Operations.AddFile(item.Name); - } + m_solutionEvents.ProjectRemoved -= _projectRemovedEventHandler; + _projectRemovedEventHandler = null; } + } - private void OnItemRemoved(ProjectItem item) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnItemAdded(ProjectItem item) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(item.ContainingProject.FullName); + P4Operations.EditFile(item.ContainingProject.FullName); + if (item.ProjectItems != null) + { for (int i = 0; i < item.FileCount; i++) { string name = item.get_FileNames((short)i); - P4Operations.DeleteFile(name); + P4Operations.AddFile(name); } } - - private void OnProjectAdded(Project project) + else { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - - P4Operations.EditFile(m_plugin.App.Solution.FullName); - P4Operations.AddFile(project.FullName); - // TODO: [jt] We should if the operation is not a add new project but rather a add existing project - // step through all the project items and add them to perforce. Or maybe we want the user - // to do this herself? + if (System.IO.File.Exists(item.Name)) + P4Operations.AddFile(item.Name); } + } - private void OnProjectRemoved(Project project) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnItemRemoved(ProjectItem item) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.App.Solution.FullName); - P4Operations.DeleteFile(project.FullName); - // TODO: [jt] Do we want to automatically delete the items from perforce here? + P4Operations.EditFile(item.ContainingProject.FullName); + + for (int i = 0; i < item.FileCount; i++) + { + string name = item.get_FileNames((short)i); + P4Operations.DeleteFile(name); } } + + private void OnProjectAdded(Project project) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.AddFile(project.FullName); + // TODO: [jt] We should if the operation is not a add new project but rather a add existing project + // step through all the project items and add them to perforce. Or maybe we want the user + // to do this herself? + } + + private void OnProjectRemoved(Project project) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.DeleteFile(project.FullName); + // TODO: [jt] Do we want to automatically delete the items from perforce here? + } } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs index 923ee06..4009c8e 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutOnSave.cs @@ -1,84 +1,80 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; -using EnvDTE; +using Aurora; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using NiftyPerforce; -namespace Aurora +namespace NiftyPerforce { - namespace NiftyPerforce + // Create a class to retrieve the OnBeforeSave event from VS + // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 + internal class RunningDocTableEvents : IVsRunningDocTableEvents3 { - // Create a class to retrieve the OnBeforeSave event from VS - // http://schmalls.com/2015/01/19/adventures-in-visual-studio-extension-development-part-2 - internal class RunningDocTableEvents : IVsRunningDocTableEvents3 + private readonly AutoCheckoutOnSave _autoCheckoutOnSave; + + public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) { - private readonly AutoCheckoutOnSave _autoCheckoutOnSave; + _autoCheckoutOnSave = autoCheckoutOnSave; + } - public RunningDocTableEvents(AutoCheckoutOnSave autoCheckoutOnSave) - { - _autoCheckoutOnSave = autoCheckoutOnSave; - } + public int OnBeforeSave(uint docCookie) + { + RunningDocumentInfo runningDocumentInfo = _autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); + AutoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); + return VSConstants.S_OK; + } - public int OnBeforeSave(uint docCookie) - { - RunningDocumentInfo runningDocumentInfo = _autoCheckoutOnSave._rdt.Value.GetDocumentInfo(docCookie); - AutoCheckoutOnSave.OnBeforeSave(runningDocumentInfo.Moniker); - return VSConstants.S_OK; - } + //////////////////////////////////////////////////////////////////// + // default implementation for the pure methods, return OK + public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) { return VSConstants.S_OK; } + public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew) { return VSConstants.S_OK; } + public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) { return VSConstants.S_OK; } + public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } + public int OnAfterSave(uint docCookie) { return VSConstants.S_OK; } + public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { return VSConstants.S_OK; } + public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } + } - //////////////////////////////////////////////////////////////////// - // default implementation for the pure methods, return OK - public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) { return VSConstants.S_OK; } - public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew) { return VSConstants.S_OK; } - public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) { return VSConstants.S_OK; } - public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } - public int OnAfterSave(uint docCookie) { return VSConstants.S_OK; } - public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { return VSConstants.S_OK; } - public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { return VSConstants.S_OK; } - } + internal class AutoCheckoutOnSave : PreCommandFeature + { + internal Lazy _rdt; + internal uint _rdte; + private readonly IServiceProvider _serviceProvider; - internal class AutoCheckoutOnSave : PreCommandFeature + public AutoCheckoutOnSave(Plugin plugin, IServiceProvider serviceProvider) + : base(plugin, "AutoCheckoutOnSave") { - internal Lazy _rdt; - internal uint _rdte; - private readonly IServiceProvider _serviceProvider; - - public AutoCheckoutOnSave(Plugin plugin, IServiceProvider serviceProvider) - : base(plugin, "AutoCheckoutOnSave") - { - ThreadHelper.ThrowIfNotOnUIThread(); - _serviceProvider = serviceProvider; - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + ThreadHelper.ThrowIfNotOnUIThread(); + _serviceProvider = serviceProvider; + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private bool RDTAdvised => _rdt != null; + private bool RDTAdvised => _rdt != null; - private void RegisterEvents(object sender = null, EventArgs e = null) + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)mPlugin.Options).AutoCheckoutOnSave) { - if (((Config)mPlugin.Options).AutoCheckoutOnSave) + if (!RDTAdvised) { - if (!RDTAdvised) - { - Log.Info("Adding handlers for automatically checking out dirty files when you save"); - _rdt = new Lazy(() => new RunningDocumentTable(_serviceProvider)); - _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); - } - } - else if (RDTAdvised) - { - Log.Info("Removing handlers for automatically checking out dirty files when you save"); - _rdt.Value.Unadvise(_rdte); - _rdt = null; + Log.Info("Adding handlers for automatically checking out dirty files when you save"); + _rdt = new Lazy(() => new RunningDocumentTable(_serviceProvider)); + _rdte = _rdt.Value.Advise(new RunningDocTableEvents(this)); } } - - internal static bool OnBeforeSave(string filename) + else if (RDTAdvised) { - return P4Operations.EditFileImmediate(filename); + Log.Info("Removing handlers for automatically checking out dirty files when you save"); + _rdt.Value.Unadvise(_rdte); + _rdt = null; } } + + internal static bool OnBeforeSave(string filename) + { + return P4Operations.EditFileImmediate(filename); + } } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index c51fc8f..9a95524 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -1,24 +1,22 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using System.Collections.Generic; +using Aurora; using EnvDTE; -using NiftyPerforce; -namespace Aurora +namespace NiftyPerforce { - namespace NiftyPerforce + internal class AutoCheckoutProject : PreCommandFeature { - internal class AutoCheckoutProject : PreCommandFeature + public AutoCheckoutProject(Plugin plugin) + : base(plugin, "AutoCheckoutProject") { - public AutoCheckoutProject(Plugin plugin) - : base(plugin, "AutoCheckoutProject") - { - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private readonly string[] _commands = - { + private readonly string[] _commands = + { "ClassViewContextMenus.ClassViewProject.Properties", "ClassViewContextMenus.ClassViewMultiselectProjectreferencesItems.Properties", "File.Properties", @@ -30,54 +28,53 @@ public AutoCheckoutProject(Plugin plugin) "File.Remove" // I don't think this actually does anything }; - private List _registeredCommands; + private List _registeredCommands; - private void RegisterEvents(object sender = null, EventArgs e = null) + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)mPlugin.Options).AutoCheckoutProject) { - if (((Config)mPlugin.Options).AutoCheckoutProject) + if (_registeredCommands == null) { - if (_registeredCommands == null) + Log.Info("Adding handlers for automatically checking out .vcproj files when you do changes to the project"); + foreach (string command in _commands) { - Log.Info("Adding handlers for automatically checking out .vcproj files when you do changes to the project"); - foreach (string command in _commands) - { - if (RegisterHandler(command, OnCheckoutSelectedProjects)) - _registeredCommands.Add(command); - else - Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutSelectedProjects), command); - } + if (RegisterHandler(command, OnCheckoutSelectedProjects)) + _registeredCommands.Add(command); + else + Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutSelectedProjects), command); } } - else if (_registeredCommands != null) - { - Log.Info("Removing handlers for automatically checking out .vcproj files when you do changes to the project"); - foreach (string command in _registeredCommands) - UnregisterHandler(command, OnCheckoutSelectedProjects); - _registeredCommands = null; - } } - - private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + else if (_registeredCommands != null) { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + Log.Info("Removing handlers for automatically checking out .vcproj files when you do changes to the project"); + foreach (string command in _registeredCommands) + UnregisterHandler(command, OnCheckoutSelectedProjects); + _registeredCommands = null; + } + } - // when I get Edit.Delete : - if (Guid == Microsoft.VisualStudio.VSConstants.CMDSETID.StandardCommandSet97_string && ID == 17) - { - // see if the active window is SolutionExplorer : - Window w = mPlugin.App.ActiveWindow; - if (w.Type != EnvDTE.vsWindowType.vsWindowTypeSolutionExplorer) - { - // it's just a delete in the text window, get out ! - return; - } - } + private void OnCheckoutSelectedProjects(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - foreach (Project project in (Array)mPlugin.App.ActiveSolutionProjects) + // when I get Edit.Delete : + if (Guid == Microsoft.VisualStudio.VSConstants.CMDSETID.StandardCommandSet97_string && ID == 17) + { + // see if the active window is SolutionExplorer : + Window w = mPlugin.App.ActiveWindow; + if (w.Type != EnvDTE.vsWindowType.vsWindowTypeSolutionExplorer) { - P4Operations.EditFileImmediate(project.FullName); + // it's just a delete in the text window, get out ! + return; } } + + foreach (Project project in (Array)mPlugin.App.ActiveSolutionProjects) + { + P4Operations.EditFileImmediate(project.FullName); + } } } } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 4bc0f4f..6114e86 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -1,106 +1,103 @@ // Copyright (C) 2006-2010 Jim Tilander. See COPYING for and README for more details. using System; using System.Collections.Generic; +using Aurora; using EnvDTE; using EnvDTE80; -using NiftyPerforce; -namespace Aurora +namespace NiftyPerforce { - namespace NiftyPerforce + internal class AutoCheckoutTextEdit : PreCommandFeature { - internal class AutoCheckoutTextEdit : PreCommandFeature - { - private EnvDTE80.TextDocumentKeyPressEvents mTextDocEvents; - private EnvDTE.TextEditorEvents mTextEditorEvents; + private EnvDTE80.TextDocumentKeyPressEvents mTextDocEvents; + private EnvDTE.TextEditorEvents mTextEditorEvents; - public AutoCheckoutTextEdit(Plugin plugin) - : base(plugin, "AutoCheckoutTextEdit") - { - ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; - RegisterEvents(); - } + public AutoCheckoutTextEdit(Plugin plugin) + : base(plugin, "AutoCheckoutTextEdit") + { + ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + RegisterEvents(); + } - private readonly string[] _commands = - { + private readonly string[] _commands = + { "Edit.Delete", "Edit.DeleteBackwards", "Edit.Paste" }; - private List _registeredCommands; - private _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler _beforeKeyPressEventHandler; - private _dispTextEditorEvents_LineChangedEventHandler _lineChangedEventHandler; + private List _registeredCommands; + private _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler _beforeKeyPressEventHandler; + private _dispTextEditorEvents_LineChangedEventHandler _lineChangedEventHandler; - private void RegisterEvents(object sender = null, EventArgs e = null) + private void RegisterEvents(object sender = null, EventArgs e = null) + { + if (((Config)mPlugin.Options).AutoCheckoutOnEdit) { - if (((Config)mPlugin.Options).AutoCheckoutOnEdit) + if (_registeredCommands == null) { - if (_registeredCommands == null) - { - Log.Info("Adding handlers for automatically checking out text files as you edit them"); - _registeredCommands = new List(); - var events = (EnvDTE80.Events2)mPlugin.App.Events; - mTextDocEvents = events.get_TextDocumentKeyPressEvents(null); - _beforeKeyPressEventHandler = new _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler(OnBeforeKeyPress); - mTextDocEvents.BeforeKeyPress += _beforeKeyPressEventHandler; + Log.Info("Adding handlers for automatically checking out text files as you edit them"); + _registeredCommands = new List(); + var events = (EnvDTE80.Events2)mPlugin.App.Events; + mTextDocEvents = events.get_TextDocumentKeyPressEvents(null); + _beforeKeyPressEventHandler = new _dispTextDocumentKeyPressEvents_BeforeKeyPressEventHandler(OnBeforeKeyPress); + mTextDocEvents.BeforeKeyPress += _beforeKeyPressEventHandler; - mTextEditorEvents = events.get_TextEditorEvents(null); - _lineChangedEventHandler = new _dispTextEditorEvents_LineChangedEventHandler(OnLineChanged); - mTextEditorEvents.LineChanged += _lineChangedEventHandler; + mTextEditorEvents = events.get_TextEditorEvents(null); + _lineChangedEventHandler = new _dispTextEditorEvents_LineChangedEventHandler(OnLineChanged); + mTextEditorEvents.LineChanged += _lineChangedEventHandler; - foreach (string command in _commands) - { - if (RegisterHandler(command, OnCheckoutCurrentDocument)) - _registeredCommands.Add(command); - else - Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutCurrentDocument), command); - } + foreach (string command in _commands) + { + if (RegisterHandler(command, OnCheckoutCurrentDocument)) + _registeredCommands.Add(command); + else + Log.Warning("Failed to register {0} to command '{1}'", nameof(OnCheckoutCurrentDocument), command); } } - else if (_registeredCommands != null) - { - Log.Info("Removing handlers for automatically checking out text files as you edit them"); - foreach (string command in _registeredCommands) - UnregisterHandler(command, OnCheckoutCurrentDocument); - _registeredCommands = null; + } + else if (_registeredCommands != null) + { + Log.Info("Removing handlers for automatically checking out text files as you edit them"); + foreach (string command in _registeredCommands) + UnregisterHandler(command, OnCheckoutCurrentDocument); + _registeredCommands = null; - mTextEditorEvents.LineChanged -= _lineChangedEventHandler; - mTextEditorEvents = null; + mTextEditorEvents.LineChanged -= _lineChangedEventHandler; + mTextEditorEvents = null; - mTextDocEvents.BeforeKeyPress -= _beforeKeyPressEventHandler; - mTextDocEvents = null; - } + mTextDocEvents.BeforeKeyPress -= _beforeKeyPressEventHandler; + mTextDocEvents = null; } + } - private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, bool InStatementCompletion, ref bool CancelKeypress) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, bool InStatementCompletion, ref bool CancelKeypress) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); - } + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); + } - // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but - // I can't get this one to fire reliably... Wonder how much these handlers will slow down the IDE? - private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if ((Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && - (Hint & (int)vsTextChanged.vsTextChangedMultiLine) == 0 && - (Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && - (Hint != 0)) - return; - if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); - } + // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but + // I can't get this one to fire reliably... Wonder how much these handlers will slow down the IDE? + private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if ((Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && + (Hint & (int)vsTextChanged.vsTextChangedMultiLine) == 0 && + (Hint & (int)vsTextChanged.vsTextChangedNewline) == 0 && + (Hint != 0)) + return; + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); + } - private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) - { - Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); - } + if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); } } } diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index 33da693..bb09f9d 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -5,7 +5,6 @@ using System.Runtime.InteropServices; using System.Threading; using Aurora; -using Aurora.NiftyPerforce; using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio.CommandBars; From 59d533ddd167d536cc0f2e0ebd77bd0053659f1c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 20:38:19 +0200 Subject: [PATCH 58/69] Add the possibility to force checkout a file, and use it when the edit button is manually pressed --- NiftyPerforce/Commands/P4EditItem.cs | 2 +- NiftyPerforce/Commands/P4EditModified.cs | 6 ++--- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 8 +++--- .../EventHandlers/AutoCheckoutTextEdit.cs | 6 ++--- NiftyPerforce/P4Operations.cs | 25 +++++++++---------- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/NiftyPerforce/Commands/P4EditItem.cs b/NiftyPerforce/Commands/P4EditItem.cs index 1833a0f..7572ba1 100644 --- a/NiftyPerforce/Commands/P4EditItem.cs +++ b/NiftyPerforce/Commands/P4EditItem.cs @@ -13,7 +13,7 @@ public P4EditItem(Plugin plugin, string canonicalName) public override void OnExecute(SelectedItem item, string fileName) { - P4Operations.EditFile(fileName); + P4Operations.EditFile(fileName, true); } } } diff --git a/NiftyPerforce/Commands/P4EditModified.cs b/NiftyPerforce/Commands/P4EditModified.cs index 5041b79..2281b0f 100644 --- a/NiftyPerforce/Commands/P4EditModified.cs +++ b/NiftyPerforce/Commands/P4EditModified.cs @@ -19,7 +19,7 @@ public override bool OnCommand() if (!Plugin.App.Solution.Saved) { Log.Info($"P4EditModified : solution {Plugin.App.Solution.FullName} was dirty, checkout"); - P4Operations.EditFile(Plugin.App.Solution.FullName); + P4Operations.EditFile(Plugin.App.Solution.FullName, false); } foreach (Project p in Plugin.App.Solution.Projects) @@ -27,7 +27,7 @@ public override bool OnCommand() if (!p.Saved) { Log.Info($"P4EditModified : project {p.FullName} was dirty, checkout"); - P4Operations.EditFile(p.FullName); + P4Operations.EditFile(p.FullName, false); } } @@ -36,7 +36,7 @@ public override bool OnCommand() if (!doc.Saved) { Log.Info($"P4EditModified : document {doc.FullName} was dirty, checkout"); - P4Operations.EditFile(doc.FullName); + P4Operations.EditFile(doc.FullName, false); } } diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 9abb5ac..7c966fb 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -84,7 +84,7 @@ private void OnItemAdded(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(item.ContainingProject.FullName); + P4Operations.EditFile(item.ContainingProject.FullName, false); if (item.ProjectItems != null) { @@ -105,7 +105,7 @@ private void OnItemRemoved(ProjectItem item) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(item.ContainingProject.FullName); + P4Operations.EditFile(item.ContainingProject.FullName, false); for (int i = 0; i < item.FileCount; i++) { @@ -118,7 +118,7 @@ private void OnProjectAdded(Project project) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.EditFile(m_plugin.App.Solution.FullName, false); P4Operations.AddFile(project.FullName); // TODO: [jt] We should if the operation is not a add new project but rather a add existing project // step through all the project items and add them to perforce. Or maybe we want the user @@ -129,7 +129,7 @@ private void OnProjectRemoved(Project project) { Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); - P4Operations.EditFile(m_plugin.App.Solution.FullName); + P4Operations.EditFile(m_plugin.App.Solution.FullName, false); P4Operations.DeleteFile(project.FullName); // TODO: [jt] Do we want to automatically delete the items from perforce here? } diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index 6114e86..e5490d0 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -75,7 +75,7 @@ private void OnBeforeKeyPress(string Keypress, EnvDTE.TextSelection Selection, b Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName, false); } // [jt] This handler checks for things like paste operations. In theory we should be able to remove the handler above, but @@ -89,7 +89,7 @@ private void OnLineChanged(TextPoint StartPoint, TextPoint EndPoint, int Hint) (Hint != 0)) return; if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName, false); } private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) @@ -97,7 +97,7 @@ private void OnCheckoutCurrentDocument(string Guid, int ID, object CustomIn, obj Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); if (mPlugin.App.ActiveDocument != null && mPlugin.App.ActiveDocument.ReadOnly && !mPlugin.App.ActiveDocument.Saved) - P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName); + P4Operations.EditFile(mPlugin.App.ActiveDocument.FullName, false); } } } diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index e53bc58..568a825 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -105,14 +105,14 @@ public static bool AddFile(string filename) return AsyncProcess.Schedule("p4.exe", GetUserInfoString() + "add \"" + filename + "\"", Path.GetDirectoryName(filename), new AsyncProcess.OnDone(UnlockOp), token); } - public static bool EditFile(string filename) + public static bool EditFile(string filename, bool force) { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFile), filename); + return Internal_CheckEditFile(new CheckoutCallback((string f) => Internal_EditFile(f, force ? EditFileFlags.Force : EditFileFlags.None)), filename); } public static bool EditFileImmediate(string filename) { - return Internal_CheckEditFile(new CheckoutCallback(Internal_EditFileImmediate), filename); + return Internal_CheckEditFile(new CheckoutCallback((string f) => Internal_EditFile(f, EditFileFlags.Immediate)), filename); } private static bool Internal_CheckEditFile(CheckoutCallback callback, string filename) @@ -147,17 +147,15 @@ void CheckoutAdditionalIfExists(string f) return result; } - private static bool Internal_EditFile(string filename) + [Flags] + private enum EditFileFlags { - return Internal_EditFile(filename, false); + None = 0, + Immediate = 1 << 0, + Force = 1 << 1, } - private static bool Internal_EditFileImmediate(string filename) - { - return Internal_EditFile(filename, true); - } - - private static bool Internal_EditFile(string filename, bool immediate) + private static bool Internal_EditFile(string filename, EditFileFlags flags) { if (filename.Length == 0) { @@ -171,9 +169,9 @@ private static bool Internal_EditFile(string filename, bool immediate) return false; } - if (!Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) + if (!flags.HasFlag(EditFileFlags.Force) && !Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) { - Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle IgnoreReadOnlyOnEdit in the options."); + Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle {nameof(Config.IgnoreReadOnlyOnEdit)} in the options."); return false; } @@ -183,6 +181,7 @@ private static bool Internal_EditFile(string filename, bool immediate) return NotifyUser("could not find p4 exe installed in perforce directory"); } + bool immediate = flags.HasFlag(EditFileFlags.Immediate); Log.Debug("EditFile" + (immediate ? "Immediate " : " ") + filename); string token = FormatToken("edit", filename); if (!LockOp(token)) From 22f829f91dcdd6564ae88213aa0c945b956d996c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Sun, 18 Jul 2021 21:58:04 +0200 Subject: [PATCH 59/69] Fix deadlock during logging --- Shared/Log.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Shared/Log.cs b/Shared/Log.cs index 6897bc4..ce61f8b 100644 --- a/Shared/Log.cs +++ b/Shared/Log.cs @@ -102,12 +102,9 @@ private static void OnMessage(Level level, string format, object[] args) formattedLine = levelName + ": " + indent + message + "\n"; } - lock (mHandlers) + foreach (IHandler handler in mHandlers) { - foreach (IHandler handler in mHandlers) - { - handler.OnMessage(level, message, formattedLine); - } + handler.OnMessage(level, message, formattedLine); } } From cee216cbf9552ab9368d36d3525b800c913162bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Sep 2021 11:13:04 +0000 Subject: [PATCH 60/69] Bump actions/setup-java from 2.1.0 to 2.3.1 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.1.0 to 2.3.1. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.1.0...v2.3.1) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index e5eacc2..afaecc6 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -59,7 +59,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 11 - uses: actions/setup-java@v2.1.0 + uses: actions/setup-java@v2.3.1 with: distribution: 'zulu' java-version: '11' From 73f6180530472ec2ba88cf5f2730b5f76f84dacf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 11:11:34 +0000 Subject: [PATCH 61/69] Bump Microsoft.VSSDK.BuildTools from 17.0.3155-preview3 to 17.0.5232 Bumps Microsoft.VSSDK.BuildTools from 17.0.3155-preview3 to 17.0.5232. --- updated-dependencies: - dependency-name: Microsoft.VSSDK.BuildTools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- NiftyPerforce/NiftyPerforce.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 416ef28..c688930 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -60,7 +60,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From b755824f8404bea16ed3bb4473d7eb03f1c07f1c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Tue, 9 Nov 2021 12:18:06 +0100 Subject: [PATCH 62/69] Improve log on startup --- NiftyPerforce/NiftyPerforcePackage.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index bb09f9d..a9b7cf1 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -85,12 +85,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke Log.Prefix = "NiftyPerforce"; } -#if DEBUG - Log.Info("NiftyPerforce (Debug)"); -#else - Log.Info("NiftyPerforce (Release)"); -#endif - // Show where we are and when we were compiled... var niftyAssembly = Assembly.GetExecutingAssembly(); Version version = niftyAssembly.GetName().Version; @@ -99,7 +93,18 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke if (informationalVersion != null) versionString += " " + informationalVersion; - Log.Info("I'm running {0} v{1} compiled on {2}", niftyAssembly.Location, versionString, System.IO.File.GetLastWriteTime(niftyAssembly.Location)); + Log.Info( + "NiftyPerforce{0} v{1} compiled on {2}" +#if DEBUG + , " (Debug!)" +#else + , string.Empty +#endif + , versionString + , System.IO.File.GetLastWriteTime(niftyAssembly.Location) + ); + + Log.Debug(" Location '{0}'", niftyAssembly.Location); // Now we can take care of registering ourselves and all our commands and hooks. Log.Debug("Booting up..."); From 115b2f5b273ad90e973487fb1fc13c3b13f85449 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Tue, 9 Nov 2021 12:41:17 +0100 Subject: [PATCH 63/69] Fix analyzer errors by adding checks --- NiftyPerforce/EventHandlers/AutoAddDelete.cs | 3 +++ NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs | 3 +++ NiftyPerforce/NiftyPerforcePackage.cs | 6 ++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/NiftyPerforce/EventHandlers/AutoAddDelete.cs b/NiftyPerforce/EventHandlers/AutoAddDelete.cs index 7c966fb..bd4eadb 100644 --- a/NiftyPerforce/EventHandlers/AutoAddDelete.cs +++ b/NiftyPerforce/EventHandlers/AutoAddDelete.cs @@ -27,6 +27,7 @@ public AutoAddDelete(Plugin plugin) m_solutionEvents = ((EnvDTE80.Events2)m_plugin.App.Events).SolutionEvents; ((Config)m_plugin.Options).OnApplyEvent += RegisterEvents; + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); RegisterEvents(); } @@ -35,6 +36,8 @@ public AutoAddDelete(Plugin plugin) private void RegisterEvents(object sender = null, EventArgs e = null) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (((Config)m_plugin.Options).AutoAdd) { if (!AddFilesHandlersInstalled) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs index e5490d0..bd645d6 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutTextEdit.cs @@ -16,6 +16,7 @@ public AutoCheckoutTextEdit(Plugin plugin) : base(plugin, "AutoCheckoutTextEdit") { ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); RegisterEvents(); } @@ -31,6 +32,8 @@ public AutoCheckoutTextEdit(Plugin plugin) private void RegisterEvents(object sender = null, EventArgs e = null) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (((Config)mPlugin.Options).AutoCheckoutOnEdit) { if (_registeredCommands == null) diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index a9b7cf1..3a40fca 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -68,8 +68,10 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke { await base.InitializeAsync(cancellationToken, progress); - // Every plugin needs a command bar. - var application = await GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2; + var dteService = GetServiceAsync(typeof(DTE)); + Microsoft.Assumes.Present(dteService); + + var application = await dteService.ConfigureAwait(false) as DTE2; var oleMenuCommandService = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; // Switches to the UI thread in order to consume some services used in command initialization From 01171098e0d49b89ef1f52011c0c597bf5547f93 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 12:23:57 +0100 Subject: [PATCH 64/69] Better handling for vs2017/vs2019 (legacy) and vs2022 - Added 2 configurations in the solution to build the legacy versions, which refers to the old vs sdk nuget packages and a different vsixmanifest - Convert AuroraCore to a shared project - Update the github actions --- .github/workflows/build-and-publish.yml | 4 +- NiftyPerforce/NiftyPerforce.csproj | 58 +++++++++++++++---- NiftyPerforce/source.extension.cs | 2 +- NiftyPerforce/source.extension.legacy.cs | 18 ++++++ .../source.extension.legacy.vsixmanifest | 25 ++++++++ NiftyPerforce/source.extension.vsixmanifest | 3 +- NiftyPlugins.sln | 20 ++++--- Shared/AuroraCore.csproj | 14 ----- Shared/AuroraCore.projitems | 24 ++++++++ Shared/AuroraCore.shproj | 13 +++++ Shared/Properties/AssemblyInfo.cs | 34 ----------- 11 files changed, 143 insertions(+), 72 deletions(-) create mode 100644 NiftyPerforce/source.extension.legacy.cs create mode 100644 NiftyPerforce/source.extension.legacy.vsixmanifest delete mode 100644 Shared/AuroraCore.csproj create mode 100644 Shared/AuroraCore.projitems create mode 100644 Shared/AuroraCore.shproj delete mode 100644 Shared/Properties/AssemblyInfo.cs diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index afaecc6..be4637a 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -45,7 +45,7 @@ jobs: runs-on: windows-latest strategy: matrix: - configuration: [Debug, Release] + configuration: ['Debug', 'Debug Legacy', 'Release', 'Release Legacy'] env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -105,4 +105,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: 'NiftyPerforce-${{ matrix.configuration }}-${{github.sha}}' - path: NiftyPerforce\bin\${{ matrix.configuration }}\net472\NiftyPerforce.vsix + path: NiftyPerforce\bin\${{ matrix.configuration }}\net472\NiftyPerforce*.vsix diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index c688930..3727c75 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -17,6 +17,14 @@ true false true + Debug;Release;Debug Legacy;Release Legacy + + + DEBUG + false + + + true @@ -31,8 +39,11 @@ True source.extension.vsixmanifest - - + + True + True + source.extension.legacy.vsixmanifest + Always true @@ -41,13 +52,6 @@ Always true - - Designer - VsixManifestGenerator - source.extension.cs - - - Menus.ctmenu Designer @@ -56,15 +60,45 @@ - - - + + + + + Designer + VsixManifestGenerator + source.extension.cs + + + + compile; build; native; contentfiles; analyzers; buildtransitive + + + + + + NiftyPerforceLegacy + + + + Designer + VsixManifestGenerator + source.extension.legacy.cs + + + + compile; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + diff --git a/NiftyPerforce/source.extension.cs b/NiftyPerforce/source.extension.cs index c2c1dec..aaf8f9c 100644 --- a/NiftyPerforce/source.extension.cs +++ b/NiftyPerforce/source.extension.cs @@ -9,7 +9,7 @@ internal sealed partial class Vsix { public const string Id = "NiftyPerforce.belkiss.3cc12a76-88f6-4e50-9ede-377ba823db79"; public const string Name = "Nifty Perforce - belkiss' fork"; - public const string Description = @"Simple Perforce integration for Visual Studio"; + public const string Description = @"Simple Perforce integration for Visual Studio 2022"; public const string Language = "en-US"; public const string Version = "3.0.0"; public const string Author = "Lambert Clara"; diff --git a/NiftyPerforce/source.extension.legacy.cs b/NiftyPerforce/source.extension.legacy.cs new file mode 100644 index 0000000..a14c06e --- /dev/null +++ b/NiftyPerforce/source.extension.legacy.cs @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------ +// +// This file was generated by VSIX Synchronizer +// +// ------------------------------------------------------------------------------ +namespace NiftyPerforce +{ + internal sealed partial class Vsix + { + public const string Id = "NiftyPerforce.belkiss.3cc12a76-88f6-4e50-9ede-377ba823db79"; + public const string Name = "Nifty Perforce (Legacy) - belkiss' fork"; + public const string Description = @"Simple Perforce integration for Visual Studio 2017/2019"; + public const string Language = "en-US"; + public const string Version = "3.0.0"; + public const string Author = "Lambert Clara"; + public const string Tags = "perforce"; + } +} diff --git a/NiftyPerforce/source.extension.legacy.vsixmanifest b/NiftyPerforce/source.extension.legacy.vsixmanifest new file mode 100644 index 0000000..9058fa5 --- /dev/null +++ b/NiftyPerforce/source.extension.legacy.vsixmanifest @@ -0,0 +1,25 @@ + + + + + Nifty Perforce (Legacy) - belkiss' fork + Simple Perforce integration for Visual Studio 2017/2019 + https://github.com/belkiss/niftyplugins + COPYING + Resources\logo.png + Resources\logo.png + perforce + + + + + + + + + + + + + + diff --git a/NiftyPerforce/source.extension.vsixmanifest b/NiftyPerforce/source.extension.vsixmanifest index 63adb44..08d1a28 100644 --- a/NiftyPerforce/source.extension.vsixmanifest +++ b/NiftyPerforce/source.extension.vsixmanifest @@ -3,7 +3,7 @@ Nifty Perforce - belkiss' fork - Simple Perforce integration for Visual Studio + Simple Perforce integration for Visual Studio 2022 https://github.com/belkiss/niftyplugins COPYING Resources\logo.png @@ -11,7 +11,6 @@ perforce - amd64 diff --git a/NiftyPlugins.sln b/NiftyPlugins.sln index 660be46..4f0b388 100644 --- a/NiftyPlugins.sln +++ b/NiftyPlugins.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 -VisualStudioVersion = 16.0.29728.190 +VisualStudioVersion = 16.0.31829.152 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NiftyPerforce", "NiftyPerforce\NiftyPerforce.csproj", "{4633E0B5-D536-4FCC-988E-29D54DA113DF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuroraCore", "Shared\AuroraCore.csproj", "{A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActions", "{EAFAD58B-C12C-4FCE-AC50-3B04143946E6}" ProjectSection(SolutionItems) = preProject .github\workflows\build-and-publish.yml = .github\workflows\build-and-publish.yml @@ -18,20 +16,28 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "AuroraCore", "Shared\AuroraCore.shproj", "{EED4A10D-51AE-418F-BF46-21AE321F9898}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Shared\AuroraCore.projitems*{4633e0b5-d536-4fcc-988e-29d54da113df}*SharedItemsImports = 5 + Shared\AuroraCore.projitems*{eed4a10d-51ae-418f-bf46-21ae321f9898}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Legacy|Any CPU = Debug Legacy|Any CPU Debug|Any CPU = Debug|Any CPU + Release Legacy|Any CPU = Release Legacy|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Debug Legacy|Any CPU.ActiveCfg = Debug Legacy|Any CPU + {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Debug Legacy|Any CPU.Build.0 = Debug Legacy|Any CPU {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Release Legacy|Any CPU.ActiveCfg = Release Legacy|Any CPU + {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Release Legacy|Any CPU.Build.0 = Release Legacy|Any CPU {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Release|Any CPU.ActiveCfg = Release|Any CPU {4633E0B5-D536-4FCC-988E-29D54DA113DF}.Release|Any CPU.Build.0 = Release|Any CPU - {A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A2D1CBE2-C37F-46E7-BF45-FCA62A79F2EE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Shared/AuroraCore.csproj b/Shared/AuroraCore.csproj deleted file mode 100644 index d3d39d9..0000000 --- a/Shared/AuroraCore.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net472 - Library - false - true - - - - - - - - \ No newline at end of file diff --git a/Shared/AuroraCore.projitems b/Shared/AuroraCore.projitems new file mode 100644 index 0000000..4bfecee --- /dev/null +++ b/Shared/AuroraCore.projitems @@ -0,0 +1,24 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + eed4a10d-51ae-418f-bf46-21ae321f9898 + + + AuroraCore + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Shared/AuroraCore.shproj b/Shared/AuroraCore.shproj new file mode 100644 index 0000000..f769aaf --- /dev/null +++ b/Shared/AuroraCore.shproj @@ -0,0 +1,13 @@ + + + + eed4a10d-51ae-418f-bf46-21ae321f9898 + 14.0 + + + + + + + + diff --git a/Shared/Properties/AssemblyInfo.cs b/Shared/Properties/AssemblyInfo.cs deleted file mode 100644 index fd58d8f..0000000 --- a/Shared/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -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("AuroraCore")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Aurora")] -[assembly: AssemblyProduct("AuroraCore")] -[assembly: AssemblyCopyright("Copyright © Aurora 2008")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1dfba225-a7ce-4366-9f9a-cf6a40f73a31")] - -// 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 Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] From 77215bbbffd6ca2f6f06414d17c82fcdaf8d858c Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 12:24:32 +0100 Subject: [PATCH 65/69] Fix analysis and style compile warnings by adding main thread checks and using var in one location --- NiftyPerforce/EventHandlers/AutoCheckoutProject.cs | 3 +++ Shared/VisualStudioLogHandler.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs index 9a95524..379c6ca 100644 --- a/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs +++ b/NiftyPerforce/EventHandlers/AutoCheckoutProject.cs @@ -12,6 +12,7 @@ public AutoCheckoutProject(Plugin plugin) : base(plugin, "AutoCheckoutProject") { ((Config)mPlugin.Options).OnApplyEvent += RegisterEvents; + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); RegisterEvents(); } @@ -32,6 +33,8 @@ public AutoCheckoutProject(Plugin plugin) private void RegisterEvents(object sender = null, EventArgs e = null) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (((Config)mPlugin.Options).AutoCheckoutProject) { if (_registeredCommands == null) diff --git a/Shared/VisualStudioLogHandler.cs b/Shared/VisualStudioLogHandler.cs index df948a7..7442377 100644 --- a/Shared/VisualStudioLogHandler.cs +++ b/Shared/VisualStudioLogHandler.cs @@ -56,7 +56,7 @@ private bool EnsurePane() if (_pane == null) { var guid = Guid.NewGuid(); - IVsOutputWindow output = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow)); + var output = (IVsOutputWindow)_serviceProvider.GetService(typeof(SVsOutputWindow)); Assumes.Present(output); output.CreatePane(ref guid, _name, 1, 0); output.GetPane(ref guid, out _pane); From 5a04455e3baddd985dfcecb20c8a225d195f5c86 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 12:25:26 +0100 Subject: [PATCH 66/69] Change ops in flight from a dictionary to a hash set to prevent throwing/catching exceptions which polluted the debug sessions - Also use a different lock object than the hashset itself --- NiftyPerforce/P4Operations.cs | 41 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 568a825..8b56891 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -19,23 +19,25 @@ internal class P4Operations private static bool g_p4vc_history_supported = false; private static bool g_p4vc_diffhave_supported = false; - private static readonly Dictionary g_opsInFlight = new Dictionary(); + private static readonly object g_opsInFlightLock = new object(); + private static readonly HashSet g_opsInFlight = new HashSet(); private static readonly HashSet g_alreadyNotified = new HashSet(); private static bool LockOp(string token) { - try + bool added = false; + lock (g_opsInFlightLock) { - lock (g_opsInFlight) - { - g_opsInFlight.Add(token, true); - } + added = g_opsInFlight.Add(token); + } + if (added) + { Log.Debug("## Locked \"" + token + "\""); return true; } - catch (ArgumentException) + else { Log.Error(token + " already in progress"); return false; @@ -45,22 +47,19 @@ private static bool LockOp(string token) private static void UnlockOp(bool ok, object token_) { string token = (string)token_; - try + bool removed = false; + lock (g_opsInFlightLock) { - lock (g_opsInFlight) - { - if (g_opsInFlight.Remove(token)) - { - Log.Debug("## Unlocked \"" + token + "\""); - } - else - { - Log.Debug("!! Failed to unlock \"" + token + "\""); - } - } + removed = g_opsInFlight.Remove(token); } - catch (ArgumentNullException) + + if (removed) + { + Log.Debug("## Unlocked \"" + token + "\""); + } + else { + Log.Debug("!! Failed to unlock \"" + token + "\""); } } @@ -171,7 +170,7 @@ private static bool Internal_EditFile(string filename, EditFileFlags flags) if (!flags.HasFlag(EditFileFlags.Force) && !Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) { - Log.Debug($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle {nameof(Config.IgnoreReadOnlyOnEdit)} in the options."); + Log.Info($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle {nameof(Config.IgnoreReadOnlyOnEdit)} in the options."); return false; } From 3ab2d7fb8c6c041276c4adb3c758821bbc60a0ed Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 12:34:57 +0100 Subject: [PATCH 67/69] Improve log message when a file is already read/write but we tried to call edit --- NiftyPerforce/P4Operations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NiftyPerforce/P4Operations.cs b/NiftyPerforce/P4Operations.cs index 8b56891..687ca47 100644 --- a/NiftyPerforce/P4Operations.cs +++ b/NiftyPerforce/P4Operations.cs @@ -170,7 +170,7 @@ private static bool Internal_EditFile(string filename, EditFileFlags flags) if (!flags.HasFlag(EditFileFlags.Force) && !Singleton.Instance.IgnoreReadOnlyOnEdit && (0 == (File.GetAttributes(filename) & FileAttributes.ReadOnly))) { - Log.Info($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, toggle {nameof(Config.IgnoreReadOnlyOnEdit)} in the options."); + Log.Info($"EditFile '{filename}' failed because file was not read only. If you want to force calling p4 edit, press the Checkout button in the menus or toggle {nameof(Config.IgnoreReadOnlyOnEdit)} in the options."); return false; } From 460141404dc04c6958fd0c4ea0890e9eb5957f86 Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 12:46:02 +0100 Subject: [PATCH 68/69] Add a define NIFTY_LEGACY when building in legacy mode, and use it to remove the cleanup legacy menus entry in vs2022+, since the original nifty could never have been installed in those versions --- NiftyPerforce/NiftyPerforce.csproj | 1 + NiftyPerforce/NiftyPerforcePackage.cs | 4 +++- NiftyPerforce/OptionsDialogPage.cs | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index 3727c75..f6f9144 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -77,6 +77,7 @@ NiftyPerforceLegacy + NIFTY_LEGACY diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index 3a40fca..60bd5e6 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -115,6 +115,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke var config = (Config)GetDialogPage(typeof(Config)); Singleton.Instance = config; +#if NIFTY_LEGACY config.OnApplyEvent += (object sender, EventArgs e) => { if (config.CleanLegacyNiftyCommands) @@ -123,6 +124,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke config.CleanLegacyNiftyCommands = false; } }; +#endif m_plugin = new Plugin(application, oleMenuCommandService, config); @@ -288,6 +290,6 @@ private static void RemoveCommandBar(string name, IVsProfferCommands3 profferCom profferCommands3.RemoveCommandBar(existingCmdBar); } - #endregion +#endregion } } diff --git a/NiftyPerforce/OptionsDialogPage.cs b/NiftyPerforce/OptionsDialogPage.cs index 82f94f3..c7a8646 100644 --- a/NiftyPerforce/OptionsDialogPage.cs +++ b/NiftyPerforce/OptionsDialogPage.cs @@ -54,9 +54,11 @@ public class Config : DialogPage [Category("Branching"), Description("Where we can find the mainline version of this file")] public string MainLinePath { get; set; } = ""; +#if NIFTY_LEGACY [Category("VSIX Legacy"), Description("Clean the legacy nifty perforce commands from the IDE when clicking ok. Will not be persisted.")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool CleanLegacyNiftyCommands { get; set; } = false; +#endif public event EventHandler OnApplyEvent; From 1a6582c42e100fa498614ecb4e26ffabf264ee0f Mon Sep 17 00:00:00 2001 From: Lambert Clara Date: Thu, 11 Nov 2021 13:15:15 +0100 Subject: [PATCH 69/69] Fix manifest, seems the name must always be source.extension.vsixmanifest, so move the legacy to a subfolder and adapt code accordingly --- .../Legacy/source.extension.cs} | 2 +- .../Legacy/source.extension.vsixmanifest} | 0 .../{ => Manifests}/source.extension.cs | 2 +- .../source.extension.vsixmanifest | 0 NiftyPerforce/NiftyPerforce.csproj | 22 ++++++++++--------- NiftyPerforce/NiftyPerforcePackage.cs | 6 +++++ NiftyPerforce/Properties/AssemblyInfo.cs | 7 +++++- 7 files changed, 26 insertions(+), 13 deletions(-) rename NiftyPerforce/{source.extension.legacy.cs => Manifests/Legacy/source.extension.cs} (95%) rename NiftyPerforce/{source.extension.legacy.vsixmanifest => Manifests/Legacy/source.extension.vsixmanifest} (100%) rename NiftyPerforce/{ => Manifests}/source.extension.cs (95%) rename NiftyPerforce/{ => Manifests}/source.extension.vsixmanifest (100%) diff --git a/NiftyPerforce/source.extension.legacy.cs b/NiftyPerforce/Manifests/Legacy/source.extension.cs similarity index 95% rename from NiftyPerforce/source.extension.legacy.cs rename to NiftyPerforce/Manifests/Legacy/source.extension.cs index a14c06e..d2d19f0 100644 --- a/NiftyPerforce/source.extension.legacy.cs +++ b/NiftyPerforce/Manifests/Legacy/source.extension.cs @@ -3,7 +3,7 @@ // This file was generated by VSIX Synchronizer // // ------------------------------------------------------------------------------ -namespace NiftyPerforce +namespace NiftyPerforce.Manifests.Legacy { internal sealed partial class Vsix { diff --git a/NiftyPerforce/source.extension.legacy.vsixmanifest b/NiftyPerforce/Manifests/Legacy/source.extension.vsixmanifest similarity index 100% rename from NiftyPerforce/source.extension.legacy.vsixmanifest rename to NiftyPerforce/Manifests/Legacy/source.extension.vsixmanifest diff --git a/NiftyPerforce/source.extension.cs b/NiftyPerforce/Manifests/source.extension.cs similarity index 95% rename from NiftyPerforce/source.extension.cs rename to NiftyPerforce/Manifests/source.extension.cs index aaf8f9c..6f770ef 100644 --- a/NiftyPerforce/source.extension.cs +++ b/NiftyPerforce/Manifests/source.extension.cs @@ -3,7 +3,7 @@ // This file was generated by VSIX Synchronizer // // ------------------------------------------------------------------------------ -namespace NiftyPerforce +namespace NiftyPerforce.Manifests { internal sealed partial class Vsix { diff --git a/NiftyPerforce/source.extension.vsixmanifest b/NiftyPerforce/Manifests/source.extension.vsixmanifest similarity index 100% rename from NiftyPerforce/source.extension.vsixmanifest rename to NiftyPerforce/Manifests/source.extension.vsixmanifest diff --git a/NiftyPerforce/NiftyPerforce.csproj b/NiftyPerforce/NiftyPerforce.csproj index f6f9144..6f87204 100644 --- a/NiftyPerforce/NiftyPerforce.csproj +++ b/NiftyPerforce/NiftyPerforce.csproj @@ -34,15 +34,15 @@ NiftyPerforce.vsct - + True True - source.extension.vsixmanifest + Manifests\source.extension.vsixmanifest - + True True - source.extension.legacy.vsixmanifest + source.extension.vsixmanifest Always @@ -63,12 +63,13 @@ - + Designer VsixManifestGenerator - source.extension.cs + Manifests\source.extension.cs - + + compile; build; native; contentfiles; analyzers; buildtransitive @@ -80,12 +81,13 @@ NIFTY_LEGACY - + Designer VsixManifestGenerator - source.extension.legacy.cs + source.extension.cs - + + compile; build; native; contentfiles; analyzers; buildtransitive diff --git a/NiftyPerforce/NiftyPerforcePackage.cs b/NiftyPerforce/NiftyPerforcePackage.cs index 60bd5e6..7a8eba0 100644 --- a/NiftyPerforce/NiftyPerforcePackage.cs +++ b/NiftyPerforce/NiftyPerforcePackage.cs @@ -12,6 +12,12 @@ using Microsoft.VisualStudio.Shell.Interop; using Task = System.Threading.Tasks.Task; +#if NIFTY_LEGACY +using NiftyPerforce.Manifests.Legacy; +#else +using NiftyPerforce.Manifests; +#endif + namespace NiftyPerforce { /// diff --git a/NiftyPerforce/Properties/AssemblyInfo.cs b/NiftyPerforce/Properties/AssemblyInfo.cs index fbc4565..3b7dea2 100644 --- a/NiftyPerforce/Properties/AssemblyInfo.cs +++ b/NiftyPerforce/Properties/AssemblyInfo.cs @@ -1,6 +1,11 @@ using System.Reflection; using System.Runtime.InteropServices; -using NiftyPerforce; + +#if NIFTY_LEGACY +using NiftyPerforce.Manifests.Legacy; +#else +using NiftyPerforce.Manifests; +#endif [assembly: AssemblyTitle(Vsix.Name)] [assembly: AssemblyDescription(Vsix.Description)]