2016-12-01 94 views
1

所以,我想更多地了解Roslyn,因此编写了一个帮助我分析解决方案的应用程序。树视图与深嵌套的对象?

应该说,我还是比较新的C#和WPF,所以我可能会错过这里重要或明显的东西。

我想在Treeview中显示我的解决方案的结构。 我已经能够我的解决方案的结构导出到文本文件里像这样的输出:

+ Analysing the following project: Testtool 
|+ Analysing the following document: Converters.cs 
||+ The following namespaces are referenced for the analysed file: 
|||- System 
|||- System.Collections.Generic 
|||- System.Linq 
|||- System.Text 
|||- System.Threading.Tasks 
|||- System.Windows.Data 
||- The file lives in the following namespace: Testtool 
||+ Analysing the following class of the current file: BooleanInverter 
|||+ The following modifiers are used for the class: 
||||- public 
||||- partial 
|||+ The following methods are defined in the currently analysed class: 
||||+ Convert 
|||||+ The following modifiers are used for the method: 
||||||- public 
||||+ ConvertBack 
|||||+ The following modifiers are used for the method: 
||||||- public 
|+ Analysing the following document: LoadingControl.xaml.cs 
||+ The following namespaces are referenced for the analysed file: 
|||- System 
|||- System.Collections.Generic 
|||- System.Linq 
|||- System.Text 
|||- System.Threading.Tasks 
|||- System.Windows 
|||- System.Windows.Controls 
|||- System.Windows.Data 
|||- System.Windows.Documents 
|||- System.Windows.Input 
|||- System.Windows.Media 
|||- System.Windows.Media.Imaging 
|||- System.Windows.Navigation 
|||- System.Windows.Shapes 
|||- System.ComponentModel 
||- The file lives in the following namespace: Testtool 
||+ Analysing the following class of the current file: LoadingControl 
|||+ The following modifiers are used for the class: 
||||- public 
||||- partial 
|||+ The following methods are defined in the currently analysed class: 
||||+ OnPropertyChanged 
|||||+ The following modifiers are used for the method: 
||||||- public 
|||+ The following properties are defined in the currently analysed class: 
||||+ SpinnerText 
|||||+ The following modifiers are used for the Property: 
||||||- public 

现在,我不知道有什么好办法将是一个对象来显示这种结构。我的意思是,如果没有更好的可能性,我会创建一个相应的对象模型,但是我觉得需要这种深度嵌套的对象感觉不对。 那么,也许有人对此有更好的想法?

+1

