diff options
Diffstat (limited to 'internal/tmuxedit/send.go')
| -rw-r--r-- | internal/tmuxedit/send.go | 92 |
1 files changed, 69 insertions, 23 deletions
diff --git a/internal/tmuxedit/send.go b/internal/tmuxedit/send.go index b85f7d3..ea63057 100644 --- a/internal/tmuxedit/send.go +++ b/internal/tmuxedit/send.go @@ -2,7 +2,9 @@ package tmuxedit import ( "fmt" + "strconv" "strings" + "time" ) // sendKeys is the seam for `tmux send-keys`. Override in tests. @@ -15,45 +17,41 @@ var sendKeys = func(paneID string, keys ...string) error { return nil } -// deduplicateText removes the original (pre-filled) text from the edited -// result. If the user kept the original and appended, only the new text is -// returned. If the user rewrote everything, the full new text is returned. +// deduplicateText compares the original (pre-filled) text with what the user +// returned from the editor. Returns empty string if unchanged (no-op), or +// the full edited text if anything changed. The caller is responsible for +// clearing existing pane input before sending the result, so we always return +// the complete text rather than stripping the original prefix. func deduplicateText(original, edited string) string { original = strings.TrimSpace(original) edited = strings.TrimSpace(edited) - if edited == "" { + if edited == "" || edited == original { return "" } - if original == "" { - return edited - } - // If the edited text starts with the original, return only the appended part - if strings.HasPrefix(edited, original) { - appended := strings.TrimSpace(edited[len(original):]) - if appended != "" { - return appended - } - // User didn't change anything; return empty to signal no-op - return "" - } - // User rewrote the prompt; return the full new text return edited } // sendTextToPane sends the given text to the target pane. It optionally -// clears existing input first (using the agent's ClearKeys), then sends -// text line-by-line using the agent's NewlineKeys between lines. +// clears existing input first (using the agent's ClearKeys sequence), then +// sends text line-by-line using the agent's NewlineKeys between lines. +// ClearKeys is space-separated; tokens like "BSpace*200" repeat a key N times +// via tmux send-keys -N. Example: "End BSpace*200" moves to end then +// sends 200 backspaces to clear the entire prompt buffer. func sendTextToPane(paneID, text string, agent AgentConfig) error { if strings.TrimSpace(text) == "" { return nil } - // Clear existing input if configured + // Clear existing input using the key sequence (space-separated). + // Each token is sent as a separate tmux send-keys call. + // A short pause after clearing lets the TUI process all queued + // keystrokes (e.g. 200 backspaces) before new text arrives. if agent.ClearFirst && agent.ClearKeys != "" { - if err := sendKeys(paneID, agent.ClearKeys); err != nil { - return fmt.Errorf("clear failed: %w", err) + if err := sendClearSequence(paneID, agent.ClearKeys); err != nil { + return err } + sleepAfterClear() } - // Send text line by line, inserting newline keys between lines + // Send text line-by-line, inserting newline keys between lines lines := strings.Split(text, "\n") for i, line := range lines { if err := sendKeys(paneID, line); err != nil { @@ -72,3 +70,51 @@ func sendTextToPane(paneID, text string, agent AgentConfig) error { } return nil } + +// sleepAfterClear pauses to let the TUI drain queued keystrokes (like bulk +// backspaces) before new text is sent. Override in tests to avoid delays. +var sleepAfterClear = func() { time.Sleep(300 * time.Millisecond) } + +// sendClearSequence parses a space-separated key sequence and sends each +// token individually. Tokens with a "*N" suffix (e.g. "BSpace*200") are +// sent N times using tmux send-keys -N for efficient bulk repeats. +func sendClearSequence(paneID, clearKeys string) error { + for _, token := range strings.Fields(clearKeys) { + key, count := parseKeyRepeat(token) + if count > 1 { + if err := sendRepeatedKey(paneID, key, count); err != nil { + return fmt.Errorf("clear key %q*%d failed: %w", key, count, err) + } + } else { + if err := sendKeys(paneID, key); err != nil { + return fmt.Errorf("clear key %q failed: %w", key, err) + } + } + } + return nil +} + +// sendRepeatedKey is the seam for `tmux send-keys -N <count>`. Override in +// tests. Uses -N for efficient bulk key repeats (e.g. 200 backspaces). +var sendRepeatedKey = func(paneID, key string, count int) error { + args := []string{"send-keys", "-t", paneID, "-N", strconv.Itoa(count), key} + _, err := runCommand("tmux", args...) + if err != nil { + return fmt.Errorf("send-keys -N failed: %w", err) + } + return nil +} + +// parseKeyRepeat splits "Key*N" into (Key, N). Returns (token, 1) if no +// repeat suffix is present or the suffix is invalid. +func parseKeyRepeat(token string) (string, int) { + idx := strings.LastIndex(token, "*") + if idx < 1 || idx >= len(token)-1 { + return token, 1 + } + n, err := strconv.Atoi(token[idx+1:]) + if err != nil || n < 1 { + return token, 1 + } + return token[:idx], n +} |
