Skip to content

Commit c37ce7a

Browse files
nwparkerm4airpetersindex
authored
fix(zsh): keep deferred line-init defined for plugins that call it by name (#26842)
* fix(zsh): keep deferred line-init defined for plugins that call it by name Fixes #26029 Co-Authored-By: petersindex <petersindex@users.noreply.github.com> * test(zsh): keep the named-call regression test out of the relay module --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: petersindex <petersindex@users.noreply.github.com>
1 parent 305474c commit c37ce7a

5 files changed

Lines changed: 73 additions & 39 deletions

File tree

‎src/main/__fixtures__/shell-wrapper-snapshots/daemon-zsh-zshenv.txt‎

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/main/__fixtures__/shell-wrapper-snapshots/local-zsh-zshenv.txt‎

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/main/__fixtures__/shell-wrapper-snapshots/relay-zsh-zshenv.txt‎

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/main/zsh-deferred-startup-line-init.live-shell.test.ts‎

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { tmpdir } from 'node:os'
33
import { join } from 'node:path'
44
import { afterEach, describe, expect, it } from 'vitest'
55
import { prependOrcaCliDirToChildPath } from './cli/orca-cli-child-path'
6+
import { getDaemonZshWrapperSpec } from './daemon/daemon-zsh-shell-ready-wrapper-spec'
67
import { POSIX_SHELL_STARTUP_COMMAND_ENV } from './pty/posix-shell-startup-command'
78
import { getZshShellReadyWrapperFile } from './providers/local-pty-shell-ready-wrapper-generation'
89
import { encodeShellStartupFeatures, selectShellStartupFeatures } from './shell-startup-features'
910
import { ZSH_WRAPPER_DIR_MARKER_FILE } from './shell-templates'
11+
import { buildZshStartupHook } from './zsh-startup-wrapper-builder'
1012
import { hasZsh, MARKERS, runZshPty, ZSH_PATH } from './zsh-startup-hook-pty-harness'
1113

1214
const itWithZsh = hasZsh ? it : it.skip
@@ -165,7 +167,6 @@ describe('zsh deferred startup after prompt-hook replacement', () => {
165167
? ['source -- "$HOME/wrapper/.zshenv"', 'source -- "$HOME/wrapper/.zshenv"']
166168
: []),
167169
'O_LK=$(command -v orca-dev)',
168-
'O_IR=${+functions[__orca_deferred_line_init]}',
169170
'O_SR=${+widgets[__orca_saved_line_init]}',
170171
...(scheduleCleanup
171172
? [
@@ -183,7 +184,6 @@ describe('zsh deferred startup after prompt-hook replacement', () => {
183184
'O_LK',
184185
'O_UC',
185186
'O_UN',
186-
'O_IR',
187187
'O_SR',
188188
...(scheduleCleanup ? ['O_SC', 'O_SE'] : []),
189189
...(scheduledPrecmd ? ['O_EV', 'O_EO'] : []),
@@ -204,9 +204,6 @@ describe('zsh deferred startup after prompt-hook replacement', () => {
204204
if (!chainedLineInit) {
205205
expect(result.values.O_UN).toBe('zle-line-init')
206206
}
207-
if (!chainedLineInit && !chainedRedraw && !repeatSource) {
208-
expect(result.values.O_IR).toBe('0')
209-
}
210207
expect(result.values.O_SR).toBe('0')
211208
if (repeatSource) {
212209
expect(result.output).not.toContain('job table full or recursion limit exceeded')
@@ -279,3 +276,72 @@ describe('zsh deferred startup after prompt-hook replacement', () => {
279276
}
280277
)
281278
})
279+
280+
describe('zsh plugins that call deferred line-init by name', () => {
281+
const roots: string[] = []
282+
283+
afterEach(() => {
284+
for (const root of roots.splice(0)) {
285+
rmSync(root, { recursive: true, force: true })
286+
}
287+
})
288+
289+
itWithZsh.each(['local', 'daemon'] as const)(
290+
'keeps the plugin call working across prompts in a %s wrapper',
291+
async (transport) => {
292+
const home = mkdtempSync(join(tmpdir(), 'orca-zsh-named-wrapper-'))
293+
roots.push(home)
294+
const wrapperDir = join(home, 'wrapper')
295+
mkdirSync(wrapperDir)
296+
// Some input plugins copy the bound widget's function name into their own body.
297+
writeFileSync(
298+
join(home, '.zshrc'),
299+
`functions[orca_test_app_mode]=\${widgets[zle-line-init]#user:}'
300+
O_AM=$((\${O_AM:-0}+1))'
301+
zle -N zle-line-init orca_test_app_mode
302+
`
303+
)
304+
const wrapper =
305+
transport === 'local'
306+
? getZshShellReadyWrapperFile()
307+
: buildZshStartupHook(getDaemonZshWrapperSpec())
308+
writeFileSync(join(wrapperDir, '.zshenv'), wrapper)
309+
writeFileSync(join(wrapperDir, ZSH_WRAPPER_DIR_MARKER_FILE), '')
310+
const env: Record<string, string> = {
311+
HOME: home,
312+
USERPROFILE: home,
313+
PATH: '/usr/bin:/bin',
314+
DEBIAN_PREVENT_KEYBOARD_CHANGES: '1',
315+
ZDOTDIR: wrapperDir
316+
}
317+
env.ORCA_SHELL_FEATURES = encodeShellStartupFeatures(
318+
selectShellStartupFeatures({
319+
shellPath: ZSH_PATH,
320+
env,
321+
hasStartupCommand: false,
322+
waitsForShellReady: true,
323+
emitsStartupIdentity: false
324+
})
325+
)
326+
327+
const result = await runZshPty({
328+
env,
329+
commands: [
330+
'true',
331+
'false',
332+
'[[ ${functions[orca_test_app_mode]} == *__orca_deferred_line_init* ]]; O_CALL_REF=$?',
333+
'__orca_deferred_line_init; O_NOOP=$?',
334+
'O_INIT=${+functions[__orca_deferred_init]}'
335+
],
336+
report: ['O_AM', 'O_CALL_REF', 'O_NOOP', 'O_INIT']
337+
})
338+
339+
expect(result.output, result.output).not.toContain('command not found')
340+
expect(result.output).toContain(MARKERS.ready)
341+
expect(Number(result.values.O_AM)).toBeGreaterThan(2)
342+
expect(result.values.O_CALL_REF).toBe('0')
343+
expect(result.values.O_NOOP).toBe('0')
344+
expect(result.values.O_INIT).toBe('0')
345+
}
346+
)
347+
})

‎src/main/zsh-deferred-startup-line-init.ts‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,5 @@ export const ZSH_DEFERRED_LINE_INIT_RETIRE_BLOCK = ` if (( \${+widgets[__orca_s
4444
zle -D zle-line-init
4545
fi`
4646

47-
// Why: add-zle-hook-widget can keep an alias of the bootstrap in its own chain.
48-
export const ZSH_DEFERRED_LINE_INIT_CLEANUP_BLOCK = ` (( $+__orca_deferred_sched_armed )) || builtin unfunction __orca_deferred_sched_init
49-
local __orca_widget __orca_line_init_bound=0
50-
for __orca_widget in "\${(v)widgets[@]}"; do
51-
if [[ "$__orca_widget" == user:__orca_deferred_line_init ]]; then
52-
__orca_line_init_bound=1
53-
break
54-
fi
55-
done
56-
(( __orca_line_init_bound )) || builtin unfunction __orca_deferred_line_init`
47+
// Why: plugins can copy a call to line-init into their own body; its guard makes it inert after startup.
48+
export const ZSH_DEFERRED_LINE_INIT_CLEANUP_BLOCK = ` (( $+__orca_deferred_sched_armed )) || builtin unfunction __orca_deferred_sched_init`

0 commit comments

Comments
 (0)