public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] ProjectScriptInfo config) { if (composition == null) { throw new ArgumentNullException(nameof(composition)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } var analyzer = new UsageAnalyzer(); while (true) { var symbolDefinitions = await analyzer.FindUsagesAsync(composition, config); var symbol = symbolDefinitions.FirstOrDefault(s => !s.IsProtected && s.SyntaxNode.IsTypeDeclaration() && HasNoUsage(s)); if (symbol == null) { break; } var rootNode = RemoveDefinition(composition.RootNode, symbol); composition = await composition.WithNewDocumentRootAsync(rootNode); } return(composition); }
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, ProjectScriptInfo config) { var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
/// <summary> /// Generates a final script of the given document. This document has been generated by the build process and /// contains the entire script in syntax tree form. /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public override async Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config) { var analyzer = new DocumentAnalyzer(); var result = await analyzer.AnalyzeAsync(composition.Document); var buffer = new StringBuilder(); var programContent = string.Join("\n", result.Parts.OfType <ProgramScriptPart>().Select(p => p.GenerateContent())); buffer.Append(programContent); buffer.Append("\n"); var extensionContent = string.Join("\n", result.Parts.OfType <ExtensionScriptPart>().Select(p => p.GenerateContent())); if (!string.IsNullOrWhiteSpace(extensionContent)) { // Extension classes are made possible by forcefully ending Space Engineer's wrapping Program class // and removing the final ending brace of the last extension class to let Space Engineers close it // for itself. // Close off the Program class buffer.Append("}\n"); buffer.Append(extensionContent); // Remove the ending brace of the last extension class var index = FindEndBrace(buffer); if (index >= 0) { buffer.Length = index; } } return(TrimPointlessWhitespace(buffer.ToString())); }
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, ProjectScriptInfo config) { _externallyReferencedTypes.Clear(); var usageAnalyzer = new UsageAnalyzer(); var typeUsages = await usageAnalyzer.FindUsagesAsync(composition, config); foreach (var typeUsage in typeUsages) { foreach (var part in typeUsage.Usage) { foreach (var location in part.Locations) { var node = composition.RootNode.FindNode(location.Location.SourceSpan); if (IsInProgram(node)) { _externallyReferencedTypes.Add(typeUsage.FullName); } } } } var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config) { var newDocument = await Simplifier.ReduceAsync(composition.Document).ConfigureAwait(false); composition = await(composition.WithDocumentAsync(newDocument).ConfigureAwait(false)); _externallyReferencedMembers.Clear(); var usageAnalyzer = new UsageAnalyzer(); var typeUsages = await usageAnalyzer.FindUsagesAsync(composition, config); foreach (var typeUsage in typeUsages) { foreach (var part in typeUsage.Usage) { foreach (var location in part.Locations) { var node = composition.RootNode.FindNode(location.Location.SourceSpan); if (!IsInProgram(node)) { _externallyReferencedMembers.Add(typeUsage.FullName); } } } } var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
/// <summary> /// Generates a final script of the given document. This document has been generated by the build process and /// contains the entire script in syntax tree form. /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public override async Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config) { //var dumper = new PreserveDebugDumper(@"e:\dump0.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); var simplifier = new CodeSimplifier(); composition = await simplifier.ProcessAsync(composition, config); //dumper = new PreserveDebugDumper(@"e:\dump1.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); var renamer = new SymbolRenamer(); composition = await renamer.ProcessAsync(composition, config); //dumper = new PreserveDebugDumper(@"e:\dump2.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); var compactor = new WhitespaceCompactor(); composition = await compactor.ProcessAsync(composition, config); //dumper = new PreserveDebugDumper(@"e:\dump3.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); var lineWrapper = new LineWrapper(); composition = await lineWrapper.ProcessAsync(composition, config); //dumper = new PreserveDebugDumper(@"e:\dump4.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); return(await GenerateScriptAsync(composition)); }
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config) { ClearLineInfo(); var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
//static char[] GetSymbolChars() //{ // var chars = new List<char>(50000); // for (var u = 0; u <= ushort.MaxValue; u++) // { // var ch = (char)u; // if (IsDeniedCharacter(ch)) // continue; // if (char.IsLetter(ch)) // chars.Add(ch); // } // return chars.ToArray(); //} //static bool IsDeniedCharacter(char ch) //{ // switch (ch) // { // case '\u180E': // case '\u0600': // case '\u00AD': // return true; // } // return false; //} //static readonly char[] BaseNChars = GetSymbolChars(); public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] MDKProjectProperties config) { if (composition == null) { throw new ArgumentNullException(nameof(composition)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } var analyzer = new SymbolAnalyzer(); var symbolDefinitions = analyzer.FindSymbols(composition, config).ToList(); symbolDefinitions.Sort((a, b) => a.SyntaxNode.FullSpan.Start); var distinctSymbolNames = new HashSet <string>(symbolDefinitions .Where(s => !s.IsProtected) .Select(s => s.Symbol.Name) .Distinct()); var symbolSrc = 0; var minifiedSymbolNames = distinctSymbolNames .ToDictionary(n => n, n => GenerateNewName(distinctSymbolNames, ref symbolSrc)); while (true) { SymbolDefinitionInfo definition = null; string newName = null; foreach (var symbolDefinition in symbolDefinitions) { if (symbolDefinition.IsProtected) { continue; } if (!minifiedSymbolNames.TryGetValue(symbolDefinition.Symbol.Name, out newName)) { continue; } definition = symbolDefinition; break; } if (definition == null) { break; } var document = composition.Document; var documentId = document.Id; var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, definition.Symbol, newName, document.Project.Solution.Options); composition = await composition.WithDocumentAsync(newSolution.GetDocument(documentId)); symbolDefinitions = analyzer.FindSymbols(composition, config).ToList(); symbolDefinitions.Sort((a, b) => a.SyntaxNode.FullSpan.Start); } return(composition); }
public ImmutableArray <SymbolDefinitionInfo> FindSymbols(ProgramComposition composition, ProjectScriptInfo config) { var root = composition.RootNode; var semanticModel = composition.SemanticModel; return(root.DescendantNodes().Where(node => node.IsSymbolDeclaration()) .Select(n => new SymbolDefinitionInfo(semanticModel.GetDeclaredSymbol(n), n)) .Where(IsNotSpecialDefinitions) .Select(WithUpdatedProtectionFlag) .ToImmutableArray()); }
public async Task <ImmutableArray <SymbolDefinitionInfo> > FindUsagesAsync(ProgramComposition composition, ProjectScriptInfo config) { var symbolDefinitions = _symbolAnalyzer.FindSymbols(composition, config).ToArray(); for (var index = 0; index < symbolDefinitions.Length; index++) { symbolDefinitions[index] = await WithUsageDataAsync(symbolDefinitions[index], composition); } return(symbolDefinitions.ToImmutableArray()); }
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config) { var newDocument = await Simplifier.ReduceAsync(composition.Document).ConfigureAwait(false); composition = await(composition.WithDocumentAsync(newDocument).ConfigureAwait(false)); _externallyReferencedMembers.Clear(); var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
/// <summary> /// Generates a final script of the given document. This document has been generated by the build process and /// contains the entire script in syntax tree form. /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public override async Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config) { //var dumper = new PreserveDebugDumper(@"e:\dump0.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); var simplifier = new CommentStripper(); composition = await simplifier.ProcessAsync(composition, config); //dumper = new PreserveDebugDumper(@"e:\dump1.txt"); //dumper.Visit(await composition.Document.GetSyntaxRootAsync()); return(await GenerateScriptAsync(composition)); }
public async override Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config) { //simplify var simplifier = new CodeSimplifier(); composition = await simplifier.ProcessAsync(composition, config); //Compact var compactor = new WhitespaceCompactor(); composition = await compactor.ProcessAsync(composition, config); //Line Wrapper var lineWrapper = new LineWrapper(); composition = await lineWrapper.ProcessAsync(composition, config); return(await base.GenerateScriptAsync(composition)); }
/// <summary> /// Generates a final script of the given document. This document has been generated by the build process and /// contains the entire script in syntax tree form. /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public override async Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config) { var simplifier = new CodeSimplifier(); composition = await simplifier.ProcessAsync(composition, config); var renamer = new SymbolRenamer(); composition = await renamer.ProcessAsync(composition, config); var compactor = new WhitespaceCompactor(); composition = await compactor.ProcessAsync(composition, config); var lineWrapper = new LineWrapper(); composition = await lineWrapper.ProcessAsync(composition, config); // return (await composition.Document.GetTextAsync()).ToString(); return(await GenerateScriptAsync(composition)); }
async Task <string> GenerateScriptAsync(ProgramComposition composition) { var root = composition.RootNode; composition = await composition.WithNewDocumentRootAsync(root); var analyzer = new DocumentAnalyzer(); var result = await analyzer.AnalyzeAsync(composition.Document); var buffer = new StringBuilder(); var programContent = string.Join("", result.Parts.OfType <ProgramScriptPart>().Select(p => p.GenerateContent())); buffer.Append(programContent); var extensionContent = string.Join("", result.Parts.OfType <ExtensionScriptPart>().Select(p => p.GenerateContent())); if (!string.IsNullOrWhiteSpace(extensionContent)) { // Extension classes are made possible by forcefully ending Space Engineer's wrapping Program class // and removing the final ending brace of the last extension class to let Space Engineers close it // for itself. // Close off the Program class. Unfortunately we do need a newline because the whitespace compactor // can't properly deal with the separation. buffer.Append("\n}"); buffer.Append(extensionContent); // Remove the ending brace of the last extension class var index = FindEndBrace(buffer); if (index >= 0) { buffer.Length = index; } } return(TrimPointlessWhitespace(buffer.ToString())); }
public async Task <ProgramComposition> ProcessAsync([NotNull] ProgramComposition composition, [NotNull] ProjectScriptInfo config) { if (composition == null) { throw new ArgumentNullException(nameof(composition)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } var nodes = new Dictionary <ISymbol, Node>(); var analyzer = new UsageAnalyzer(); var symbolDefinitions = await analyzer.FindUsagesAsync(composition, config); var symbolLookup = symbolDefinitions.GroupBy(d => d.Symbol).ToDictionary(g => g.Key, g => g.ToList()); var rootNode = composition.RootNode; foreach (var definition in symbolDefinitions) { if (!(definition.Symbol is ITypeSymbol typeSymbol)) { continue; } if (typeSymbol.TypeKind == TypeKind.TypeParameter) { continue; } if (!nodes.TryGetValue(definition.Symbol, out var node)) { nodes[definition.Symbol] = node = new Node(definition); } else { node.Definitions.Add(definition); } foreach (var usage in definition.Usage) { foreach (var location in usage.Locations) { var enclosingSymbol = await FindTypeSymbolAsync(rootNode, location); var enclosingSymbolDefinitions = symbolLookup[enclosingSymbol]; if (!nodes.TryGetValue(enclosingSymbol, out var referencingNode)) { nodes[enclosingSymbol] = referencingNode = new Node(enclosingSymbolDefinitions); } if (node != referencingNode) { referencingNode.References.Add(node); } } } } var program = symbolDefinitions.FirstOrDefault(d => d.FullName == "Program"); if (program == null || !nodes.TryGetValue(program.Symbol, out var programNode)) { throw new BuildException(string.Format(Text.TypeTrimmer_ProcessAsync_NoEntryPoint, composition.Document.Project.FilePath)); } var usedNodes = new List <Node>(); var queue = new Queue <Node>(); var visitedNodes = new HashSet <Node>(); queue.Enqueue(programNode); while (queue.Count > 0) { var node = queue.Dequeue(); if (!visitedNodes.Add(node)) { continue; } usedNodes.Add(node); foreach (var reference in node.References) { queue.Enqueue(reference); } } var usedSymbolDefinitions = usedNodes.SelectMany(n => n.Definitions).ToImmutableHashSet(); var unusedSymbolDefinitions = symbolDefinitions.Where(definition => IsEligibleForRemoval(definition) && !usedSymbolDefinitions.Contains(definition)).ToList(); var nodesToRemove = unusedSymbolDefinitions.Select(definition => definition.FullName).ToImmutableHashSet(); var walker = new RemovalWalker(nodesToRemove); rootNode = walker.Visit(rootNode); foreach (var symbol in unusedSymbolDefinitions) { rootNode = RemoveDefinition(rootNode, symbol); } return(await composition.WithNewDocumentRootAsync(rootNode)); }
async Task <SymbolDefinitionInfo> WithUsageDataAsync(SymbolDefinitionInfo definition, ProgramComposition composition) { var references = (await SymbolFinder.FindReferencesAsync(definition.Symbol, composition.Document.Project.Solution)) .ToImmutableArray(); definition = definition.WithUsageData(references); // Check for extension class usage var symbol = definition.Symbol; if (symbol.IsDefinition && symbol is ITypeSymbol typeSymbol && typeSymbol.TypeKind == TypeKind.Class && typeSymbol.IsStatic && typeSymbol.ContainingType == null) { var members = typeSymbol.GetMembers().Where(m => m is IMethodSymbol methodSymbol && methodSymbol.IsStatic && methodSymbol.IsExtensionMethod).ToArray(); foreach (var member in members) { references = references.AddRange((await SymbolFinder.FindReferencesAsync(member, composition.Document.Project.Solution))); } definition = definition.WithUsageData(references); } return(definition); }
async Task <SymbolDefinitionInfo> WithUsageDataAsync(SymbolDefinitionInfo definition, ProgramComposition composition) { var references = (await SymbolFinder.FindReferencesAsync(definition.Symbol, composition.Document.Project.Solution)) .ToImmutableArray(); return(definition.WithUsageData(references)); }
/// <summary> /// Generate a Space Engineers script file from the given document /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public abstract Task <string> GenerateAsync(ProgramComposition composition, ProjectScriptInfo config);
/// <summary> /// Generate a Space Engineers script file from the given document /// </summary> /// <param name="composition"></param> /// <param name="config"></param> /// <returns></returns> public abstract Task <string> GenerateAsync(ProgramComposition composition, MDKProjectProperties config);