-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathformat.go
More file actions
214 lines (177 loc) · 5.81 KB
/
format.go
File metadata and controls
214 lines (177 loc) · 5.81 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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package main
import (
"fmt"
"regexp"
"strings"
"sync"
)
// syntaxPattern holds a compiled regex and its associated color
type syntaxPattern struct {
regex *regexp.Regexp
color string
}
// syntaxPatterns holds compiled regex patterns for syntax highlighting
var syntaxPatterns []syntaxPattern
// syntaxPatternsOnce ensures patterns are compiled only once
var syntaxPatternsOnce sync.Once
// initSyntaxPatterns initializes the syntax highlighting patterns
func initSyntaxPatterns() {
syntaxPatterns = []syntaxPattern{
// Strings (double-quoted)
{regexp.MustCompile(`"[^"\\]*(?:\\.[^"\\]*)*"`), Yellow},
// Strings (single-quoted)
{regexp.MustCompile(`'[^'\\]*(?:\\.[^'\\]*)*'`), Yellow},
// Environment variables ($VAR, ${VAR})
{regexp.MustCompile(`\$\{?[A-Z_][A-Z0-9_]*\}?`), Cyan},
// Flags (--flag, -f)
{regexp.MustCompile(`\s(--?[a-zA-Z][-a-zA-Z0-9]*)`), BrightYellow},
// Numbers
{regexp.MustCompile(`\b\d+\.?\d*\b`), Magenta},
}
}
// FormatConfig holds formatting options
type FormatConfig struct {
Colors *ColorScheme
ShowLineNo bool
Indent string
}
// DefaultFormatConfig returns the default formatting config
func DefaultFormatConfig() *FormatConfig {
return &FormatConfig{
Colors: GetScheme(),
ShowLineNo: true,
Indent: " ",
}
}
// FormatCodeBlock formats a code block with optional line numbers and syntax highlighting
func FormatCodeBlock(code string, config *FormatConfig) string {
if config == nil {
config = DefaultFormatConfig()
}
c := config.Colors
lines := strings.Split(code, "\n")
var result strings.Builder
// Calculate line number width for alignment
lineNoWidth := len(fmt.Sprintf("%d", len(lines)))
for i, line := range lines {
if config.ShowLineNo {
lineNo := i + 1
// Dim line numbers
result.WriteString(fmt.Sprintf("%s%*d%s %s", c.LabelDim, lineNoWidth, lineNo, c.Reset, config.Indent))
} else {
result.WriteString(config.Indent)
}
// Apply basic syntax highlighting
highlightedLine := highlightSyntax(line, c)
result.WriteString(highlightedLine)
result.WriteString("\n")
}
return result.String()
}
// highlightSyntax applies basic syntax highlighting to a line of code
func highlightSyntax(line string, c *ColorScheme) string {
// Skip if no colors
if c.Reset == "" {
return line
}
// Initialize patterns once (thread-safe)
syntaxPatternsOnce.Do(initSyntaxPatterns)
result := line
for _, p := range syntaxPatterns {
result = p.regex.ReplaceAllStringFunc(result, func(match string) string {
return p.color + match + c.Reset
})
}
return result
}
// FormatFilePath formats a file path with appropriate highlighting
func FormatFilePath(path string, c *ColorScheme) string {
return c.FilePath + path + c.Reset
}
// FormatCommand formats a command with appropriate highlighting
func FormatCommand(cmd string, c *ColorScheme) string {
return Bold + cmd + c.Reset
}
// FormatBullet returns a formatted bullet point
func FormatBullet(c *ColorScheme) string {
return c.ToolArrow + "●" + c.Reset
}
// FormatArrow returns a formatted arrow for tool calls
func FormatArrow(c *ColorScheme) string {
return c.ToolArrow + "→" + c.Reset
}
// FormatSuccess returns a formatted success checkmark
func FormatSuccess(c *ColorScheme) string {
return c.Success + "✓" + c.Reset
}
// FormatError returns a formatted error cross
func FormatError(c *ColorScheme) string {
return c.Error + "✗" + c.Reset
}
// FormatDiffLine formats a diff line with + or - prefix
func FormatDiffLine(line string, isAddition bool, c *ColorScheme) string {
if isAddition {
return c.DiffAdd + "+ " + line + c.Reset
}
return c.DiffRemove + "- " + line + c.Reset
}
// FormatSectionSeparator returns a formatted section separator
func FormatSectionSeparator(width int, c *ColorScheme) string {
return c.Separator + strings.Repeat("─", width) + c.Reset
}
// FormatLabel formats a label (like "Tokens:", "Cost:")
func FormatLabel(label string, c *ColorScheme) string {
return c.LabelDim + label + c.Reset
}
// FormatValue formats a value (numbers, important data)
func FormatValue(value string, c *ColorScheme) string {
return c.ValueBright + value + c.Reset
}
// FormatToolName formats a tool name
func FormatToolName(name string, c *ColorScheme) string {
return c.ToolName + name + c.Reset
}
// FormatAgentContext formats an agent context badge
func FormatAgentContext(agentType string, status string, c *ColorScheme) string {
return fmt.Sprintf("%s[%s%s%s: %s%s%s]%s",
c.AgentBrackets, c.AgentType, agentType, c.AgentBrackets,
c.AgentStatus, status, c.AgentBrackets, c.Reset)
}
// FormatThinkingPrefix formats the thinking block prefix
func FormatThinkingPrefix(c *ColorScheme) string {
return c.ThinkingPrefix + "[THINKING]" + c.Reset
}
// IndentLines indents all lines in a string
func IndentLines(s string, indent string) string {
lines := strings.Split(s, "\n")
for i, line := range lines {
if line != "" {
lines[i] = indent + line
}
}
return strings.Join(lines, "\n")
}
// FormatMCPToolName shortens MCP tool names from the format
// mcp__plugin_foo_bar__baz to plugin:foo:bar:baz.
// The mcp__ prefix is stripped, the double underscore separator
// becomes a colon, and underscores in the plugin part become colons.
func FormatMCPToolName(name string) string {
// MCP tools have the format: mcp__<plugin>__<tool_name>
const mcpPrefix = "mcp__"
if !strings.HasPrefix(name, mcpPrefix) {
return name
}
// Remove mcp__ prefix
remaining := name[len(mcpPrefix):]
// Find the __ separator between plugin and tool name
sepIdx := strings.Index(remaining, "__")
if sepIdx == -1 {
// Malformed MCP name, return original
return name
}
plugin := remaining[:sepIdx]
toolName := remaining[sepIdx+2:] // +2 to skip the "__"
// Convert underscores in plugin part to colons for hierarchical naming
plugin = strings.ReplaceAll(plugin, "_", ":")
return plugin + ":" + toolName
}