Repository navigation
Expand file tree
/
Copy pathformatter.go
More file actions
121 lines (100 loc) · 3.13 KB
/
Copy pathformatter.go
File metadata and controls
121 lines (100 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package stackparse
import (
"fmt"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/tree"
)
// Formatter handles the formatting of parsed stack traces
type Formatter struct {
config *Config
}
// NewFormatter creates a new Formatter instance
//
// Mostly used internally, but exposed for some edge cases
func NewFormatter(config *Config) *Formatter {
return &Formatter{
config: config,
}
}
// Format converts a StackTrace into a formatted string
//
// If you are using this by itself without calling stackparse.Parse(), you should call this after the parser.
func (f *Formatter) Format(traces []*StackTrace) string {
if !f.config.Colorize {
f.config.Theme.DisableStyles() // disable styles, leave formatting styles
}
var result []string
for _, trace := range traces {
result = append(result, f.formatTrace(trace))
}
return strings.Join(result, "\n\n")
}
func (f *Formatter) formatTrace(trace *StackTrace) string {
// var result []string
var result *tree.Tree
// Format goroutine header
header := fmt.Sprintf("Goroutine %s: %s",
trace.GoroutineID, trace.GoroutineState)
// result = append(result, f.config.Theme.Goroutine.Render(header))
result = tree.New().Root(f.config.Theme.Goroutine.Render(header))
// Calculate widths and counts
functionCounts := f.countFunctions(trace.Entries)
// Format entries
for _, entry := range trace.Entries {
formattedEntry := f.formatEntry(entry, functionCounts)
// result = append(result, formattedEntry)
result.Child(formattedEntry)
}
// return strings.Join(result, "\n")
return result.String()
}
func (f *Formatter) countFunctions(entries []StackEntry) map[string]int {
counts := make(map[string]int)
for _, entry := range entries {
counts[entry.FunctionName]++
}
return counts
}
func (f *Formatter) formatEntry(entry StackEntry, functionCounts map[string]int) string {
var currentTree *tree.Tree
// Function name with potential truncation indicator
displayName := entry.FunctionName
if entry.FullName != "" && entry.FullName != entry.FunctionName {
displayName = displayName + " ..."
}
if entry.IsCreatedBy {
createdByInfo := fmt.Sprintf("Created by: %s (goroutine %s)",
displayName, entry.CreatedByGoroutine)
currentTree = tree.New().Root(
f.config.Theme.CreatedBy.Render(createdByInfo),
)
} else {
// Create base function node
funcInfo := lipgloss.JoinHorizontal(
lipgloss.Left,
f.config.Theme.Function.Render(displayName),
f.config.Theme.Args.Render(fmt.Sprintf("(%s)", entry.Args)),
)
// Add repeat count if needed
if count := functionCounts[entry.FunctionName]; count > 1 {
funcInfo += f.config.Theme.Repeat.Render(
fmt.Sprintf(" (repeated %d times)", count),
)
}
currentTree = tree.New().Root(funcInfo)
}
// Add file location as child node if available
if entry.File != "" {
fileInfo := lipgloss.JoinHorizontal(
lipgloss.Left,
f.config.Theme.File.Render(entry.File),
f.config.Theme.Line.Render(fmt.Sprintf(":%s", entry.Line)),
)
if entry.Offset != "" {
fileInfo += f.config.Theme.Line.Render(fmt.Sprintf(" +%s", entry.Offset))
}
currentTree.Child(fileInfo)
}
return currentTree.String()
}