Exemple #1
0
            /// <summary>
            /// Converts a stack trace to formatted HTML with styling and linkifiation.
            /// </summary>
            /// <param name="stackTrace">The stack trace to HTMLify.</param>
            /// <param name="settings">The <see cref="StackTraceSettings"/> to use in this render.</param>
            /// <returns>An HTML-pretty version of the stack trace.</returns>
            public static string HtmlPrettify(string stackTrace, StackTraceSettings settings)
            {
                string GetBetween(Capture prev, Capture next) =>
                stackTrace.Substring(prev.Index + prev.Length, next.Index - (prev.Index + prev.Length));

                int pos     = 0;
                var sb      = StringBuilderCache.Get();
                var matches = _regex.Matches(stackTrace);

                for (var mi = 0; mi < matches.Count; mi++)
                {
                    Match m                      = matches[mi];
                    Group leadIn                 = m.Groups[Groups.LeadIn],
                          frame                  = m.Groups[Groups.Frame],
                          type                   = m.Groups[Groups.Type],
                          asyncMethod            = m.Groups[Groups.AsyncMethod],
                          method                 = m.Groups[Groups.Method],
                          allParams              = m.Groups[Groups.Params],
                          sourceInfo             = m.Groups[Groups.SourceInfo],
                          path                   = m.Groups[Groups.Path],
                          linePrefix             = m.Groups[Groups.LinePrefix],
                          line                   = m.Groups[Groups.Line];
                    CaptureCollection paramTypes = m.Groups[Groups.ParamType].Captures,
                                      paramNames = m.Groups[Groups.ParamName].Captures;
                    bool nextIsAsync             = false;
                    if (mi < matches.Count - 1)
                    {
                        Group nextFrame = matches[mi + 1].Groups[Groups.Frame];
                        nextIsAsync = _asyncFrames.Contains(nextFrame.Value);
                    }

                    var isAsync = _asyncFrames.Contains(frame.Value);

                    // The initial message may be above an async frame
                    if (sb.Length == 0 && isAsync && leadIn.Index > pos)
                    {
                        sb.Append("<span class=\"stack stack-row\">")
                        .Append("<span class=\"stack misc\">")
                        .AppendHtmlEncode(stackTrace.Substring(pos, leadIn.Index - pos).Trim(NewLine_CarriageReturn))
                        .Append("</span>")
                        .Append("</span>");
                        pos += sb.Length;
                    }

                    sb.Append(isAsync ? "<span class=\"stack stack-row async\">" : "<span class=\"stack stack-row\">");

                    if (leadIn.Index > pos)
                    {
                        var miscContent = stackTrace.Substring(pos, leadIn.Index - pos);
                        if (miscContent.Contains(EndStack))
                        {
                            // Handle end-of-stack removals and redundant multilines remaining
                            miscContent = miscContent.Replace(EndStack, "")
                                          .Replace("\r\n\r\n", "\r\n")
                                          .Replace("\n\n", "\n\n");
                        }

                        sb.Append("<span class=\"stack misc\">")
                        .AppendHtmlEncode(miscContent)
                        .Append("</span>");
                    }
                    sb.Append("<span class=\"stack leadin\">")
                    .AppendHtmlEncode(leadIn.Value)
                    .Append("</span>");

                    // Check if the next line is the end of an async hand-off
                    var nextEndStack = stackTrace.IndexOf(EndStack, m.Index + m.Length);
                    if ((nextEndStack > -1 && nextEndStack < m.Index + m.Length + 3) || (!isAsync && nextIsAsync))
                    {
                        sb.Append("<span class=\"stack async-tag\">async</span> ");
                    }

                    if (asyncMethod.Success)
                    {
                        sb.Append("<span class=\"stack type\">")
                        .AppendGenerics(GetBetween(leadIn, asyncMethod), settings)
                        .Append("</span>")
                        .Append("<span class=\"stack method\">")
                        .AppendHtmlEncode(asyncMethod.Value)
                        .Append("</span>")
                        .Append("<span class=\"stack type\">")
                        .AppendGenerics(GetBetween(asyncMethod, method), settings);
                        sb.Append("</span>");
                    }
                    else
                    {
                        sb.Append("<span class=\"stack type\">")
                        .AppendGenerics(type.Value, settings)
                        .Append("<span class=\"stack dot\">")
                        .AppendHtmlEncode(GetBetween(type, method))   // "."
                        .Append("</span>")
                        .Append("</span>");
                    }
                    sb.Append("<span class=\"stack method-section\">")
                    .Append("<span class=\"stack method\">")
                    .AppendHtmlEncode(NormalizeMethodName(method.Value))
                    .Append("</span>");

                    if (paramTypes.Count > 0)
                    {
                        sb.Append("<span class=\"stack parens\">")
                        .Append(GetBetween(method, paramTypes[0]))
                        .Append("</span>");
                        for (var i = 0; i < paramTypes.Count; i++)
                        {
                            if (i > 0)
                            {
                                sb.Append("<span class=\"stack misc\">")
                                .AppendHtmlEncode(GetBetween(paramNames[i - 1], paramTypes[i]))   // ", "
                                .Append("</span>");
                            }
                            sb.Append("<span class=\"stack paramType\">")
                            .AppendGenerics(paramTypes[i].Value, settings)
                            .Append("</span>")
                            .AppendHtmlEncode(GetBetween(paramTypes[i], paramNames[i]))   // " "
                            .Append("<span class=\"stack paramName\">")
                            .AppendHtmlEncode(paramNames[i].Value)
                            .Append("</span>");
                        }
                        var last = paramNames[paramTypes.Count - 1];
                        sb.Append("<span class=\"stack parens\">")
                        .AppendHtmlEncode(allParams.Value.Substring(last.Index + last.Length - allParams.Index))
                        .Append("</span>");
                    }
                    else
                    {
                        sb.Append("<span class=\"stack parens\">")
                        .AppendHtmlEncode(allParams.Value)   // "()"
                        .Append("</span>");
                    }
                    sb.Append("</span>"); // method-section for table layout

                    if (sourceInfo.Value.HasValue())
                    {
                        sb.Append("<span class=\"stack source-section\">");

                        var curPath = sourceInfo.Value;
                        if (settings.LinkReplacements.Count > 0)
                        {
                            foreach (var replacement in settings.LinkReplacements)
                            {
                                curPath = replacement.Key.Replace(curPath, replacement.Value);
                            }
                        }

                        if (curPath != sourceInfo.Value)
                        {
                            sb.Append("<span class=\"stack misc\">")
                            .AppendHtmlEncode(GetBetween(allParams, sourceInfo))
                            .Append("</span>")
                            .Append(curPath);
                        }
                        else if (path.Value.HasValue())
                        {
                            var subPath = GetSubPath(path.Value, type.Value);

                            sb.Append("<span class=\"stack misc\">")
                            .AppendHtmlEncode(GetBetween(allParams, path))
                            .Append("</span>")
                            .Append("<span class=\"stack path\">")
                            .AppendHtmlEncode(subPath)
                            .Append("</span>")
                            .AppendHtmlEncode(GetBetween(path, linePrefix))
                            .Append("<span class=\"stack line-prefix\">")
                            .AppendHtmlEncode(linePrefix.Value)
                            .Append("</span>")
                            .Append("<span class=\"stack line\">")
                            .AppendHtmlEncode(line.Value)
                            .Append("</span>");
                        }
                        sb.Append("</span>");
                    }

                    sb.Append("</span>");

                    pos = frame.Index + frame.Length;
                }

                // append anything left
                sb.Append("<span class=\"stack misc\">");
                LinkifyRest(sb, stackTrace, pos, settings.LinkReplacements);
                sb.Append("</span>");

                return(sb.ToStringRecycle());
            }
