Beispiel #1
0
        public void Populate(SearchResult result)
        {
            if (result == null)
            {
                return;
            }

            if (result.MatchedByType && result.WordsInFields.Count == 0)
            {
                Highlights.Add(new HighlightedText {
                    Text = OriginalType
                });
                Highlights.Add(" " + TextUtilities.ShortenValue(result.Node.ToString(), "..."));

                AddDuration(result);

                return;
            }

            Highlights.Add(OriginalType);

            foreach (var wordsInField in result.WordsInFields.GroupBy(t => t.field, t => t.match))
            {
                Highlights.Add(" ");

                var fieldText = wordsInField.Key;

                //NameValueNode is speial case: have to show name=value when seached only in one (name or value)
                var named = SearchResult.Node as NameValueNode;
                if (named != null && wordsInField.Key.Equals(named.Value))
                {
                    Highlights.Add(named.Name + " = ");
                }

                fieldText = TextUtilities.ShortenValue(fieldText, "...");

                var highlightSpans = TextUtilities.GetHighlightedSpansInText(fieldText, wordsInField);
                int index          = 0;
                foreach (var span in highlightSpans)
                {
                    if (span.Start > index)
                    {
                        Highlights.Add(fieldText.Substring(index, span.Start - index));
                    }

                    Highlights.Add(new HighlightedText {
                        Text = fieldText.Substring(span.Start, span.Length)
                    });
                    index = span.End;
                }

                if (index < fieldText.Length)
                {
                    Highlights.Add(fieldText.Substring(index, fieldText.Length - index));
                }

                //NameValueNode is speial case: have to show name=value when seached only in one (name or value)
                if (named != null && wordsInField.Key.Equals(named.Name))
                {
                    Highlights.Add(" = " + TextUtilities.ShortenValue(named.Value, "..."));
                }
            }

            AddDuration(result);
        }