也许[SyntaxVisualizer源代码(https://github.com/dotnet/roslyn/tree/614299ff83da9959fa07131c6d0ffbc58873b6ae/src/ Tools/Source/SyntaxVisualizer)将会帮助你 –

回答

0

正如Dudi已经指出的那样,Syntax Visualizer也提供了一个树视图和整个语法树,但是,看着你的控制台输出,你似乎想要显示“裸语法树”的摘要。实际上,我会去寻找一个相应的对象模型,因为你的分析很大程度上总结了语法树,所以你不应该需要那么多的视图模型。

我没有任何使用WPF树视图的经验,但可能你只能绑定到顶级视图模型,这将代表Solution,它的孩子返回Project视图模型。其余的使用隐式数据模板。

如果你想要一些这样的示例代码,我很高兴补充说。

+0

我会发布一个适合我的解决方案。同时感谢您的回答,它真的帮助了我! – CuttingWide

0

首先非常感谢您的回答。他们都帮助我并提出了一些好的想法。这就是说,我仍然非常不喜欢“纯粹的对象模型”方法。但经过一番搜索之后,我认为我有一个能够满足我需求的解决方案,同时仍然可以保持我的观点。 Roslyn的Syntax Visualizer的源代码将我引入其中。 要点:

  • 定义一个“SyntaxTreeNode”属性。该属性可以包含 其他SyntaxTreeNode对象的列表。
  • 以给定方式对解决方案进行迭代,提取我想要的信息 。
  • 实现一种指定“parentNode”的方法,新节点应该添加到 。检查当前检查的SyntaxNode/Token的种类。
  • 根据节点的类型设置相应的参数 (这里还有一些改进/重构的好空间)。
  • 构建子节点后,将其添加到确定的父节点。
  • 将属性定义为ObservableCollection,然后将它绑定到TreeViews ItemSource。

请注意,我使用的是Syncfusions sfTreeGrid,因此处理列的方式可能会有所不同。但它应该明白这一点。

SyntaxTreeNode定义

public class SyntaxTreeNode 
{ 
    // A node can contain other nodes 
    public ObservableCollection<SyntaxTreeNode> SyntaxTreeNodes { get; set; } 
    public string Name { get; set; } 
    public string Type { get; set; } 
} 

在这种检查解决方案

// The property used for binding the ItemsSource of the sfTreeView 
    private ObservableCollection<SyntaxTreeNode> _TreeViewNodes; 
    public ObservableCollection<SyntaxTreeNode> TreeViewNodes 
    { 
     get { return _TreeViewNodes; } 
     set 
     { 
      _TreeViewNodes = value; 
     } 
    } 

中央方法获取分层对象的类。还没有适当重构。 public void displaySyntaxTree() { //初始化属性 TreeViewNodes = new ObservableCollection();

 // Open the solution from the supplied path 
     string solutionPath = @"Path to your solution"; 
     MSBuildWorkspace msWorkspace = MSBuildWorkspace.Create(); 
     Solution solution = msWorkspace.OpenSolutionAsync(solutionPath).Result; 

     // Analyze each project. 
     foreach (var project in solution.Projects) 
     { 
      // Set project node 
      SyntaxTreeNode projectNode = new SyntaxTreeNode(); 
      projectNode.Name = project.Name; 
      projectNode.Type = "Project"; 
      projectNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>(); 

      // Add the defined node to the collection (first tree level). 
      TreeViewNodes.Add(projectNode); 

      // Set the recently added node as a “parentNode”. This will be used later to add the prebuild subnode (or a list of subnodes). 
      SyntaxTreeNode parentProjectNode = CommonDataAccess.TreeViewNodes[CommonDataAccess.TreeViewNodes.Count - 1]; 

      // Get the list of referenced assemblies for the project. Subsequently create a new node that will then show them (a “root node” for the references if you want to say so). At this point the node does NOT yet get added to the collection that’s bound to the TreeView. 
      IReadOnlyList<MetadataReference> projectReferences = project.MetadataReferences; 
      SyntaxTreeNode referenceNode = new SyntaxTreeNode(); 
      referenceNode.Name = "Referenced assemblies"; 
      referenceNode.Type = "Reference list"; 
      referenceNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>(); 

      // Create a new node for each reference entry under the reference “root node” defined above. 
      foreach (MetadataReference reference in projectReferences) 
      { 
       SyntaxTreeNode refNode = new SyntaxTreeNode(); 
       int refNameStart = reference.Display.LastIndexOf("\\") + 1; 
       int refNameLength = reference.Display.Length - refNameStart; 
       refNode.Name = reference.Display.Substring(refNameStart, refNameLength); 
       refNode.Type = "Reference"; 
       referenceNode.SyntaxTreeNodes.Add(refNode); 
      } 

      // Now add the prebuild reference “root node” with the single reference nodes to the earlier defined project node (therefore creating a structure with two sublevels). 
      parentProjectNode.SyntaxTreeNodes.Add(referenceNode); 

      // Now go through the documents for the current project. 
      foreach (Document document in project.Documents) 
      { 
       // Add a subnode to the current project for the document. 
       SyntaxTreeNode TreeNode = new SyntaxTreeNode(); 
       TreeNode.Name = document.Name; 
       TreeNode.Type = "File"; 

       parentProjectNode.SyntaxTreeNodes.Add(TreeNode); 

       string path = document.FilePath; 
       FileStream stream = File.OpenRead(path); 

       SyntaxTree tree = CSharpSyntaxTree.ParseText(SourceText.From(stream), path: path); 

       CompilationUnitSyntax root = (CompilationUnitSyntax)tree.GetRoot(); 

       // Similar to the project level, determine the node for each document now and set it as “parentNode”. So this is actually now on a sublevel (most likely the first). 
       SyntaxTreeNode actNode = parentProjectNode.SyntaxTreeNodes[parentProjectNode.SyntaxTreeNodes.Count - 1]; 

       // Call the method for adding the subnodes for the current document. Passing with ref so no need to return a node. 
       getSubNodes(root, ref actNode); 
      } 
     } 
    } 

    // Simple method for getting the childs of the SyntaxTree, init the sublevel collection and adding prepared subnodes to the parent node. 
    public void getSubNodes(CompilationUnitSyntax parentTree, ref SyntaxTreeNode parentNode) 
    { 
     ChildSyntaxList childs = parentTree.ChildNodesAndTokens(); 
     List<SyntaxTreeNode> nodesToAdd = iterateSubNodes(childs); 
     foreach (SyntaxTreeNode node in nodesToAdd) 
     { 
      if (parentNode.SyntaxTreeNodes == null) 
       parentNode.SyntaxTreeNodes = new ObservableCollection<SyntaxTreeNode>(); 
      parentNode.SyntaxTreeNodes.Add(node); 
     } 
    } 

    // Method for examing the kind of the current node. If no proper kind was found “recursively” searching through the nodes. Has to be improved, expanded and refactored still (dictionary could be suitable for this). 
    public List<SyntaxTreeNode> iterateSubNodes (ChildSyntaxList syntaxList) 
    { 
     List<SyntaxTreeNode> nodesToReturn = new List<SyntaxTreeNode>(); 

     foreach (SyntaxNodeOrToken nodeOrToken in syntaxList) 
     { 
      SyntaxKind childKind = nodeOrToken.Kind(); 
      if (childKind == SyntaxKind.UsingDirective) 
      { 
       SyntaxTreeNode tokenToAdd = new SyntaxTreeNode(); 
       UsingDirectiveSyntax usingNode = (UsingDirectiveSyntax)nodeOrToken; 
       tokenToAdd = addUsing(usingNode); 
       nodesToReturn.Add(tokenToAdd); 
      } 
      else if (childKind == SyntaxKind.NamespaceDeclaration) 
      { 
       SyntaxTreeNode tokenToAdd = new SyntaxTreeNode(); 
       NamespaceDeclarationSyntax namespaceNode = (NamespaceDeclarationSyntax)nodeOrToken; 
       tokenToAdd = addNamespace(namespaceNode); 
       nodesToReturn.Add(tokenToAdd); 
      } 
      else 
      { 
       iterateSubNodes(nodeOrToken.ChildNodesAndTokens()); 
      } 
     } 

     // Return the node (or a list of them) for further processing. 
     return nodesToReturn; 
    } 

    // Method for defining the parameters for a NameSpaceDeclaration. 
    private SyntaxTreeNode addNamespace(NamespaceDeclarationSyntax namespaceSyntax) 
    { 
     SyntaxTreeNode nodeToReturn = new SyntaxTreeNode(); 
     nodeToReturn.Name = namespaceSyntax.Name.ToString(); 
     nodeToReturn.Type = "Namespace Definition"; 

     return nodeToReturn; 
    } 

    // The same as for Namespace, here just for usingDirectives. 
    private SyntaxTreeNode addUsing(UsingDirectiveSyntax usingSyntax) 
    { 
     SyntaxTreeNode nodeToReturn = new SyntaxTreeNode(); 
     nodeToReturn.Name = usingSyntax.Name.ToString(); 
     nodeToReturn.Type = usingSyntax.Kind().ToString(); 

     return nodeToReturn; 
    } 

上面的代码给我到底这样一个观点: enter image description here