2014-04-23 8 views
5

मुझे लगता है कि एक जावास्क्रिप्ट पुस्तकालय बुलाया js-अनुक्रम का लाभ उठाता है नेट यूएमएल अनुक्रम आरेख बनाने के लिए एक ओपन सोर्स प्रोजेक्ट बनाने पर काम कर रहा हूँ के साथ रेफरी/बाहर मानकों के साथ स्थिर कक्षाओं में तरीकों और विधियों के माध्यम से आमंत्रण का उपयोग करने की -diagrams। मैं कर रहा हूँ नहीं यकीन है कि रोसलिन इस काम के लिए सही उपकरण है, लेकिन मैंने सोचा कि मैं इसे एक शॉट देना होगा तो मैं एक साथ जो सभी तरीकों और उनके आमंत्रण प्राप्त करने के लिए प्रयास करता है अवधारणा कोड के कुछ सबूत डाल दिया है और फिर एक के रूप में इन आमंत्रण आउटपुट कि जेएस-अनुक्रम-आरेखों द्वारा व्याख्या की जा सकती है।कैसे विस्तार के तरीकों, रोसलिन

कोड कुछ उत्पादन उत्पन्न करता है, लेकिन यह सब कुछ पर कब्जा नहीं है। मैं विस्तार विधियों के माध्यम से invocations पर कब्जा नहीं कर सकता, स्थैतिक वर्गों में स्थिर तरीकों के आह्वान।

मैं out मानकों के साथ तरीकों में से आमंत्रण क्यों दिखाई देता है, लेकिन किसी भी रूप है कि BaseMethodDeclarationSyntax

फैली यहाँ में नहीं कोड (ध्यान में रखना इस अवधारणा कोड का सबूत है और इसलिए मैं पूरी तरह से सबसे अच्छा है का पालन नहीं किया है प्रथाओं, लेकिन मैं एक कोड की समीक्षा यहाँ का अनुरोध नहीं कर रहा हूँ ... भी, मैं कार्य तो मैं इंतजार के साथ चारों ओर खिलवाड़ कर रहा हूँ का उपयोग करने के लिए प्रयोग किया जाता रहा हूँ, लेकिन नहीं पूरी तरह से यकीन है कि मैं इसे ठीक से अभी तक का उपयोग कर रहा हूँ)

https://gist.github.com/SoundLogic/11193841

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Reflection.Emit; 
using System.Threading.Tasks; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using Microsoft.CodeAnalysis.Formatting; 
using Microsoft.CodeAnalysis.MSBuild; 
using Microsoft.CodeAnalysis.FindSymbols; 
using System.Collections.Immutable; 

