protected bool TryGetValidProject(out Project project, out MDKProjectProperties projectProperties) { var package = (MDKPackage)Package; package.Echo("MDK Command", Id, "is attempting to retrieve an MDK project"); var dte2 = (EnvDTE80.DTE2)Package.DTE; project = ((IEnumerable)dte2.ToolWindows.SolutionExplorer.SelectedItems) .OfType <UIHierarchyItem>() .Select(item => item.Object) .OfType <Project>() .FirstOrDefault(); if (project == null) { package.Echo("MDK Command", Id, "failed because no project could be found"); projectProperties = null; return(false); } projectProperties = MDKProjectProperties.Load(project.FullName, project.Name); if (projectProperties.IsValid) { package.Echo("MDK Command", Id, "retrieved a valid project"); return(true); } package.Echo("MDK Command", Id, "did not retrieve a valid project"); return(false); }
bool IsDebugDocument(string filePath, MDKProjectProperties 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)); }
async void Refresh() { SaveAndCloseCommand.IsEnabled = false; CancelCommand.IsEnabled = false; _isWorking = true; var cache = new WhitelistCache(); await cache.RefreshAsync(_package.InstallPath.FullName); var dte2 = (EnvDTE80.DTE2)_package.DTE; var projects = ((IEnumerable)dte2.ToolWindows.SolutionExplorer.SelectedItems) .OfType <UIHierarchyItem>() .Select(item => item.Object) .OfType <Project>(); foreach (var project in projects) { var projectProperties = MDKProjectProperties.Load(project.FullName, project.Name); if (projectProperties.IsValid) { var targetCacheFile = Path.Combine(Path.GetDirectoryName(projectProperties.FileName) ?? ".", "MDK\\whitelist.cache"); var sourceCacheFile = Path.Combine(_package.InstallPath.FullName, "Analyzers\\whitelist.cache"); if (File.Exists(sourceCacheFile)) { File.Copy(sourceCacheFile, targetCacheFile, true); } } } _isDone = true; SaveAndCloseCommand.IsEnabled = true; SaveAndClose(); }
/// <summary> /// Creates a new instance of <see cref="ScriptOptionsDialogModel"/> /// </summary> /// <param name="package"></param> /// <param name="projectProperties"></param> public ScriptOptionsDialogModel([NotNull] MDKPackage package, [NotNull] MDKProjectProperties projectProperties) { if (package == null) { throw new ArgumentNullException(nameof(package)); } ActiveProject = projectProperties ?? throw new ArgumentNullException(nameof(projectProperties)); }
/// <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, MDKProjectProperties 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="projectProperties"></param> public ScriptOptionsDialogModel([NotNull] MDKPackage package, [NotNull] MDKProjectProperties projectProperties) { if (package == null) { throw new ArgumentNullException(nameof(package)); } Minifiers = new Collection <KeyValuePair <MinifyLevel, string> > { new KeyValuePair <MinifyLevel, string>(MinifyLevel.None, "None"), new KeyValuePair <MinifyLevel, string>(MinifyLevel.StripComments, "Strip Comments"), new KeyValuePair <MinifyLevel, string>(MinifyLevel.Full, "Full"), }; ActiveProject = projectProperties ?? throw new ArgumentNullException(nameof(projectProperties)); _selectedMinifier = this.Minifiers.FirstOrDefault(m => ActiveProject.Options.MinifyLevel == m.Key); }
protected bool TryGetValidProject(out Project project, out MDKProjectProperties projectProperties) { var dte2 = (EnvDTE80.DTE2)Package.DTE; project = ((IEnumerable)dte2.ToolWindows.SolutionExplorer.SelectedItems) .OfType <UIHierarchyItem>() .Select(item => item.Object) .OfType <Project>() .FirstOrDefault(); if (project == null) { projectProperties = null; return(false); } projectProperties = MDKProjectProperties.Load(project.FullName, project.Name); return(projectProperties.IsValid); }
//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, MDKProjectProperties 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, 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)); }
/// <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);
public async Task <ProgramComposition> ProcessAsync(ProgramComposition composition, MDKProjectProperties config) { var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
protected bool TryGetValidProject(out MDKProjectProperties projectProperties) => TryGetValidProject(out _, out projectProperties);
/// <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 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 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)); }
async Task <ProjectContent> LoadContentAsync(Project project, MDKProjectProperties config) { var usingDirectives = ImmutableArray.CreateBuilder <UsingDirectiveSyntax>(); var parts = ImmutableArray.CreateBuilder <ScriptPart>(); var documents = project.Documents .Where(document => !IsDebugDocument(document.FilePath, config)) .ToList(); var readmeDocuments = project.AdditionalDocuments .Where(document => DirectoryOf(document)?.Equals(Path.GetDirectoryName(project.FilePath), StringComparison.CurrentCultureIgnoreCase) ?? false) .Where(document => ExtensionOf(document).Equals("readme", StringComparison.CurrentCultureIgnoreCase)) .OrderBy(NameOf) .ToList(); var readMe = new StringBuilder(); for (int i = 0, n = readmeDocuments.Count - 1; i <= n; i++) { var document = readmeDocuments[i]; if (i > 0) { readMe.Append('\n'); } readMe.Append(await LoadReadMeContentsAsync(document)); } documents.RemoveAll(d => readmeDocuments.Contains(d)); WrapAsComment(readMe); var legacyReadmeDocument = project.Documents .Where(document => DirectoryOf(document)?.Equals(Path.GetDirectoryName(project.FilePath), StringComparison.CurrentCultureIgnoreCase) ?? false) .FirstOrDefault(document => NameOf(document).Equals("readme", StringComparison.CurrentCultureIgnoreCase)); if (legacyReadmeDocument != null) { documents.Remove(legacyReadmeDocument); if (readMe.Length > 0) { readMe.Append('\n'); } readMe.Append(await LoadReadMeContentsAsync(legacyReadmeDocument)); } readmeDocuments.Add(legacyReadmeDocument); 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.Length > 0? readMe.ToString() : null)); }
/// <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) { _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 root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
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 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)); }
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); if (typeUsage.Symbol is ITypeSymbol typeSymbol) { foreach (var interfaceName in typeSymbol.AllInterfaces.Select(GetFullName)) { _externallyReferencedMembers.Add(interfaceName); } if (typeSymbol.BaseType != null) { _externallyReferencedMembers.Add(GetFullName(typeSymbol.BaseType)); } } } } } } var root = composition.RootNode; root = Visit(root); return(await composition.WithNewDocumentRootAsync(root)); }
public async Task <ImmutableArray <SymbolDefinitionInfo> > FindUsagesAsync(ProgramComposition composition, MDKProjectProperties 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()); }