Beispiel #2
0
        /// <summary>
        /// Try to update the project data given a project started event. This is useful if the project
        /// was created (e.g. as a parent) before we saw the started event.
        /// <remarks>Does nothing if the data has already been set or the new data is null.</remarks>
        /// </summary>
        /// <param name="args">The <see cref="ProjectStartedEventArgs"/> instance containing the event data.</param>
        public void UpdateProject(Project project, ProjectStartedEventArgs args)
        {
            if (project.Name == null && args != null)
            {
                project.StartTime    = args.Timestamp;
                project.Name         = Intern(Path.GetFileName(args.ProjectFile));
                project.ProjectFile  = Intern(args.ProjectFile);
                project.EntryTargets = string.IsNullOrWhiteSpace(args.TargetNames)
                    ? ImmutableArray <string> .Empty
                    : stringTable.InternList(TextUtilities.SplitSemicolonDelimitedList(args.TargetNames));
                project.TargetsText = args.TargetNames;

                var evaluationId = BuildEventContext.InvalidEvaluationId;
                if (args.BuildEventContext.EvaluationId > BuildEventContext.InvalidEvaluationId)
                {
                    evaluationId = args.BuildEventContext.EvaluationId;
                }
                else if (args.ParentProjectBuildEventContext != null && args.ParentProjectBuildEventContext.EvaluationId > BuildEventContext.InvalidEvaluationId)
                {
                    evaluationId = args.ParentProjectBuildEventContext.EvaluationId;
                }

                project.EvaluationId = evaluationId;
                if (evaluationId != BuildEventContext.InvalidEvaluationId)
                {
                    project.EvaluationText = Intern("id:" + evaluationId);
                }

                project.GlobalProperties = stringTable.InternStringDictionary(args.GlobalProperties) ?? ImmutableDictionary <string, string> .Empty;

                if (args.GlobalProperties != null)
                {
                    AddGlobalProperties(project);
                }

                if (!string.IsNullOrEmpty(args.TargetNames))
                {
                    AddEntryTargets(project);
                }

                if (args.Properties != null)
                {
                    var properties = project.GetOrCreateNodeWithName <Folder>(Strings.Properties);
                    AddProperties(
                        properties,
                        args
                        .Properties
                        .Cast <DictionaryEntry>()
                        .OrderBy(d => d.Key)
                        .Select(d => new KeyValuePair <string, string>(
                                    Intern(Convert.ToString(d.Key)),
                                    Intern(Convert.ToString(d.Value)))),
                        project);
                }

                if (args.Items != null)
                {
                    RetrieveProjectInstance(project, args);

                    var items = project.GetOrCreateNodeWithName <Folder>("Items");
                    foreach (DictionaryEntry kvp in args.Items.OfType <DictionaryEntry>().OrderBy(i => i.Key))
                    {
                        var itemName  = Intern(Convert.ToString(kvp.Key));
                        var itemGroup = items.GetOrCreateNodeWithName <Folder>(itemName);

                        var item = new Item();

                        var taskItem = kvp.Value as ITaskItem;
                        if (taskItem != null)
                        {
                            item.Text = Intern(taskItem.ItemSpec);
                            foreach (DictionaryEntry metadataName in taskItem.CloneCustomMetadata())
                            {
                                item.AddChild(new Metadata
                                {
                                    Name  = Intern(Convert.ToString(metadataName.Key)),
                                    Value = Intern(Convert.ToString(metadataName.Value))
                                });
                            }

                            itemGroup.AddChild(item);
                        }
                    }
                }
            }
        }
        public void Populate(SearchResult result)
        {
            if (result == null)
            {
                return;
            }

            var node = result.Node;

            if (result.WordsInFields.Count == 0)
            {
                if (result.MatchedByType)
                {
                    Highlights.Add(new HighlightedText {
                        Text = OriginalType
                    });
                }

                Highlights.Add((Highlights.Count > 0 ? " " : "") + TextUtilities.ShortenValue(GetNodeText(node), "..."));

                AddDuration(result);

                return;
            }

            if (OriginalType != Strings.Folder)
            {
                Highlights.Add(OriginalType);
            }

            // NameValueNode is special case: have to show name=value when searched only in one (name or value)
            var nameValueNode = node as NameValueNode;
            var namedNode     = node as NamedNode;

            bool nameFound          = false;
            bool valueFound         = false;
            bool namedNodeNameFound = false;

            foreach (var fieldText in result.WordsInFields)
            {
                if (nameValueNode != null)
                {
                    if (!nameFound && fieldText.field.Equals(nameValueNode.Name))
                    {
                        nameFound = true;
                    }

                    if (!valueFound && fieldText.field.Equals(nameValueNode.Value))
                    {
                        valueFound = true;
                    }
                }
                else if (namedNode != null && !namedNodeNameFound)
                {
                    if (fieldText.field.Equals(namedNode.Name))
                    {
                        namedNodeNameFound = true;
                    }
                }
            }

            if (namedNode != null && !namedNodeNameFound)
            {
                Highlights.Add((Highlights.Count > 0 ? " " : "") + namedNode.Name);
                if (GetNodeDifferentiator(node) is object differentiator)
                {
                    Highlights.Add(differentiator);
                }
            }

            foreach (var wordsInField in result.WordsInFields.GroupBy(t => t.field, t => t.match))
            {
                var fieldText = wordsInField.Key;
                if (fieldText == OriginalType)
                {
                    // OriginalType already added above
                    continue;
                }

                if (Highlights.Count > 0)
                {
                    Highlights.Add(" ");
                }

                if (nameValueNode != null && fieldText.Equals(nameValueNode.Value) && !nameFound)
                {
                    Highlights.Add(nameValueNode.Name + " = ");
                }

                fieldText = TextUtilities.ShortenValue(fieldText, "...");

                var highlightSpans = TextUtilities.GetHighlightedSpansInText(fieldText, wordsInField);
                int index          = 0;
                foreach (var span in highlightSpans)
                {
                    if (span.Start > index)
                    {
                        Highlights.Add(fieldText.Substring(index, span.Start - index));
                    }

                    Highlights.Add(new HighlightedText {
                        Text = fieldText.Substring(span.Start, span.Length)
                    });
                    index = span.End;
                }

                if (index < fieldText.Length)
                {
                    Highlights.Add(fieldText.Substring(index, fieldText.Length - index));
                }

                if (nameValueNode != null && wordsInField.Key.Equals(nameValueNode.Name))
                {
                    if (!valueFound)
                    {
                        Highlights.Add(" = " + TextUtilities.ShortenValue(nameValueNode.Value, "..."));
                    }
                    else
                    {
                        Highlights.Add(" = ");
                    }
                }

                if (namedNode != null && namedNode.Name == wordsInField.Key)
                {
                    if (GetNodeDifferentiator(node) is object differentiator)
                    {
                        Highlights.Add(differentiator);
                    }
                }
            }

            AddDuration(result);
        }
        public override string ToString()
        {
            var sb = new StringBuilder();

            sb.Append($"Project Name={Name} File={ProjectFile}");
            if (EntryTargets != null && EntryTargets.Any())
            {
                sb.Append($" Targets=[{string.Join(", ", EntryTargets)}]");
            }

            if (GlobalProperties != null)
            {
                sb.Append($" GlobalProperties=[{string.Join(", ", GlobalProperties.Select(kvp => $"{kvp.Key}={TextUtilities.ShortenValue(kvp.Value, "...", maxChars: 150)}"))}]");
            }

            return(sb.ToString());
        }
        public static void HandleThereWasAConflict(Parameter parameter, string message, StringCache stringTable)
        {
            var      numberOfLeadingSpaces = TextUtilities.GetNumberOfLeadingSpaces(message);
            TreeNode node = parameter;

            switch (numberOfLeadingSpaces)
            {
            case 0:
                parameter.AddChild(new Item()
                {
                    Text = stringTable.Intern(message)
                });
                break;

            case 4:
                node = parameter.LastChild as TreeNode ?? node;
                Add(node, message, 4);
                break;

            case 6:
            {
                if (parameter.LastChild is TreeNode item)
                {
                    node = item;
                    if (item.LastChild is TreeNode item2)
                    {
                        node = item2;
                    }
                }
                Add(node, message, 6);
            }
            break;

            case 8:
            {
                if (parameter.LastChild is TreeNode item)
                {
                    node = item;
                    if (item.LastChild is TreeNode item2)
                    {
                        node = item2;
                        if (item2.LastChild is TreeNode item3)
                        {
                            node = item3;
                        }
                    }
                }
                Add(node, message, 8);
            }
            break;

            default:
                Add(node, message, 0);
                break;
            }

            void Add(TreeNode parent, string text, int spaces)
            {
                text = text.Substring(spaces);
                parent.AddChild(new Item
                {
                    Text = stringTable.Intern(text)
                });
            }
        }
        /// <summary>
        /// Handles a generic BuildMessage event and assigns it to the appropriate logging node.
        /// </summary>
        /// <param name="args">The <see cref="BuildMessageEventArgs"/> instance containing the event data.</param>
        public void AddMessage(LazyFormattedBuildEventArgs args, string message)
        {
            message = Intern(message);

            TreeNode node        = null;
            var      messageNode = new Message
            {
                Text      = message,
                Timestamp = args.Timestamp
            };
            BaseNode nodeToAdd = messageNode;

            var buildEventContext = args.BuildEventContext;

            if (buildEventContext?.TaskId > 0)
            {
                node = GetTask(args);
                if (node is Task task)
                {
                    if (task.Name == "ResolveAssemblyReference")
                    {
                        Folder inputs  = task.FindChild <Folder>(Strings.Inputs);
                        Folder results = task.FindChild <Folder>(Strings.Results);
                        node = results ?? inputs;

                        if (message.StartsWith("    ", StringComparison.Ordinal))
                        {
                            message = message.Substring(4);

                            var parameter = node?.FindLastChild <Parameter>();
                            if (parameter != null)
                            {
                                bool thereWasAConflict = Strings.IsThereWasAConflictPrefix(parameter.ToString()); //parameter.ToString().StartsWith(Strings.ThereWasAConflictPrefix);
                                if (thereWasAConflict)
                                {
                                    HandleThereWasAConflict(parameter, message, stringTable);
                                    return;
                                }

                                if (!string.IsNullOrWhiteSpace(message))
                                {
                                    node = parameter;

                                    if (message.StartsWith("    ", StringComparison.Ordinal))
                                    {
                                        message = message.Substring(4);

                                        var lastItem = parameter.FindLastChild <Item>();

                                        // only indent if it's not a "For SearchPath..." message - that one needs to be directly under parameter
                                        // also don't indent if it's under AssemblyFoldersEx in Results
                                        if (lastItem != null &&
                                            !Strings.ForSearchPathPrefix.IsMatch(message) &&
                                            !parameter.Name.StartsWith("AssemblyFoldersEx", StringComparison.Ordinal))
                                        {
                                            node = lastItem;
                                        }
                                    }

                                    if (!string.IsNullOrEmpty(message))
                                    {
                                        var equals = message.IndexOf('=');
                                        if (equals != -1)
                                        {
                                            var kvp = TextUtilities.ParseNameValue(message);
                                            node.AddChild(new Metadata
                                            {
                                                Name  = Intern(kvp.Key.TrimEnd(space)),
                                                Value = Intern(kvp.Value.TrimStart(space))
                                            });
                                        }
                                        else
                                        {
                                            node.AddChild(new Item()
                                            {
                                                Text = Intern(message)
                                            });
                                        }
                                    }
                                }

                                return;
                            }
                        }
                        else
                        {
                            if (results == null)
                            {
                                bool isResult = Strings.UnifiedPrimaryReferencePrefix.IsMatch(message) ||
                                                Strings.PrimaryReferencePrefix.IsMatch(message) ||
                                                Strings.DependencyPrefix.IsMatch(message) ||
                                                Strings.UnifiedDependencyPrefix.IsMatch(message) ||
                                                Strings.AssemblyFoldersExLocation.IsMatch(message) ||
                                                Strings.IsThereWasAConflictPrefix(message);

                                if (isResult)
                                {
                                    results = task.GetOrCreateNodeWithName <Folder>(Strings.Results);
                                    node    = results;
                                }
                                else
                                {
                                    if (inputs == null)
                                    {
                                        inputs = task.GetOrCreateNodeWithName <Folder>(Strings.Inputs);
                                    }

                                    node = inputs;
                                }
                            }
                            else
                            {
                                node = results;
                            }

                            node.GetOrCreateNodeWithName <Parameter>(Intern(message.TrimEnd(':')));
                            return;
                        }
                    }
                    else if (string.Equals(task.Name, "MSBuild", StringComparison.OrdinalIgnoreCase))
                    {
                        var additionalPropertiesMatch = Strings.AdditionalPropertiesPrefix.Match(message);
                        if (message.StartsWith(Strings.GlobalPropertiesPrefix, StringComparison.Ordinal) ||
                            additionalPropertiesMatch.Success ||
                            Strings.OverridingGlobalPropertiesPrefix.IsMatch(message) ||
                            message.StartsWith(Strings.RemovingPropertiesPrefix, StringComparison.Ordinal) ||
                            Strings.RemovingProjectProperties.IsMatch(message))
                        {
                            if (additionalPropertiesMatch.Success)
                            {
                                node = node.GetOrCreateNodeWithName <Folder>(Strings.AdditionalProperties);
                            }

                            node.GetOrCreateNodeWithName <Folder>(message);
                            return;
                        }

                        node = node.FindLastChild <Folder>() ?? node;
                        if (message.Length > 2 && message[0] == ' ' && message[1] == ' ')
                        {
                            if (node is Folder f && f.Name == Strings.AdditionalProperties)
                            {
                                node = f.FindLastChild <Folder>() ?? node;
                            }

                            message = message.Substring(2);
                        }

                        var kvp = TextUtilities.ParseNameValue(message);
                        if (kvp.Value == "")
                        {
                            nodeToAdd = new Item
                            {
                                Text = Intern(kvp.Key)
                            };
                        }
                        else
                        {
                            nodeToAdd = new Property
                            {
                                Name  = Intern(kvp.Key),
                                Value = Intern(kvp.Value)
                            };
                        }
                    }
                    else if (string.Equals(task.Name, "RestoreTask"))
                    {
                        // just throw these away to save space
                        // https://github.com/NuGet/Home/issues/10383
                        if (message.StartsWith(Strings.RestoreTask_CheckingCompatibilityFor, StringComparison.Ordinal))
                        {
                            return;
                        }
                        else if (message.StartsWith("  GET", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("GET");
                        }
                        else if (message.StartsWith("  CACHE", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("CACHE");
                        }
                        else if (message.StartsWith("  OK", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("OK");
                        }
                        else if (message.StartsWith("  NotFound", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("NotFound");
                        }
                        else if (message.StartsWith("PackageSignatureVerificationLog:", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("PackageSignatureVerificationLog");
                        }
                        else if (message.StartsWith("Writing assets file to disk", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Assets file");
                        }
                        else if (message.StartsWith("Writing cache file to disk", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Cache file");
                        }
                        else if (message.StartsWith("Persisting dg to", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("dg file");
                        }
                        else if (message.StartsWith("Generating MSBuild file", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("MSBuild file");
                        }
                        else if (message.StartsWith("Lock not required", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Lock not required");
                        }
                        else if (message.StartsWith("Installing", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Installing");
                        }
                        else if (message.StartsWith("Restoring packages for", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Restoring packages for");
                        }
                        else if (message.StartsWith("Reading project file", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Reading project file");
                        }
                        else if (message.StartsWith("Scanning packages for", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Scanning packages for");
                        }
                        else if (message.StartsWith("Merging in runtimes", StringComparison.Ordinal))
                        {
                            node = node.GetOrCreateNodeWithName <Folder>("Merging in runtimes");
                        }
                        else if (
                            message.StartsWith(Strings.RestoreTask_CheckingCompatibilityFor, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_CheckingCompatibilityOfPackages, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_AcquiringLockForTheInstallation, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_AcquiredLockForTheInstallation, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_CompletedInstallationOf, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_ResolvingConflictsFor, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_AllPackagesAndProjectsAreCompatible, StringComparison.Ordinal) ||
                            message.StartsWith(Strings.RestoreTask_Committing, StringComparison.Ordinal)
                            )
                        {
                            return;
                        }
                    }
                }
            }
            else if (buildEventContext?.TargetId > 0)
            {
                node = GetTarget(args);

                if (Strings.TaskSkippedFalseCondition.Match(message).Success)
                {
                    messageNode.IsLowRelevance = true;
                }
            }
            else if (buildEventContext?.ProjectContextId > 0)
            {
                var project = construction.GetOrAddProject(buildEventContext.ProjectContextId);
                node = project;

                if (Strings.IsTargetSkipped(message))
                {
                    var targetName = Intern(TextUtilities.ParseQuotedSubstring(message));
                    if (targetName != null)
                    {
                        var skippedTarget = project.GetOrAddTargetByName(targetName, args.Timestamp);
                        skippedTarget.StartTime = args.Timestamp;
                        skippedTarget.EndTime   = args.Timestamp;
                        node = skippedTarget;
                        messageNode.IsLowRelevance = true;
                    }
                }
            }
            else if (buildEventContext.EvaluationId != -1)
            {
                node = construction.EvaluationFolder;

                var evaluationId = buildEventContext.EvaluationId;
                var evaluation   = construction.Build.FindEvaluation(evaluationId);
                if (evaluation != null)
                {
                    node = evaluation;
                }

                if (Strings.PropertyReassignmentRegex.IsMatch(message))
                {
                    TimedNode properties;
                    if (evaluation != null)
                    {
                        properties = evaluation.PropertyReassignmentFolder;
                    }
                    else
                    {
                        properties = node.GetOrCreateNodeWithName <TimedNode>(Strings.PropertyReassignmentFolder, addAtBeginning: true);
                    }

                    var propertyName = Strings.GetPropertyName(message);
                    node = properties.GetOrCreateNodeWithName <Folder>(propertyName);
                }

                if (node != null && node.FindChild <Message>(message) != null)
                {
                    // avoid duplicate messages
                    return;
                }
            }

            if (node == null)
            {
                node = construction.Build;

                if (Strings.IsEvaluationMessage(message))
                {
                    if (!evaluationMessagesAlreadySeen.Add(message))
                    {
                        return;
                    }

                    node = construction.EvaluationFolder;
                }
                else if (Strings.PropertyReassignmentRegex.IsMatch(message))
                {
                    if (!evaluationMessagesAlreadySeen.Add(message))
                    {
                        return;
                    }

                    var properties = construction.EvaluationFolder.GetOrCreateNodeWithName <Folder>(Strings.PropertyReassignmentFolder);
                    node = properties.GetOrCreateNodeWithName <Folder>(Strings.GetPropertyName(message));
                }
                else if (Strings.IsTargetDoesNotExistAndWillBeSkipped(message))
                {
                    var folder = construction.EvaluationFolder;
                    node = folder;
                    messageNode.IsLowRelevance = true;
                }
                else if (buildEventContext != null && (buildEventContext.NodeId == 0 &&
                                                       buildEventContext.ProjectContextId == 0 &&
                                                       buildEventContext.ProjectInstanceId == 0 &&
                                                       buildEventContext.TargetId == 0 &&
                                                       buildEventContext.TaskId == 0))
                {
                    // must be Detailed Build Summary
                    // https://github.com/Microsoft/msbuild/blob/master/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs#L509
                    DetailedSummary.AppendLine(message);
                    return;
                }
            }

            node.AddChild(nodeToAdd);
        }
        /// <summary>
        /// Handles a generic BuildMessage event and assigns it to the appropriate logging node.
        /// </summary>
        /// <param name="args">The <see cref="BuildMessageEventArgs"/> instance containing the event data.</param>
        public void AddMessage(LazyFormattedBuildEventArgs args, string message)
        {
            message = stringTable.Intern(message);

            TreeNode node        = null;
            var      messageNode = new Message
            {
                Text      = message,
                Timestamp = args.Timestamp
            };
            object nodeToAdd = messageNode;

            if (args.BuildEventContext?.TaskId > 0)
            {
                node = construction
                       .GetOrAddProject(args.BuildEventContext.ProjectContextId)
                       .GetTargetById(args.BuildEventContext.TargetId)
                       .GetTaskById(args.BuildEventContext.TaskId);
                var task = node as Task;
                if (task != null)
                {
                    if (task.Name == "ResolveAssemblyReference")
                    {
                        Folder inputs  = task.GetOrCreateNodeWithName <Folder>("Inputs");
                        Folder results = task.FindChild <Folder>("Results");
                        node = results ?? inputs;

                        if (message.StartsWith("    "))
                        {
                            message = message.Substring(4);

                            var parameter = node.FindLastChild <Parameter>();
                            if (parameter != null)
                            {
                                bool thereWasAConflict = parameter.ToString().StartsWith(Strings.ThereWasAConflictPrefix);
                                if (thereWasAConflict)
                                {
                                    HandleThereWasAConflict(parameter, message, stringTable);
                                    return;
                                }

                                if (!string.IsNullOrWhiteSpace(message))
                                {
                                    node = parameter;

                                    if (message.StartsWith("    "))
                                    {
                                        message = message.Substring(4);

                                        var lastItem = parameter.FindLastChild <Item>();

                                        // only indent if it's not a "For SearchPath..." message - that one needs to be directly under parameter
                                        // also don't indent if it's under AssemblyFoldersEx in Results
                                        if (lastItem != null &&
                                            !message.StartsWith(Strings.ForSearchPathPrefix) &&
                                            !parameter.Name.StartsWith("AssemblyFoldersEx"))
                                        {
                                            node = lastItem;
                                        }
                                    }

                                    if (!string.IsNullOrEmpty(message))
                                    {
                                        var equals = message.IndexOf('=');
                                        if (equals != -1)
                                        {
                                            var kvp = TextUtilities.ParseNameValue(message);
                                            node.AddChild(new Metadata
                                            {
                                                Name  = stringTable.Intern(kvp.Key.TrimEnd(space)),
                                                Value = stringTable.Intern(kvp.Value.TrimStart(space))
                                            });
                                        }
                                        else
                                        {
                                            node.AddChild(new Item()
                                            {
                                                Text = stringTable.Intern(message)
                                            });
                                        }
                                    }
                                }

                                return;
                            }
                        }
                        else
                        {
                            if (results == null)
                            {
                                bool isResult = message.StartsWith(Strings.UnifiedPrimaryReferencePrefix) ||
                                                message.StartsWith(Strings.PrimaryReferencePrefix) ||
                                                message.StartsWith(Strings.DependencyPrefix) ||
                                                message.StartsWith(Strings.UnifiedDependencyPrefix) ||
                                                message.StartsWith(Strings.AssemblyFoldersExLocation) ||
                                                message.StartsWith(Strings.ThereWasAConflictPrefix);

                                if (isResult)
                                {
                                    results = task.GetOrCreateNodeWithName <Folder>("Results");
                                    node    = results;
                                }
                                else
                                {
                                    node = inputs;
                                }
                            }
                            else
                            {
                                node = results;
                            }

                            node.GetOrCreateNodeWithName <Parameter>(stringTable.Intern(message.TrimEnd(':')));
                            return;
                        }
                    }
                    else if (task.Name == "MSBuild")
                    {
                        if (message.StartsWith(Strings.GlobalPropertiesPrefix) ||
                            message.StartsWith(Strings.AdditionalPropertiesPrefix) ||
                            message.StartsWith(Strings.OverridingGlobalPropertiesPrefix) ||
                            message.StartsWith(Strings.RemovingPropertiesPrefix))
                        {
                            node.GetOrCreateNodeWithName <Folder>(message);
                            return;
                        }

                        node = node.FindLastChild <Folder>();
                        if (message[0] == ' ' && message[1] == ' ')
                        {
                            message = message.Substring(2);
                        }

                        var kvp = TextUtilities.ParseNameValue(message);
                        if (kvp.Value == "")
                        {
                            nodeToAdd = new Item
                            {
                                Text = stringTable.Intern(kvp.Key)
                            };
                        }
                        else
                        {
                            nodeToAdd = new Property
                            {
                                Name  = stringTable.Intern(kvp.Key),
                                Value = stringTable.Intern(kvp.Value)
                            };
                        }
                    }
                }
            }
            else if (args.BuildEventContext?.TargetId > 0)
            {
                node = construction
                       .GetOrAddProject(args.BuildEventContext.ProjectContextId)
                       .GetTargetById(args.BuildEventContext.TargetId);

                if (Strings.IsTaskSkipped(message))
                {
                    messageNode.IsLowRelevance = true;
                }
            }
            else if (args.BuildEventContext?.ProjectContextId > 0)
            {
                var project = construction.GetOrAddProject(args.BuildEventContext.ProjectContextId);
                node = project;

                if (Strings.IsTargetSkipped(message))
                {
                    var targetName = stringTable.Intern(TextUtilities.ParseQuotedSubstring(message));
                    if (targetName != null)
                    {
                        node = project.GetOrAddTargetByName(targetName);
                        messageNode.IsLowRelevance = true;
                    }
                }
            }
            else if (Reflector.GetEvaluationId(args.BuildEventContext) is int evaluationId && evaluationId != -1)
            {
                var evaluation = construction.EvaluationFolder;
                var project    = evaluation.FindChild <Project>(p => p.Id == evaluationId);
                node = project;

                if (node != null && node.FindChild <Message>(message) != null)
                {
                    // avoid duplicate messages
                    return;
                }
            }

            if (node == null)
            {
                node = construction.Build;

                if (Strings.IsEvaluationMessage(message))
                {
                    if (!evaluationMessagesAlreadySeen.Add(message))
                    {
                        return;
                    }

                    node = construction.EvaluationFolder;
                }
                else if (Strings.IsTargetDoesNotExistAndWillBeSkipped(message))
                {
                    var folder = construction.EvaluationFolder;
                    node = folder;
                    messageNode.IsLowRelevance = true;
                }
                else if (args.BuildEventContext != null && (args.BuildEventContext.NodeId == 0 &&
                                                            args.BuildEventContext.ProjectContextId == 0 &&
                                                            args.BuildEventContext.ProjectInstanceId == 0 &&
                                                            args.BuildEventContext.TargetId == 0 &&
                                                            args.BuildEventContext.TaskId == 0))
                {
                    // must be Detailed Build Summary
                    // https://github.com/Microsoft/msbuild/blob/master/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs#L509
                    DetailedSummary.AppendLine(message);
                    return;
                }
            }

            node.AddChild(nodeToAdd);
        }