diff --git a/src/main/daemon/shell-ready.test.ts b/src/main/daemon/shell-ready.test.ts index b741ef057d0..f4660721d39 100644 --- a/src/main/daemon/shell-ready.test.ts +++ b/src/main/daemon/shell-ready.test.ts @@ -378,16 +378,21 @@ describePosix('daemon shell-ready launch config', () => { ) itWithZsh( - 'still runs user add-zle-hook-widget line-init hooks after the marker', + 'waits for user add-zle-hook-widget line-init hooks before emitting the marker', async () => { const { getShellReadyLaunchConfig } = await importFreshShellReady() const config = getShellReadyLaunchConfig('/bin/zsh') const tempHome = mkdtempSync(join(tmpdir(), 'orca-zsh-azhw-')) - const userHookOutput = 'ORCA-TEST-USER-HOOK' + const userHookStarted = 'ORCA-TEST-USER-HOOK-STARTED' + const userHookFinished = 'ORCA-TEST-USER-HOOK-FINISHED' writeFileSync( join(tempHome, '.zshrc'), [ - `__orca_test_line_init_hook() { printf "${userHookOutput}" }`, + '__orca_test_line_init_hook() {', + ` printf "${userHookStarted}"`, + ' sleep 0.1', + ` printf "${userHookFinished}"`, + '}', 'autoload -Uz add-zle-hook-widget', 'zle -N __orca_test_line_init_hook', 'add-zle-hook-widget line-init __orca_test_line_init_hook', @@ -399,14 +404,15 @@ describePosix('daemon shell-ready launch config', () => { tempHome, wrapperZdotdir: config.env.ZDOTDIR, isDone: (current) => - current.includes(SHELL_READY_MARKER_OUTPUT) && current.includes(userHookOutput) + current.includes(SHELL_READY_MARKER_OUTPUT) && current.includes(userHookFinished) }) // Why: the marker widget chains to the previously installed widget, so - // an azhw dispatcher registered by user config must keep dispatching. + // a slow azhw dispatcher must finish before Orca can safely type input. expect(output).toContain(SHELL_READY_MARKER_OUTPUT) - expect(output).toContain(userHookOutput) - expect(output.indexOf(SHELL_READY_MARKER_OUTPUT)).toBeLessThan( - output.indexOf(userHookOutput) + expect(output).toContain(userHookStarted) + expect(output).toContain(userHookFinished) + expect(output.indexOf(userHookFinished)).toBeLessThan( + output.indexOf(SHELL_READY_MARKER_OUTPUT) ) } finally { rmSync(tempHome, { recursive: true, force: true }) @@ -445,8 +451,8 @@ describePosix('daemon shell-ready launch config', () => { }) expect(output).toContain(SHELL_READY_MARKER_OUTPUT) expect(output).toContain(userHookOutput) - expect(output.indexOf(SHELL_READY_MARKER_OUTPUT)).toBeLessThan( - output.indexOf(userHookOutput) + expect(output.indexOf(userHookOutput)).toBeLessThan( + output.indexOf(SHELL_READY_MARKER_OUTPUT) ) // Why: idempotent — the marker must fire exactly once per prompt, not // duplicated by the second registration. diff --git a/src/main/shell-templates.ts b/src/main/shell-templates.ts index da520112dca..296980ac639 100644 --- a/src/main/shell-templates.ts +++ b/src/main/shell-templates.ts @@ -130,8 +130,8 @@ fi // when an earlier hook exits non-zero, and a pre-existing raw user widget // (e.g. oh-my-zsh vi-mode without VI_MODE_SET_CURSOR) is preserved as the // first hook and fails — silently suppressing the marker and stalling every -// startup command on the pre-ready timeout. Instead, own zle-line-init: emit -// the marker first, then chain to whatever widget was installed before. +// startup command on the pre-ready timeout. Instead, own zle-line-init, run +// the prior widget ourselves, then emit the marker even if that widget fails. export function getZshShellReadyMarkerRegistrationBlock(escapedMarker: string): string { return `if [[ "\${ORCA_SHELL_READY_MARKER:-0}" == "1" ]]; then # Why: capture the prior zle-line-init so the marker chains to it. On a @@ -148,12 +148,13 @@ export function getZshShellReadyMarkerRegistrationBlock(escapedMarker: string): __orca_prev_line_init_fn="" fi __orca_prompt_mark() { - printf "${escapedMarker}" # Why: call the prior hook as a plain function, not an aliased widget, so - # $WIDGET stays zle-line-init for add-zle-hook-widget dispatchers. + # $WIDGET stays zle-line-init for add-zle-hook-widget dispatchers. Readiness + # comes afterward because a slow hook means zle cannot accept input yet. if [[ -n "\${__orca_prev_line_init_fn:-}" ]]; then - "\${__orca_prev_line_init_fn}" "$@" + "\${__orca_prev_line_init_fn}" "$@" || true fi + printf "${escapedMarker}" } zle -N zle-line-init __orca_prompt_mark fi