Roslyn: WorkspaceChangeKind.DocumentRemoved never raised - c#

I'm working on Roslyn plugin to Visual Studio. I am trying to subscribe to an event that would be raised after a file is renamed. I am using Workspace.WorkspaceChanged but it doesn't raise DocumentRemoved.
Shouldn't WorkspaceChanged (with Kind=DocumentRemoved) be raised after an item is renamed?
Is there any other way to get notifications about solution item renames? I was trying to subscribe to DTE events but no luck either.
This is the way how I get a workspace:
var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));
_myWorkspace = componentModel.GetService<VisualStudioWorkspace>();

It has been a couple of years i did something with this. I once wrote a VS plugin that searched through files in a solution. It worked with rename as well if i remember(unfortunate i can't check because it was for VS 2015). I did it with DTE events back than. I created an DteEventHandler and added to the DocumentEvents.DocumentSaved my function.
dte = Package.GetGlobalService(typeof(DTE)) as DTE2;
events = dte.Events;
docEv = events.DocumentEvents;
docEv.DocumentSaved += ScanDocumentForFunction;
The other way is probably to go through the solution file. The solutionfile contains all csproj files and you can get all files from there.
dte = FillIndexListCommandPackage.GetGlobalService(typeof(DTE)) as DTE2;
var solutionnamearr = dte.Solution.FullName.Split('\\');
...
if you use git it would probably the easiest way to just call a git status in a command windows and pipe the result through to your plugin logic.
i hope i was some help or pointed you at least in the right direction.

Related

How to get "Include Paths" property of Microsoft Macro Assembler in Visual Studio by a plugin?

I'm workng to get include path resolved by some VS plugin (asm-dude in fact). Include path in microsoft macro assembler looks like this:
includepath
Include file resolve part in asm-dude lies in: https://github.com/HJLebbink/asm-dude/blob/vxix2022-B/VS/CSHARP/asm-dude-vsix/Tools/LabelGraph.cs#L602
Anyway, at the beginning I think I just need to get the value of IncludePath property, and then other things can be done in a minute. But after reading some docs I realized I'm in a mess. It seems that VS prevents me to get names of all properties, but I can only get the value by the name.
Codes I write are like:
DTE dte = Package.GetGlobalService(typeof(SDTE)) as DTE;
Projects projects = dte.Solution.Projects;
if (projects.Count != 0)
{
VCProject project = (VCProject)projects.Item(1).Object;
VCConfiguration cfg = project.ActiveConfiguration;
if (cfg != null)
{
string includePathStr = cfg.GetEvaluatedPropertyValue("IncludePaths");
}
}
but in vein, it gets include path of msvc, not MASM
I cast Project to VCProject because it's a VC project. Although I can iterate properties of a non-VCProject's configuration, but it doesn't seem to work on VCProject, because it doesn't have a (at least not public) member named properties. All these docs tell me that I can only get its value by name, but the problem is I don't know its name. Or I'm completely wrong? I must admit that I'm new to VS plugins.
refs I used so far:
https://learn.microsoft.com/en-us/previous-versions/dn655034(v=vs.140)?redirectedfrom=MSDN
https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.vcprojectengine.vcconfiguration?view=visualstudiosdk-2022

How do I retrieve text from the Visual Studio editor for use with Roslyn SyntaxTree?

I am attempting to write a Visual Studio extension that will analyze the C# code displayed in the editor and possibly update the code based on what I find. This would be on demand (via a menu item), and not using an analyzer and code fix.
There are a number of examples and samples on the Internet, but they all start either with the source code hard-coded in the samples, or create a new document, or look at each file in the VS solution that is open. How do I access the source code from the active editor window?
In a comment to my original question, #SJP gave a link to #Frank Bakker's answer to the question at Calling Roslyn from VSIX Command. This does work as outlined.
#JoshVarty provided a hint of the direction to go in his answer above. I combined that with code provided by #user1912383 for how to get an IWpfTextView answering the question Find an IVsTextView or IWpfTextView for a given ProjectItem, in 2010 RC extension. Here is the code I came up with:
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
IVsTextView activeView = null;
ErrorHandler.ThrowOnFailure(textManager.GetActiveView(1, null, out activeView));
var editorAdapter = componentModel.GetService<IVsEditorAdaptersFactoryService>();
var textView = editorAdapter.GetWpfTextView(activeView);
var document = (textView.TextBuffer.ContentType.TypeName.Equals("CSharp"))
? textView : null;
In a comment after #user1912383's code mentioned above, #kman mentioned that this does not work for document types such as .sql files. It does, however, work for .cs files which is what I will be using it with.
First, you need to install the Microsoft.CodeAnalysis.EditorFeatures.Text package.
Then you need to add the appropriate using statement:
using Microsoft.CodeAnalysis.Text;
Now you can map between Visual Studio concepts (ITextSnapshot, ITextBuffer etc.) and Roslyn concepts (Document, SourceText etc.) with the extension methods found here: https://github.com/dotnet/roslyn/blob/master/src/EditorFeatures/Text/Extensions.cs
For example:
ITextSnapshot snapshot = ... //Get this from Visual Studio
var documents = snapshot.GetRelatedDocuments(); //There may be more than one

How to output a file from a roslyn code analyzer?

I'm using the roslyn API to write a DiagnosticAnalyzer and CodeFix.
After I have collected all strings and string-interpolations, I want to write all of them to a file but I am not sure how to do this the best way.
Of course I can always simply do a File.WriteAllText(...) but I'd like to expose more control to the user.
I'm also not sure about how to best trigger the generation of this file, so my questions are:
I do not want to hard-code the filename, what would be the best way to expose this setting to the user of the code-analyzer? A config file? If so, how would I access that? ie: How do I know the directory?
If one string is missing from the file, I'd like to to suggest a code fix like "Project contains changed or new strings, regenerate string file". Is this the best way to do this? Or is it possible to add a button or something to visual studio?
I'm calling the devenv.com executable from the commandline to trigger builds, is there a way to force my code-fix to run either while building, or before/after? Or would I have to "manually" load the solution with roslyn and execute my codefix?
I've just completed a project on this. There are a few things that you will need to do / know.
You will probably need to switch you're portable class library to a class library. otherwise you will have trouble calling the File.WriteAllText()
You can see how to Convert a portable class library to a regular here
This will potentially not appropriately work for when trying to apply all changes to document/project/solution. When Calling from a document/project/solution, the changes are precalcuated and applied in a preview window. If you cancel, an undo action is triggered to undo all changes, if you write to a file during this time, and do not register an undo action you will not undo the changes to the file.
I've opened a bug with roslyn but you can handle instances by override the preview you can see how to do so here
And one more final thing you may need to know is how to access the Solution from the analyzer which, Currently there is a hack I've written to do so here
As Tamas said you can use additional files you can see how to do so here
You can use additional files, but I know on the version I'm using resource files, are not marked as additional files by default they are embeddedResources.
So, for my users to not have to manually mark the resource as additonalFiles I wrote a function to get out the Designer.cs files associated with resource files from the csproj file using xDoc you can use it as an example if you choose to parse the csproj file:
protected List<string> GetEmbeddedResourceResxDocumentPaths(Project project)
{
XDocument xmldoc = XDocument.Load(project.FilePath);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
var resxFiles = new List<string>();
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
var includeExtension = Path.GetExtension(includePath);
if (0 == string.Compare(includeExtension, RESX_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
{
var outputTag = resource.Elements(msbuild + LAST_GENERATED_TAG).FirstOrDefault();
if (null != outputTag)
{
resxFiles.Add(outputTag.Value);
}
}
}
return resxFiles;
}
For config files you can use the AdditionalFiles msbuild property, which is passed to the analyzers through the context. See here.

Dynamically compiled project losing resources

I need to compile source code of big project dynamically and output type can be Windows Application or Class Library.
Code is nicely executed and its possible to make .dll or .exe files, but problem is that, when I'm trying to make .exe file - it's losing resources like project icon. Result file doesn't include assembly information to.
Any way to solve this? (Expected result should be the same, that manual Build function on project file in Visual Studio 2015).
Thank you!
var workspace = MSBuildWorkspace.Create();
//Locating project file that is WindowsApplication
var project = workspace.OpenProjectAsync(#"C:\RoslynTestProjectExe\RoslynTestProjectExe.csproj").Result;
var metadataReferences = project.MetadataReferences;
// removing all references
foreach (var reference in metadataReferences)
{
project = project.RemoveMetadataReference(reference);
}
//getting new path of dlls location and adding them to project
var param = CreateParamString(); //my own function that returns list of references
foreach (var par in param)
{
project = project.AddMetadataReference(MetadataReference.CreateFromFile(par));
}
//compiling
var projectCompilation = project.GetCompilationAsync().Result;
using (var stream = new MemoryStream())
{
var result = projectCompilation.Emit(stream);
if (result.Success)
{
/// Getting result
//writing exe file
using (var file = File.Create(Path.Combine(_buildPath, fileName)))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
}
We never really designed the workspace API to include all the information you need to emit like this; in particular when you're calling Emit there's an EmitOptions you can pass that includes, amongst other things, resource information. But we don't expose that information since this scenario wasn't hugely considered. We've done some of the work in the past to enable this but ultimately never merged it. You might wish to consider filing a bug so we officially have the request somewhere.
So what can you do? I think there's a few options. You might consider not using Roslyn at all but rather modifying the project file and building that with the MSBuild APIs. Unfortunately I don't know what you're ultimately trying to achieve here (it would help if you mentioned it), but there's a lot more than just the compiler invocation that is involved in building a project. Changing references potentially changes other things too.
It'd also be possible, of course, to update MSBuildWorkspace yourself to pass this through. If you were to modify the Roslyn code, you'll see we implement a series of interfaces named "ICscHostObject#" (where # is a number) and we get passed the information from MSBuild to that. It looks like we already stash that in the command line arguments, so you might be able to pass that to our command line parser and get the data back you need that way.

Equivalent to VCPostBuildEventTool for C# project in visual studio 2010

I am writing an add-in for visual studio, which creates a test project for a production code project.
It should support C, C++ and C# projects.
for C/C++ projects I can progarmatically set the post and pre-build events using VCPostBuildEventTool.
this how I get it from the Project object:
VCProject vcTestProj = (VCProject)testProj.Object;
IVCCollection testProjCfgs = (IVCCollection)vcTestProj.Configurations;
VCConfiguration testProjDebugCfg = (VCConfiguration)testProjCfgs.Item("Debug");
VCLinkerTool testProjLinkerTool = (VCLinkerTool)((IVCCollection)testProjDebugCfg.Tools).Item("VCLinkerTool");
VCCLCompilerTool testProjectCompileTool = (VCCLCompilerTool)((IVCCollection)testProjDebugCfg.Tools).Item("C/C++ Compiler Tool");
VCPreBuildEventTool testProjPreBuildTool = (VCPreBuildEventTool)((IVCCollection)testProjDebugCfg.Tools).Item("Pre-Build Event Tool");
VCPostBuildEventTool testProjPostBuildTool = (VCPostBuildEventTool)((IVCCollection)testProjDebugCfg.Tools).Item("Post-Build Event Tool");
But a C# project can not be used as a VCProject, and I can't find a way to access it's pre and post build events progarmatically.
Anyway to do that?
C#/VB.NET projects are treated as the same, so they are part of ProjectProperties3 (or ProjectProperties2) interface,
http://msdn.microsoft.com/en-us/library/vslangproj2.projectproperties2.postbuildevent.aspx
How to get to that level is mentioned in
http://msdn.microsoft.com/en-us/library/ms228958.aspx

Categories