Detailed changes
@@ -245,28 +245,28 @@ func getDiagnostics(filePath string, lsps map[string]*lsp.Client) string {
return projectDiagnostics[i] < projectDiagnostics[j]
})
- output := ""
+ var output strings.Builder
if len(fileDiagnostics) > 0 {
- output += "\n<file_diagnostics>\n"
+ output.WriteString("\n<file_diagnostics>\n")
if len(fileDiagnostics) > 10 {
- output += strings.Join(fileDiagnostics[:10], "\n")
- output += fmt.Sprintf("\n... and %d more diagnostics", len(fileDiagnostics)-10)
+ output.WriteString(strings.Join(fileDiagnostics[:10], "\n"))
+ fmt.Fprintf(&output, "\n... and %d more diagnostics", len(fileDiagnostics)-10)
} else {
- output += strings.Join(fileDiagnostics, "\n")
+ output.WriteString(strings.Join(fileDiagnostics, "\n"))
}
- output += "\n</file_diagnostics>\n"
+ output.WriteString("\n</file_diagnostics>\n")
}
if len(projectDiagnostics) > 0 {
- output += "\n<project_diagnostics>\n"
+ output.WriteString("\n<project_diagnostics>\n")
if len(projectDiagnostics) > 10 {
- output += strings.Join(projectDiagnostics[:10], "\n")
- output += fmt.Sprintf("\n... and %d more diagnostics", len(projectDiagnostics)-10)
+ output.WriteString(strings.Join(projectDiagnostics[:10], "\n"))
+ fmt.Fprintf(&output, "\n... and %d more diagnostics", len(projectDiagnostics)-10)
} else {
- output += strings.Join(projectDiagnostics, "\n")
+ output.WriteString(strings.Join(projectDiagnostics, "\n"))
}
- output += "\n</project_diagnostics>\n"
+ output.WriteString("\n</project_diagnostics>\n")
}
if len(fileDiagnostics) > 0 || len(projectDiagnostics) > 0 {
@@ -275,13 +275,13 @@ func getDiagnostics(filePath string, lsps map[string]*lsp.Client) string {
projectErrors := countSeverity(projectDiagnostics, "Error")
projectWarnings := countSeverity(projectDiagnostics, "Warn")
- output += "\n<diagnostic_summary>\n"
- output += fmt.Sprintf("Current file: %d errors, %d warnings\n", fileErrors, fileWarnings)
- output += fmt.Sprintf("Project: %d errors, %d warnings\n", projectErrors, projectWarnings)
- output += "</diagnostic_summary>\n"
+ output.WriteString("\n<diagnostic_summary>\n")
+ fmt.Fprintf(&output, "Current file: %d errors, %d warnings\n", fileErrors, fileWarnings)
+ fmt.Fprintf(&output, "Project: %d errors, %d warnings\n", projectErrors, projectWarnings)
+ output.WriteString("</diagnostic_summary>\n")
}
- return output
+ return output.String()
}
func countSeverity(diagnostics []string, severity string) int {
@@ -198,35 +198,35 @@ func (g *grepTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
return ToolResponse{}, fmt.Errorf("error searching files: %w", err)
}
- var output string
+ var output strings.Builder
if len(matches) == 0 {
- output = "No files found"
+ output.WriteString("No files found")
} else {
- output = fmt.Sprintf("Found %d matches\n", len(matches))
+ fmt.Fprintf(&output, "Found %d matches\n", len(matches))
currentFile := ""
for _, match := range matches {
if currentFile != match.path {
if currentFile != "" {
- output += "\n"
+ output.WriteString("\n")
}
currentFile = match.path
- output += fmt.Sprintf("%s:\n", match.path)
+ fmt.Fprintf(&output, "%s:\n", match.path)
}
if match.lineNum > 0 {
- output += fmt.Sprintf(" Line %d: %s\n", match.lineNum, match.lineText)
+ fmt.Fprintf(&output, " Line %d: %s\n", match.lineNum, match.lineText)
} else {
- output += fmt.Sprintf(" %s\n", match.path)
+ fmt.Fprintf(&output, " %s\n", match.path)
}
}
if truncated {
- output += "\n(Results are truncated. Consider using a more specific path or pattern.)"
+ output.WriteString("\n(Results are truncated. Consider using a more specific path or pattern.)")
}
}
return WithResponseMetadata(
- NewTextResponse(output),
+ NewTextResponse(output.String()),
GrepResponseMetadata{
NumberOfMatches: len(matches),
Truncated: truncated,
@@ -209,7 +209,7 @@ func printNode(builder *strings.Builder, node *TreeNode, level int) {
nodeName := node.Name
if node.Type == "directory" {
- nodeName += string(filepath.Separator)
+ nodeName = nodeName + string(filepath.Separator)
}
fmt.Fprintf(builder, "%s- %s\n", indent, nodeName)
@@ -2,7 +2,6 @@ package editor
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type EditorKeyMap struct {
@@ -23,10 +22,18 @@ func DefaultEditorKeyMap() EditorKeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k EditorKeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Send,
+ k.OpenEditor,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k EditorKeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package completions
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -33,10 +32,20 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Down,
+ k.Up,
+ k.Select,
+ k.Cancel,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -1,8 +1,6 @@
package layout
import (
- "reflect"
-
"github.com/charmbracelet/bubbles/v2/help"
"github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea/v2"
@@ -27,14 +25,7 @@ type Positionable interface {
SetPosition(x, y int) tea.Cmd
}
-func KeyMapToSlice(t any) (bindings []key.Binding) {
- typ := reflect.TypeOf(t)
- if typ.Kind() != reflect.Struct {
- return nil
- }
- for i := range typ.NumField() {
- v := reflect.ValueOf(t).Field(i)
- bindings = append(bindings, v.Interface().(key.Binding))
- }
- return
+// KeyMapProvider defines an interface for types that can provide their key bindings as a slice
+type KeyMapProvider interface {
+ KeyBindings() []key.Binding
}
@@ -2,7 +2,6 @@ package list
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -53,10 +52,26 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Down,
+ k.Up,
+ k.NDown,
+ k.NUp,
+ k.DownOneItem,
+ k.UpOneItem,
+ k.HalfPageDown,
+ k.HalfPageUp,
+ k.Home,
+ k.End,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package status
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -33,10 +32,20 @@ func DefaultKeyMap(tabHelp string) KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Tab,
+ k.Commands,
+ k.Sessions,
+ k.Help,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package commands
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type CommandsDialogKeyMap struct {
@@ -38,10 +37,21 @@ func DefaultCommandsDialogKeyMap() CommandsDialogKeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k CommandsDialogKeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Select,
+ k.Next,
+ k.Previous,
+ k.Tab,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k CommandsDialogKeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -86,10 +96,19 @@ func DefaultArgumentsDialogKeyMap() ArgumentsDialogKeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k ArgumentsDialogKeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Confirm,
+ k.Next,
+ k.Previous,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k ArgumentsDialogKeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package compact
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
// KeyMap defines the key bindings for the compact dialog.
@@ -40,10 +39,21 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.ChangeSelection,
+ k.Select,
+ k.Y,
+ k.N,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package filepicker
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
// KeyMap defines keyboard bindings for dialog management.
@@ -45,10 +44,22 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Select,
+ k.Down,
+ k.Up,
+ k.Forward,
+ k.Backward,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package init
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -38,10 +37,21 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.ChangeSelection,
+ k.Select,
+ k.Y,
+ k.N,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package dialogs
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
// KeyMap defines keyboard bindings for dialog management.
@@ -18,10 +17,17 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package models
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -33,10 +32,20 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Select,
+ k.Next,
+ k.Previous,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package permissions
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -48,10 +47,23 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Left,
+ k.Right,
+ k.Tab,
+ k.Select,
+ k.Allow,
+ k.AllowSession,
+ k.Deny,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package quit
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
// KeyMap defines the keyboard bindings for the quit dialog.
@@ -44,10 +43,22 @@ func DefaultKeymap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.LeftRight,
+ k.EnterSpace,
+ k.Yes,
+ k.No,
+ k.Tab,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package sessions
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -33,10 +32,20 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Select,
+ k.Next,
+ k.Previous,
+ k.Close,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package tui
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -42,7 +41,7 @@ func DefaultKeyMap() KeyMap {
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -50,6 +49,17 @@ func (k KeyMap) FullHelp() [][]key.Binding {
return m
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Logs,
+ k.Quit,
+ k.Help,
+ k.Commands,
+ k.Sessions,
+ }
+}
+
// ShortHelp implements help.KeyMap.
func (k KeyMap) ShortHelp() []key.Binding {
return []key.Binding{}
@@ -2,7 +2,6 @@ package chat
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -33,10 +32,20 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.NewSession,
+ k.FilePicker,
+ k.Cancel,
+ k.Tab,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])
@@ -2,7 +2,6 @@ package logs
import (
"github.com/charmbracelet/bubbles/v2/key"
- "github.com/charmbracelet/crush/internal/tui/components/core/layout"
)
type KeyMap struct {
@@ -18,10 +17,17 @@ func DefaultKeyMap() KeyMap {
}
}
+// KeyBindings implements layout.KeyMapProvider
+func (k KeyMap) KeyBindings() []key.Binding {
+ return []key.Binding{
+ k.Back,
+ }
+}
+
// FullHelp implements help.KeyMap.
func (k KeyMap) FullHelp() [][]key.Binding {
m := [][]key.Binding{}
- slice := layout.KeyMapToSlice(k)
+ slice := k.KeyBindings()
for i := 0; i < len(slice); i += 4 {
end := min(i+4, len(slice))
m = append(m, slice[i:end])