using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
using Tango.BL.Entities;
using Tango.Logging;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Modules;
using Tango.MachineStudio.DB;
using Tango.MachineStudio.MachineDesigner;
using Tango.MachineStudio.Stubs;
namespace Tango.MachineStudio.UI.Modules
{
<using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Tango.Scripting.Parsing
{
public class ScriptParser
{
private const string fakeScript = "CurrentScript";
private string fakeScriptName = "CurrentScript.";
private String ReplaceFakeScript(String str)
{
if (str != null)
{
return str.Replace(fakeScriptName, "").Replace(fakeScript, "");
}
return str;
}
private CompilationUnitSyntax GetRoot(String code)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
var root = (CompilationUnitSyntax)tree.GetRoot();
return root;
}
private SemanticModel GetSemanticModel(String code)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
var compilation = CSharpCompilation.Create("CSharpScript").AddSyntaxTrees(tree);
SemanticModel model = compilation.GetSemanticModel(tree);
return model;
}
public List<ScriptSymbol> GetContextSymbols(String code, int caretOffset)
{
var currentNode = GetCaretOffsetNode(code, caretOffset);
if (currentNode == null) return new List<ScriptSymbol>();
if (currentNode.Ancestors().OfType<BaseTypeDeclarationSyntax>().Count() == 0)
{
var usings = GetUsingsFull(code);
int removedLength = usings.Select(x => x.Length).Sum();
foreach (var use in usings)
{
code = code.Replace(use, "");
}
String scriptStart = $"public class {fakeScript}\n{{\n";
code = $"{scriptStart}{code}\n}}";
caretOffset = caretOffset - removedLength + scriptStart.Length;
}
var model = GetSemanticModel(code);
var symbols = model.LookupSymbols(caretOffset).ToList().Where(x => x.Kind == SymbolKind.Property || x.Kind == SymbolKind.Field || x.Kind == SymbolKind.Parameter || x.Kind == SymbolKind.Local || x.Kind == SymbolKind.Method).ToList();
List<ScriptSymbol> vars = new List<ScriptSymbol>();
foreach (var symbol in symbols.DistinctBy(x => x.Name))
{
if (symbol.ContainingSymbol.GetType().Name == "LambdaSymbol")
{
var invocationNode = currentNode.Ancestors().OfType<InvocationExpressionSyntax>().FirstOrDefault();
if (invocationNode != null)
{
var expressionNode = invocationNode.Expression as MemberAccessExpressionSyntax;
if (expressionNode != null && expressionNode.Name != null)
{
var name = expressionNode.Name as GenericNameSyntax;
if (name != null)
{
var type = name.TypeArgumentList.Arguments.FirstOrDefault()?.ToString();
vars.Add(new ScriptSymbol()
{
Name = symbol.Name,
Type = ReplaceFakeScript(type),
Class = ReplaceFakeScript(symbol.ContainingType?.Name),
Kind = symbol.Kind,
Accessibility = symbol.DeclaredAccessibility,
ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
Summary = GetSymbolDocumentation(symbol),
});
}
}
}
}
if (symbol.Kind == SymbolKind.Method)
{
var prop = symbol.GetType().GetProperty("ReturnType");
if (prop != null)
{
ScriptSymbol m = new ScriptSymbol()
{
Name = symbol.Name,
Type = ReplaceFakeScript(prop.GetValue(symbol).ToString()),
Class = ReplaceFakeScript(symbol.ContainingType?.Name),
Kind = symbol.Kind,
Accessibility = symbol.DeclaredAccessibility,
ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
Summary = GetSymbolDocumentation(symbol),
};
m.Parameters = GetMethodSymbolParameters(symbol);
vars.Add(m);
}
}
else
{
var prop = symbol.GetType().GetProperty("Type");
if (prop != null)
{
vars.Add(new ScriptSymbol()
{
Name = symbol.Name,
Type = ReplaceFakeScript(prop.GetValue(symbol).ToString()),
Class = ReplaceFakeScript(symbol.ContainingType?.Name),
Kind = symbol.Kind,
Accessibility = symbol.DeclaredAccessibility,
ContainingNamespace = ReplaceFakeScript(symbol.ContainingNamespace?.Name),
Summary = GetSymbolDocumentation(symbol),
});
}
}
}
return vars.Where(x => x.Type != "?").ToList();
}
public List<String> GetUsings(String code)
{
Regex r = new Regex("(using [^;^\n]+;)");
var matches = r.Matches(code);
return matches.OfType<Match>().Select(x => x.Value.Replace("using ", "").Replace(";", "").Replace("\n", "").Replace("\t", "").Replace("\r", "")).ToList();
}
public List<String> GetUsingsFull(String code)
{
Regex r = new Regex("(using [^;^\n]+;)");
var matches = r.Matches(code);
return matches.OfType<Match>().Select(x => x.Value).ToList();
}
public List<ScriptType> GetDeclaredTypes(String code)
{
List<ScriptType> types = new List<ScriptType>();
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
var compilation = CSharpCompilation.Create("CSharpScript").AddSyntaxTrees(tree);
SemanticModel model = compilation.GetSemanticModel(tree);
foreach (var d in root.DescendantNodes().Where(x => x is ClassDeclarationSyntax || x is EnumDeclarationSyntax || x is InterfaceDeclarationSyntax).OfType<BaseTypeDeclarationSyntax>())
{
var type = model.GetDeclaredSymbol(d);
if (!String.IsNullOrWhiteSpace(type.Name))
{
ScriptType scriptType = new ScriptType();
scriptType.Name = type.Name;
scriptType.Kind = type.TypeKind;
scriptType.ContainingNamespace = type.ContainingNamespace?.Name;
scriptType.Summary = GetNodeDocumentation(d);
foreach (var symbol in d.DescendantNodes().OfType<PropertyDeclarationSyntax>())
{
var symbolModel = model.GetDeclaredSymbol(symbol);
scriptType.Symbols.Add(new ScriptSymbol()
{
Class = scriptType.Name,
Accessibility = symbolModel.DeclaredAccessibility,
Kind = SymbolKind.Property,
Name = symbolModel.Name,
Type = symbolModel.Type.ToString(),
ContainingNamespace = symbolModel.ContainingNamespace?.Name,
Summary = GetNodeDocumentation(symbol),
});
}
foreach (var symbol in d.DescendantNodes().OfType<FieldDeclarationSyntax>())
{
var symbolModel = model.GetDeclaredSymbol(symbol.Declaration.Variables.FirstOrDefault()) as IFieldSymbol;
if (symbolModel != null)
{
scriptType.Symbols.Add(new ScriptSymbol()
{
Class = scriptType.Name,
Accessibility = symbolModel.DeclaredAccessibility,
Kind = SymbolKind.Field,
Name = symbolModel.Name,
Type = symbolModel.Type.ToString(),
ContainingNamespace = symbolModel.ContainingNamespace?.Name,
Summary = GetNodeDocumentation(symbol),
});
}
}
foreach (var symbol in d.DescendantNodes().OfType<MethodDeclarationSyntax>())
{
var symbolModel = model.GetDeclaredSymbol(symbol);
if (symbolModel != null)
{
ScriptSymbol m = new ScriptSymbol()
{
Class = scriptType.Name,
Accessibility = symbolModel.DeclaredAccessibility,
Kind = SymbolKind.Method,
Name = symbolModel.Name,
Type = symbolModel.ReturnType.ToString(),
ContainingNamespace = symbolModel.ContainingNamespace?.Name,
Summary = GetNodeDocumentation(symbol),
};
foreach (var p in symbol.DescendantNodes().OfType<ParameterSyntax>())
{
if (p.Type != null && p.Identifier != null)
{
m.Parameters.Add(new KeyValuePair<string, string>(p.Type.ToString(), p.Identifier.ToString()));
}
}
scriptType.Symbols.Add(m);
}
}
types.Add(scriptType);
}
}
return types;
}
public ObjectCreationExpressionSyntax GetCurrentConstructionExpression(String code)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
return root.DescendantNodes().OfType<ObjectCreationExpressionSyntax>().FirstOrDefault();
}
public T GetExpressionFirst<T>(String line) where T : CSharpSyntaxNode
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(line);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
return root.DescendantNodes().OfType<T>().FirstOrDefault();
}
public List<T> GetExpressions<T>(String line) where T : CSharpSyntaxNode
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(line);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
return root.DescendantNodes().OfType<T>().ToList();
}
public List<T> GetDirectExpressions<T>(SyntaxNode node) where T : CSharpSyntaxNode
{
return node.DescendantNodes().OfType<T>().ToList();
}
private SyntaxNode GetCaretOffsetNode(String code, int offset)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
return root.DescendantNodes().Where(x => offset >= x.FullSpan.Start && offset <= x.FullSpan.End).OrderBy(x => x.FullSpan.Length).FirstOrDefault();
}
private List<SyntaxNode> GetNodeAncestors(SyntaxNode node)
{
return node.Ancestors().ToList();
}
private String GetNodeDocumentation(SyntaxNode node)
{
try
{
var trivia = node.GetLeadingTrivia().FirstOrDefault(t => t.Kind() == SyntaxKind.SingleLineCommentTrivia);
if (trivia != null && !String.IsNullOrWhiteSpace(trivia.ToString()))
{
return trivia.ToString().Replace("//", "");
}
}
catch { }
return "No documentation.";
}
private String GetSymbolDocumentation(ISymbol symbol)
{
if (symbol != null)
{
var prop = symbol.GetType().GetProperty("SyntaxNode");
if (prop != null)
{
var node = prop.GetValue(symbol) as SyntaxNode;
if (node != null)
{
return GetNodeDocumentation(node.Parent);
}
}
}
return "No documentation.";
}
private SyntaxNode GetSymbolSyntaxNode(ISymbol symbol)
{
if (symbol != null)
{
var prop = symbol.GetType().GetProperty("SyntaxNode");
if (prop != null)
{
var node = prop.GetValue(symbol) as SyntaxNode;
return node;
}
}
return null;
}
private List<KeyValuePair<String, String>> GetMethodSymbolParameters(ISymbol symbol)
{
List<KeyValuePair<String, String>> parameters = new List<KeyValuePair<string, string>>();
try
{
var prop = symbol.GetType().GetProperty("Parameters");
if (prop != null)
{
var array = prop.GetValue(symbol) as IEnumerable;
foreach (var item in array)
{
var type = item.GetType().GetProperty("Type").GetValue(item).ToString();
var value = item.GetType().GetProperty("Name").GetValue(item).ToString();
parameters.Add(new KeyValuePair<string, string>(type, value));
}
}
}
catch { }
return parameters;
}
public String IndentCSharpCode(String code)
{
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot().NormalizeWhitespace();
var ret = root.ToFullString();
return ret;
}
}
}