Showing posts with label VSIX. Show all posts
Showing posts with label VSIX. Show all posts

Monday, June 15, 2020

Never set 'Copy To Output Directory' to 'Copy always'

What if there was a simple setting that could save you hours?

Visual Studio Properties window showing the "Copy to Output Directory" setting

I thought there was something wrong with the Test Explorer.
I'd make a change in a configuration file that was used by some of my automated tests. Then I'd run all the tests again to see if that broke/fixed anything.
Only, it would take a couple of minutes - before the tests even started! Because the code was being recompiled first.
I would get frustrated.
I would lose my flow.
It was not an enjoyable or productive situation to be in.

And, I thought the culprit was the test harness. Why was it triggering a rebuild of the code? And a rescanning of the built assemblies to look for tests? When nothing in the code had changed?
All that was changed was an external file that was referenced by some of the tests.

It turns out, the problem was due to my lack of understanding of how MSBuild works and how the value of the Copy to Output Directory property is used.

---

For years I've been concerned by the number of times I've heard developers told to set the value of the Copy to Output Directory property (to a value other than "Do not copy") for files that have no need to be copied to the output directory. It seemed like a waste of effort. If the file wasn't needed in the output directory, why waste effort (admittedly--not a lot of effort) copying files around? Ultimately, this didn't seem all that important. I found more important things to focus on and stopped worrying about other people copying files unnecessarily.
Maybe I shouldn't.
Maybe I should have actually learned more about the setting. It may have helped me sooner.

---

I lived with it, taking a while to rebuild and then run my tests. Until I couldn't.
A few weeks ago, I reached my breaking point. I couldn't take it anymore. I would find out why my code was being rebuilt unnecessarily. Even if it took days, I would be better off in the long run to address this delay. If it was something I was doing, I'd fix it. If it was a genuine bug in the tools, I would find out where and raise it accordingly.

You're smart enough to know that I wouldn't have gotten this far if I didn't have a conclusion to this story. I know you've also figured out from the title of this post what the solution was. Anyway...

It took me the best part of the day reading through diagnostic build logs and testing me to the limits of my web-searching abilities but I got there in the end.
The "culprits" were files with the "Copy to Output Directory" set to "Copy always".

Ok, but why?

That's a very good question, and one I asked too.