namespace Diagrams 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string solutionName = "Diagrams"; 
      string solutionExtension = ".sln"; 
      string solutionFileName = solutionName + solutionExtension; 
      string rootPath = @"C:\Workspace\"; 
      string solutionPath = rootPath + solutionName + @"\" + solutionFileName; 

      MSBuildWorkspace workspace = MSBuildWorkspace.Create(); 
      DiagramGenerator diagramGenerator = new DiagramGenerator(solutionPath, workspace); 
      diagramGenerator.ProcessSolution(); 

      #region reference 

      //TODO: would ReferencedSymbol.Locations be a better way of accessing MethodDeclarationSyntaxes? 
      //INamedTypeSymbol programClass = compilation.GetTypeByMetadataName("DotNetDiagrams.Program"); 

      //IMethodSymbol barMethod = programClass.GetMembers("Bar").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol; 
      //IMethodSymbol fooMethod = programClass.GetMembers("Foo").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol; 

      //ITypeSymbol fooSymbol = fooMethod.ContainingType; 
      //ITypeSymbol barSymbol = barMethod.ContainingType; 

      //Debug.Assert(barMethod != null); 
      //Debug.Assert(fooMethod != null); 

      //List<ReferencedSymbol> barReferencedSymbols = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList(); 
      //List<ReferencedSymbol> fooReferencedSymbols = SymbolFinder.FindReferencesAsync(fooMethod, solution).Result.ToList(); 

      //Debug.Assert(barReferencedSymbols.First().Locations.Count() == 1); 
      //Debug.Assert(fooReferencedSymbols.First().Locations.Count() == 0); 

      #endregion 

      Console.ReadKey(); 
     } 
    } 

    class DiagramGenerator 
    { 
     private Solution _solution; 

     public DiagramGenerator(string solutionPath, MSBuildWorkspace workspace) 
     { 
      _solution = workspace.OpenSolutionAsync(solutionPath).Result; 
     } 

     public async void ProcessSolution() 
     { 
      foreach (Project project in _solution.Projects) 
      { 
       Compilation compilation = await project.GetCompilationAsync(); 
       ProcessCompilation(compilation); 
      } 
     } 

     private async void ProcessCompilation(Compilation compilation) 
     { 
      var trees = compilation.SyntaxTrees; 

      foreach (var tree in trees) 
      { 
       var root = await tree.GetRootAsync(); 
       var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>(); 

       foreach (var @class in classes) 
       { 
        ProcessClass(@class, compilation, tree, root); 
       } 
      } 
     } 

     private void ProcessClass(
       ClassDeclarationSyntax @class 
      , Compilation compilation 
      , SyntaxTree tree 
      , SyntaxNode root) 
     { 
      var methods = @class.DescendantNodes().OfType<MethodDeclarationSyntax>(); 

      foreach (var method in methods) 
      { 
       var model = compilation.GetSemanticModel(tree); 
       // Get MethodSymbol corresponding to method 
       var methodSymbol = model.GetDeclaredSymbol(method); 
       // Get all InvocationExpressionSyntax in the above code. 
       var allInvocations = root.DescendantNodes().OfType<InvocationExpressionSyntax>(); 
       // Use GetSymbolInfo() to find invocations of target method 
       var matchingInvocations = 
        allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol)); 

       ProcessMethod(matchingInvocations, method, @class); 
      } 

      var delegates = @class.DescendantNodes().OfType<DelegateDeclarationSyntax>(); 

      foreach (var @delegate in delegates) 
      { 
       var model = compilation.GetSemanticModel(tree); 
       // Get MethodSymbol corresponding to method 
       var methodSymbol = model.GetDeclaredSymbol(@delegate); 
       // Get all InvocationExpressionSyntax in the above code. 
       var allInvocations = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>(); 
       // Use GetSymbolInfo() to find invocations of target method 
       var matchingInvocations = 
        allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol)); 

       ProcessDelegates(matchingInvocations, @delegate, @class); 
      } 

     } 

     private void ProcessMethod(
       IEnumerable<InvocationExpressionSyntax> matchingInvocations 
      , MethodDeclarationSyntax methodDeclarationSyntax 
      , ClassDeclarationSyntax classDeclarationSyntax) 
     { 
      foreach (var invocation in matchingInvocations) 
      { 
       MethodDeclarationSyntax actingMethodDeclarationSyntax = null; 
       if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax)) 
       { 
        var r = methodDeclarationSyntax; 
        var m = actingMethodDeclarationSyntax; 

        PrintCallerInfo(
         invocation 
         , classDeclarationSyntax 
         , m.Identifier.ToFullString() 
         , r.ReturnType.ToFullString() 
         , r.Identifier.ToFullString() 
         , r.ParameterList.ToFullString() 
         , r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty 
         ); 
       } 
      } 
     } 

     private void ProcessDelegates( 
       IEnumerable<InvocationExpressionSyntax> matchingInvocations 
      , DelegateDeclarationSyntax delegateDeclarationSyntax 
      , ClassDeclarationSyntax classDeclarationSyntax) 
     { 
      foreach (var invocation in matchingInvocations) 
      { 
       DelegateDeclarationSyntax actingMethodDeclarationSyntax = null; 

       if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax)) 
       { 
        var r = delegateDeclarationSyntax; 
        var m = actingMethodDeclarationSyntax; 

        PrintCallerInfo(
         invocation 
         , classDeclarationSyntax 
         , m.Identifier.ToFullString() 
         , r.ReturnType.ToFullString() 
         , r.Identifier.ToFullString() 
         , r.ParameterList.ToFullString() 
         , r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty 
        ); 
       } 
      } 
     } 

     private void PrintCallerInfo(
       InvocationExpressionSyntax invocation 
      , ClassDeclarationSyntax classBeingCalled 
      , string callingMethodName 
      , string returnType 
      , string calledMethodName 
      , string calledMethodArguments 
      , string calledMethodTypeParameters = null) 
     { 
      ClassDeclarationSyntax parentClassDeclarationSyntax = null; 
      if (!SyntaxNodeHelper.TryGetParentSyntax(invocation, out parentClassDeclarationSyntax)) 
      { 
       throw new Exception(); 
      } 

      calledMethodTypeParameters = calledMethodTypeParameters ?? String.Empty; 

      var actedUpon = classBeingCalled.Identifier.ValueText; 
      var actor = parentClassDeclarationSyntax.Identifier.ValueText; 
      var callInfo = callingMethodName + "=>" + calledMethodName + calledMethodTypeParameters + calledMethodArguments; 
      var returnCallInfo = returnType; 

      string info = BuildCallInfo(
        actor 
       , actedUpon 
       , callInfo 
       , returnCallInfo); 

      Console.Write(info); 
     } 

     private string BuildCallInfo(string actor, string actedUpon, string callInfo, string returnInfo) 
     { 
      const string calls = "->"; 
      const string returns = "-->"; 
      const string descriptionSeparator = ": "; 

      string callingInfo = actor + calls + actedUpon + descriptionSeparator + callInfo; 
      string returningInfo = actedUpon + returns + actor + descriptionSeparator + "returns " + returnInfo; 

      callingInfo = callingInfo.RemoveNewLines(true); 
      returningInfo = returningInfo.RemoveNewLines(true); 

      string result = callingInfo + Environment.NewLine; 
      result += returningInfo + Environment.NewLine; 

      return result; 
     } 
    } 

    static class SyntaxNodeHelper 
    { 
     public static bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result) 
      where T : SyntaxNode 
     { 
      // set defaults 
      result = null; 

      if (syntaxNode == null) 
      { 
       return false; 
      } 

      try 
      { 
       syntaxNode = syntaxNode.Parent; 

       if (syntaxNode == null) 
       { 
        return false; 
       } 

       if (syntaxNode.GetType() == typeof (T)) 
       { 
        result = syntaxNode as T; 
        return true; 
       } 

       return TryGetParentSyntax<T>(syntaxNode, out result); 
      } 
      catch 
      { 
       return false; 
      } 
     } 
    } 

    public static class StringEx 
    { 
     public static string RemoveNewLines(this string stringWithNewLines, bool cleanWhitespace = false) 
     { 
      string stringWithoutNewLines = null; 
      List<char> splitElementList = Environment.NewLine.ToCharArray().ToList(); 

      if (cleanWhitespace) 
      { 
       splitElementList.AddRange(" ".ToCharArray().ToList()); 
      } 

      char[] splitElements = splitElementList.ToArray(); 

      var stringElements = stringWithNewLines.Split(splitElements, StringSplitOptions.RemoveEmptyEntries); 
      if (stringElements.Any()) 
      { 
       stringWithoutNewLines = stringElements.Aggregate(stringWithoutNewLines, (current, element) => current + (current == null ? element : " " + element)); 
      } 

      return stringWithoutNewLines ?? stringWithNewLines; 
     } 
    } 
} 

