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
|
package hexaiaction
import (
"context"
"fmt"
"io"
"reflect"
"sync"
"codeberg.org/snonux/hexai/internal/appconfig"
)
type actionRequest struct {
parts InputParts
cfg actionConfig
client chatDoer
stderr io.Writer
selectedCustom *appconfig.CustomAction
}
// ActionHandler executes one tmux action kind.
type ActionHandler interface {
Execute(context.Context, actionRequest) (string, error)
}
// CodeActionHandler is kept as a compatibility name for tmux code-action handlers.
type CodeActionHandler = ActionHandler
type actionHandlerFunc func(context.Context, actionRequest) (string, error)
func (f actionHandlerFunc) Execute(ctx context.Context, req actionRequest) (string, error) {
if f == nil {
return "", fmt.Errorf("hexaiaction: nil action handler")
}
return f(ctx, req)
}
type actionHandlerRegistry struct {
mu sync.RWMutex
handlers map[ActionKind]ActionHandler
}
func newActionHandlerRegistry() *actionHandlerRegistry {
return &actionHandlerRegistry{handlers: make(map[ActionKind]ActionHandler)}
}
func (r *actionHandlerRegistry) register(kind ActionKind, handler ActionHandler) {
if kind == "" {
panic("hexaiaction: cannot register empty action kind")
}
if isNilActionHandler(handler) {
panic(fmt.Sprintf("hexaiaction: cannot register nil handler for %q", kind))
}
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.handlers[kind]; exists {
panic(fmt.Sprintf("hexaiaction: handler already registered for %q", kind))
}
r.handlers[kind] = handler
}
func isNilActionHandler(handler ActionHandler) bool {
if handler == nil {
return true
}
value := reflect.ValueOf(handler)
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return value.IsNil()
default:
return false
}
}
func (r *actionHandlerRegistry) lookup(kind ActionKind) (ActionHandler, bool) {
r.mu.RLock()
defer r.mu.RUnlock()
handler, ok := r.handlers[kind]
return handler, ok
}
func (r *actionHandlerRegistry) snapshot() map[ActionKind]ActionHandler {
r.mu.RLock()
defer r.mu.RUnlock()
handlers := make(map[ActionKind]ActionHandler, len(r.handlers))
for kind, handler := range r.handlers {
handlers[kind] = handler
}
return handlers
}
var actionHandlers = newActionHandlerRegistry()
func registerActionHandler(kind ActionKind, handler ActionHandler) {
actionHandlers.register(kind, handler)
}
func lookupActionHandler(kind ActionKind) (ActionHandler, bool) {
return actionHandlers.lookup(kind)
}
func codeActionHandlers() map[ActionKind]CodeActionHandler {
return actionHandlers.snapshot()
}
|