Skip to content

Commit 39847ba

Browse files
AIdarthwalsh
authored andcommitted
AI generated
1 parent a19cab7 commit 39847ba

2 files changed

Lines changed: 214 additions & 3 deletions

File tree

apps/pwsh.pushd-prompt.PLAN.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# PLAN: Push-Location Stack Display in Oh My Posh
2+
3+
## Problem
4+
5+
When using `Push-Location`/`Pop-Location` to navigate directories, the stack is invisible — no standard prompt segment shows it. You can get lost: how many `Pop-Location` calls do you need? Where will you land?
6+
7+
**Goal:** show the pushd stack (or at minimum the depth) in the oh-my-posh prompt, with no display when the stack is empty.
8+
9+
## Scope
10+
11+
Two decisions to make before implementing:
12+
13+
1. **What to show:** count-only (`2x ~/proj`) vs top-of-stack path (`↩ ~/proj`) vs full stack (`~/proj ← ~/tmp`)
14+
2. **How to implement:** built-in `.StackCount` (OMP Path segment) vs env vars via `Set-PoshContext` hook
15+
16+
## Background
17+
18+
### Getting the stack in PowerShell
19+
20+
[`Get-Location -Stack`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-location?view=powershell-7.5) exposes the current location stack. Most-recently pushed path is first:
21+
22+
```powershell
23+
# All paths
24+
Get-Location -Stack | Select-Object -ExpandProperty Path
25+
26+
# Top of stack only
27+
(Get-Location -Stack | Select-Object -First 1).Path
28+
```
29+
30+
### Option A: `.StackCount` in the built-in Path segment
31+
32+
[OMP's Path segment](https://ohmyposh.dev/docs/segments/system/path) exposes `.StackCount` — shows only the count, not paths:
33+
34+
```json
35+
{
36+
"type": "path",
37+
"template": "{{ if gt .StackCount 0 }}{{ .StackCount }}x {{ end }}{{ .Path }}"
38+
}
39+
```
40+
41+
- (+) Zero config, no profile changes
42+
- (-) Count only — no idea which directory is on top
43+
44+
### Option B: `Set-PoshContext` hook + env vars + text segment
45+
46+
Since [OMP v28](https://github.com/JanDeDobbeleer/oh-my-posh/releases/tag/v28.0.0) removed the old `command` segment, the recommended pattern is to compute values in `Set-PoshContext` (PowerShell's prompt hook) and read them via `.Env.VAR` in a `text` segment.
47+
48+
> **Note:** In pwsh the hook is `Set-PoshContext` (an alias). The lowercase `set_poshcontext()` is the zsh/bash/fish variant. The docs show both; use the alias form for pwsh.
49+
50+
Add to `$PROFILE`:
51+
52+
```powershell
53+
function Set-EnvVar([bool]$originalStatus) {
54+
$stack = @(Get-Location -Stack | Select-Object -ExpandProperty Path)
55+
$env:POSH_PUSHD_TOP = if ($stack.Count) { $stack[0] } else { "" }
56+
$env:POSH_PUSHD_STACK = if ($stack.Count) { $stack -join " <- " } else { "" }
57+
$env:POSH_PUSHD_COUNT = $stack.Count
58+
}
59+
New-Alias -Name 'Set-PoshContext' -Value 'Set-EnvVar' -Scope Global -Force
60+
```
61+
62+
Add a `text` segment to the OMP config:
63+
64+
```json
65+
{
66+
"type": "text",
67+
"template": "{{ if .Env.POSH_PUSHD_TOP }}↩ {{ .Env.POSH_PUSHD_TOP }}{{ end }}"
68+
}
69+
```
70+
71+
Or show the full stack:
72+
73+
```json
74+
{
75+
"type": "text",
76+
"template": "{{ if .Env.POSH_PUSHD_STACK }}⇄ {{ .Env.POSH_PUSHD_STACK }}{{ end }}"
77+
}
78+
```
79+
80+
- (+) Shows actual paths, not just count
81+
- (+) Composable — can format/truncate arbitrarily in PowerShell
82+
- (-) One extra function in `$PROFILE`; env vars persist in child processes (cosmetic issue)
83+
84+
## POC Steps
85+
86+
- [ ] Try Option A first (`.StackCount` in existing Path segment template) — zero friction, check if count-only is good enough
87+
- [ ] If count isn't enough: add `Set-PoshContext` + `POSH_PUSHD_TOP` env var + `text` segment
88+
- [ ] Verify the env var approach works with the existing `Set-PoshContext` alias if one already exists in `$PROFILE`
89+
- [ ] Test with: 0 pushes (nothing shows), 1 push (top shown), 3 pushes (only top shown or full stack)
90+
- [ ] Decide on truncation: show full path, or `~/...` abbreviated? Add home-path substitution if needed:
91+
```powershell
92+
$env:POSH_PUSHD_TOP = $stack[0] -replace [regex]::Escape($HOME), "~"
93+
```
94+
95+
## Testing: Pester + `oh-my-posh print primary --plain`
96+
97+
`oh-my-posh print primary` renders the prompt from a config file without a live terminal. `--plain` strips ANSI codes for exact string comparison. Combined with Pester, this gives snapshot tests for the rendered prompt.
98+
99+
Key flags for deterministic output:
100+
101+
| Flag | Purpose |
102+
|---|---|
103+
| `--config <path>` | Which OMP theme to render |
104+
| `--plain` | Strip ANSI, makes string comparison work |
105+
| `--terminal-width 120` | Fixed width so line-breaking is deterministic |
106+
| `--pwd` / `--pswd` | Override current directory shown in prompt |
107+
| `--stack-count <n>` | Override stack count (for `.StackCount`-based approach) |
108+
109+
> **Note:** `--stack-count` only feeds the `.StackCount` variable in the Path segment. To test the env-var approach (Option B), set `$env:POSH_PUSHD_TOP` before calling `oh-my-posh print`.
110+
111+
### Testing the `Set-PoshContext` logic independently
112+
113+
The hook is plain PowerShell — test it in isolation before wiring to OMP:
114+
115+
```powershell
116+
Describe "Set-PoshContext populates env vars" {
117+
BeforeEach {
118+
$env:POSH_PUSHD_TOP = ""
119+
$env:POSH_PUSHD_COUNT = 0
120+
}
121+
122+
It "clears env vars when stack is empty" {
123+
# Start fresh, don't push anything
124+
Set-EnvVar $true
125+
$env:POSH_PUSHD_TOP | Should -BeExactly ""
126+
[int]$env:POSH_PUSHD_COUNT | Should -Be 0
127+
}
128+
129+
It "shows top of stack after Push-Location" {
130+
$tmp = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ("posh-test-" + [guid]::NewGuid()))
131+
Push-Location $tmp.FullName
132+
Set-EnvVar $true
133+
$env:POSH_PUSHD_TOP | Should -BeExactly $tmp.FullName
134+
Pop-Location
135+
Remove-Item -Recurse -Force $tmp
136+
}
137+
}
138+
```
139+
140+
### Snapshot testing rendered prompt output
141+
142+
Spawn a fresh `pwsh` process so the location stack is isolated (the stack is per-runspace):
143+
144+
```powershell
145+
Describe "OMP prompt rendering" {
146+
BeforeAll {
147+
$script:Config = Join-Path $PSScriptRoot "../.go-my-posh.yaml"
148+
}
149+
150+
It "shows no pushd indicator when stack is empty" {
151+
$out = & oh-my-posh print primary `
152+
--config $script:Config `
153+
--shell pwsh `
154+
--plain `
155+
--terminal-width 120 `
156+
--pwd (Get-Location).Path `
157+
--pswd (Get-Location).Path `
158+
--stack-count 0
159+
160+
$out | Should -Not -Match "↩"
161+
$out | Should -Not -Match " +" # no double spaces from missing segment
162+
}
163+
164+
It "renders pushd indicator via env var (full chain)" {
165+
$script = @"
166+
`$env:POSH_PUSHD_TOP = "$HOME"
167+
oh-my-posh print primary --config '$script:Config' --plain --terminal-width 120
168+
"@
169+
$out = pwsh -NoProfile -NonInteractive -Command $script
170+
$out | Should -Match ([regex]::Escape("↩"))
171+
}
172+
}
173+
```
174+
175+
**Snapshot approach (recommended for spacing/alignment regressions):** once output looks right, store it:
176+
177+
```powershell
178+
$expected = Get-Content "$PSScriptRoot/snapshots/prompt-no-stack.txt" -Raw
179+
$out | Should -BeExactly $expected
180+
```
181+
182+
Update snapshots intentionally when you change the template; `git diff snapshots/` is the review UI. See [[testing-golden]] for the general pattern.
183+
184+
### What can't be integration-tested here
185+
186+
- Terminal reflow/resize behavior (terminal-specific, not OMP's output)
187+
- iTerm2/Windows Terminal rendering of specific Unicode glyphs
188+
189+
## Risks
190+
191+
**`Set-PoshContext` alias conflict:** if `$PROFILE` already has a `Set-PoshContext` alias or function, `New-Alias -Force` will overwrite it silently. Check first; consider merging into one function.
192+
193+
**Env var persistence:** `$env:POSH_PUSHD_TOP` is visible to child processes. Unlikely to cause issues but worth knowing.
194+
195+
**Stack is per named stack:** `Push-Location` supports named stacks via `-StackName`. `Get-Location -Stack` returns the *default* stack only. If using named stacks, the hook needs `Get-Location -StackName <name>` for each stack.
196+
197+
**Prompt perf:** `Get-Location -Stack` is fast (in-memory), but if the prompt is already slow (check with `oh-my-posh debug`), any hook adds up. See [[shell.prompt#debugging prompt being slow]].
198+
199+
## Unresolved Questions
200+
201+
- [ ] Is count-only (Option A) good enough, or is seeing the actual path important enough to add the hook?
202+
- [ ] What truncation/formatting for long paths? Home substitution (`~`)? Max chars?
203+
- [ ] If using Option B: should the full stack be shown, or just top-of-stack?
204+
- [ ] Does `POSH_PUSHD_COUNT` need to be an int in the env var, or is string fine for the template?
205+
206+
## Related
207+
208+
- [[shell.prompt#app-idea pushd stack display in prompt]] — origin of this plan
209+
- [[testing-golden]] — golden/snapshot testing patterns; Pester snapshots follow the same principles
210+
- [[pwsh.raii.PLAN]]`Push-Location`/`Pop-Location` as RAII; related to stack management
211+
- [OMP Path segment `.StackCount`](https://ohmyposh.dev/docs/segments/system/path)
212+
- [OMP Templates `.Env`](https://ohmyposh.dev/docs/configuration/templates)
213+
- [OMP v28 release — removed `command` segment](https://github.com/JanDeDobbeleer/oh-my-posh/releases/tag/v28.0.0)

apps/shell.prompt.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,7 @@ When using `pushd`/`popd` to navigate directories, the current directory stack d
135135
Idea: add a prompt segment that shows the pushd stack depth (or the full stack) when it's non-empty, so you know you have stashed locations to pop back to.
136136

137137
- In oh-my-zsh: `$DIRSTACK` array is available; a custom theme segment could show `${#DIRSTACK[@]}` when `> 0`
138-
- In oh-my-posh: would need a custom `command` segment running `(Get-Location -Stack).Count` (pwsh) or `dirs -p | wc -l` (zsh/bash) -- see [[cli.args#app-idea Unix combined-flag rewriter]]
139-
140-
---
138+
- In oh-my-posh: see [[pwsh.pushd-prompt.PLAN]] for POC options, implementation approaches, and Pester snapshot testing
141139

142140
## Autocorrect
143141
Currently using git config `help.autocorrect=10` which helps with typos in sub-commands

0 commit comments

Comments
 (0)