Exemple #2
0
        public static StringBuilder AppendGenerics(this StringBuilder sb, string typeOrMethod, StackTraceSettings settings)
        {
            const string _dotSpan = "<span class=\"stack dot\">.</span>";

            if (!settings.EnablePrettyGenerics)
            {
                return(sb.AppendHtmlEncode(typeOrMethod));
            }
            // Check the common framework list above
            _commonGenerics.TryGetValue(typeOrMethod, out string[] args);

            // Break each type down by namespace and class (remember, we *could* have nested generic classes)
            var classes = typeOrMethod.Split(_dot);

            // Loop through each dot component of the type, e.g. "System", "Collections", "Generics"
            for (var i = 0; i < classes.Length; i++)
            {
                if (i > 0)
                {
                    sb.Append(_dotSpan);
                }
                var match = _genericTypeRegex.Match(classes[i]);
                if (match.Success)
                {
                    // If arguments aren't known, get the defaults
                    if (args == null && int.TryParse(match.Groups["ArgCount"].Value, out int count))
                    {
                        if (count == 1)
                        {
                            args = _singleT;
                        }
                        else
                        {
                            args = new string[count];
                            for (var j = 0; j < count; j++)
                            {
                                args[j] = "T" + (j + 1).ToString(); // <T>, or <T1, T2, T3>
                            }
                        }
                    }
                    // In the known case, BaseClass is "System.Collections.Generic.Dictionary"
                    // In the unknown case, we're hitting here at "Class" only
                    sb.AppendHtmlEncode(match.Groups["BaseClass"].Value);
                    AppendArgs(args);
                }
                else
                {
                    sb.AppendHtmlEncode(classes[i]);
                }
            }
            return(sb);

            void AppendArgs(string[] tArgs)
            {
                switch (settings.Language)
                {
                case StackTraceSettings.CodeLanguage.VB:
                    sb.Append("(Of ");
                    break;

                case StackTraceSettings.CodeLanguage.CSharp:
                case StackTraceSettings.CodeLanguage.FSharp:
                    sb.Append("&lt;");
                    break;
                }
                // Don't put crazy amounts of arguments in here
                if (tArgs.Length > 5)
                {
                    sb.Append("<span class=\"stack generic-type\">").Append(tArgs[0]).Append("</span>")
                    .Append(",")
                    .Append("<span class=\"stack generic-type\">").Append(tArgs[1]).Append("</span>")
                    .Append(",")
                    .Append("<span class=\"stack generic-type\">").Append(tArgs[2]).Append("</span>")
                    .Append("…")
                    .Append("<span class=\"stack generic-type\">").Append(tArgs[tArgs.Length - 1]).Append("</span>");
                }
                else
                {
                    for (int i = 0; i < tArgs.Length; i++)
                    {
                        if (i > 0)
                        {
                            sb.Append(",");
                        }
                        if (settings.IncludeGenericTypeNames)
                        {
                            sb.Append("<span class=\"stack generic-type\">");
                            if (settings.Language == StackTraceSettings.CodeLanguage.FSharp)
                            {
                                sb.Append("'");
                            }
                            sb.Append(tArgs[i])
                            .Append("</span>");
                        }
                    }
                }

                switch (settings.Language)
                {
                case StackTraceSettings.CodeLanguage.VB:
                    sb.Append(")");
                    break;

                case StackTraceSettings.CodeLanguage.CSharp:
                case StackTraceSettings.CodeLanguage.FSharp:
                    sb.Append("&gt;");
                    break;
                }
            }
        }
