diff --git a/src/engine/block.go b/src/engine/block.go index ae01cd7c..ee2e7937 100644 --- a/src/engine/block.go +++ b/src/engine/block.go @@ -14,6 +14,9 @@ type BlockType string // BlockAlignment aligment of a Block type BlockAlignment string +// Overflow defines how to handle a right block that overflows with the previous block +type Overflow string + const ( // Prompt writes one or more Segments Prompt BlockType = "prompt" @@ -25,6 +28,10 @@ const ( Left BlockAlignment = "left" // Right aligns right Right BlockAlignment = "right" + // Break adds a line break + Break Overflow = "break" + // Hide hides the block + Hide Overflow = "hide" ) // Block defines a part of the prompt with optional segments @@ -36,6 +43,7 @@ type Block struct { Segments []*Segment `json:"segments,omitempty"` Newline bool `json:"newline,omitempty"` Filler string `json:"filler,omitempty"` + Overflow Overflow `json:"overflow,omitempty"` env environment.Environment writer color.Writer diff --git a/src/engine/engine.go b/src/engine/engine.go index ae24dbbc..8addeb88 100644 --- a/src/engine/engine.go +++ b/src/engine/engine.go @@ -40,8 +40,8 @@ func (e *Engine) string() string { return e.console.String() } -func (e *Engine) canWriteRPrompt() bool { - if e.rprompt == "" || e.Plain { +func (e *Engine) canWriteRPrompt(rprompt bool) bool { + if rprompt && (e.rprompt == "" || e.Plain) { return false } consoleWidth, err := e.Env.TerminalWidth() @@ -55,7 +55,10 @@ func (e *Engine) canWriteRPrompt() bool { overflow := promptWidth % consoleWidth availableSpace = consoleWidth - overflow } - promptBreathingRoom := 30 + promptBreathingRoom := 5 + if rprompt { + promptBreathingRoom = 30 + } canWrite := (availableSpace - e.rpromptLength) >= promptBreathingRoom return canWrite } @@ -107,6 +110,15 @@ func (e *Engine) shouldFill(block *Block, length int) (string, bool) { } func (e *Engine) renderBlock(block *Block) { + defer func() { + // Due to a bug in Powershell, the end of the line needs to be cleared. + // If this doesn't happen, the portion after the prompt gets colored in the background + // color of the line above the new input line. Clearing the line fixes this, + // but can hopefully one day be removed when this is resolved natively. + if e.Env.Shell() == shell.PWSH || e.Env.Shell() == shell.PWSH5 { + e.writeANSI(e.Ansi.ClearAfter()) + } + }() // when in bash, for rprompt blocks we need to write plain // and wrap in escaped mode or the prompt will not render correctly if e.Env.Shell() == shell.BASH && (block.Type == RPrompt || block.Alignment == Right) { @@ -135,20 +147,34 @@ func (e *Engine) renderBlock(block *Block) { text, length := block.RenderSegments() e.currentLineLength += length e.write(text) - break + return } if block.Alignment != Right { - break + return } text, length := block.RenderSegments() - padText, hasPadText := e.shouldFill(block, length) - if hasPadText { + e.rpromptLength = length + + if !e.canWriteRPrompt(false) { + switch block.Overflow { + case Break: + e.newline() + case Hide: + // make sure to fill if needed + if padText, OK := e.shouldFill(block, 0); OK { + e.write(padText) + } + return + } + } + + if padText, OK := e.shouldFill(block, length); OK { // in this case we can print plain e.write(padText) e.write(text) - break + return } // this can contain ANSI escape sequences ansi := e.Ansi @@ -167,13 +193,6 @@ func (e *Engine) renderBlock(block *Block) { case RPrompt: e.rprompt, e.rpromptLength = block.RenderSegments() } - // Due to a bug in Powershell, the end of the line needs to be cleared. - // If this doesn't happen, the portion after the prompt gets colored in the background - // color of the line above the new input line. Clearing the line fixes this, - // but can hopefully one day be removed when this is resolved natively. - if e.Env.Shell() == shell.PWSH || e.Env.Shell() == shell.PWSH5 { - e.writeANSI(e.Ansi.ClearAfter()) - } } // debug will loop through your config file and output the timings for each segments @@ -231,7 +250,7 @@ func (e *Engine) print() string { prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt) return prompt case shell.PWSH, shell.PWSH5, shell.PLAIN, shell.NU: - if !e.canWriteRPrompt() { + if !e.canWriteRPrompt(true) { break } e.write(e.Ansi.SaveCursorPosition()) @@ -240,7 +259,7 @@ func (e *Engine) print() string { e.write(e.rprompt) e.write(e.Ansi.RestoreCursorPosition()) case shell.BASH: - if !e.canWriteRPrompt() { + if !e.canWriteRPrompt(true) { break } // in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly diff --git a/src/engine/engine_test.go b/src/engine/engine_test.go index 81034969..710c21fa 100644 --- a/src/engine/engine_test.go +++ b/src/engine/engine_test.go @@ -39,7 +39,7 @@ func TestCanWriteRPrompt(t *testing.T) { currentLineLength: tc.PromptLength, rprompt: "hello", } - got := engine.canWriteRPrompt() + got := engine.canWriteRPrompt(true) assert.Equal(t, tc.Expected, got, tc.Case) } } diff --git a/themes/schema.json b/themes/schema.json index aa28f2d6..6fea2e4e 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -125,6 +125,29 @@ "required": ["type", "segments"], "title": "RPrompt definition, contains 1 or more segments to render to the right of the cursor" } + }, + { + "if": { + "properties": { + "type": { + "const": "prompt" + }, + "alignment": { + "const": "right" + } + } + }, + "then": { + "properties": { + "type": { + "type": "string", + "title": "Block overflow", + "description": "https://ohmyposh.dev/docs/configuration/block#overflow", + "enum": ["break", "hide"], + "default": "" + } + } + } } ], "properties": { diff --git a/website/docs/configuration/block.mdx b/website/docs/configuration/block.mdx index 6e7cf64a..af60b387 100644 --- a/website/docs/configuration/block.mdx +++ b/website/docs/configuration/block.mdx @@ -71,6 +71,7 @@ segments = [ - newline: `boolean` - alignment: `left` | `right` - filler: `string` +- overflow: `break` | `hide` - segments: `array` of one or more `segments` ### Type @@ -100,6 +101,11 @@ to be repeated to this property. Add this property to the _right_ aligned block. "filler": "." ``` +### Overflow + +When the right aligned block is so long it will overflow the left aligned block, the engine will either +break the block or hide it based on the setting. By default it is printed as is on the same line. + ### Segments Array of one or more segments.