feat: hide/break right aligned block on overflow

resolves #2442
This commit is contained in:
Jan De Dobbeleer 2022-06-27 19:39:31 +02:00 committed by Jan De Dobbeleer
parent d6b56de4c1
commit f403db826f
5 changed files with 74 additions and 18 deletions

View file

@ -14,6 +14,9 @@ type BlockType string
// BlockAlignment aligment of a Block // BlockAlignment aligment of a Block
type BlockAlignment string type BlockAlignment string
// Overflow defines how to handle a right block that overflows with the previous block
type Overflow string
const ( const (
// Prompt writes one or more Segments // Prompt writes one or more Segments
Prompt BlockType = "prompt" Prompt BlockType = "prompt"
@ -25,6 +28,10 @@ const (
Left BlockAlignment = "left" Left BlockAlignment = "left"
// Right aligns right // Right aligns right
Right BlockAlignment = "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 // Block defines a part of the prompt with optional segments
@ -36,6 +43,7 @@ type Block struct {
Segments []*Segment `json:"segments,omitempty"` Segments []*Segment `json:"segments,omitempty"`
Newline bool `json:"newline,omitempty"` Newline bool `json:"newline,omitempty"`
Filler string `json:"filler,omitempty"` Filler string `json:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty"`
env environment.Environment env environment.Environment
writer color.Writer writer color.Writer

View file

@ -40,8 +40,8 @@ func (e *Engine) string() string {
return e.console.String() return e.console.String()
} }
func (e *Engine) canWriteRPrompt() bool { func (e *Engine) canWriteRPrompt(rprompt bool) bool {
if e.rprompt == "" || e.Plain { if rprompt && (e.rprompt == "" || e.Plain) {
return false return false
} }
consoleWidth, err := e.Env.TerminalWidth() consoleWidth, err := e.Env.TerminalWidth()
@ -55,7 +55,10 @@ func (e *Engine) canWriteRPrompt() bool {
overflow := promptWidth % consoleWidth overflow := promptWidth % consoleWidth
availableSpace = consoleWidth - overflow availableSpace = consoleWidth - overflow
} }
promptBreathingRoom := 30 promptBreathingRoom := 5
if rprompt {
promptBreathingRoom = 30
}
canWrite := (availableSpace - e.rpromptLength) >= promptBreathingRoom canWrite := (availableSpace - e.rpromptLength) >= promptBreathingRoom
return canWrite return canWrite
} }
@ -107,6 +110,15 @@ func (e *Engine) shouldFill(block *Block, length int) (string, bool) {
} }
func (e *Engine) renderBlock(block *Block) { 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 // when in bash, for rprompt blocks we need to write plain
// and wrap in escaped mode or the prompt will not render correctly // and wrap in escaped mode or the prompt will not render correctly
if e.Env.Shell() == shell.BASH && (block.Type == RPrompt || block.Alignment == Right) { 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() text, length := block.RenderSegments()
e.currentLineLength += length e.currentLineLength += length
e.write(text) e.write(text)
break return
} }
if block.Alignment != Right { if block.Alignment != Right {
break return
} }
text, length := block.RenderSegments() text, length := block.RenderSegments()
padText, hasPadText := e.shouldFill(block, length) e.rpromptLength = length
if hasPadText {
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 // in this case we can print plain
e.write(padText) e.write(padText)
e.write(text) e.write(text)
break return
} }
// this can contain ANSI escape sequences // this can contain ANSI escape sequences
ansi := e.Ansi ansi := e.Ansi
@ -167,13 +193,6 @@ func (e *Engine) renderBlock(block *Block) {
case RPrompt: case RPrompt:
e.rprompt, e.rpromptLength = block.RenderSegments() 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 // 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) prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
return prompt return prompt
case shell.PWSH, shell.PWSH5, shell.PLAIN, shell.NU: case shell.PWSH, shell.PWSH5, shell.PLAIN, shell.NU:
if !e.canWriteRPrompt() { if !e.canWriteRPrompt(true) {
break break
} }
e.write(e.Ansi.SaveCursorPosition()) e.write(e.Ansi.SaveCursorPosition())
@ -240,7 +259,7 @@ func (e *Engine) print() string {
e.write(e.rprompt) e.write(e.rprompt)
e.write(e.Ansi.RestoreCursorPosition()) e.write(e.Ansi.RestoreCursorPosition())
case shell.BASH: case shell.BASH:
if !e.canWriteRPrompt() { if !e.canWriteRPrompt(true) {
break break
} }
// in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly // in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly

View file

@ -39,7 +39,7 @@ func TestCanWriteRPrompt(t *testing.T) {
currentLineLength: tc.PromptLength, currentLineLength: tc.PromptLength,
rprompt: "hello", rprompt: "hello",
} }
got := engine.canWriteRPrompt() got := engine.canWriteRPrompt(true)
assert.Equal(t, tc.Expected, got, tc.Case) assert.Equal(t, tc.Expected, got, tc.Case)
} }
} }

View file

@ -125,6 +125,29 @@
"required": ["type", "segments"], "required": ["type", "segments"],
"title": "RPrompt definition, contains 1 or more segments to render to the right of the cursor" "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": { "properties": {

View file

@ -71,6 +71,7 @@ segments = [
- newline: `boolean` - newline: `boolean`
- alignment: `left` | `right` - alignment: `left` | `right`
- filler: `string` - filler: `string`
- overflow: `break` | `hide`
- segments: `array` of one or more `segments` - segments: `array` of one or more `segments`
### Type ### Type
@ -100,6 +101,11 @@ to be repeated to this property. Add this property to the _right_ aligned block.
"filler": "." "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 ### Segments
Array of one or more segments. Array of one or more segments.