Exemple #3
0
            /// <summary>
            /// Converts a stack trace to formatted HTML with styling and linkifiation.
            /// </summary>
            /// <param name="stackTrace">The stack trace to HTMLify.</param>
            /// <param name="settings">The settings to use when prettifying this stack trace.</param>
            /// <returns>An HTML-pretty version of the stack trace.</returns>
            public static string HtmlPrettify(string stackTrace, StackTraceSettings settings)
            {
                string GetBetween(Capture prev, Capture next) =>
                stackTrace.Substring(prev.Index + prev.Length, next.Index - (prev.Index + prev.Length));

                int pos = 0;
                var sb  = StringBuilderCache.Get();

                foreach (Match m in _regex.Matches(stackTrace))
                {
                    Group leadIn                 = m.Groups[Groups.LeadIn],
                               frame             = m.Groups[Groups.Frame],
                               type              = m.Groups[Groups.Type],
                               asyncMethod       = m.Groups[Groups.AsyncMethod],
                               method            = m.Groups[Groups.Method],
                               allParams         = m.Groups[Groups.Params],
                               path              = m.Groups[Groups.Path], // TODO: URLs
                               linePrefix        = m.Groups[Groups.LinePrefix],
                               line              = m.Groups[Groups.Line];
                    CaptureCollection paramTypes = m.Groups[Groups.ParamType].Captures,
                                      paramNames = m.Groups[Groups.ParamName].Captures;

                    var isAsync = _asyncFrames.Contains(frame.Value);
                    sb.Append(isAsync ? "<span class=\"stack row async\">" : "<span class=\"stack row\">");

                    sb.Append("<span class=\"stack misc\">")
                    .AppendHtmlEncode(stackTrace.Substring(pos, leadIn.Index - pos))
                    .Append("</span>")
                    .Append("<span class=\"stack leadin\">")
                    .AppendHtmlEncode(leadIn.Value)
                    .Append("</span>");

                    // Check if the next line is the end of an async hand-off
                    var nextEndStack = stackTrace.IndexOf(EndStack, m.Index + m.Length);
                    if (nextEndStack > -1 && nextEndStack < m.Index + m.Length + 3)
                    {
                        sb.Append("<span class=\"stack async-tag\">async</span> ");
                    }

                    if (asyncMethod.Success)
                    {
                        sb.Append("<span class=\"stack type\">")
                        .AppendGenerics(GetBetween(leadIn, asyncMethod), settings)
                        .Append("</span>")
                        .Append("<span class=\"stack method\">")
                        .AppendHtmlEncode(asyncMethod.Value)
                        .Append("</span>")
                        .Append("<span class=\"stack type\">")
                        .AppendGenerics(GetBetween(asyncMethod, method), settings);
                        sb.Append("</span>");
                    }
                    else
                    {
                        sb.Append("<span class=\"stack type\">")
                        .AppendGenerics(type.Value, settings)
                        .Append("<span class=\"stack dot\">")
                        .AppendHtmlEncode(GetBetween(type, method))   // "."
                        .Append("</span>")
                        .Append("</span>");
                    }
                    sb.Append("<span class=\"stack method-section\">")
                    .Append("<span class=\"stack method\">")
                    .AppendHtmlEncode(method.Value)
                    .Append("</span>");

                    if (paramTypes.Count > 0)
                    {
                        sb.Append("<span class=\"stack parens\">")
                        .Append(GetBetween(method, paramTypes[0]))
                        .Append("</span>");
                        for (var i = 0; i < paramTypes.Count; i++)
                        {
                            if (i > 0)
                            {
                                sb.Append("<span class=\"stack misc\">")
                                .AppendHtmlEncode(GetBetween(paramNames[i - 1], paramTypes[i]))   // ", "
                                .Append("</span>");
                            }
                            sb.Append("<span class=\"stack paramType\">")
                            .AppendGenerics(paramTypes[i].Value, settings)
                            .Append("</span>")
                            .AppendHtmlEncode(GetBetween(paramTypes[i], paramNames[i]))   // " "
                            .Append("<span class=\"stack paramName\">")
                            .AppendHtmlEncode(paramNames[i].Value)
                            .Append("</span>");
                        }
                        var last = paramNames[paramTypes.Count - 1];
                        sb.Append("<span class=\"stack parens\">")
                        .AppendHtmlEncode(allParams.Value.Substring(last.Index + last.Length - allParams.Index))
                        .Append("</span>");
                    }
                    else
                    {
                        sb.Append("<span class=\"stack parens\">")
                        .AppendHtmlEncode(allParams.Value)   // "()"
                        .Append("</span>");
                    }
                    sb.Append("</span>"); // method-section for table layout

                    // TODO: regular expression replacement for SourceLink
                    if (path.Value.HasValue())
                    {
                        var subPath = GetSubPath(path.Value, type.Value);

                        sb.Append("<span class=\"stack source-section\">")
                        .Append("<span class=\"stack misc\">")
                        .AppendHtmlEncode(GetBetween(allParams, path))
                        .Append("</span>")
                        .Append("<span class=\"stack path\">")
                        .AppendHtmlEncode(subPath)
                        .Append("</span>")
                        .AppendHtmlEncode(GetBetween(path, linePrefix))
                        .Append("<span class=\"stack line-prefix\">")
                        .AppendHtmlEncode(linePrefix.Value)
                        .Append("</span>")
                        .Append("<span class=\"stack line\">")
                        .AppendHtmlEncode(line.Value)
                        .Append("</span>")
                        .Append("</span>");
                    }

                    sb.Append("</span>");

                    pos = frame.Index + frame.Length;
                }
                // append anything left
                sb.Append("<span class=\"stack misc\">")
                .AppendHtmlEncode(stackTrace.Substring(pos))
                .Append("</span>");

                return(sb.ToStringRecycle());
            }