bool IsDebugDocument(string filePath, ProjectScriptInfo config) { var fileName = Path.GetFileName(filePath); if (string.IsNullOrWhiteSpace(fileName)) { return(true); } if (fileName.Contains(".NETFramework,Version=")) { return(true); } if (fileName.EndsWith(".debug", StringComparison.CurrentCultureIgnoreCase)) { return(true); } if (fileName.IndexOf(".debug.", StringComparison.CurrentCultureIgnoreCase) >= 0) { return(true); } return(config.IsIgnoredFilePath(filePath)); }
/// <summary> /// Composes a script solution into a single <c>Program</c> document /// </summary> /// <param name="project"></param> /// <param name="config"></param> /// <returns></returns> public async Task <ProgramComposition> ComposeAsync(Project project, ProjectScriptInfo config) { var content = await LoadContentAsync(project, config).ConfigureAwait(false); var document = CreateProgramDocument(project, content); return(await ProgramComposition.CreateAsync(document, content.Readme).ConfigureAwait(false)); }
/// <summary> /// Creates a new instance of <see cref="ScriptOptionsDialogModel"/> /// </summary> /// <param name="package"></param> /// <param name="projectScriptInfo"></param> public ScriptOptionsDialogModel([NotNull] MDKPackage package, [NotNull] ProjectScriptInfo projectScriptInfo) { if (package == null) { throw new ArgumentNullException(nameof(package)); } ActiveProject = projectScriptInfo ?? throw new ArgumentNullException(nameof(projectScriptInfo)); }
protected bool TryGetValidProject(out Project project, out ProjectScriptInfo projectInfo) { var dte2 = (EnvDTE80.DTE2)Package.DTE; project = ((IEnumerable)dte2.ToolWindows.SolutionExplorer.SelectedItems) .OfType <UIHierarchyItem>() .Select(item => item.Object) .OfType <Project>() .FirstOrDefault(); if (project == null) { projectInfo = null; return(false); } projectInfo = ProjectScriptInfo.Load(project.FullName, project.Name); return(projectInfo.IsValid); }
async Task <ProjectContent> LoadContentAsync(Project project, ProjectScriptInfo config) { var usingDirectives = ImmutableArray.CreateBuilder <UsingDirectiveSyntax>(); var parts = ImmutableArray.CreateBuilder <ScriptPart>(); var documents = project.Documents .Where(document => !IsDebugDocument(document.FilePath, config)) .ToList(); var readmeDocument = project.Documents .Where(document => DirectoryOf(document)?.Equals(Path.GetDirectoryName(project.FilePath), StringComparison.CurrentCultureIgnoreCase) ?? false) .FirstOrDefault(document => NameOf(document).Equals("readme", StringComparison.CurrentCultureIgnoreCase)); string readme = null; if (readmeDocument != null) { documents.Remove(readmeDocument); readme = (await readmeDocument.GetTextAsync()).ToString().Replace("\r\n", "\n"); if (!readme.EndsWith("\n")) { readme += "\n"; } } for (var index = 0; index < documents.Count; index++) { var document = documents[index]; var result = await _analyzer.AnalyzeAndTransformAsync(document, Macros).ConfigureAwait(false); if (result == null) { continue; } usingDirectives.AddRange(result.UsingDirectives); parts.AddRange(result.Parts); } var comparer = new UsingDirectiveComparer(); return(new ProjectContent(usingDirectives.Distinct(comparer).ToImmutableArray(), parts.ToImmutable(), readme)); }
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, ProjectScriptInfo config) { var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
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()); }
/// <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 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([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); }
protected bool TryGetValidProject(out ProjectScriptInfo projectInfo) => TryGetValidProject(out _, out projectInfo);
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 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); }
/// <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())); }
/// <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)); }
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([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)); }
/// <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);