Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
03d5b6a
feat(config): add refresh interval for periodic prompt updates
sanki92 Nov 2, 2025
a0ca87a
fix(bash): capture parent PID before subshell
sanki92 Nov 2, 2025
4e1e72c
fix(fish): capture parent PID for signal delivery
sanki92 Nov 2, 2025
72079c3
fix(bash): add exit trap to cleanup timer process
sanki92 Nov 2, 2025
b267bd8
fix(fish): add exit handler to cleanup timer process
sanki92 Nov 2, 2025
81be569
refactor(zsh): remove unnecessary TMOUT export
sanki92 Nov 2, 2025
1c398cf
refactor(cli): remove unused POSH_REFRESH_INTERVAL env var
sanki92 Nov 2, 2025
fee5e77
fix(bash): preserve sub-second precision in timer
sanki92 Nov 2, 2025
777c65d
fix(fish): preserve sub-second precision in timer
sanki92 Nov 2, 2025
a4a1baf
fix(zsh): add validation for minimum refresh interval
sanki92 Nov 2, 2025
9dd103d
fix(fish): use $fish_pid for parent process reference
sanki92 Nov 2, 2025
7ce711c
fix(fish): add variable check before killing timer process
sanki92 Nov 2, 2025
91e4205
fix(bash): verify variable exists before killing timer
sanki92 Nov 2, 2025
4797c37
refactor(bash): scope _omp_refresh_pid as local variable
sanki92 Nov 4, 2025
47a41c0
refactor(bash): scope parent_pid as local variable
sanki92 Nov 4, 2025
fa44e3b
fix(bash): prevent duplicate timer processes
sanki92 Nov 4, 2025
af8d69f
fix(pwsh): prevent duplicate timer and event registrations
sanki92 Nov 4, 2025
6f7850e
style(zsh): remove trailing whitespace
sanki92 Nov 4, 2025
c3472ea
fix(zsh): redirect warning message to stderr
sanki92 Nov 4, 2025
7aee74e
fix(bash): remove local keyword to allow trap access to PID
sanki92 Nov 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ func runInit(sh, command string) {

switch {
case printOutput, debug:
output = shell.PrintInit(env, feats, &startTime)
output = shell.PrintInit(env, feats, &startTime, cfg.RefreshInterval)
default:
output = shell.Init(env, feats)
output = shell.Init(env, feats, cfg.RefreshInterval)
}

shellDSC := shell.DSC()
Expand Down
9 changes: 9 additions & 0 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Config struct {
Extends string `json:"extends,omitempty" toml:"extends,omitempty" yaml:"extends,omitempty"`
AccentColor color.Ansi `json:"accent_color,omitempty" toml:"accent_color,omitempty" yaml:"accent_color,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty" yaml:"console_title_template,omitempty"`
RefreshInterval int `json:"refresh_interval,omitempty" toml:"refresh_interval,omitempty" yaml:"refresh_interval,omitempty"`
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty" yaml:"pwd,omitempty"`
Source string `json:"-" toml:"-" yaml:"-"`
Format string `json:"-" toml:"-" yaml:"-"`
Expand Down Expand Up @@ -157,6 +158,14 @@ func (cfg *Config) Features(env runtime.Environment) shell.Features {
feats |= shell.Tooltips
}

if cfg.RefreshInterval > 0 {
supportedShells := []string{shell.PWSH, shell.ZSH, shell.BASH, shell.FISH}
if slices.Contains(supportedShells, env.Shell()) {
log.Debug("refresh interval enabled")
feats |= shell.RefreshInterval
}
}

if env.Shell() == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(terminal.PromptMark) {
log.Debug("prompt mark enabled")
feats |= shell.PromptMark
Expand Down
72 changes: 72 additions & 0 deletions src/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,75 @@
cache.DeleteAll(cache.Device)
}
}

func TestRefreshIntervalFeature(t *testing.T) {
cases := []struct {
Case string

Check failure on line 189 in src/config/config_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

File is not properly formatted (gofmt)
Shell string
RefreshInterval int
ExpectedEnabled bool
}{
{
Case: "pwsh with refresh interval",
Shell: shell.PWSH,
RefreshInterval: 5000,
ExpectedEnabled: true,
},
{
Case: "zsh with refresh interval",
Shell: shell.ZSH,
RefreshInterval: 3000,
ExpectedEnabled: true,
},
{
Case: "bash with refresh interval",
Shell: shell.BASH,
RefreshInterval: 3000,
ExpectedEnabled: true,
},
{
Case: "fish with refresh interval",
Shell: shell.FISH,
RefreshInterval: 3000,
ExpectedEnabled: true,
},
{
Case: "pwsh without refresh interval",
Shell: shell.PWSH,
RefreshInterval: 0,
ExpectedEnabled: false,
},
{
Case: "elvish with refresh interval (unsupported)",
Shell: shell.ELVISH,
RefreshInterval: 5000,
ExpectedEnabled: false,
},
{
Case: "cmd with refresh interval (unsupported)",
Shell: shell.CMD,
RefreshInterval: 5000,
ExpectedEnabled: false,
},
}

for _, tc := range cases {
env := &mock.Environment{}
env.On("Shell").Return(tc.Shell)
env.On("Getenv", "OMP_CACHE_DISABLED").Return("")

cfg := &Config{
RefreshInterval: tc.RefreshInterval,
Blocks: []*Block{},
Upgrade: &upgrade.Config{
Auto: false,
DisplayNotice: false,
},
}

feats := cfg.Features(env)
hasRefreshInterval := feats&shell.RefreshInterval != 0

assert.Equal(t, tc.ExpectedEnabled, hasRefreshInterval, tc.Case)
}
}
2 changes: 2 additions & 0 deletions src/shell/bash.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ bleopt prompt_ps1_final='$(
--shell-version="$BASH_VERSION" \
--escape=false
)'`
case RefreshInterval:
return "enable_posh_refresh_interval"
case PromptMark, PoshGit, Azure, LineError, Jobs, Tooltips, Async:
fallthrough
default:
Expand Down
6 changes: 4 additions & 2 deletions src/shell/bash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ func TestBashFeatures(t *testing.T) {
_omp_ftcs_marks=1
"$_omp_executable" upgrade --auto
"$_omp_executable" notice
_omp_cursor_positioning=1`
_omp_cursor_positioning=1
enable_posh_refresh_interval`

assert.Equal(t, want, got)
}
Expand Down Expand Up @@ -48,7 +49,8 @@ bleopt prompt_rps1='$(
--terminal-width="${COLUMNS-0}" \
--escape=false
)'
_omp_cursor_positioning=1`
_omp_cursor_positioning=1
enable_posh_refresh_interval`