यहां कोई भी मार्गदर्शन की सराहना की जाएगी!

+2

Anecdotally मैं कहूंगा कि रोसलिन काम के लिए सही उपकरण है, लेकिन आप आदेश की जानकारी गुम है, वह (कुछ बिंदु भी रोसलिन संकलक होगा पर पकड़ रखने के लिए कस्टम भागों के साथ यह विस्तार करने के लिए आवश्यकता हो सकती है पता है कि विस्तार विधियां क्या थीं)। आप एक निशान यहाँ भी प्रज्वलन किया जा सकता है के रूप में यह इतना नया है, वापस रिपोर्ट और अपने खुद के सवाल का जवाब कृपया आप इस बाहर अतः :-) –

+2

एक वाक्य रचना नोड आगंतुक लिखने पर विचार के साथ कहीं भी मिलना चाहिए। – SLaks

+0

@AdamHouldsworth मैं हमेशा जवाब के साथ वापस रिपोर्ट है मैं इसके साथ कहीं भी इसके अलावा, इस परियोजना पूरी तरह से खुला स्रोत हो जाएगा :) चाहिए और मैं GitHub लिंक साझा करेंगे एक बार मैं P.O.C. पिछले स्थानांतरित कर दिया है – Jordan

उत्तर

1

ProcessClass विधि में methodSymbol का उपयोग करते हुए मैं एंडी सुझाव लिया और नीचे के साथ आया था (हालांकि मुझे लगता है कि इस बारे में जाने के लिए एक आसान तरीका हो सकता है):

private async Task<List<MethodDeclarationSyntax>> GetMethodSymbolReferences(IMethodSymbol methodSymbol) 
{ 
    var references = new List<MethodDeclarationSyntax>(); 

    var referencingSymbols = await SymbolFinder.FindCallersAsync(methodSymbol, _solution); 
    var referencingSymbolsList = referencingSymbols as IList<SymbolCallerInfo> ?? referencingSymbols.ToList(); 

    if (!referencingSymbolsList.Any(s => s.Locations.Any())) 
    { 
     return references; 
    } 

    foreach (var referenceSymbol in referencingSymbolsList) 
    { 
     foreach (var location in referenceSymbol.Locations) 
     { 
      var position = location.SourceSpan.Start; 
      var root = await location.SourceTree.GetRootAsync(); 
      var nodes = root.FindToken(position).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>(); 

      references.AddRange(nodes); 
     } 
    } 

    return references; 
} 

और द्वारा उत्पन्न जिसके परिणामस्वरूप छवि js-sequence-diagrams में उत्पादन पाठ प्लग (मैं इस के लिए पूर्ण स्रोत के साथ github gist अद्यतन किया है किसी को भी यह खोजना चाहिए उपयोगी - मैं विधि मानकों बाहर रखा गया तो आरेख को पचाने था आसान है, लेकिन इन वैकल्पिक रूप से फिर से चालू किया जा सकता है):

संपादित करें :

मैं कोड (देखें github gist) तो अब कॉल (FindCallersAsync से परिणामों के माध्यम से बुला विधि के भीतर से एक कहा जाता विधि की अवधि शुरू स्थान के आधार पर) वे बना रहे थे क्रम में दिखाए जाते हैं नवीनीकृत किया है:

enter image description here

संबंधित मुद्दे