Skip to content

Commit 3428478

Browse files
fix(zsh): use global scope for typeset to support lazy loading (#2776)
Co-authored-by: Tushar Mathur <tusharmath@gmail.com>
1 parent 32a961d commit 3428478

4 files changed

Lines changed: 35 additions & 19 deletions

File tree

crates/forge_main/src/zsh/plugin.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ pub fn generate_zsh_plugin() -> Result<String> {
4444
output.push_str(&completions_str);
4545

4646
// Set environment variable to indicate plugin is loaded (with timestamp)
47-
output.push_str("\n_FORGE_PLUGIN_LOADED=$(date +%s)\n");
47+
// Use typeset -g so the variable is global even when eval'd inside a function
48+
// (e.g. lazy-loading plugin managers like zinit, zplug, zsh-defer)
49+
output.push_str("\ntypeset -g _FORGE_PLUGIN_LOADED=$(date +%s)\n");
4850

4951
Ok(output)
5052
}
@@ -55,7 +57,9 @@ pub fn generate_zsh_theme() -> Result<String> {
5557
super::normalize_script(include_str!("../../../../shell-plugin/forge.theme.zsh"));
5658

5759
// Set environment variable to indicate theme is loaded (with timestamp)
58-
content.push_str("\n_FORGE_THEME_LOADED=$(date +%s)\n");
60+
// Use typeset -g so the variable is global even when eval'd inside a function
61+
// (e.g. lazy-loading plugin managers like zinit, zplug, zsh-defer)
62+
content.push_str("\ntypeset -g _FORGE_THEME_LOADED=$(date +%s)\n");
5963

6064
Ok(content)
6165
}

shell-plugin/forge.theme.zsh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/env zsh
22

33
# Enable prompt substitution for RPROMPT
4-
setopt PROMPT_SUBST
4+
# Use emulate to ensure setopt applies globally even when sourced from a function
5+
emulate -R zsh -c 'setopt PROMPT_SUBST'
56

67
# Model and agent info with token count
78
# Fully formatted output directly from Rust
@@ -23,6 +24,7 @@ function _forge_prompt_info() {
2324

2425
# Right prompt: agent and model with token count (uses single forge prompt command)
2526
# Set RPROMPT if empty, otherwise append to existing value
27+
# Use typeset -g to ensure RPROMPT is set globally even when sourced from a function
2628
if [[ -z "$_FORGE_THEME_LOADED" ]]; then
27-
RPROMPT='$(_forge_prompt_info)'"${RPROMPT:+ ${RPROMPT}}"
29+
typeset -g RPROMPT='$(_forge_prompt_info)'"${RPROMPT:+ ${RPROMPT}}"
2830
fi

shell-plugin/lib/config.zsh

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,38 @@
11
#!/usr/bin/env zsh
22

33
# Configuration variables for forge plugin
4-
# Using typeset to keep variables local to plugin scope and prevent public exposure
4+
# Using typeset -gh (global + hidden) so variables survive lazy-loading
5+
# from within a function scope (e.g. zinit, zplug, zsh-defer) while
6+
# staying hidden from `typeset` listings.
57

6-
typeset -h _FORGE_BIN="${FORGE_BIN:-forge}"
7-
typeset -h _FORGE_CONVERSATION_PATTERN=":"
8-
typeset -h _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}"
9-
typeset -h _FORGE_DELIMITER='\s\s+'
10-
typeset -h _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp"
8+
typeset -gh _FORGE_BIN="${FORGE_BIN:-forge}"
9+
typeset -gh _FORGE_CONVERSATION_PATTERN=":"
10+
typeset -gh _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}"
11+
typeset -gh _FORGE_DELIMITER='\s\s+'
12+
typeset -gh _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp"
1113

1214
# Detect fd command - Ubuntu/Debian use 'fdfind', others use 'fd'
13-
typeset -h _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')"
15+
typeset -gh _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')"
1416

1517
# Detect bat command - use bat if available, otherwise fall back to cat
1618
if command -v bat &>/dev/null; then
17-
typeset -h _FORGE_CAT_CMD="bat --color=always --style=numbers,changes --line-range=:500"
19+
typeset -gh _FORGE_CAT_CMD="bat --color=always --style=numbers,changes --line-range=:500"
1820
else
19-
typeset -h _FORGE_CAT_CMD="cat"
21+
typeset -gh _FORGE_CAT_CMD="cat"
2022
fi
2123

2224
# Commands cache - loaded lazily on first use
23-
typeset -h _FORGE_COMMANDS=""
25+
typeset -gh _FORGE_COMMANDS=""
2426

2527
# Hidden variables to be used only via the ForgeCLI
26-
typeset -h _FORGE_CONVERSATION_ID
27-
typeset -h _FORGE_ACTIVE_AGENT
28+
typeset -gh _FORGE_CONVERSATION_ID
29+
typeset -gh _FORGE_ACTIVE_AGENT
2830

2931
# Previous conversation ID for :conversation - (like cd -)
30-
typeset -h _FORGE_PREVIOUS_CONVERSATION_ID
32+
typeset -gh _FORGE_PREVIOUS_CONVERSATION_ID
3133

3234
# Session-scoped model and provider overrides (set via :model / :m).
3335
# When non-empty, these are passed as --model / --provider to every forge
3436
# invocation for the lifetime of the current shell session.
35-
typeset -h _FORGE_SESSION_MODEL
36-
typeset -h _FORGE_SESSION_PROVIDER
37+
typeset -gh _FORGE_SESSION_MODEL
38+
typeset -gh _FORGE_SESSION_PROVIDER

shell-plugin/lib/highlight.zsh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
# Syntax highlighting configuration for forge commands
44
# Style the conversation pattern with appropriate highlighting
55
# Keywords in yellow, rest in default white
6+
#
7+
# Use global declarations so we update the shared zsh-syntax-highlighting
8+
# collections even when sourced from within a function (lazy-loading plugin
9+
# managers). Patterns must remain an associative array because the pattern
10+
# highlighter stores regex => style entries in ZSH_HIGHLIGHT_PATTERNS.
11+
12+
typeset -gA ZSH_HIGHLIGHT_PATTERNS
13+
typeset -ga ZSH_HIGHLIGHT_HIGHLIGHTERS
614

715
# Style tagged files
816
ZSH_HIGHLIGHT_PATTERNS+=('@\[[^]]#\]' 'fg=cyan,bold')

0 commit comments

Comments
 (0)