feat(shell): support vimode segment in fish

Extend the existing `vimode` segment to fish. When a config contains a
`vimode` segment and the active shell is fish, Oh My Posh enables an
`_omp_enable_vimode` hook that registers an `--on-variable fish_bind_mode`
handler. The handler mirrors fish's bind mode into `POSH_VI_MODE` (translated
to the keymap names the segment already understands: `default` -> `vicmd`,
`insert` -> `viins`, `replace`/`replace_one` -> `replace`, `visual` passthrough)
and re-renders the prompt on every mode change.

The hook only activates when vi or hybrid key bindings are in use, and it
overrides fish's built-in `fish_mode_prompt` so the mode is shown solely by the
segment. The segment implementation itself is unchanged.

Related #5438

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
JamBalaya56562
2026-07-18 20:56:38 +02:00
committed by Jan De Dobbeleer
co-authored by Claude Opus 4.8
parent 36a96bbde3
commit 302c3df9c4
6 changed files with 63 additions and 6 deletions
+1 -1
View File
@@ -211,7 +211,7 @@ func (cfg *Config) Features(env runtime.Environment) shell.Features {
}
}
if segment.Type == VIMODE && slices.Contains([]string{shell.ZSH, shell.PWSH}, env.Shell()) {
if segment.Type == VIMODE && slices.Contains([]string{shell.ZSH, shell.PWSH, shell.FISH}, env.Shell()) {
log.Debug("vi mode tracking enabled")
feats |= shell.VIMode
}
+5
View File
@@ -212,6 +212,11 @@ func TestFeaturesVIMode(t *testing.T) {
Shell: shell.PWSH,
ExpectedFeats: shell.VIMode,
},
{
Case: "fish enables vi mode tracking",
Shell: shell.FISH,
ExpectedFeats: shell.VIMode,
},
{
Case: "bash does not enable vi mode tracking",
Shell: shell.BASH,
+3 -1
View File
@@ -29,7 +29,9 @@ func (f Features) Fish() Code {
return unixUpgrade
case Notice:
return unixNotice
case RPrompt, PoshGit, Azure, LineError, Jobs, Async, KeyHandlers, VIMode:
case VIMode:
return "_omp_enable_vimode"
case RPrompt, PoshGit, Azure, LineError, Jobs, Async, KeyHandlers:
fallthrough
default:
return ""
+1
View File
@@ -19,6 +19,7 @@ set --global _omp_ftcs_marks 1
set --global _omp_prompt_mark 1
set --global _omp_cursor_positioning 1
set --global _omp_enable_streaming 1
_omp_enable_vimode
set --global _omp_transient_rprompt 1`
assert.Equal(t, want, got)
+35
View File
@@ -747,3 +747,38 @@ function omp_repaint_prompt
set --global _omp_new_prompt 1
commandline --function repaint
end
# vi mode tracking — mirror the active fish bind mode into POSH_VI_MODE and
# re-render the prompt whenever it changes. Enabled through the vimode segment.
function _omp_enable_vimode
# only track when vi (or hybrid) key bindings are active
contains -- "$fish_key_bindings" fish_vi_key_bindings fish_hybrid_key_bindings
or return
# translate fish's bind mode into the keymap names the vimode segment expects
function _omp_set_vimode
switch $fish_bind_mode
case default
set --global --export POSH_VI_MODE vicmd
case replace replace_one
set --global --export POSH_VI_MODE replace
case insert
set --global --export POSH_VI_MODE viins
case '*'
set --global --export POSH_VI_MODE $fish_bind_mode
end
end
# re-render the prompt on every mode change
function _omp_render_vimode --on-variable fish_bind_mode
_omp_set_vimode
omp_repaint_prompt
end
# oh-my-posh renders the mode through the vimode segment, so silence fish's
# built-in mode indicator to avoid a duplicate
function fish_mode_prompt
end
_omp_set_vimode
end
+18 -4
View File
@@ -7,12 +7,15 @@ sidebar_label: Vi mode
## What
Display the current Vi mode (insert / normal / visual …) in the prompt. Useful
when using ZSH [keymaps][zsh-vi-mode] via `bindkey -v` or PowerShell PSReadLine
`-EditMode Vi` so you can tell at a glance which mode you are in.
when using ZSH [keymaps][zsh-vi-mode] via `bindkey -v`, PowerShell PSReadLine
`-EditMode Vi`, or fish [vi key bindings][fish-vi-mode] so you can tell at a
glance which mode you are in.
In ZSH, adding this segment automatically registers a `zle-keymap-select` hook.
In PowerShell, it registers a PSReadLine [Vi mode change handler][pwsh-vi-mode].
Both re-render the prompt every time the active mode changes.
In fish, it registers an [`--on-variable fish_bind_mode`][fish-bind-mode] handler
(only when vi or hybrid key bindings are active). All of them re-render the
prompt every time the active mode changes.
:::caution PowerShell cursor indicator
@@ -22,6 +25,15 @@ segment in the prompt instead.
:::
:::caution fish mode prompt
fish support overrides `fish_mode_prompt` with an empty function so fish's
built-in `[N]` / `[I]` mode indicator does not show up next to the mode
rendered by this segment. Make sure vi key bindings are enabled (e.g.
`fish_vi_key_bindings`) for the segment to display.
:::
## Sample Configuration
import Config from "@site/src/components/Config.js";
@@ -51,9 +63,11 @@ import Config from "@site/src/components/Config.js";
| Name | Type | Description |
| --------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `.Mode` | `string` | the normalized mode: `insert`, `normal`, `visual`, `viopp`, `replace`, or the raw keymap when unmapped |
| `.Keymap` | `string` | the raw mode value ([`$KEYMAP`][zsh-keymap] in ZSH, e.g. `main`, `viins`, `vicmd`, `visual`, `viopp`; `viins` or `vicmd` in PowerShell PSReadLine) |
| `.Keymap` | `string` | the raw mode value ([`$KEYMAP`][zsh-keymap] in ZSH, e.g. `main`, `viins`, `vicmd`, `visual`, `viopp`; `viins` or `vicmd` in PowerShell PSReadLine; `viins`, `vicmd`, `visual` or `replace` in fish) |
[templates]: /docs/configuration/templates
[zsh-vi-mode]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Keymaps
[zsh-keymap]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#index-KEYMAP
[pwsh-vi-mode]: https://learn.microsoft.com/en-us/powershell/module/psreadline/set-psreadlineoption#-vimodechangehandler
[fish-vi-mode]: https://fishshell.com/docs/current/interactive.html#vi-mode-commands
[fish-bind-mode]: https://fishshell.com/docs/current/language.html#the-fish-bind-mode-variable