JanDeDobbeleer / oh-my-posh

The most customisable and low-latency cross platform/shell prompt renderer
https://ohmyposh.dev
MIT License
17.02k stars 2.37k forks source link

An ability to handle ViModeIndicator #335

Closed kt81 closed 3 years ago

kt81 commented 3 years ago

Prerequisites

Description

I want to show vi-mode indicator on prompt or somewhere. But oh-my-posh3 seems be not able to treat PSReadLine's -ViModeIndicator options:

# In profile ps1
Set-PSReadlineOption -ViModeIndicator Script -ViModeChangeHandler {
    Param($mode)
    $Env:SHELL_VI_MODE = $mode
}
// in segments block
{
    "type": "envvar",
    "style": "powerline",
    "foreground": "#26C6DA",
    "background": "#546E7A",
    "properties": {
    "var_name": "SHELL_VI_MODE"
}

Environment

Steps to Reproduce

  1. Set some parameter of ViModeIndicator (Cursor | Prompt | Script)
  2. In shell, push ESC and i or a.

Expected behavior: vi mode indicator is shown on Prompt or cursor and showing state is changed immediately.

Actual behavior: vi mode indicator is not shown.

JanDeDobbeleer commented 3 years ago

@kt81 I just did a quick debug and the env var segment works as designed, any change to that environment variable is reflected in the prompt (at least when set manually).

image

The segment I used was based on yours, I believe you forgot to add the final enclosing bracket:

{
    "type": "envvar",
    "style": "powerline",
    "powerline_symbol": "",
    "foreground": "#26C6DA",
    "background": "#546E7A",
    "properties": {
        "var_name": "SHELL_VI_MODE"
    }
},

I'm not familiar with vi mode in PowerShell, but one things for certain, the prompt can only change its visual state when it's re-rendered. Meaning when I press ESC + i or a, which does not re-render the prompt, it can't reflect those changes unless you press a command + ENTER.

From what I can see, there are two modes: Command and Insert. Only the second acts like I'm used to, the first actually hides the prompt meaning it's not re-rendered and will still show Insert. Exiting Command mode always reverts to Insert mode which is why, even when changing, the prompt "still" (once again) shows Insert.

The fact that in the documentation it highlights a change in prompt cursor makes sense from PSReadLine's perspective as it's in control of that part of the prompt, whereas oh-my-posh hooks into the prompt function preceding that functionality.

The only way to get this to work "successfully" is by abusing the ViModeChangeHandler hook and rewrite the line at that point in time. Meaning you need to either reference the correct oh-my-posh binary when using the module (see the module's bin folder) or go for a manual installation with Scoop for example. I was successful when I changed the the function towards the example below, which puts the cursor back to the beginning of the line and rewrites the entire line (be careful as using a multiline prompt will require more logic if you have that).

Set-PSReadLineOption -EditMode Vi
Set-PSReadlineOption -ViModeIndicator Script -ViModeChangeHandler {
    Param($mode)
    $Env:SHELL_VI_MODE = $mode
    # go back to the beginning of the line
    Write-Host -NoNewLine "`e[1000D"
    # rewrite the prompt manually
    write-Host -NoNewLine (oh-my-posh --shell pwsh --config ~/.jandedobbeleer.omp.json)
}

That way it shows Command when in command mode, which obviously disappears as soon as you go back to insert mode.

https://user-images.githubusercontent.com/2492783/104230589-0f8ccc80-544e-11eb-9525-7f050f536652.mov

I hope this provides an answer towards what you were looking for, unfortunately, as this falls outside of the scope of oh-my-posh, there's nothing more we can do to facilitate this.

kt81 commented 3 years ago

@JanDeDobbeleer Thank you for your reply and great example. The example seems little hacky but very helpful. I'll try find good feeling way base on it.

The segment I used was based on yours, I believe you forgot to add the final enclosing bracket: Oh, it seems copy-and-paste miss, sorry.

JanDeDobbeleer commented 3 years ago

The example seems little hacky

Well, all CLI tooling feels that way at times 😅

kt81 commented 3 years ago

Oh sorry, I didn't know much about it. 😭

mateusmedeiros commented 2 years ago

@JanDeDobbeleer Thank you very much for taking the time to describe that workaround even though the issue itself was deemed outside the scope of oh-my-posh by you. It gave me the last piece I needed to make it work the way I wanted.

Just dropping this comment to say that, for me, a slightly less hacky workaround was to call [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() (which is described right here by Microsoft) instead of sending the input directly.

Because my prompt has a newline segment/spans more than one line, a simple "go back and rewrite from here" didn't look quite right. With the InvokePrompt it seemed to work equally fine for me with prompts spanning any amount of lines.

So in my profile it ended up like this, basically:

  $Env:VI_MODE_SEGMENT = "INS"
  Set-PSReadlineOption -ViModeIndicator Script -ViModeChangeHandler {
    param($mode)

    if ($mode -eq "Command") {
      $Env:VI_MODE_SEGMENT = "COM"
    } else {
      $Env:VI_MODE_SEGMENT = "INS"
    }

    [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
  }
  oh-my-posh --init --shell pwsh --config ~/.local/share/oh-my-posh/Themes/pure.omp.json | Invoke-Expression

Also thank you for the insanely cool program, I'm turning to PowerShell for the first time since I first tried it back in 2.0 era and this time I liked it enough to take the plunge, and "oh-my-posh3"/"oh-my-posh" helped immensely.

EDIT: oh my (no pun intended), my brain is still on 2021 time, so I thought the last comment on this issue was some days ago 😂 So maybe Invoke-Prompt didn't even exist at the time.

JanDeDobbeleer commented 2 years ago

@mateusmedeiros it did, I just didn't know it at the time. We now also use it for transient prompt.

roachsinai commented 2 years ago

@mateusmedeiros thanks for your post, helps a lot.

But seems there is something wrong for me, as I wanna show <<< on rprompt after press ESC (then $env:SHELL_VI_PROMPT will be set to <<<), so I add

    {
      "alignment": "right",
      "segments": [
        {
          "type": "envvar",
          "background": "#546E7A",
          "foreground": "#26C6DA",
          "properties": {
            "var_name": "SHELL_VI_PROMPT"
          },
          "style": "text"
        }
      ],
      "type": "rprompt"
    }

to my custom omp.json but pwsh show nothing after I press ESC.

@JanDeDobbeleer Any suggestions will be very helpful, thanks.

My omp is lastest, just update today.

JanDeDobbeleer commented 2 years ago

@roachsinai the text segment was altered lately so you first have to adjust it as follows:

    {
      "alignment": "right",
      "segments": [
        {
          "type": "text",
          "background": "#546E7A",
          "foreground": "#26C6DA",
          "properties": {
            "template": " {{ .Env.SHELL_VI_PROMPT }} "
          },
          "style": "plain"
        }
      ],
      "type": "rprompt"
    }
roachsinai commented 2 years ago

@JanDeDobbeleer works, thanks a lot!!!

github-actions[bot] commented 7 months ago

This issue has been automatically locked since there has not been any recent activity (i.e. last half year) after it was closed. It helps our maintainers focus on the active issues. If you have found a problem that seems similar, please open a discussion first, complete the body with all the details necessary to reproduce, and mention this issue as reference.