From 58b08f62c99c4b4a6f7d7cf1ccd64923b82d2a98 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 14 Feb 2023 09:18:29 -0600 Subject: [PATCH 01/21] Added ability to cancel WindowFrame close --- .../Windows/WindowFrame.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs index b4e6f40..df0af42 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs @@ -380,6 +380,11 @@ int IVsWindowFrameNotify3.OnClose(ref uint pgrfSaveOptions) { WindowFrameCloseEventArgs e = new((FrameCloseOption)pgrfSaveOptions); OnClose(this, e); + + if (e.Cancel) + { + return VSConstants.E_ABORT; + } } InvokeStatusChanged(); return VSConstants.S_OK; @@ -492,6 +497,11 @@ public class WindowFrameCloseEventArgs : EventArgs /// public FrameCloseOption CloseOption { get; private set; } + /// + /// Gets or sets a value indicating whether the event should be canceled. + /// + public bool Cancel { get; set; } + /// /// Creates an event argument instance with the initial close option. /// From 09d217a5e257c515e6aa9be514e3cd93e88af2f5 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 20:48:34 +1000 Subject: [PATCH 02/21] FrameShow is not flagged. --- .../Windows/WindowFrame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs index df0af42..649557f 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs @@ -1,4 +1,4 @@ -// ================================================================================================ +// ================================================================================================ // WindowFrame.cs // // Created: 2008.07.02, by Istvan Novak (DeepDiver) @@ -440,7 +440,6 @@ public enum FramePosition /// /// Specifies options when the show state of a window frame changes. /// - [Flags] public enum FrameShow { /// Reason unknown From a6ae3f79d76cfd24c3846f19c41f341c1c14d6c4 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 20:51:19 +1000 Subject: [PATCH 03/21] Removed duplicate and obsolete enum values from FrameShow. --- .../Windows/WindowFrame.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs index 649557f..804692a 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs @@ -442,12 +442,8 @@ public enum FramePosition /// public enum FrameShow { - /// Reason unknown - Unknown = 0, - /// Obsolete; use WinHidden. - Hidden = __FRAMESHOW.FRAMESHOW_Hidden, /// Window (tabbed or otherwise) is hidden. - WinHidden = __FRAMESHOW.FRAMESHOW_WinHidden, + Hidden = __FRAMESHOW.FRAMESHOW_WinHidden, /// A nontabbed window is made visible. Shown = __FRAMESHOW.FRAMESHOW_WinShown, /// A tabbed window is activated (made visible). From b8b3d10d4bcb55e7345e3597589a6029aa38b097 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 20:52:03 +1000 Subject: [PATCH 04/21] Added comments explaining what the equivalent __SHOWFRAME enum value is. --- .../Windows/WindowFrame.cs | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs index 804692a..9ea41c8 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs @@ -232,7 +232,7 @@ public async Task IsOnScreenAsync() public async Task GetDocumentViewAsync() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - + // Force the loading of a document that may be pending initialization. // See https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/delayed-document-loading _frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out _); @@ -442,23 +442,50 @@ public enum FramePosition /// public enum FrameShow { - /// Window (tabbed or otherwise) is hidden. + /// + /// Window (tabbed or otherwise) is hidden. + /// Equivalent to . + /// Hidden = __FRAMESHOW.FRAMESHOW_WinHidden, - /// A nontabbed window is made visible. + /// + /// A non-tabbed window is made visible. + /// Equivalent to . + /// Shown = __FRAMESHOW.FRAMESHOW_WinShown, - /// A tabbed window is activated (made visible). + /// + /// A tabbed window is activated (made visible). + /// Equivalent to . + /// TabActivated = __FRAMESHOW.FRAMESHOW_TabActivated, - /// A tabbed window is deactivated. + /// + /// A tabbed window is deactivated. + /// Equivalent to . + /// TabDeactivated = __FRAMESHOW.FRAMESHOW_TabDeactivated, - /// Window is restored to normal state. + /// + /// Window is restored to normal state. + /// Equivalent to . + /// Restored = __FRAMESHOW.FRAMESHOW_WinRestored, - /// Window is minimized. + /// + /// Window is minimized. + /// Equivalent to . + /// Minimized = __FRAMESHOW.FRAMESHOW_WinMinimized, - /// Window is maximized. + /// + /// Window is maximized. + /// Equivalent to . + /// Maximized = __FRAMESHOW.FRAMESHOW_WinMaximized, - /// Multi-instance tool window destroyed. + /// + /// Multi-instance tool window destroyed. + /// Equivalent to . + /// DestroyMultipleInstance = __FRAMESHOW.FRAMESHOW_DestroyMultInst, - /// Autohidden window is about to slide into view. + /// + /// Auto-hidden window is about to slide into view. + /// Equivalent to . + /// AutoHideSlideBegin = __FRAMESHOW.FRAMESHOW_AutoHideSlideBegin } From 4e04081ff8ff84b8a545ec8b5e19c9aad8f15dc2 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 21:03:56 +1000 Subject: [PATCH 05/21] Extended FrameShow enum to include values from __FRAMESHOW2, __FRAMESHOW3 and __FRAMESHOW4. --- .../Windows/WindowFrame.cs | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs index 9ea41c8..3cc5b18 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowFrame.cs @@ -1,4 +1,4 @@ -// ================================================================================================ +// ================================================================================================ // WindowFrame.cs // // Created: 2008.07.02, by Istvan Novak (DeepDiver) @@ -440,6 +440,13 @@ public enum FramePosition /// /// Specifies options when the show state of a window frame changes. /// + /// + /// This combines the values from + /// , + /// , + /// and + /// . + /// public enum FrameShow { /// @@ -486,7 +493,32 @@ public enum FrameShow /// Auto-hidden window is about to slide into view. /// Equivalent to . /// - AutoHideSlideBegin = __FRAMESHOW.FRAMESHOW_AutoHideSlideBegin + AutoHideSlideBegin = __FRAMESHOW.FRAMESHOW_AutoHideSlideBegin, + /// + /// A window is about to be hidden. + /// Equivalent to . + /// + BeforeHidden = __FRAMESHOW2.FRAMESHOW_BeforeWinHidden, + /// + /// Auto-hidden window is finished sliding into view. + /// Equivalent to . + /// + AutoHideSlideEnd = __FRAMESHOW2.FRAMESHOW_AutoHideSlideEnd, + /// + /// A window is activated (made visible). + /// Equivalent to . + /// + Activated = __FRAMESHOW3.FRAMESHOW_WinActivated, + /// + /// The window's inner content received keyboard focus. + /// Equivalent to . + /// + ContentGotFocus = __FRAMESHOW4.FRAMESHOW_WinContentGotFocus, + /// + /// The window's inner content lost keyboard focus. + /// Equivalent to . + /// + ContentLostFocus = __FRAMESHOW4.FRAMESHOW_WinContentLostFocus } /// @@ -573,4 +605,16 @@ public WindowFrameDockChangedEventArgs(Rectangle position, bool docked) Docked = docked; } } + + +#if VS14 + /// + /// __FRAMESHOW4 was first defined in Visual Studio 15. + /// + internal enum __FRAMESHOW4 + { + FRAMESHOW_WinContentGotFocus = 13, + FRAMESHOW_WinContentLostFocus + } +#endif } \ No newline at end of file From 5cf38c2a6698ab8eff2de10bb36204c6826f9e62 Mon Sep 17 00:00:00 2001 From: reduckted Date: Sun, 19 Feb 2023 11:51:09 +1000 Subject: [PATCH 06/21] Updated the nuget.org package source URL. See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file#package-source-sections --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acbdce5..59e8389 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Create a file called **nuget.config** in your solution folder, and paste in the - + From ae290851c6604ad67822b660fdde3eda7b6eef17 Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 22:07:11 +1000 Subject: [PATCH 07/21] Added the ability to get a WindowFrame wrapper for a tool window's frame. --- .../Windows/ToolkitToolWindowPane.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs index eea8866..84d0239 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/ToolkitToolWindowPane.cs @@ -1,5 +1,6 @@ using System; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; namespace Community.VisualStudio.Toolkit { @@ -10,6 +11,8 @@ namespace Community.VisualStudio.Toolkit public abstract class ToolkitToolWindowPane : ToolWindowPane { private bool _isInitialized; + private WindowFrame? _windowFrame; + private bool _isWindowFrameAvailable; /// protected override void Initialize() @@ -22,5 +25,72 @@ protected override void Initialize() internal bool IsInitialized => _isInitialized; internal event EventHandler? Initialized; + + /// + public override void OnToolWindowCreated() + { + base.OnToolWindowCreated(); + _isWindowFrameAvailable = true; + WindowFrameAvailable?.Invoke(this, EventArgs.Empty); + } + + /// + /// Indicates whether the method can be called. + /// + public bool IsWindowFrameAvailable => _isWindowFrameAvailable; + + /// + /// Raised when Visual Studio creates the tool window's frame. + /// The property can be accessed from this point onwards. + /// + public event EventHandler? WindowFrameAvailable; + + /// + /// Gets the tool window's window frame. + /// + /// This method can only be called after Visual Studio has created the window frame. + /// You can detect this in various ways: + /// + /// + /// Override the method. + /// When this method is called, the window frame will be available. + /// + /// + /// Listen for the event. + /// When the event is raised, the window frame will be available. + /// + /// + /// Check the property. + /// + /// + /// + /// + /// The window frame is not available. + protected WindowFrame GetWindowFrame() + { + if (_windowFrame is null) + { + // The `Frame` property has to be set by Visual Studio, so it might + // be null at this point. It's also typed as an `object` even though + // internally it's stored as an `IVsWindowFrame`, so we can use + // type matching to both cast and confirm that it's not null. + if (Frame is IVsWindowFrame vsWindowFrame) + { + // We could create the WindowFrame in `OnToolWindowCreated`, + // but we delay-create it for two reasons: + // 1. It may not ever be needed. + // 2. If a derived class also overrides `OnToolWindowCreated`, then the window + // frame would only be available after it called `base.OnToolWindowCreated()`. + // Delay-creating it means that it will be available before that call is made. + _windowFrame = new WindowFrame(vsWindowFrame); + } + else + { + throw new InvalidOperationException("The tool window's frame is not available yet."); + } + } + + return _windowFrame; + } } } From 63fc5c347f685a52ec1b3fbc7cb36c9228c8ee1e Mon Sep 17 00:00:00 2001 From: reduckted Date: Thu, 16 Feb 2023 22:07:43 +1000 Subject: [PATCH 08/21] Added an example of using the tool window's GetWindowFrame() method. --- demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs b/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs index 8bcd501..13348a6 100644 --- a/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs +++ b/demo/VSSDK.TestExtension/ToolWindows/RunnerWindow.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.Design; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -30,6 +31,13 @@ public Pane() { BitmapImageMoniker = KnownMonikers.StatusInformation; ToolBar = new CommandID(PackageGuids.TestExtension, PackageIds.RunnerWindowToolbar); + WindowFrameAvailable += (_, _) => Debug.WriteLine("RunnerWindow frame is now available"); + } + + public override void OnToolWindowCreated() + { + base.OnToolWindowCreated(); + GetWindowFrame().OnShow += (_, args) => Debug.WriteLine($"RunnerWindow state changed: {args.Reason}"); } } } From 52d2365f87bad917c1f4475903dd8b5d4ae28e74 Mon Sep 17 00:00:00 2001 From: reduckted Date: Tue, 6 Jun 2023 21:11:21 +1000 Subject: [PATCH 09/21] Skipped the Newtonsoft.Json version when using the newer VS SDK. --- .../NewtonsoftJsonVersionCheck.targets | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/toolkit/nuget/build/imports/NewtonsoftJsonVersionCheck.targets b/src/toolkit/nuget/build/imports/NewtonsoftJsonVersionCheck.targets index 2ece1d1..e53f458 100644 --- a/src/toolkit/nuget/build/imports/NewtonsoftJsonVersionCheck.targets +++ b/src/toolkit/nuget/build/imports/NewtonsoftJsonVersionCheck.targets @@ -1,6 +1,6 @@ - + + + + @(CVST_NewtonsoftJsonReferenceVersion) - + @(CVST_VisualStudioSdkReferenceVersion) + + 17.5.33428.388 + $([MSBuild]::VersionGreaterThan('$(CVST_VisualStudioSdkVersion)', '$(CVST_NewtonsoftJsonSdkMaxVersion)')) + true + + + + true $([MSBuild]::VersionLessThanOrEquals('$(CVST_NewtonsoftJsonVersion)', '$(CVST_NewtonsoftJsonMaxVersion)')) true From 8969a29f0682963133f6545e6e4d87c05c33aa58 Mon Sep 17 00:00:00 2001 From: Robert van der Hulst Date: Fri, 23 Jun 2023 13:36:02 +0200 Subject: [PATCH 10/21] Updated some Guids: removed literals and replaced with a reference to the value from the Interop DLL --- .../Windows/WindowGuids.cs | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowGuids.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowGuids.cs index b4363b4..47a8fc9 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowGuids.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Windows/WindowGuids.cs @@ -1,23 +1,34 @@ -namespace Community.VisualStudio.Toolkit +using static EnvDTE.Constants; +namespace Community.VisualStudio.Toolkit { /// /// Provides GUIDs that are used to identify Visual Studio tool windows. /// public class WindowGuids { -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public const string MainWindow = "{9DDABE98-1D02-11D3-89A1-00C04F688DDE}"; + /// The Main window. + public const string MainWindow = vsWindowKindMainWindow; + /// The Document well public const string DocumentWell = "DocumentWell"; - public const string CallStack = "{0504FF91-9D61-11D0-A794-00A0C9110051}"; + /// The CallStack window. + public const string CallStack = vsWindowKindCallStack; - public const string Locals = "{4A18F9D0-B838-11D0-93EB-00A0C90F2734}"; + /// The Locals window. + public const string Locals = vsWindowKindLocals; - public const string Watch = "{90243340-BD7A-11D0-93EF-00A0C90F2734}"; + /// The Auto window. + public const string AutoLocals = vsWindowKindAutoLocals; - public const string Properties = "{EEFA5220-E298-11D0-8F78-00A0C9110057}"; -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member + /// The Watch window. + public const string Watch = vsWindowKindWatch; + + /// The Web Browser window. + public const string WebBrowser = vsWindowKindWebBrowser; + + /// The Properties window. + public const string Properties = vsWindowKindProperties; /// The Git Changes window introduced in Visual Studio 2019. public const string GitChanges = "{1C64B9C2-E352-428E-A56D-0ACE190B99A6}"; @@ -31,53 +42,53 @@ public class WindowGuids /// /// Provides a list of components that can be added to a project, typically through the Windows Forms designer. /// - public const string Toolbox = "{B1E99781-AB81-11D0-B683-00AA00A3EE26}"; + public const string Toolbox = vsext_wk_Toolbox; /// /// Provides a list of properties associated with the currently selected file or project in the Solution Explorer. /// - public const string PropertyBrowser = "{EEFA5220-E298-11D0-8F78-00A0C9110057}"; + public const string PropertyBrowser = vsWindowKindProperties; /// /// Provides a list of projects and their files in a solution. /// - public const string SolutionExplorer = "{3AE79031-E1BC-11D0-8F78-00A0C9110057}"; + public const string SolutionExplorer = vsWindowKindSolutionExplorer; /// Displays any output generated by a build process or a program being debugged. - public const string OutputWindow = "{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}"; + public const string OutputWindow = vsWindowKindOutput; /// Provides an outline of an HTML file. - public const string DocOutline = "{25F7E850-FFA1-11D0-B63F-00A0C922E851}"; + public const string DocOutline = vsWindowKindDocumentOutline; /// Provides a list of user-specified tasks to be accomplished for a project. - public const string TaskList = "{4A9B7E51-AA16-11D0-A8C5-00A0C921A4D2}"; + public const string TaskList = vsWindowKindTaskList; /// Provides a list of servers used for connecting to databases. - public const string ServerExplorer = "{74946827-37A0-11D2-A273-00C04F8EF4FF}"; + public const string ServerExplorer = vsWindowKindServerExplorer; /// Provides a list of macros available for use in the Visual Studio shell. - public const string MacroExplorer = "{07CD18B4-3BA1-11D2-890A-0060083196C6}"; + public const string MacroExplorer = vsWindowKindMacroExplorer; /// Provides links to help topics based on the current context. - public const string ContextWindow = "{66DBA47C-61DF-11D2-AA79-00C04F990343}"; + public const string ContextWindow = vsWindowKindDynamicHelp; /// Provides a list of all classes in a solution. - public const string ClassView = "{C9C0AE26-AA77-11D2-B3F0-0000F87570EE}"; + public const string ClassView = vsWindowKindClassView; /// Provides a list of resources available in a project. - public const string ResourceView = "{2D7728C2-DE0A-45B5-99AA-89B609DFDE73}"; + public const string ResourceView = vsWindowKindResourceView; /// Provides the ability to search for a symbol. - public const string ObjectSearchWindow = "{53024D34-0EF5-11D3-87E0-00C04F7971A5}"; + public const string ObjectSearchWindow = vsWindowKindFindSymbol; /// Provides a way to execute commands directly in the Visual Studio shell. - public const string CommandWindow = "{28836128-FC2C-11D2-A433-00C04F72D18A}"; + public const string CommandWindow = vsWindowKindCommandWindow; /// Provides the results of searching for a symbol. - public const string ObjectSearchResultsWindow = "{68487888-204A-11D3-87EB-00C04F7971A5}"; + public const string ObjectSearchResultsWindow = vsWindowKindFindSymbolResults; /// Provides a list of components installed on the system. - public const string ObjectBrowser = "{269A02DC-6AF8-11D3-BDC4-00C04F688E50}"; + public const string ObjectBrowser = vsWindowKindObjectBrowser; /// For internal use only. public const string BrowserDoc = "{6B8E94B4-0949-4D9C-A81F-C1B9B744185C}"; @@ -85,20 +96,20 @@ public class WindowGuids /// Provides the ability to search and replace on the currently opened file. public const string FindReplace = #if VS14 || VS15 - "{CF2DDC32-8CAD-11D2-9302-005345000000}" + vsWindowKindFindReplace #else "{6324226F-61B6-4F28-92EE-18D4B5FE1E48}" #endif ; - + /// Provides the ability to search files for a string. public const string FindAdvanced = "{E830EC50-C2B5-11D2-9375-0080C747D9A0}"; /// Provides a list of the search results. - public const string FindResults1 = "{0F887920-C2B6-11D2-9375-0080C747D9A0}"; + public const string FindResults1 = vsWindowKindFindResults1; /// Provides a second list of search results. - public const string FindResults2 = "{0F887921-C2B6-11D2-9375-0080C747D9A0}"; + public const string FindResults2 = vsWindowKindFindResults2; /// Provides a command prompt for PowerShell. public const string DeveloperPowerShell = "{D212F56B-C48A-434C-A121-1C5D80B59B9F}"; From d9f2a8421bd412e119985dcc2f80ec879006e565 Mon Sep 17 00:00:00 2001 From: Robert van der Hulst Date: Mon, 26 Jun 2023 18:12:49 +0200 Subject: [PATCH 11/21] Make sure only a single SameWordHighlighter is attached to a textbuffer. Each TextView will register its events itself with the tagger from the textbuffer and when the textview is closed then the events are unregistered again. The previous implementation was allocating way too many samewordhighlighters. Also changed the color for the same word highlighters in the example code. --- demo/VSSDK.TestExtension/MEF/HighlightWord.cs | 18 ++++++- .../MEF/SameWordHighlighterBase.cs | 51 +++++++++++-------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/demo/VSSDK.TestExtension/MEF/HighlightWord.cs b/demo/VSSDK.TestExtension/MEF/HighlightWord.cs index 9581009..1803635 100644 --- a/demo/VSSDK.TestExtension/MEF/HighlightWord.cs +++ b/demo/VSSDK.TestExtension/MEF/HighlightWord.cs @@ -4,9 +4,25 @@ using Community.VisualStudio.Toolkit; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text.Editor; - +using Microsoft.VisualStudio.Text.Classification; +using System.Windows.Media; namespace TestExtension.MEF { + + [Export(typeof(EditorFormatDefinition))] + [Name("MarkerFormatDefinition/HighlightWordFormatDefinition")] + [UserVisible(true)] + internal class HighlightWordFormatDefinition : MarkerFormatDefinition + { + public HighlightWordFormatDefinition() + { + this.BackgroundColor = Colors.LightBlue; + this.ForegroundColor = Colors.DarkBlue; + this.DisplayName = "Highlight Word"; + this.ZOrder = 5; + } + } + /// /// This class demonstrates a HighlightWord tagger for text files /// and it only highlights whole words starting with a Letter diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs index 0e34adc..a812f5e 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs @@ -49,9 +49,9 @@ public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where { ITextStructureNavigator? navigator = _textStructureNavigatorSelector?.GetTextStructureNavigator(textView.TextBuffer); - var tagger = textView.Properties.GetOrCreateSingletonProperty(() => + var tagger = buffer.Properties.GetOrCreateSingletonProperty(() => new SameWordHighlighterTagger(textView, buffer, _textSearchService, navigator, this)); - tagger.Counter += 1; + tagger.RegisterEvents(textView); return (ITagger)tagger; } @@ -62,10 +62,9 @@ internal class HighlightWordTag : TextMarkerTag public HighlightWordTag(string tagName) : base(tagName) { } } - internal class SameWordHighlighterTagger : ITagger, IDisposable + internal class SameWordHighlighterTagger : ITagger { internal int Counter; - private readonly ITextView _view; private readonly ITextBuffer _buffer; private readonly ITextSearchService? _textSearchService; private readonly ITextStructureNavigator? _textStructureNavigator; @@ -74,30 +73,52 @@ internal class SameWordHighlighterTagger : ITagger, IDisposabl private SnapshotSpan? _currentWord; private SnapshotPoint _requestedPoint; private bool _isDisposed; + private string _fileName=""; private readonly object _syncLock = new(); public SameWordHighlighterTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService? textSearchService, ITextStructureNavigator? textStructureNavigator, SameWordHighlighterBase tagger) { - _view = view; + _fileName = sourceBuffer.GetFileName(); + System.Diagnostics.Debug.WriteLine("Create new tagger for "+_fileName); _buffer = sourceBuffer; _textSearchService = textSearchService; _textStructureNavigator = textStructureNavigator; _tagger = tagger; _wordSpans = new NormalizedSnapshotSpanCollection(); _currentWord = null; - _view.Caret.PositionChanged += CaretPositionChanged; - _view.LayoutChanged += ViewLayoutChanged; Counter = 0; } + internal void RegisterEvents(ITextView textView) + { + + textView.Caret.PositionChanged += CaretPositionChanged; + textView.LayoutChanged += ViewLayoutChanged; + textView.Closed += TextView_Closed; + Counter += 1; + System.Diagnostics.Debug.WriteLine($"RegisterEvents {_fileName}: #{Counter} "); + } + internal void UnRegisterEvents(ITextView textView) + { + textView.Caret.PositionChanged -= CaretPositionChanged; + textView.LayoutChanged -= ViewLayoutChanged; + textView.Closed -= TextView_Closed; + Counter -= 1; + System.Diagnostics.Debug.WriteLine($"UnRegisterEvents {_fileName}: #{Counter} "); + } private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { if (e.NewSnapshot != e.OldSnapshot) { - UpdateAtCaretPosition(_view.Caret.Position); + var view = (ITextView)sender; + UpdateAtCaretPosition(view.Caret.Position); } } + private void TextView_Closed(object sender, EventArgs e) + { + UnRegisterEvents((ITextView)sender); + } private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { @@ -228,20 +249,6 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanCol } } - public void Dispose() - { - if (!_isDisposed) - { - this.Counter -= 1; - if (this.Counter == 0) - { - _view.Caret.PositionChanged -= CaretPositionChanged; - _view.LayoutChanged -= ViewLayoutChanged; - _isDisposed = true; - } - } - } - public event EventHandler? TagsChanged; } } From bd8e6288463513013dc8ddb1d583d44d4876949b Mon Sep 17 00:00:00 2001 From: Robert van der Hulst Date: Mon, 26 Jun 2023 18:15:34 +0200 Subject: [PATCH 12/21] Commented out debugging code --- .../MEF/SameWordHighlighterBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs index a812f5e..e1e02af 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/MEF/SameWordHighlighterBase.cs @@ -80,7 +80,7 @@ public SameWordHighlighterTagger(ITextView view, ITextBuffer sourceBuffer, IText ITextStructureNavigator? textStructureNavigator, SameWordHighlighterBase tagger) { _fileName = sourceBuffer.GetFileName(); - System.Diagnostics.Debug.WriteLine("Create new tagger for "+_fileName); + //System.Diagnostics.Debug.WriteLine("Create new tagger for "+_fileName); _buffer = sourceBuffer; _textSearchService = textSearchService; _textStructureNavigator = textStructureNavigator; @@ -97,7 +97,7 @@ internal void RegisterEvents(ITextView textView) textView.LayoutChanged += ViewLayoutChanged; textView.Closed += TextView_Closed; Counter += 1; - System.Diagnostics.Debug.WriteLine($"RegisterEvents {_fileName}: #{Counter} "); + //System.Diagnostics.Debug.WriteLine($"RegisterEvents {_fileName}: #{Counter} "); } internal void UnRegisterEvents(ITextView textView) { @@ -105,7 +105,7 @@ internal void UnRegisterEvents(ITextView textView) textView.LayoutChanged -= ViewLayoutChanged; textView.Closed -= TextView_Closed; Counter -= 1; - System.Diagnostics.Debug.WriteLine($"UnRegisterEvents {_fileName}: #{Counter} "); + //System.Diagnostics.Debug.WriteLine($"UnRegisterEvents {_fileName}: #{Counter} "); } private void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { From 1e8616bfd8e9b006c72e3b126dad8d57ffa407b6 Mon Sep 17 00:00:00 2001 From: reduckted Date: Wed, 16 Aug 2023 11:06:36 +1000 Subject: [PATCH 13/21] Replaced Moq with NSubstitute. --- ...nity.VisualStudio.Toolkit.UnitTests.csproj | 2 +- .../OptionModelPropertyWrapperTests.cs | 156 ++++++++++-------- 2 files changed, 90 insertions(+), 68 deletions(-) diff --git a/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/Community.VisualStudio.Toolkit.UnitTests.csproj b/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/Community.VisualStudio.Toolkit.UnitTests.csproj index 322905d..7777e81 100644 --- a/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/Community.VisualStudio.Toolkit.UnitTests.csproj +++ b/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/Community.VisualStudio.Toolkit.UnitTests.csproj @@ -19,8 +19,8 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/OptionModelPropertyWrapperTests.cs b/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/OptionModelPropertyWrapperTests.cs index 2e55ac9..a0f5547 100644 --- a/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/OptionModelPropertyWrapperTests.cs +++ b/test/toolkit/Community.VisualStudio.Toolkit.UnitTests/OptionModelPropertyWrapperTests.cs @@ -9,7 +9,8 @@ using System.Text; using FluentAssertions; using Microsoft.VisualStudio.Settings; -using Moq; +using NSubstitute; +using NSubstitute.Exceptions; using Xunit; using Xunit.Abstractions; @@ -599,21 +600,21 @@ private void SettingStoreTest_String(BaseOptionModel? overrideAssertEquality = null) where TOptMdl : BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); void SetupSettingsStoreGet(string valueToReturn) { - mock.Setup(x => x.GetString(collectionPath, propertyName)).Returns(valueToReturn); + mock.GetString(collectionPath, propertyName).Returns(valueToReturn); } - Action verifyGet = (int callCount, string failMessage) => - mock.Verify(x => x.GetString(collectionPath, propertyName), Times.Exactly(callCount), failMessage); - Action verifySet = (int callCount, string expectedValue, string failMessage) => - mock.Verify(x => x.SetString(collectionPath, propertyName, expectedValue), Times.Exactly(callCount), failMessage); + Action verifyGet = (int callCount) => + mock.Received(callCount).GetString(collectionPath, propertyName); + Action verifySet = (int callCount, string expectedValue) => + mock.Received(callCount).SetString(collectionPath, propertyName, expectedValue); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, verifyGet, verifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, verifyGet, verifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore, overrideAssertEquality); } @@ -646,22 +647,21 @@ private void SettingStoreTest_Int32(BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); - mock.Setup(x => x.SetInt32(collectionPath, propertyName, It.IsAny())); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); void SetupSettingsStoreGet(int valueToReturn) { - mock.Setup(x => x.GetInt32(collectionPath, propertyName)).Returns(valueToReturn); + mock.GetInt32(collectionPath, propertyName).Returns(valueToReturn); } - Action verifyGet = (int callCount, string failMessage) => - mock.Verify(x => x.GetInt32(collectionPath, propertyName), Times.Exactly(callCount), failMessage); - Action verifySet = (int callCount, int expectedValue, string failMessage) => - mock.Verify(x => x.SetInt32(collectionPath, propertyName, expectedValue), Times.Exactly(callCount), failMessage); + Action verifyGet = (int callCount) => + mock.Received(callCount).GetInt32(collectionPath, propertyName); + Action verifySet = (int callCount, int expectedValue) => + mock.Received(callCount).SetInt32(collectionPath, propertyName, expectedValue); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, verifyGet, verifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, verifyGet, verifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore); } @@ -694,22 +694,21 @@ private void SettingStoreTest_UInt32(BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); - mock.Setup(x => x.SetUInt32(collectionPath, propertyName, It.IsAny())); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); void SetupSettingsStoreGet(uint valueToReturn) { - mock.Setup(x => x.GetUInt32(collectionPath, propertyName)).Returns(valueToReturn); + mock.GetUInt32(collectionPath, propertyName).Returns(valueToReturn); } - Action verifyGet = (int callCount, string failMessage) => - mock.Verify(x => x.GetUInt32(collectionPath, propertyName), Times.Exactly(callCount), failMessage); - Action verifySet = (int callCount, uint expectedValue, string failMessage) => - mock.Verify(x => x.SetUInt32(collectionPath, propertyName, expectedValue), Times.Exactly(callCount), failMessage); + Action verifyGet = (int callCount) => + mock.Received(callCount).GetUInt32(collectionPath, propertyName); + Action verifySet = (int callCount, uint expectedValue) => + mock.Received(callCount).SetUInt32(collectionPath, propertyName, expectedValue); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, verifyGet, verifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, verifyGet, verifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore); } @@ -742,22 +741,21 @@ private void SettingStoreTest_Int64(BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); - mock.Setup(x => x.SetInt64(collectionPath, propertyName, It.IsAny())); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); void SetupSettingsStoreGet(long valueToReturn) { - mock.Setup(x => x.GetInt64(collectionPath, propertyName)).Returns(valueToReturn); + mock.GetInt64(collectionPath, propertyName).Returns(valueToReturn); } - Action verifyGet = (int callCount, string failMessage) => - mock.Verify(x => x.GetInt64(collectionPath, propertyName), Times.Exactly(callCount), failMessage); - Action verifySet = (int callCount, long expectedValue, string failMessage) => - mock.Verify(x => x.SetInt64(collectionPath, propertyName, expectedValue), Times.Exactly(callCount), failMessage); + Action verifyGet = (int callCount) => + mock.Received(callCount).GetInt64(collectionPath, propertyName); + Action verifySet = (int callCount, long expectedValue) => + mock.Received(callCount).SetInt64(collectionPath, propertyName, expectedValue); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, verifyGet, verifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, verifyGet, verifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore); } @@ -790,22 +788,21 @@ private void SettingStoreTest_UInt64(BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); - mock.Setup(x => x.SetUInt64(collectionPath, propertyName, It.IsAny())); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); void SetupSettingsStoreGet(ulong valueToReturn) { - mock.Setup(x => x.GetUInt64(collectionPath, propertyName)).Returns(valueToReturn); + mock.GetUInt64(collectionPath, propertyName).Returns(valueToReturn); } - Action verifyGet = (int callCount, string failMessage) => - mock.Verify(x => x.GetUInt64(collectionPath, propertyName), Times.Exactly(callCount), failMessage); - Action verifySet = (int callCount, ulong expectedValue, string failMessage) => - mock.Verify(x => x.SetUInt64(collectionPath, propertyName, expectedValue), Times.Exactly(callCount), failMessage); + Action verifyGet = (int callCount) => + mock.Received(callCount).GetUInt64(collectionPath, propertyName); + Action verifySet = (int callCount, ulong expectedValue) => + mock.Received(callCount).SetUInt64(collectionPath, propertyName, expectedValue); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, verifyGet, verifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, verifyGet, verifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore); } @@ -842,13 +839,14 @@ private void SettingStoreTest_MemoryStream(BaseOptionMod Action? overrideAssertEquality = null) where TOptMdl : BaseOptionModel, new() { - Mock mock = new(); - mock.Setup(x => x.CollectionExists(collectionPath)).Returns(true); - mock.Setup(x => x.PropertyExists(collectionPath, propertyName)).Returns(true); + WritableSettingsStore mock = Substitute.For(); + mock.CollectionExists(collectionPath).Returns(true); + mock.PropertyExists(collectionPath, propertyName).Returns(true); List setInvocations = new(); - mock.Setup(x => x.SetMemoryStream(collectionPath, propertyName, It.IsAny())).Callback((string _, string __, MemoryStream stream) => + mock.When((x) => x.SetMemoryStream(collectionPath, propertyName, Arg.Any())).Do((args) => { + MemoryStream stream = args.ArgAt(2); if (stream == null) setInvocations.Add(null); else @@ -857,15 +855,15 @@ private void SettingStoreTest_MemoryStream(BaseOptionMod void SetupSettingsStoreGet(byte[] valueToReturn) { - mock.Setup(x => x.GetMemoryStream(collectionPath, propertyName)).Returns(new MemoryStream(valueToReturn)); + mock.GetMemoryStream(collectionPath, propertyName).Returns(new MemoryStream(valueToReturn)); } - void VerifyGet(int callCount, string failMessage) + void VerifyGet(int callCount) { - mock.Verify(x => x.GetMemoryStream(collectionPath, propertyName), Times.Exactly(callCount), failMessage); + mock.Received(callCount).GetMemoryStream(collectionPath, propertyName); } - void VerifySet(int callCount, byte[] expectedValue, string because) + void VerifySet(int callCount, byte[] expectedValue) { int matchingCount = 0; foreach (byte[]? buffer in setInvocations) @@ -877,13 +875,13 @@ void VerifySet(int callCount, byte[] expectedValue, string because) else if (buffer.SequenceEqual(expectedValue)) matchingCount++; } - callCount.Should().Be(matchingCount, because); + callCount.Should().Be(matchingCount); } _output.WriteLine("expectedDefaultValueInStore (as string): {0}", System.Text.Encoding.UTF8.GetString(expectedDefaultValueInStore)); _output.WriteLine("expectedAlternateValueInStore (as string): {0}", System.Text.Encoding.UTF8.GetString(expectedAlternateValueInStore)); - SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock.Object, SetupSettingsStoreGet, VerifyGet, VerifySet, + SettingStoreTest(objUt, propertyUt, getProperty, setProperty, mock, SetupSettingsStoreGet, VerifyGet, VerifySet, defaultValueProperty, expectedDefaultValueInStore, expectedAlternateValueProperty, expectedAlternateValueInStore, overrideAssertEquality); } @@ -899,9 +897,9 @@ void VerifySet(int callCount, byte[] expectedValue, string because) /// Method to configure the settings store to return the provided value when the get method on the is /// called with the expected parameters. Signature is TStorageType valueToReturn /// Method to assert the settings store get method on the was - /// called. Signature is int expectedCallCount, string because. + /// called. Signature is int expectedCallCount. /// Method to assert the settings store set method on the was - /// called with the expected parameters. Signature is int expectedCallCount, TStorageType expectedValue, string because + /// called with the expected parameters. Signature is int expectedCallCount, TStorageType expectedValue /// Prior to test, the property will be set to this value using . /// This is the expected value of the property after Load returns when the setting store get /// method is configured to return . Must be different @@ -919,7 +917,7 @@ void VerifySet(int callCount, byte[] expectedValue, string because) /// Load operations rather than the standard assertions. Signature is TestValueType valueBeingTested, /// TPropertyType expected, TPropertyType actual, string because private void SettingStoreTest(BaseOptionModel objUt, PropertyInfo propertyUt, Func getProperty, Action setProperty, - WritableSettingsStore settingsStore, Action setupSettingsStoreGet, Action verifySettingsStoreGetWasCalled, Action verifySettingsStoreSetWasCalled, + WritableSettingsStore settingsStore, Action setupSettingsStoreGet, Action verifySettingsStoreGetWasCalled, Action verifySettingsStoreSetWasCalled, TPropertyType? defaultValueProperty, TStorageType expectedDefaultValueInStore, TPropertyType? expectedAlternateValueProperty, TStorageType expectedAlternateValueInStore, Action? overrideAssertEquality = null) @@ -941,7 +939,7 @@ private void SettingStoreTest(BaseOptionMo // Test save of default value bool saveMethodResult = uut.Save(objUt, settingsStore); - verifySettingsStoreSetWasCalled(1, expectedDefaultValueInStore, "because Save with the defaultValueProperty should result" + + VerifySet(1, expectedDefaultValueInStore, "because Save with the defaultValueProperty should result " + "in the settings store set method being called with expectedDefaultValueInStore"); saveMethodResult.Should().BeTrue("because we expect Save to return true for saving default value to store."); @@ -956,11 +954,11 @@ private void SettingStoreTest(BaseOptionMo else overrideAssertEquality(TestValueType.Alternate, expectedAlternateValueProperty, actualPropertyValue, propertyValueLoadMismatchBecause); loadMethodResult.Should().BeTrue("because we expect Load of the alternate value to report success when the property was successfully set."); - verifySettingsStoreGetWasCalled(1, "because the proper Settings Store Get method should be called during load for the alternate value."); + VerifyGet(1, "because the proper Settings Store Get method should be called during load for the alternate value."); // Test save process. Verify expected Settings Store Set method was called with proper value. saveMethodResult = uut.Save(objUt, settingsStore); - verifySettingsStoreSetWasCalled(1, expectedAlternateValueInStore, "because Save with the expectedAlternateValueProperty should result" + + VerifySet(1, expectedAlternateValueInStore, "because Save with the expectedAlternateValueProperty should result " + "in the settings store set method being called with expectedAlternateValueInStore"); saveMethodResult.Should().BeTrue("because we expect Save to return true for saving alternate value to store."); @@ -974,8 +972,32 @@ private void SettingStoreTest(BaseOptionMo actualPropertyValue.Should().BeEquivalentTo(defaultValueProperty, propertyValueLoadMismatchBecause); else overrideAssertEquality(TestValueType.Default, defaultValueProperty, actualPropertyValue, propertyValueLoadMismatchBecause); - verifySettingsStoreGetWasCalled(2, "because the proper Settings Store Get method should be called again during load for the default value."); + VerifyGet(2, "because the proper Settings Store Get method should be called again during load for the default value."); loadMethodResult.Should().BeTrue("because we expect Load of the default value to report success when the property was successfully set."); + + void VerifyGet(int expectedCallCount, string because) + { + try + { + verifySettingsStoreGetWasCalled(expectedCallCount); + } + catch (ReceivedCallsException ex) + { + throw new ReceivedCallsException($"{ex.Message} {because}", ex); + } + } + + void VerifySet(int expectedCallCount, TStorageType expectedValue, string because) + { + try + { + verifySettingsStoreSetWasCalled(expectedCallCount, expectedValue); + } + catch (ReceivedCallsException ex) + { + throw new ReceivedCallsException($"{ex.Message} {because}", ex); + } + } } #endregion Test Helper Methods and Main Test Method From 9c6d082d9ba674444cd901e382f2b337d23c68ed Mon Sep 17 00:00:00 2001 From: Mads Kristensen Date: Wed, 16 Aug 2023 14:32:28 -0700 Subject: [PATCH 14/21] Added REPL and OUTPUT to ContentTypes.cs --- .../Helpers/ContentTypes.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/ContentTypes.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/ContentTypes.cs index 94a4f34..d23c8de 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/ContentTypes.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/ContentTypes.cs @@ -24,6 +24,8 @@ public class ContentTypes public const string Json = "JSON"; public const string Xaml = "XAML"; public const string Xml = "XML"; + public const string Repl = "REPL"; + public const string Output = "Output"; #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } } From 94f0e3c3e0134884cae44c625b4e39da602faaca Mon Sep 17 00:00:00 2001 From: talanc Date: Sat, 26 Aug 2023 21:55:33 +1000 Subject: [PATCH 15/21] Update DocumentView.cs Fixed typo. --- .../Documents/DocumentView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Documents/DocumentView.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Documents/DocumentView.cs index 5e1e626..6994881 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Documents/DocumentView.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Documents/DocumentView.cs @@ -42,7 +42,7 @@ internal DocumentView(IVsWindowFrame nativeFrame) public WindowFrame? WindowFrame { get; } /// - /// The text view loaded int he window frame. + /// The text view loaded in the window frame. /// public IWpfTextView? TextView { get; } From 3335a42794342193e1017089b4f817a7ca2425ab Mon Sep 17 00:00:00 2001 From: Tony Hallett Date: Tue, 17 Oct 2023 09:34:50 +0100 Subject: [PATCH 16/21] Correct doc comments --- .../ExtensionMethods/AsyncPackageExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs index 90d6167..b658a30 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs @@ -49,7 +49,7 @@ public static async Task> RegisterCommandsAsync(this AsyncPa } /// - /// Automatically calls the method for every command that has the applied. + /// Automatically calls the method for every BaseToolWindow<> in the package or provided assemblies. /// /// /// From 20bbdbcb0ecba73bb9a6064fc826b1176caba109 Mon Sep 17 00:00:00 2001 From: reduckted Date: Wed, 1 Nov 2023 20:36:15 +1000 Subject: [PATCH 17/21] Change RegisterToolWindows to be ToolkitPackage extension method. --- .../AsyncPackageExtensions.cs | 29 ------------- .../ToolkitPackageExtensions.cs | 43 +++++++++++++++++++ .../VSSDK.Helpers.Shared.projitems | 1 + 3 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/ToolkitPackageExtensions.cs diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs index b658a30..4726c05 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/AsyncPackageExtensions.cs @@ -47,34 +47,5 @@ public static async Task> RegisterCommandsAsync(this AsyncPa } return commands; } - - /// - /// Automatically calls the method for every BaseToolWindow<> in the package or provided assemblies. - /// - /// - /// - /// - public static void RegisterToolWindows(this AsyncPackage package, params Assembly[] assemblies) - { - List assembliesList = assemblies.ToList(); - Assembly packageAssembly = package.GetType().Assembly; - if (!assembliesList.Contains(packageAssembly)) - assembliesList.Add(packageAssembly); - - Type baseToolWindowType = typeof(BaseToolWindow<>); - IEnumerable toolWindowTypes = assembliesList.SelectMany(x => x.GetTypes()) - .Where(x => - !x.IsAbstract - && x.IsAssignableToGenericType(baseToolWindowType)); - - foreach (Type? toolWindowtype in toolWindowTypes) - { - MethodInfo initializeMethod = toolWindowtype.GetMethod( - "Initialize", - BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); - - initializeMethod.Invoke(null, new object[] { package }); - } - } } } diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/ToolkitPackageExtensions.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/ToolkitPackageExtensions.cs new file mode 100644 index 0000000..c5aefa6 --- /dev/null +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/ExtensionMethods/ToolkitPackageExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.VisualStudio.Shell; + +namespace Community.VisualStudio.Toolkit +{ + /// + /// Extensions for an + /// + public static class ToolkitPackageExtensions + { + /// + /// Automatically calls the + /// method for every in the package or provided assemblies. + /// + /// The package that contains the tool windows to register. + /// The additional assemblies to look for tool windows in. + public static void RegisterToolWindows(this ToolkitPackage package, params Assembly[] assemblies) + { + List assembliesList = assemblies.ToList(); + Assembly packageAssembly = package.GetType().Assembly; + if (!assembliesList.Contains(packageAssembly)) + assembliesList.Add(packageAssembly); + + Type baseToolWindowType = typeof(BaseToolWindow<>); + IEnumerable toolWindowTypes = assembliesList.SelectMany(x => x.GetTypes()) + .Where(x => + !x.IsAbstract + && x.IsAssignableToGenericType(baseToolWindowType)); + + foreach (Type? toolWindowtype in toolWindowTypes) + { + MethodInfo initializeMethod = toolWindowtype.GetMethod( + "Initialize", + BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + + initializeMethod.Invoke(null, new object[] { package }); + } + } + } +} diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/VSSDK.Helpers.Shared.projitems b/src/toolkit/Community.VisualStudio.Toolkit.Shared/VSSDK.Helpers.Shared.projitems index e5c6c98..b1eaf39 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/VSSDK.Helpers.Shared.projitems +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/VSSDK.Helpers.Shared.projitems @@ -43,6 +43,7 @@ + From 2015c678413b78b57d5bf61e5a52eff7ce3c74e1 Mon Sep 17 00:00:00 2001 From: Matthias Voigt Date: Fri, 10 Nov 2023 15:33:15 +0100 Subject: [PATCH 18/21] Added Write without new line to OutputWindowPane Added methods void Write(string value); async Task WriteAsync(string value); Moved logic from WriteLineAsync to WriteAsync --- .../Helpers/OutputWindowPane.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs index 4a24c2f..b9505f7 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs @@ -236,6 +236,18 @@ public void WriteLine(string value) }); } + /// + /// Writes the given text to the Output window pane. + /// + /// The text value to write. + public void Write(string value) + { + ThreadHelper.JoinableTaskFactory.Run(async () => + { + await WriteAsync(value); + }); + } + /// /// Writes a new line to the Output window pane. /// @@ -249,6 +261,15 @@ public Task WriteLineAsync() /// /// The text value to write. May be an empty string, in which case a newline is written. public async Task WriteLineAsync(string value) + { + await WriteAsync(value + Environment.NewLine); + } + + /// + /// Writes the given text to the Output window pane. + /// + /// The text value to write. May be an empty string, in which case a newline is written. + public async Task WriteAsync(string value) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -261,11 +282,11 @@ public async Task WriteLineAsync(string value) if (_pane is IVsOutputWindowPaneNoPump nopump) { - nopump.OutputStringNoPump(value + Environment.NewLine); + nopump.OutputStringNoPump(value); } else { - ErrorHandler.ThrowOnFailure(_pane.OutputStringThreadSafe(value + Environment.NewLine)); + ErrorHandler.ThrowOnFailure(_pane.OutputStringThreadSafe(value)); } } From f30bbc56f50d66fcf0fe132ff75ed47990d3106a Mon Sep 17 00:00:00 2001 From: Matthias Voigt Date: Mon, 13 Nov 2023 11:12:51 +0100 Subject: [PATCH 19/21] Fixed param documentation for OutputWindowPane.WriteAsync(string value) --- .../Helpers/OutputWindowPane.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs index b9505f7..498518b 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Helpers/OutputWindowPane.cs @@ -268,7 +268,7 @@ public async Task WriteLineAsync(string value) /// /// Writes the given text to the Output window pane. /// - /// The text value to write. May be an empty string, in which case a newline is written. + /// The text value to write. public async Task WriteAsync(string value) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); From 2fac3c44fdeff50c93908fe943b34ab194e340e3 Mon Sep 17 00:00:00 2001 From: Mads Kristensen Date: Tue, 6 Feb 2024 10:07:52 -0800 Subject: [PATCH 20/21] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d67a29f..65233ee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ deploy: - provider: NuGet artifact: /package/ api_key: - secure: 4z/fHvSKQx2UG4a1aMzIN53MmzruQxIFtp91QzSAcSoEfuvNef7nDM2n8iBduo6u + secure: TCK4iz4NnVAK6iAZbpocgig7CBxDTywbtFXcj61NfCr2aEHkT1mPuSj7/yLEkkp9 on: branch: master appveyor_repo_commit_message_extended: /\[release\]/ From 56480b635352118905f0ed2b2f67de7038f117b4 Mon Sep 17 00:00:00 2001 From: Robert van der Hulst Date: Thu, 15 Aug 2024 10:35:27 +0200 Subject: [PATCH 21/21] QueryStatus is used to enable commands that are not enabled by default, such as Goto Definition in the Sourcecode Editor --- .../Commands/Commands.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Commands/Commands.cs b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Commands/Commands.cs index a7e3072..b7241b4 100644 --- a/src/toolkit/Community.VisualStudio.Toolkit.Shared/Commands/Commands.cs +++ b/src/toolkit/Community.VisualStudio.Toolkit.Shared/Commands/Commands.cs @@ -140,6 +140,11 @@ public CommandInterceptor(CommandID cmd, Func func) public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + if (prgCmds[0].cmdID == _cmd.ID) + { + prgCmds[0].cmdf = (uint)OLECMDF.OLECMDF_ENABLED | (uint)OLECMDF.OLECMDF_SUPPORTED; + return VSConstants.S_OK; + } return (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED; }