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
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

View file

@ -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

View file

@ -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)
}
}

View file

@ -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": {

View file

@ -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.