assert.Equal(t, want, got)

Expand Down
1 change: 1 addition & 0 deletions src/shell/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
RPrompt
CursorPositioning
Async
RefreshInterval
)

// getAllFeatures returns all defined feature flags by iterating through bit positions
Expand Down
2 changes: 2 additions & 0 deletions src/shell/fish.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
return unixUpgrade
case Notice:
return unixNotice
case RefreshInterval:
return "enable_posh_refresh_interval"

Check failure on line 27 in src/shell/fish.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

string `enable_posh_refresh_interval` has 3 occurrences, make it a constant (goconst)
case RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning, Async:
fallthrough
default:
Expand Down
3 changes: 2 additions & 1 deletion src/shell/fish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ set --global _omp_transient_prompt 1
set --global _omp_ftcs_marks 1
"$_omp_executable" upgrade --auto
"$_omp_executable" notice
set --global _omp_prompt_mark 1`
set --global _omp_prompt_mark 1
enable_posh_refresh_interval`

assert.Equal(t, want, got)
}
Expand Down
9 changes: 5 additions & 4 deletions src/shell/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ func getExecutablePath(env runtime.Environment) (string, error) {
return executable, nil
}

func Init(env runtime.Environment, feats Features) string {
func Init(env runtime.Environment, feats Features, refreshInterval int) string {
switch env.Flags().Shell {
case ELVISH, PWSH:
if env.Flags().Shell != ELVISH && !env.Flags().Eval {
return PrintInit(env, feats, nil)
return PrintInit(env, feats, nil, refreshInterval)
}

executable, err := getExecutablePath(env)
Expand Down Expand Up @@ -79,13 +79,13 @@ func Init(env runtime.Environment, feats Features) string {

return fmt.Sprintf(command, executable, env.Flags().Shell, config, additionalParams)
case ZSH, BASH, FISH, CMD, XONSH, NU:
return PrintInit(env, feats, nil)
return PrintInit(env, feats, nil, refreshInterval)
default:
return fmt.Sprintf(`echo "%s is not supported by Oh My Posh"`, env.Flags().Shell)
}
}

func PrintInit(env runtime.Environment, features Features, startTime *time.Time) string {
func PrintInit(env runtime.Environment, features Features, startTime *time.Time, refreshInterval int) string {
async := features&Async != 0

if scriptPath, OK := hasScript(env); OK {
Expand Down Expand Up @@ -133,6 +133,7 @@ func PrintInit(env runtime.Environment, features Features, startTime *time.Time)
init := strings.NewReplacer(
"::OMP::", executable,
"::SESSION_ID::", cache.SessionID(),
"::REFRESH_INTERVAL::", fmt.Sprintf("%d", refreshInterval),
).Replace(script)

shellScript := features.Lines(env.Flags().Shell).String(init)
Expand Down
2 changes: 2 additions & 0 deletions src/shell/pwsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func (f Features) Pwsh() Code {
return "& $global:_ompExecutable upgrade --auto"
case Notice:
return "& $global:_ompExecutable notice"
case RefreshInterval:
return "Enable-PoshRefreshInterval"
case PromptMark, RPrompt, CursorPositioning, Async:
fallthrough
default:
Expand Down
5 changes: 3 additions & 2 deletions src/shell/pwsh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)

var allFeatures = Tooltips | LineError | Transient | Jobs | Azure | PoshGit | FTCSMarks | Upgrade | Notice | PromptMark | RPrompt | CursorPositioning
var allFeatures = Tooltips | LineError | Transient | Jobs | Azure | PoshGit | FTCSMarks | Upgrade | Notice | PromptMark | RPrompt | CursorPositioning | RefreshInterval

func TestPwshFeatures(t *testing.T) {
got := allFeatures.Lines(PWSH).String("")
Expand All @@ -21,7 +21,8 @@ Enable-PoshTooltips
Enable-PoshTransientPrompt
$global:_ompFTCSMarks = $true
& $global:_ompExecutable upgrade --auto
& $global:_ompExecutable notice`
& $global:_ompExecutable notice
Enable-PoshRefreshInterval`

assert.Equal(t, want, got)
}
Expand Down
30 changes: 30 additions & 0 deletions src/shell/scripts/omp.bash
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,34 @@ function _omp_install_hook() {
PROMPT_COMMAND=(_omp_hook "${prompt_command[@]}")
}

function enable_posh_refresh_interval() {
local interval=::REFRESH_INTERVAL::
if [[ $interval -le 0 ]]; then
return
fi

# Convert milliseconds to seconds for bash (preserve sub-second precision)
local timeout_seconds=$(awk "BEGIN {print $interval/1000}")

Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on line 178. This should be removed for consistency with coding standards.

Suggested change

Copilot uses AI. Check for mistakes.
function _omp_refresh_prompt() {
# Trigger prompt refresh by calling the hook
_omp_hook
}

# Use a background process with sleep to trigger refresh
parent_pid=$$
(
while true; do
sleep "$timeout_seconds"
kill -WINCH $parent_pid 2>/dev/null
done
) &

Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on line 192. This should be removed for consistency with coding standards.

Suggested change

Copilot uses AI. Check for mistakes.
_omp_refresh_pid=$!
# Ensure background process is killed when shell exits
trap '[ -n "$_omp_refresh_pid" ] && kill $_omp_refresh_pid 2>/dev/null' EXIT
# Set up SIGWINCH handler to refresh prompt
trap '_omp_hook' WINCH
}

_omp_install_hook
24 changes: 24 additions & 0 deletions src/shell/scripts/omp.fish
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,30 @@ bind \cc _omp_ctrl_c_key_handler -M default
bind \cc _omp_ctrl_c_key_handler -M insert
bind \cc _omp_ctrl_c_key_handler -M visual

function enable_posh_refresh_interval
set --local interval ::REFRESH_INTERVAL::
if test $interval -le 0
return
end

# Convert milliseconds to seconds for fish
set --local timeout_seconds (math "$interval / 1000.0")

Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on line 246. This should be removed for consistency with coding standards.

Suggested change

Copilot uses AI. Check for mistakes.
function _omp_refresh_timer --on-signal SIGUSR1
omp_repaint_prompt
end

Comment on lines +246 to +250
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on line 250. This should be removed for consistency with coding standards.

Suggested change
function _omp_refresh_timer --on-signal SIGUSR1
omp_repaint_prompt
end
function _omp_refresh_timer --on-signal SIGUSR1
omp_repaint_prompt
end

Copilot uses AI. Check for mistakes.
# Start background timer process
fish -c "set parent_pid $fish_pid; while true; sleep $timeout_seconds; kill -SIGUSR1 \$parent_pid; end" &
set --global _omp_refresh_pid $last_pid
end

# Clean up background timer process on shell exit
function _omp_cleanup_refresh --on-event fish_exit
if set -q _omp_refresh_pid
kill $_omp_refresh_pid 2>/dev/null
end
end
# legacy functions
function enable_poshtransientprompt
return
Expand Down
37 changes: 36 additions & 1 deletion src/shell/scripts/omp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {

### Exported Functions ###

function Set-PoshContext([bool]$originalStatus) {}
function Set-PoshContext([bool]$originalStatus) {
Start-PoshRefreshTimer
}

function Enable-PoshTooltips {
if ($script:ConstrainedLanguageMode) {
Expand Down Expand Up @@ -408,6 +410,32 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
Set-PSReadLineOption -PromptText $validLine, $errorLine
}

function Enable-PoshRefreshInterval {
$interval = ::REFRESH_INTERVAL::
if ($interval -le 0) {
return
}

$script:PoshRefreshTimer = New-Object -Type Timers.Timer
$script:PoshRefreshTimer.Interval = $interval

Register-ObjectEvent -InputObject $script:PoshRefreshTimer -EventName Elapsed -SourceIdentifier "Posh.RefreshTimer.Elapsed" -Action {
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
} | Out-Null
}

function Start-PoshRefreshTimer {
if ($null -eq $script:PoshRefreshTimer) {
return
}

if ($script:PoshRefreshTimer.Enabled) {
$script:PoshRefreshTimer.Stop()
}

$script:PoshRefreshTimer.Start()
}

# perform cleanup on removal so a new initialization in current session works
if (!$script:ConstrainedLanguageMode) {
$ExecutionContext.SessionState.Module.OnRemove += {
Expand All @@ -418,6 +446,12 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
(Get-PSReadLineOption).ContinuationPrompt = $script:OriginalContinuationPrompt
(Get-PSReadLineOption).PromptText = $script:OriginalPromptText

if ($null -ne $script:PoshRefreshTimer) {
$script:PoshRefreshTimer.Stop()
$script:PoshRefreshTimer.Dispose()
Unregister-Event -SourceIdentifier "Posh.RefreshTimer.Elapsed" -ErrorAction SilentlyContinue
}

if ((Get-PSReadLineKeyHandler Spacebar).Function -eq 'OhMyPoshSpaceKeyHandler') {
Remove-PSReadLineKeyHandler Spacebar
}
Expand All @@ -443,6 +477,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
"Enable-PoshTooltips"
"Enable-PoshTransientPrompt"
"Enable-PoshLineError"
"Enable-PoshRefreshInterval"
"Set-TransientPrompt"
"prompt"
)
Expand Down
30 changes: 30 additions & 0 deletions src/shell/scripts/omp.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -235,5 +235,35 @@ function enable_poshtooltips() {
_omp_create_widget $widget _omp_render_tooltip
}

function enable_posh_refresh_interval() {
local interval=::REFRESH_INTERVAL::
if [[ $interval -le 0 ]]; then
return
fi

# TMOUT in zsh only supports integer seconds; sub-second values are unreliable.
if [[ $interval -lt 1000 ]]; then
print "Warning: zsh TMOUT does not support intervals less than 1000ms. Setting refresh interval to 1000ms."
interval=1000
fi

# Convert milliseconds to seconds for zsh's TMOUT
local timeout_seconds=$(( interval / 1000 ))

function _omp_refresh_prompt() {
# Only refresh if we're at the command prompt (not during command execution)
if [[ -z $BUFFER ]]; then
zle && zle .reset-prompt
fi
}

# Use TMOUT to trigger prompt refresh
TMOUT=$timeout_seconds

function TRAPALRM() {
_omp_refresh_prompt
}
}

# legacy functions
function enable_poshtransientprompt() {}
Loading
Loading