It turns out that the behavior is due to the way this value is interpreted.
If you know that you always want the latest version of the file in the output directory, under some conditions, it can be quicker (and easier) to copy the file than spend time determining if the files are different.
That's good, but why does it trigger a rebuild? Why not just copy the file built previously?
Because the file built previously may have been modified (or created) by the build. The build process interprets "Copy Always" to mean "copy the file once the build has completed, so it's the most recent version." Because the build process may modify the file, it must rebuild the project to ensure that the file is the latest version. This is easier than trying to work out if the file will be changed by the build process before it is run. (If that's possible at all.)

So, to make that clear:
- "Copy always" means "make sure I always have the most up to date version."
- Because the build process may modify the file, the build process must be run to make sure you have the most up to date version of that file.

As an alternative, "Copy if newer" won't force a rebuild. The argument for why this is the case is (in my opinion) a bit vague, but it avoids those unnecessary builds, and that's what I'm interested in here.


Given how it works, why might you ever want to use "Copy Always"?

I don't know an official reason, but the only one I could come up with is if you needed a file to have a timestamp that corresponds to the build time. That's it.
I can think of no other reason to use the setting "Copy always." Please share if you have another.



Ok. So, what can you do to make sure you're not using the "Copy always" setting?

There are three options.
  1. You could step through every file and check the property. - Very slow. Very tedious, and likely you might miss something.
  2. Open the project file in a text (or XML) editor and check or make changes there as necessary. - Slightly easier but a hassle if you have lots of projects and not something you'll want to do repeatedly.
  3. Install this extension, and it will check every project for you whenever you open one. If it finds a file with the dreaded "Copy always" setting, it will let you know in the output window. It also adds a context menu entry on the project file to fix (change to "copy if newer") all files in that project.


Partial screenshot of entry in context menu


Please install this Visual Studio extension and let me know how you find it or if there are any scenarios it doesn't handle.

.


Was the above useful?
Did you learn anything?
Have you changed a project (or projects) you work on because of this?
Have you thought about how much time this can save you? How much time is spent on each unnecessary build?
How many times each day is your code rebuilt when nothing has changed? How many days will you spend (have you spent) working on that code? How many people are/will working/work on that code-base?
Could it add up to hours?
And how much is an hour worth to you? Or the company you work for?
Isn't that at least worth buying me a coffee to say thank you?
While we can't do it in person, you can do it virtually via https://www.buymeacoffee.com/mrlacey.
I'll be very grateful for whatever you can contribute.
Or, you could sponsor me to continue to create tools like the extension mentioned above by becoming a sponsor on GitHub.

Wednesday, May 27, 2020

My first Visual Studio Code extension - Git Status Backgrounds (GitStatusBg)

tldr: get it from here and try it out.

I was part of an email discussion about changes that could be added or made to Windows to make it better for developers.
Someone said they wished that Visual Studio (VS) and Visual Studio Code (VSCode) could use different color backgrounds for different projects in a solution.

I know how to build an extension that identifies files from different projects and how to change the background color of the editor, so I reached out to them to understand exactly what they were after.

Upon reflection, they decided that what would help them the most would be if VSCode could better indicate the git status of the file they were working on. They were writing docs and each stage of the documentation was a new file that built upon what was their already. For each new stage, they'd add a new file that was based on the previous step and make the changes there. The problem was that having lots of similar files meant that it was easy to accidentally change the wrong one.
What they wanted was a way to easily identify the new (untracked) files--as they are the ones they will be writing in, and any modified files--as that means they're changing something that already exists.

That's not what I was expecting, nor was it what I was thinking about, but I was intrigued.

I've made lots of extensions for VS but none for VSCode.
For a while, I've wanted to branch out and this seemed like a good opportunity.

I hoped that what I know about building extensions for VS will help with building them for VSCode.
It didn't.

The only thing I've learned building extensions for VS that applied to VSCode was to be persistent and search broadly to find out how to do certain things.

The extension I've made is very simple but in creating it I had to learn a number of things:
  • how to build a basic extension
  • how to respond to opening and switching between documents
  • how to apply visual changes to a document
  • how to get the git status of a file (much harder to work out than I expected)
  • how to share functionality between different extensions
  • how to work with configurable settings in an extension

In places, the VSCode documentation was better than for VS, but in others, it was also quite lacking.
I think the problem with creating documentation for extensibility features is that its necessary to assume a lot about the person doing the development and the documentation needs to be fairly abstract as every extension that is built will be different.
Anyway, if you're considering building an extension for VS or VSCode I highly recommend supplementing the official docs by searching for opensource code that does something similar and learn from that. It's one of the reasons that I share all my extension code on GitHub.

So, what did I build?
This:

VSCode screenshot showing different documents with different color backgrounds
Notice the subtle background tinting of the two open documents.

It colors the background of open documents if the are untracked or have been modified. The colors are customizable but the default matches the colors used in the Explorer and Source Control panels.

Hopefully, with this installed, fewer edits will now be made to the wrong documents. Or any edits in the wrong document will now be spotted sooner.


Wednesday, May 13, 2020

More fun with comments

This is probably the least interesting screenshot I've ever shared
and this one is only slightly more remarkable
Yes, it's the same method from the same file.
But, did you spot the difference?

It's only small.
Look at the left margin.
There are more of the little outlining boxes with minus signs in them in the second image.

The first image is the default behavior. It shows something I hadn't noticed before.
By default, in a method, only multi-line comments starting with a triple-slash can be collapsed.

And the second screenshot?
Well, it turns out that as I wrote a Visual Studio extension that works with comments, these are the kind of things I learn.
As such the second screenshot shows a minor feature of the latest version (1.8) of that extension. It now lets Visual Studio know how to collapse (and expand)  multi-line comments within method blocks. (That's not a piece of code I ever imagined writing!)

Additionally, it also:
- fixes a bug in how it identifies which block to expand and collapse.
- fixes a bug that affects anyone who uses tabs instead of spaces.

Now go: download, install and review the extension for yourself.

Tuesday, April 28, 2020

Collapse Comments (v1.4) and expand them

Comments (in code) can be really helpful. But, at other times they can get in the way of the code you're working on.

It's for the times when I just want the comments to get out the way that I created the Collapse Comments extension for Visual Studio 2019. It adds a simple command to collapse all the comments in the open document that you can invoke by pressing Ctrl+M, Ctrl+C.

I've just released version 1.4 and this includes something new. It includes the ability to do exactly the opposite of what I specified above. It adds another command that will expand all comments and collapse everything else.
It's not a scenario I have to deal with a lot but came as a suggestion (thanks Morten) who frequently looks at the decompiled code (and comments) that Visual Studio can generate. The generated file lists method names with comments (which are automatically collapsed upon opening) and it's the comments that are most important here for understanding all the different overloads. For this reason, it makes sense to be able to quickly expand all the comments by pressing Ctrl+M, Ctrl+D.


There's an option to control whether to treat using (or imports) directives like comments, and it works with C#, VB.NET, and XML documents.


Get it now, for FREE, from the Visual Studio Marketplace.

Got thoughts about this? Why not leave a comment below? I promise not to hide it away ;)

Monday, April 20, 2020

How to use VSIXSignTool

Trying to be a good developer/publisher, I sign my Visual Studio extensions.


Signing VSIXs is optional but it sends a good signal. Plus I have a code-signing certificate because I had to get one to sign NuGet packages as they MUST be signed.

But knowing how to sign a VSIX isn't obvious.
When I first wanted to sign an extension I was using Visual Studio 2017 and found the Extensibility Tools for Visual Studio from Mads Kristensen. This includes a UI for signing the generated package.

This was all good until I moved to exclusively use Visual Studio 2019. Those extensibility tools aren't supported in VS2019 so I was stuck.

To unblock myself, I ported the signing functionality, from the above-referenced tools, into a separate package that works with VS2019. A few hundred other people have also used this so I guess they were in a similar situation to me.

Fast forward to a week or so ago and I came across the NuGet package Microsoft.VSSDK.Vsixsigntool

This looked very interesting but lacked instructions on how to use it.
There's a link to some release notes but they're for "Visual Studio 2015 Update 2".
The "Project Site" link goes to the general landing page for Visual Studio Extensibility. :(

After more searching than I thought should be necessary, I eventually found https://docs.microsoft.com/en-us/visualstudio/extensibility/signing-vsix-packages?view=vs-2019
This page should be helpful but only includes some vague descriptions of what to do. :(

To try and work out how to automatically sign my generated packages using this tool, I did what I often do to solve problems relating to extension development, I searched GitHub to work out how other people had done it. Eventually, I came to this solution.

I added the following to my project file.

  <PropertyGroup>
    <VsixSignTool>$(NuGetPackageRoot)Microsoft.VSSDK.Vsixsigntool\16.2.29116.78\tools\vssdk\vsixsigntool.exe</VsixSignTool>
    <VsixSignCommand>$(VsixSignTool) sign /f $(SIGN_CERTIFICATE) /p $(SIGN_PASSWORD) /sha1 $(SIGN_CERT_HASH) /fd sha256</VsixSignCommand>
  </PropertyGroup>
  <Target Name="AfterBuild" DependsOnTargets="CoreCompile" Condition="Exists('$(SIGN_CERTIFICATE)')">
    <Message Text="Signing $(TargetVsixContainer)" Condition="'$(Configuration)' == 'Release'" />
    <Exec Command="$(VsixSignCommand) $(MSBuildProjectDirectory)\$(TargetVsixContainer)" Condition="'$(Configuration)' == 'Release'" />
  </Target>

This relies on two things:

  1. The NuGet package is referenced in the project.
  2. Add environment variables for the relevant parameters.


Points of note:
  • This allows for the certificate to be in different places on different machines.
  • This means there is no need to check the certificate into the code repository.
  • The password doesn't get checked in with the source.
  • If the environment variables aren't specified the process is skipped. This is how I avoid any issue with running this on a public CI server. It only needs to be defined on the machine that builds the release version for publishing.
  • The certificate hash was found with the following command.
>certutil -p ****** -dump ./filename.pfx

I also learned that it's possible to reference environment variables in MSBuild files. However, it's necessary to restart Visual Studio for it to pick up any changes (or additions) to these.

With this all set up, every time I build a release version of my extensions they are automatically signed without me having to do anything else. This simplifies the process and avoids me needing to enter the certificate file path and password. Yay!

There are probably other solutions with online key vaults and other such things but this works for me. Maybe this will help you too.

Saturday, April 18, 2020

ConstVisualizer v1.2 released and out of preview

My Const Visualizer extension is now at version 1.2 and out of preview.

Version 1.0 was only released this week but it has allowed me to identify some use cases that I hadn't previously accounted for. This is why I originally released it with a preview tag.

Having previously seen an increasingly broad range of code-bases, I had a very strong suspicion that there might have been things I originally missed. Thanks to people who tried out the first version and gave me feedback, the less obvious cases (to me) are now covered and it should work for all use cases where the definition of the constant is in the same solution.

For anyone who needs (or wants) to see constants defined in other code-bases (such as in referenced libraries) I'll wait for them to raise an issue to request this. ;)

Tuesday, April 14, 2020

Comment Links v1.2

I've just released an update to my Comment Links extension.

Version 1.2 adds the ability to create links through new context menu options on the editor.


No more manually composing links yourself. Now you can just put the cursor where you want the link to go to and create it that way.
The formatted link is copied to the clipboard for you to paste wherever you want it.
Copied links are also listed in the Output Pane so you can also access them from there (and even navigate from there too.)

Thanks to Michael for the suggestion.

View constants where you use them in your C# code

When debugging or just trying to understand code, it can be useful to see the value of constants. Yes, you can try and remember the values but seeing them is often easier.

The above is enabled by a new extension, I've just published, called Const Visualizer.

Yes, there's a lot of similarity between this and String Resource Visualizer.


Const Visualizer is something I've been meaning to create for a while but I've done it now as a pre-cursor to adding new functionality in the String Resource Visualizer. It's forced me to learn how to integrate Roslyn code analysis into an extension and look at other ways of analyzing the contents of a solution.

Why not put all the functionality related to const values and string resources in one extension?
Two reasons. 1. Because I prefer lots of simple tools that each focus on a single task. These can then be combined and used as preferred.  and 2. Because the existing "String Resource Visualizer" name doesn't expand well to cover other functionality too.

Please install it, give it a try, and see what you think. Does it make your coding-life easier too?

Friday, April 10, 2020

Comment Links - v1.1

Version 1 only shipped yesterday but there's a new version already.

Version 1.1 includes three changes:


  • The ability to support file and path names containing spaces. (including if they're URL encoded)
  • Support for search terms that include spaces
  • Indicating when a file can't be found (in the status bar). This also includes line numbers that don't exist or search text that can't be found.


These changes are the result of issues submitted on GitHub. Please raise any other feature requests there ;)


  • There was also a bugfix relating to navigating to the line containing the search text.


Download and enjoy.

Stay safe.


Thursday, April 09, 2020

Navigate between files of different languages in Visual Studio

Visual Studio has great tooling for navigating within code when it is in the same (programming) language.
However, if your solution contains code in more than one language and you want a quick way to navigate between files you're out of luck. Until now!

Presenting: Comment Links
Yes, it's (another) new Visual Studio 2019 extension. This one lets you put links between files anywhere in your code. In theory, this could be anywhere in your code, but in comments makes the most sense. Just include the text `link:` (case-insensitive) followed by the file name.


A green button with an arrow on it will, when clicked, open the named file.

You can also have the file open on a specific line by including the line number after `#L` at the end of the file name.

// link:mapManager.js#L25

 Or you can jump to the first occurrence of a specific piece of text

// link:mapManager.js:UpdateLocalData

For compatibility with text fragment anchors you can also use `#:~:text=` after the file name to specify the text.

// link:mapManager.js#:~:text=UpdateLocalData


Version 1.0 is in preview and available from the Visual Studio Marketplace now.
Please give it a try and tell me what you think or suggest new features on GitHub.


This project was the result of a comment from one of my GitHub sponsors. If you join them I'm likely to take any suggestions you make more seriously. ;)

Thursday, April 02, 2020

Automatically collapse code comments when opening a document

I've just released a new version (1.2) of my Collapse Comments extension for Visual Studio.

Amongst the changes are two new configurable options.


Whether using (or imports in VB) directives are collapsed as well as comments is now configurable. This is the old behavior and so is enabled by default.

The other new option is the ability to automatically close comments whenever a new document is opened. This is off by default as it's a change in behavior.

Please let me know what you think of these changes, or, even better, leave a review :)


Monday, March 23, 2020

Open URLs in the error/warning description

It can be more effort than it feels necessary to navigate to a URL in a message description.

To get to it you might type it into a browser, or more likely, copy the whole message and then copy the URL and then paste it into a browser.

But, you're a busy developer. You have better things to spend your time on than unnecessarily cutting and pasting URLs that you can't easily open.

What you need is a way to open that URL in a browser without all the faff.

Congratulations, you're in luck. I've just updated my Error Helper extension to enable just this scenario.


After you install the extension, you can simply right-click on the entry in the error list and select Open URL. I'm sure you can guess what will happen then ;)
What could be simpler?

And for the avoidance of any doubt, the other options highlighted above will copy the description of the selected entry into the clipboard or search for the description in your default browser.

If you haven't already go install it now.

Don't just take my word for it, here's what others have said.





Monday, March 16, 2020

Introducing: Clearly Editable - making it clear which documents can be edited in Visual Studio

The way some people* use Visual Studio means that they may use or open generated or read-only documents.

There can be lots of reasons for this:

  • They might generate code with a tool.
  • They may need to debug the compiler-generated code.
  • They might use a version control tool that locks files by making them read-only.
  • Many other, valid, reasons...

When working with such files, it can be REALLY frustrating when:

  • you start typing in a document but nothing happens (because it's read-only.) or
  • you make some changes in a generated document (because you don't realize it's generated) and those changes are lost when the file is regenerated.


Visual Studio already has some ways to try and help avoid the above scenarios but they're not always enough.

When a read-only file is open this icon is shown in the tab.
But it's small and easy to miss.

Generated documents typically have a comment in the header of the document to point out that it's been generated and should not be edited directly.
//---------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//---------------------------------------------------------
But, comments aren't always read, or even visible. It can also be easy to forget that a document is generated when switching between documents or if it's been open for a long time.


I present an alternative solution.

It changes the background color of the editor to make it harder to forget or miss if a file is read-only or generated.

The image below shows three open documents.

  • The document on the left is a generated file and has the background set to a pale green color.
  • The middle document is a read-only file and has the background set to a pale red color.
  • The document on the right is neither generated or read-only, just like most files you work with and so there are no modifications to the configured theme colors.



The intention here is that different background colors are harder to miss.

The colors are configurable.

Change them to what you prefer and what works with your other theme/color settings.

You can set the color by specifying a color name or an RGB HEX code.
Opacity is set by providing a percentage so that is can work with named colors and because calculating RGBA (or ARGB) isn't the simplest thing in the world for everyone.
You can also disable the highlighting of either type of file. Or, disable all functionality without having to uninstall the extension and restart Visual Studio.

Go to Tools > Options > Clearly Editable

Options dialog showing the default settings.


Give it a try by installing it from the Visual Studio Marketplace.
If you like it, please leave a review.
If you encounter any problems or have suggestions, please open an issue on GitHub.



* If this isn't you, that's fine. I have other extensions that might be useful to you.

Wednesday, February 12, 2020

Resource Pseudo Localization - Protecting developers from themselves

I have a tool to help test the localization of strings in an app. It does this by making changes to the resource files (.resx & .resw) to help make it easy to test that all content is coming from the resource file. There are a number of different options available and more on this is covered in my book.


I've just released a new version (3.0) that adds a single new feature.

The new feature is that it creates a backup of the original file before it changes the content.

I know that it can be frustrating when tools create unnecessary extra files but I feel this is better than someone getting into a situation where they lose lots of work.

There's an option to turn this off if it's not wanted but it's on by default as that's better for avoiding unexpected negative consequences.

This isn't the result of someone having a problem. Rather, it's me trying to avoid a problem before it occurs.
Someone did ask how to get back after they'd toggled one of the options (you just need to toggle it again to undo the change) but I realized that not everything is obvious to everyone and there are all sorts of developers with various skills, knowledge, and experience.
In case anyone ever converts all their resources to something that can be automatically reversed, and they don't have another copy, and they don't have source control. At least they'll now have a backup.

If you're interested in trying out this extension, you can get it from the VS Marketplace.

Tuesday, January 21, 2020

String Resource Visualizer - v 1.5

Sometimes it can be useful to think about lots of different solutions to a problem before picking the one to implement.
Other times you know just the right thing to do straight away.

A request came in for String Resource Visualizer to add support for a language other than the default.

Here's a screenshot of the current version in action.


This is all good and useful, but the request is to help developers whose first or preferred language isn't the same as the default culture for the app's code.

This is a request I wanted the tool to be able to support so I started to think about the different ways to implement this.

Then I stopped.
There's one key thing to define: the language to use in preference to the default.
Secondarily it might be possible to have rules about which languages to use for which solution, or order of preferred languages, and possibly other configurable options.

Rather than make things unnecessarily complicated I chose to use the existing system for handling settings. I then chose to make the change as simple as possible. Opting to only support a single configurable option with reasonable fallback options as there's no point in creating more complex functionality until it's wanted.

I've been thinking a lot about not just jumping to the first solution that comes to mind and trying to look at multiple possible solutions to find what's best. It's just that this didn't seem like the right opportunity to explore this. Some times the first solution is good enough. Especially, when following existing patterns.

As of the just-released, version 1.5, there's now a way to also see the resources for a language/culture that isn't the default.

Options page for specifying the preferred culture

This will cause resources in the specified language to be shown when they exist. The default is then used as a fallback.
Hopefully, this image provides a good example of this in action.



The new version is available in the marketplace now.
If you're an existing user and don't need this functionality you may still benefit from updating as there are also performance improvements in this new version.



Sunday, January 05, 2020

The specified ITextSnapshot doesn't belong to the correct TextBuffer - a solution

I'm assuming you're reading this because you encountered this error message in a Visual Studio log and didn't know what to do about it.

"The specified ITextSnapshot doesn't belong to the correct TextBuffer."

You may even have found a number of other blog and forum posts looking for answers but finding none.
I've been there myself.

This is a record of how I fixed this issue. This applies to how I addressed it in an extension I'm writing. If you're just using VS and not writing an extension yourself this is unlikely to be useful or interesting. If you are investigating this because of an extension you're writing, please note that you might be having this issue because of another issue to me.

Disclaimer over, I noticed this error occurring when a config file was deleted from the project.

The call stack in the log wasn't immediately helpful either.

System.ArgumentException: The specified ITextSnapshot doesn&apos;t belong to the correct TextBuffer.&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.SnapshotSpan.TranslateTo(ITextSnapshot targetSnapshot, SpanTrackingMode spanTrackingMode)&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.Tagging.Implementation.TagAggregator`1.&lt;GetTagsForBuffer&gt;d__47.MoveNext()&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.Tagging.Implementation.TagAggregator`1.&lt;InternalGetTags&gt;d__51.MoveNext()&#x000D;&#x000A;   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.AdornmentLibrary.Squiggles.Implementation.SquiggleVisualManager.GetNormalizedSquiggleSpanCollections(ITextViewLine line)&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.AdornmentLibrary.Squiggles.Implementation.SquiggleVisualManager.UpdateVisualsOn(IEnumerable`1 lines, Boolean removeOldSquiggles)&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.AdornmentLibrary.Squiggles.Implementation.SquiggleVisualManager.UpdateSquigglesOnInvalidatedSpans()&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.AdornmentLibrary.Squiggles.Implementation.SquiggleVisualManager.OnLayoutChanged(Object sender, TextViewLayoutChangedEventArgs e)&#x000D;&#x000A;   at Microsoft.VisualStudio.Text.Utilities.GuardedOperations.RaiseEvent[TArgs](Object sender, EventHandler`1 eventHandlers, TArgs args)

However, on reflection, it did provide a hint.
The mention of `GetTagsForBuffer` made me look at what I was doing with tagging.

In my `ITagger<T>` implementation I was using a cache of created tags to avoid needing to recreate them unnecessarily. However, it seems that changing the project that an open file is in causes a new TextBuffer to be created.
The consequence of this is that my cached tags now refer to a Snapshot with a different TextBuffer and that causes VS to have (but log) an exception.

My solution is that in the GetTags method, I check that the TextBuffer of the NormalizedSnapshotSpanCollection matches the TextBuffer of the SnapShot of the cached tags.
I only return the tags if they do match and if they don't I invalidate the cache for that file.

Hopefully, this helps save someone else some time trying to debug a similar issue.