Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/config/segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type Segment struct {
Toggled bool `json:"toggled,omitempty" toml:"toggled,omitempty" yaml:"toggled,omitempty"`
Pending bool `json:"-" toml:"-" yaml:"-"`
Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty" yaml:"interactive,omitempty"`
MultilineKeepPrompt bool `json:"multiline_keepprompt,omitempty" toml:"multiline_keepprompt,omitempty" yaml:"multiline_keepprompt,omitempty"`
}

// segmentAlias is used to avoid recursion during unmarshaling
Expand Down
28 changes: 20 additions & 8 deletions src/prompt/extra.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package prompt

import (
"fmt"
"strings"

"github.com/jandedobbeleer/oh-my-posh/src/color"
"github.com/jandedobbeleer/oh-my-posh/src/config"
"github.com/jandedobbeleer/oh-my-posh/src/regex"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
Expand Down Expand Up @@ -56,7 +58,7 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
}
}

promptText, err := template.Render(getTemplate(prompt.Template), nil)
promptText, err := template.Render(getTemplate(prompt.Template), e)
if err != nil {
promptText = err.Error()
}
Expand Down Expand Up @@ -89,15 +91,25 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {

switch e.Env.Shell() {
case shell.ZSH:
if promptType == Transient {
if !e.Env.Flags().Eval {
break
}
if !e.Env.Flags().Eval {
break
}

prompt := fmt.Sprintf("PS1=%s", shell.QuotePosixStr(str))
if promptType == Transient {
evalOutput := fmt.Sprintf("PS1=%s", shell.QuotePosixStr(str))
// empty RPROMPT
prompt += "\nRPROMPT=''"
return prompt
evalOutput += "\nRPROMPT=''"
return evalOutput
}

if promptType == Secondary {
evalOutput := fmt.Sprintf("_omp_secondary_prompt=%s", shell.QuotePosixStr(str))
plain := regex.ReplaceAllString(terminal.AnsiRegex, str, "")
plain = strings.ReplaceAll(plain, "%{", "")
plain = strings.ReplaceAll(plain, "%}", "")
evalOutput += fmt.Sprintf("\n_omp_secondary_prompt_plain=%s", shell.QuotePosixStr(plain))
evalOutput += fmt.Sprintf("\nPOSH_MULTILINE_KEEPPROMPT=%t", prompt.MultilineKeepPrompt)
return evalOutput
}
case shell.PWSH:
if promptType == Transient {
Expand Down
67 changes: 62 additions & 5 deletions src/shell/scripts/omp.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ _omp_primary_prompt=""
_omp_streaming_supported=""

# set secondary prompt
_omp_secondary_prompt=$($_omp_executable print secondary --shell=zsh)
eval "$($_omp_executable print secondary --shell=zsh --eval)"

function _omp_set_cursor_position() {
# not supported in Midnight Commander
Expand Down Expand Up @@ -256,13 +256,60 @@ function _omp_render_tooltip() {
zle .reset-prompt
}

function _omp_is_buffer_complete() {
# 1. Syntax check
if ! zsh -n <<EOF 2>/dev/null
$PREBUFFER$BUFFER
EOF
then
return 1
fi

# 2. Check for odd number of trailing backslashes
local bs_count=0
local i=${#BUFFER}
# Zsh indices start at 1
while (( i > 0 )); do
# Check current char
if [[ "${BUFFER[i]}" == '\' ]]; then
(( bs_count++ ))
(( i-- ))
else
break
fi
done

if (( bs_count % 2 != 0 )); then
return 1
fi

return 0
}

function _omp_zle-line-init() {
[[ $CONTEXT == start ]] || return 0

# Start regular line editor.
(( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[1]
zle .recursive-edit
local -i ret=$?

local -i ret=0
if [[ $POSH_MULTILINE_KEEPPROMPT == "true" ]]; then
while true; do
zle .recursive-edit
ret=$?

if ((ret)) || _omp_is_buffer_complete; then
break
fi

BUFFER+=$'\n'$_omp_secondary_prompt_plain
CURSOR=$#BUFFER
done
else
zle .recursive-edit
ret=$?
fi

(( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2]

# We need this workaround because when the `filler` is set,
Expand All @@ -275,8 +322,18 @@ function _omp_zle-line-init() {
# kill streaming before transient prompt to prevent handler overwriting it
_omp_cleanup_stream

eval "$(_omp_get_prompt transient --eval $terminal_width_option)"
zle .reset-prompt
if ((ret == 0)); then
local saved_buffer=$BUFFER
local saved_cursor=$CURSOR
BUFFER=""
eval "$(_omp_get_prompt transient --eval $terminal_width_option)"
zle .reset-prompt
BUFFER=$saved_buffer
CURSOR=$saved_cursor
else
eval "$(_omp_get_prompt transient --eval $terminal_width_option)"
zle .reset-prompt
fi

if ((ret)); then
# TODO (fix): this is not equal to sending a SIGINT, since the status code ($?) is set to 1 instead of 130.
Expand Down
29 changes: 28 additions & 1 deletion themes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5141,7 +5141,34 @@
"description": "https://ohmyposh.dev/docs/configuration/line-error"
},
"secondary_prompt": {
"$ref": "#/definitions/extra_prompt",
"type": "object",
"default": {},
"properties": {
"template": {
"type": "string",
"title": "Prompt Template"
},
"foreground": {
"$ref": "#/definitions/color"
},
"foreground_templates": {
"$ref": "#/definitions/templates",
"description": "https://ohmyposh.dev/docs/configuration/colors#color-templates"
},
"background": {
"$ref": "#/definitions/color"
},
"background_templates": {
"$ref": "#/definitions/templates",
"description": "https://ohmyposh.dev/docs/configuration/colors#color-templates"
},
"multiline_keepprompt": {
"type": "boolean",
"title": "Multiline Keep Prompt",
"description": "Treat multi-line commands as a single block to support transient prompts correctly",
"default": false
}
},
"title": "Secondary Prompt Setting",
"description": "https://ohmyposh.dev/docs/configuration/secondary-prompt"
},
Expand Down
13 changes: 13 additions & 0 deletions website/docs/configuration/transient.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ By enabling Transient Prompt, you can replace the prompt with some other content

![After Transient](/img/transient-after.gif)

:::tip
In Zsh, the transient prompt correctly handles multi-line commands, ensuring the prompt stays full while you are typing and only becomes transient once the entire command is submitted.
:::

## Configuration

You need to extend or create a custom theme with your transient prompt. For example:
Expand All @@ -51,6 +55,15 @@ You need to extend or create a custom theme with your transient prompt. For exam
| `filler` | `string` | when you want to create a line with a repeated set of characters spanning the width of the terminal. Will be added _after_ the `template` text |
| `newline` | `boolean` | add a newline before the prompt |

### Secondary Prompt

The secondary prompt is used when you have a multi-line command.

| Name | Type | Description |
| ---------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `template` | `string` | a go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the properties below - defaults to `> ` |
| `multiline_keepprompt` | `boolean` | when true, treats multi-line commands as a single block to support transient prompts correctly (Zsh only). Defaults to `false` |

## Enable the feature

Oh My posh handles enabling the feature automatically for all shells except `cmd` when the config contains a
Expand Down