diff options
Diffstat (limited to 'scripts/tmux-edit-send')
| -rwxr-xr-x | scripts/tmux-edit-send | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/scripts/tmux-edit-send b/scripts/tmux-edit-send new file mode 100755 index 0000000..05fe62d --- /dev/null +++ b/scripts/tmux-edit-send @@ -0,0 +1,246 @@ +#!/usr/bin/env bash +set -u -o pipefail + +LOG_ENABLED=0 +log_file="${TMPDIR:-/tmp}/tmux-edit-send.log" +log() { + if [ "$LOG_ENABLED" -eq 1 ]; then + printf '%s\n' "$*" >> "$log_file" + fi +} + +# Read the target pane id from a temp file created by tmux binding. +read_target_from_file() { + local file_path="$1" + local pane_id + if [ -n "$file_path" ] && [ -f "$file_path" ]; then + pane_id="$(sed -n '1p' "$file_path" | tr -d '[:space:]')" + # Ensure pane ID has % prefix + if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]]; then + pane_id="%${pane_id}" + fi + printf '%s' "$pane_id" + fi +} + +# Read the target pane id from tmux environment if present. +read_target_from_env() { + local env_line pane_id + env_line="$(tmux show-environment -g TMUX_EDIT_TARGET 2>/dev/null || true)" + case "$env_line" in + TMUX_EDIT_TARGET=*) + pane_id="${env_line#TMUX_EDIT_TARGET=}" + # Ensure pane ID has % prefix + if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]] && [[ "$pane_id" =~ ^[0-9]+$ ]]; then + pane_id="%${pane_id}" + fi + printf '%s' "$pane_id" + ;; + esac +} + +# Resolve the target pane id, falling back to the last pane. +resolve_target_pane() { + local candidate="$1" + local current_pane last_pane + + current_pane="$(tmux display-message -p "#{pane_id}" 2>/dev/null || true)" + log "current pane=${current_pane:-<empty>}" + + # Ensure candidate has % prefix if it's a pane ID + if [ -n "$candidate" ] && [[ "$candidate" =~ ^[0-9]+$ ]]; then + candidate="%${candidate}" + log "normalized candidate to $candidate" + fi + + if [ -n "$candidate" ] && [[ "$candidate" == *"#{"* ]]; then + log "format target detected, clearing" + candidate="" + fi + if [ -z "$candidate" ]; then + candidate="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)" + log "using last pane as fallback: $candidate" + elif [ "$candidate" = "$current_pane" ]; then + last_pane="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)" + if [ -n "$last_pane" ]; then + candidate="$last_pane" + log "candidate was current, using last pane: $candidate" + fi + fi + printf '%s' "$candidate" +} + +# Capture the latest multi-line prompt content from the pane. +capture_prompt_text() { + local target="$1" + tmux capture-pane -p -t "$target" -S -2000 2>/dev/null | awk ' + function trim_box(line) { + sub(/^ *│ ?/, "", line) + sub(/ *│ *$/, "", line) + sub(/[[:space:]]+$/, "", line) + return line + } + /^ *│ *→/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0 { + if (text != "") last = text + text = "" + capture = 1 + line = $0 + sub(/^.*→ ?/, "", line) + line = trim_box(line) + if (line != "") text = line + next + } + capture { + if ($0 ~ /^ *└/) { + capture = 0 + if (text != "") last = text + next + } + if ($0 ~ /^ *│/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0) { + line = trim_box($0) + if (line != "") { + if (text != "") text = text " " line + else text = line + } + } + } + END { + if (text != "") last = text + if (last != "") print last + } + ' +} + +# Write captured prompt text into the temp file if available. +prefill_tmpfile() { + local tmpfile="$1" + local prompt_text="$2" + if [ -n "$prompt_text" ]; then + printf '%s\n' "$prompt_text" > "$tmpfile" + fi +} + +# Ensure the target pane exists before sending keys. +validate_target_pane() { + local target="$1" + local pane target_found + if [ -z "$target" ]; then + log "error: no target pane determined" + echo "Could not determine target pane." >&2 + return 1 + fi + target_found=0 + log "validate: looking for target='$target' in all panes:" + for pane in $(tmux list-panes -a -F "#{pane_id}" 2>/dev/null || true); do + log "validate: checking pane='$pane'" + if [ "$pane" = "$target" ]; then + target_found=1 + log "validate: MATCH FOUND!" + break + fi + done + if [ "$target_found" -ne 1 ]; then + log "error: target pane not found: $target" + echo "Target pane not found: $target" >&2 + return 1 + fi + log "validate: target pane validated successfully" +} + +# Send temp file contents to the target pane line by line. +send_content() { + local target="$1" + local tmpfile="$2" + local prompt_text="$3" + local first_line=1 + local line + log "send_content: target=$target, prompt_text='$prompt_text'" + while IFS= read -r line || [ -n "$line" ]; do + log "send_content: read line='$line'" + if [ "$first_line" -eq 1 ] && [ -n "$prompt_text" ]; then + if [[ "$line" == "$prompt_text"* ]]; then + local old_line="$line" + line="${line#"$prompt_text"}" + log "send_content: stripped prompt, was='$old_line' now='$line'" + fi + fi + first_line=0 + log "send_content: sending line='$line'" + tmux send-keys -t "$target" -l "$line" + tmux send-keys -t "$target" Enter + done < "$tmpfile" + log "sent content to $target" +} + +# Main entry point. +main() { + local target_file="${1:-}" + local target + local editor="${EDITOR:-vi}" + local tmpfile + local prompt_text + + log "=== tmux-edit-send starting ===" + log "target_file=$target_file" + log "EDITOR=$editor" + + target="$(read_target_from_file "$target_file" || true)" + if [ -n "$target" ]; then + log "file target=${target:-<empty>}" + rm -f "$target_file" + fi + if [ -z "$target" ]; then + target="${TMUX_EDIT_TARGET:-}" + fi + log "env target=${target:-<empty>}" + if [ -z "$target" ]; then + target="$(read_target_from_env || true)" + fi + log "tmux env target=${target:-<empty>}" + target="$(resolve_target_pane "$target")" + log "fallback target=${target:-<empty>}" + + tmpfile="$(mktemp)" + log "created tmpfile=$tmpfile" + if [ ! -f "$tmpfile" ]; then + log "ERROR: mktemp failed to create file" + echo "ERROR: mktemp failed" >&2 + exit 1 + fi + mv "$tmpfile" "${tmpfile}.md" 2>&1 | while read -r line; do log "mv output: $line"; done + tmpfile="${tmpfile}.md" + log "renamed to tmpfile=$tmpfile" + if [ ! -f "$tmpfile" ]; then + log "ERROR: tmpfile does not exist after rename" + echo "ERROR: tmpfile rename failed" >&2 + exit 1 + fi + trap 'rm -f "$tmpfile"' EXIT + + log "capturing prompt text from target=$target" + prompt_text="$(capture_prompt_text "$target")" + log "captured prompt_text='$prompt_text'" + prefill_tmpfile "$tmpfile" "$prompt_text" + log "prefilled tmpfile" + + log "launching editor: $editor $tmpfile" + "$editor" "$tmpfile" + local editor_exit=$? + log "editor exited with status $editor_exit" + + if [ ! -s "$tmpfile" ]; then + log "empty file, nothing sent" + exit 0 + fi + + log "tmpfile contents:" + log "$(cat "$tmpfile")" + + log "validating target pane" + validate_target_pane "$target" + log "sending content to target=$target" + send_content "$target" "$tmpfile" "$prompt_text" + log "=== tmux-edit-send completed ===" +} + +main "$@" |
