DrG*_*iff 5 c# visual-studio-extensions roslyn roslyn-code-analysis
我正在研究使用VisualStudioWorkspace更新现有代码的Visual Studio扩展(VSIX)中Roslyn编译器的使用。花了近几天的时间进行阅读,似乎有几种方法可以实现此目标。。。我只是不确定哪种方法最适合我。
好的,让我们假设用户在Visual Studio 2015中打开了解决方案。他们单击我的扩展,然后(通过表单)告诉我要向接口添加以下方法定义:
GetSomeDataResponse GetSomeData(GetSomeDataRequest request);
Run Code Online (Sandbox Code Playgroud)
他们还告诉我接口的名称,即ITheInterface。
接口中已经包含一些代码:
namespace TheProjectName.Interfaces
{
using System;
public interface ITheInterface
{
/// <summary>
/// A lonely method.
/// </summary>
LonelyMethodResponse LonelyMethod(LonelyMethodRequest request);
}
}
Run Code Online (Sandbox Code Playgroud)
好的,所以我可以使用以下方法加载接口文档:
Document myInterface = this.Workspace.CurrentSolution?.Projects?
.FirstOrDefault(p
=> p.Name.Equals("TheProjectName"))
?.Documents?
.FirstOrDefault(d
=> d.Name.Equals("ITheInterface.cs"));
Run Code Online (Sandbox Code Playgroud)
那么,现在将我的新方法添加到此现有接口中的最佳方法是什么,最好也写XML注释(三斜杠注释)呢?请记住,请求和响应类型(GetSomeDataRequest和GetSomeDataResponse)可能实际上还不存在。我对此很陌生,所以如果您可以提供代码示例,那将是非常棒的。
更新
我认为最好的方法(可能)只是插入一些文本,而不是尝试以编程方式构建方法声明。
我尝试了以下操作,但最终出现了一个我不理解的异常:
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("}");
Run Code Online (Sandbox Code Playgroud)
检查一下,一切都很好(我的真实代码添加了格式和XML注释,但为清楚起见将其删除)。
因此,知道这些是不可变的,我尝试将其保存如下:
var updatedSourceText = SourceText.From(sb.ToString());
var newInterfaceDocument = myInterface.WithText(updatedSourceText);
var newProject = newInterfaceDocument.Project;
var newSolution = newProject.Solution;
this.Workspace.TryApplyChanges(newSolution);
Run Code Online (Sandbox Code Playgroud)
但这创建了以下异常:
bufferAdapter is not a VsTextDocData
Run Code Online (Sandbox Code Playgroud)
在Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.InvisibleEditor.Provider(Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetAdapter(IVsTextBuffer bufferAdapter) ,Microsoft.VisualStudio.LanguageServices.RoslynVisualStudioWorkspace.OpenInvisibleEditor(IVisualStudioHostDocument hostDocument),Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument.UpdateText(SourceTexts。 .Implementation.ProjectSystem.VisualStudioWorkspaceImpl。Microsoft.CodeAnalysis.Workspace上的ApplyDocumentTextChanged(DocumentId documentId,SourceText newText)在Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.TrySolyApplyChanges(Solution
如果我是你,我会利用 Roslyn 的所有好处,即我会使用SyntaxTree的Document而不是处理文件文本(你可以在不使用 Roslyn 的情况下完成后者)。
例如:
...
SyntaxNode root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
var interfaceDeclaration = root.DescendantNodes(node => node.IsKind(SyntaxKind.InterfaceDeclaration)).FirstOrDefault() as InterfaceDeclarationSyntax;
if (interfaceDeclaration == null) return;
var methodToInsert= GetMethodDeclarationSyntax(returnTypeName: "GetSomeDataResponse ",
methodName: "GetSomeData",
parameterTypes: new[] { "GetSomeDataRequest" },
paramterNames: new[] { "request" });
var newInterfaceDeclaration = interfaceDeclaration.AddMembers(methodToInsert);
var newRoot = root.ReplaceNode(interfaceDeclaration, newInterfaceDeclaration);
// this will format all nodes that have Formatter.Annotation
newRoot = Formatter.Format(newRoot, Formatter.Annotation, workspace);
workspace.TryApplyChanges(document.WithSyntaxRoot(newRoot).Project.Solution);
...
public MethodDeclarationSyntax GetMethodDeclarationSyntax(string returnTypeName, string methodName, string[] parameterTypes, string[] paramterNames)
{
var parameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(GetParametersList(parameterTypes, paramterNames)));
return SyntaxFactory.MethodDeclaration(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
returnType: SyntaxFactory.ParseTypeName(returnTypeName),
explicitInterfaceSpecifier: null,
identifier: SyntaxFactory.Identifier(methodName),
typeParameterList: null,
parameterList: parameterList,
constraintClauses: SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(),
body: null,
semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))
// Annotate that this node should be formatted
.WithAdditionalAnnotations(Formatter.Annotation);
}
private IEnumerable<ParameterSyntax> GetParametersList(string[] parameterTypes, string[] paramterNames)
{
for (int i = 0; i < parameterTypes.Length; i++)
{
yield return SyntaxFactory.Parameter(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
type: SyntaxFactory.ParseTypeName(parameterTypes[i]),
identifier: SyntaxFactory.Identifier(paramterNames[i]),
@default: null);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这是非常原始的代码,Roslyn API 在分析/处理语法树、获取符号信息/引用等方面非常强大。我建议您查看此页面和此页面以供参考。