I'm investigating the use of the Roslyn compiler within a Visual Studio Extension (VSIX) that uses the VisualStudioWorkspace to update existing code. Having spent the last few days reading up on this, there seem to be several ways to achieve this....I'm just not sure which is the best approach for me.
Okay, so let's assume that the User has their solution open in Visual Studio 2015. They click on my Extension and (via a form) they tell me that they want to add the following method definition to an interface:
GetSomeDataResponse GetSomeData(GetSomeDataRequest request);
They also tell me the name of the interface, it's ITheInterface.
The interface already has some code in it:
namespace TheProjectName.Interfaces
{
using System;
public interface ITheInterface
{
/// <summary>
/// A lonely method.
/// </summary>
LonelyMethodResponse LonelyMethod(LonelyMethodRequest request);
}
}
Okay, so I can load the Interface Document using the following:
Document myInterface = this.Workspace.CurrentSolution?.Projects?
.FirstOrDefault(p
=> p.Name.Equals("TheProjectName"))
?.Documents?
.FirstOrDefault(d
=> d.Name.Equals("ITheInterface.cs"));
So, what is the best way to now add my new method to this existing interface, ideally writing in the XML comment (triple-slash comment) too? Bear in mind that the request and response types (GetSomeDataRequest and GetSomeDataResponse) may not actually exist yet. I'm very new to this, so if you can provide code examples then that would be terrific.
UPDATE
I decided that (probably) the best approach would be simply to inject in some text, rather than try to programmatically build up the method declaration.
I tried the following, but ended up with an exception that I don't comprehend:
SourceText sourceText = await myInterface.GetTextAsync();
string text = sourceText.ToString();
var sb = new StringBuilder();
// I want to all the text up to and including the last
// method, but without the closing "}" for the interface and the namespace
sb.Append(text.Substring(0, text.LastIndexOf("}", text.LastIndexOf("}") - 1)));
// Now add my method and close the interface and namespace.
sb.AppendLine("GetSomeDataResponse GetSomeData(GetSomeDataRequest request);");
sb.AppendLine("}");
sb.AppendLine("}");
Inspecting this, it's all good (my real code adds formatting and XML comments, but removed that for clarity).
So, knowing that these are immutable, I tried to save it as follows:
var updatedSourceText = SourceText.From(sb.ToString());
var newInterfaceDocument = myInterface.WithText(updatedSourceText);
var newProject = newInterfaceDocument.Project;
var newSolution = newProject.Solution;
this.Workspace.TryApplyChanges(newSolution);
But this created the following exception:
bufferAdapter is not a VsTextDocData
at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetAdapter(IVsTextBuffer bufferAdapter) at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetDocumentBuffer(IVsTextBuffer bufferAdapter) at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.InvisibleEditor..ctor(IServiceProvider serviceProvider, String filePath, Boolean needsSave, Boolean needsUndoDisabled) at Microsoft.VisualStudio.LanguageServices.RoslynVisualStudioWorkspace.OpenInvisibleEditor(IVisualStudioHostDocument hostDocument) at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument.UpdateText(SourceText newText) at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) at Microsoft.CodeAnalysis.Workspace.ApplyProjectChanges(ProjectChanges projectChanges) at Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Solution newSolution) at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.TryApplyChanges(Solution newSolution)
SourceText
(which has additional source file information attached to it), by callingSourceText.WithChanges(new TextChange(...))
, see this answer for an example. – Antitank