Example #1
0
        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));
        }
Example #2
0
        /// <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));
        }
Example #3
0
 /// <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);
        }
Example #5
0
        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));
        }
Example #6
0
        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));
        }
Example #7
0
        public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, ProjectScriptInfo config)
        {
            var root = composition.RootNode;

            root = Visit(root);
            return(await composition.WithNewDocumentRootAsync(root));
        }
Example #8
0
        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());
        }
Example #9
0
        /// <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));
        }
Example #10
0
        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);
Example #12
0
        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);
        }
Example #13
0
        /// <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()));
        }
Example #14
0
        /// <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));
        }
Example #15
0
        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());
        }
Example #16
0
        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));
        }
Example #17
0
 /// <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);