Fix Shift+Enter tmux follow-up regressions

This commit is contained in:
austinpower1258 2026-03-30 03:10:20 -07:00
parent 9080248393
commit 9ce4997ced
4 changed files with 106 additions and 14 deletions

View file

@ -61,7 +61,7 @@ _CMUX_ASYNC_JOB_TIMEOUT="${_CMUX_ASYNC_JOB_TIMEOUT:-20}"
_CMUX_PORTS_LAST_RUN="${_CMUX_PORTS_LAST_RUN:-0}"
_CMUX_SHELL_ACTIVITY_LAST="${_CMUX_SHELL_ACTIVITY_LAST:-}"
_CMUX_TMUX_STATE_LAST="${_CMUX_TMUX_STATE_LAST:-}"
_CMUX_TMUX_STATE_SIGNATURE_LAST="${_CMUX_TMUX_STATE_SIGNATURE_LAST:-}"
_CMUX_TTY_NAME="${_CMUX_TTY_NAME:-}"
_CMUX_TTY_REPORTED="${_CMUX_TTY_REPORTED:-0}"
_CMUX_TMUX_PUSH_SIGNATURE="${_CMUX_TMUX_PUSH_SIGNATURE:-}"
@ -275,6 +275,13 @@ _cmux_report_tmux_state_payload() {
printf '%s\n' "report_tmux_state $state --tab=$CMUX_TAB_ID --panel=$CMUX_PANEL_ID"
}
_cmux_tmux_state_report_signature() {
local payload="$1"
[[ -n "$payload" ]] || return 0
[[ -n "$CMUX_SOCKET_PATH" ]] || return 0
printf '%s\037%s\n' "$CMUX_SOCKET_PATH" "$payload"
}
_cmux_report_tmux_state() {
[[ -S "$CMUX_SOCKET_PATH" ]] || return 0
@ -282,10 +289,11 @@ _cmux_report_tmux_state() {
payload="$(_cmux_report_tmux_state_payload)"
[[ -n "$payload" ]] || return 0
local state="${payload#report_tmux_state }"
state="${state%% *}"
[[ "$_CMUX_TMUX_STATE_LAST" == "$state" ]] && return 0
_CMUX_TMUX_STATE_LAST="$state"
local signature=""
signature="$(_cmux_tmux_state_report_signature "$payload")"
[[ -n "$signature" ]] || return 0
[[ "$_CMUX_TMUX_STATE_SIGNATURE_LAST" == "$signature" ]] && return 0
_CMUX_TMUX_STATE_SIGNATURE_LAST="$signature"
{
_cmux_send "$payload"
} >/dev/null 2>&1 & disown

View file

@ -74,7 +74,7 @@ typeset -g _CMUX_ASYNC_JOB_TIMEOUT=20
typeset -g _CMUX_PORTS_LAST_RUN=0
typeset -g _CMUX_CMD_START=0
typeset -g _CMUX_SHELL_ACTIVITY_LAST=""
typeset -g _CMUX_TMUX_STATE_LAST=""
typeset -g _CMUX_TMUX_STATE_SIGNATURE_LAST=""
typeset -g _CMUX_TTY_NAME=""
typeset -g _CMUX_TTY_REPORTED=0
typeset -g _CMUX_GHOSTTY_SEMANTIC_PATCHED=0
@ -380,6 +380,13 @@ _cmux_report_tmux_state_payload() {
print -r -- "report_tmux_state $state --tab=$CMUX_TAB_ID --panel=$CMUX_PANEL_ID"
}
_cmux_tmux_state_report_signature() {
local payload="$1"
[[ -n "$payload" ]] || return 0
[[ -n "$CMUX_SOCKET_PATH" ]] || return 0
print -r -- "${CMUX_SOCKET_PATH}"$'\x1f'"${payload}"
}
_cmux_report_tmux_state() {
[[ -S "$CMUX_SOCKET_PATH" ]] || return 0
@ -387,10 +394,11 @@ _cmux_report_tmux_state() {
payload="$(_cmux_report_tmux_state_payload)"
[[ -n "$payload" ]] || return 0
local state="${payload#report_tmux_state }"
state="${state%% *}"
[[ "$_CMUX_TMUX_STATE_LAST" == "$state" ]] && return 0
_CMUX_TMUX_STATE_LAST="$state"
local signature=""
signature="$(_cmux_tmux_state_report_signature "$payload")"
[[ -n "$signature" ]] || return 0
[[ "$_CMUX_TMUX_STATE_SIGNATURE_LAST" == "$signature" ]] && return 0
_CMUX_TMUX_STATE_SIGNATURE_LAST="$signature"
_cmux_send_bg "$payload"
}

View file

@ -1672,6 +1672,11 @@ class GhosttyApp {
var containsExplicitShiftEnterDirective = false
mutating func recordKeybind(_ value: String) {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if trimmed.isEmpty || trimmed == "clear" {
containsExplicitShiftEnterDirective = false
return
}
if GhosttyApp.keybindDirectiveTargetsShiftEnter(value) {
containsExplicitShiftEnterDirective = true
}
@ -1694,7 +1699,7 @@ class GhosttyApp {
var loadedRecursivePaths = Set<String>()
var index = 0
while index < recursiveConfigPaths.count && !summary.containsExplicitShiftEnterDirective {
while index < recursiveConfigPaths.count {
let path = NSString(string: recursiveConfigPaths[index]).expandingTildeInPath
index += 1
@ -1814,9 +1819,6 @@ class GhosttyApp {
case "keybind":
guard let value = entry.value else { continue }
summary.recordKeybind(value)
if summary.containsExplicitShiftEnterDirective {
return
}
case "config-file":
guard let value = entry.value else { continue }
applyConfigFileDirective(

View file

@ -2522,6 +2522,28 @@ final class GhosttyMouseFocusTests: XCTestCase {
)
}
func testUserConfigDefinesShiftEnterBindingHonorsLaterClearInIncludedFile() throws {
let dir = FileManager.default.temporaryDirectory
.appendingPathComponent("cmux-test-shift-enter-clear-\(UUID().uuidString)")
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: dir) }
let included = dir.appendingPathComponent("bindings.conf")
try "keybind = clear\n"
.write(to: included, atomically: true, encoding: .utf8)
let main = dir.appendingPathComponent("config")
try """
keybind = shift+enter=text:\\x0a
config-file = \(included.path)
"""
.write(to: main, atomically: true, encoding: .utf8)
XCTAssertFalse(
GhosttyApp.userConfigDefinesShiftEnterBinding(configPaths: [main.path])
)
}
func testUserConfigDefinesShiftEnterBindingIgnoresOtherModifierCombinations() throws {
try withTempConfig("keybind = cmd+shift+enter=text:\\x0a\n") { path in
XCTAssertFalse(
@ -2993,6 +3015,58 @@ final class ZshShellIntegrationHandoffTests: XCTestCase {
)
}
func testShellIntegrationResendsTmuxStateWhenSocketTargetChanges() throws {
let fileManager = FileManager.default
let root = fileManager.temporaryDirectory
.appendingPathComponent("cmux-zsh-tmux-state-resend-\(UUID().uuidString)")
try fileManager.createDirectory(at: root, withIntermediateDirectories: true)
defer { try? fileManager.removeItem(at: root) }
let socketA = root.appendingPathComponent("cmux-a.sock").path
let socketB = root.appendingPathComponent("cmux-b.sock").path
let output = try runInteractiveZsh(
cmuxLoadGhosttyIntegration: false,
cmuxLoadShellIntegration: true,
command: """
python3 -c 'import os, socket, sys, time; path = sys.argv[1]; \
os.path.exists(path) and os.unlink(path); \
s = socket.socket(socket.AF_UNIX); s.bind(path); s.listen(1); time.sleep(3)' "$CMUX_SOCKET_PATH" &
server_a=$!
sleep 0.1
functions[_cmux_send_bg]='print -r -- "$1"'
_CMUX_TMUX_STATE_SIGNATURE_LAST=""
_cmux_report_tmux_state
kill $server_a >/dev/null 2>&1
wait $server_a >/dev/null 2>&1
export CMUX_SOCKET_PATH="\(socketB)"
python3 -c 'import os, socket, sys, time; path = sys.argv[1]; \
os.path.exists(path) and os.unlink(path); \
s = socket.socket(socket.AF_UNIX); s.bind(path); s.listen(1); time.sleep(3)' "$CMUX_SOCKET_PATH" &
server_b=$!
sleep 0.1
_cmux_report_tmux_state
kill $server_b >/dev/null 2>&1
wait $server_b >/dev/null 2>&1
""",
extraEnvironment: [
"TMUX": "/tmp/tmux-current,123,0",
"CMUX_SOCKET_PATH": socketA,
"CMUX_TAB_ID": "11111111-1111-1111-1111-111111111111",
"CMUX_PANEL_ID": "99999999-9999-9999-9999-999999999999",
]
)
XCTAssertEqual(
output,
"""
report_tmux_state inside --tab=11111111-1111-1111-1111-111111111111 --panel=99999999-9999-9999-9999-999999999999
report_tmux_state inside --tab=11111111-1111-1111-1111-111111111111 --panel=99999999-9999-9999-9999-999999999999
"""
)
}
private func runInteractiveZsh(cmuxLoadGhosttyIntegration: Bool) throws -> String {
try runInteractiveZsh(
cmuxLoadGhosttyIntegration: cmuxLoadGhosttyIntegration,