Skip to content

Commit

Permalink
Merge pull request #13 from elk-language/better-line-wrapping
Browse files Browse the repository at this point in the history
Better line wrapping
  • Loading branch information
Verseth authored Jul 16, 2023
2 parents b11f8ce + 323b89c commit f7078ba
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 39 deletions.
10 changes: 10 additions & 0 deletions document.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ func (d *Document) CursorPositionRow() (row istrings.RuneNumber) {
return
}

// TextEndPositionRow returns the row of the end of the current text. (0-based.)
func (d *Document) TextEndPositionRow() (row istrings.RuneNumber) {
textLength := istrings.RuneCount(d.Text)
if textLength == 0 {
return 0
}
row, _ = d.findLineStartIndex(textLength - 1)
return
}

// CursorPositionCol returns the current column. (0-based.)
func (d *Document) CursorPositionCol() (col istrings.RuneNumber) {
_, index := d.findLineStartIndex(d.cursorPosition)
Expand Down
16 changes: 16 additions & 0 deletions position_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ func TestPositionAtEndOfString(t *testing.T) {
Y: 0,
},
},
"one word": {
input: "foo",
columns: 20,
want: Position{
X: 3,
Y: 0,
},
},
"one-line fits in columns": {
input: "foo bar",
columns: 20,
Expand All @@ -48,6 +56,14 @@ func TestPositionAtEndOfString(t *testing.T) {
Y: 2,
},
},
"one-line wrapping": {
input: "foobar",
columns: 3,
want: Position{
X: 0,
Y: 2,
},
},
}

for name, tc := range tests {
Expand Down
10 changes: 5 additions & 5 deletions prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ keySwitch:
p.buf = NewBuffer()
p.history.Clear()
case Up, ControlP:
cursor := p.buf.Document().GetCursorPosition(p.renderer.col)
if cursor.Y != 0 {
line := p.buf.Document().CursorPositionRow()
if line > 0 {
p.buf.CursorUp(1)
break
}
Expand All @@ -222,9 +222,9 @@ keySwitch:
}

case Down, ControlN:
endOfTextCursor := p.buf.Document().GetEndOfTextPosition(p.renderer.col)
cursor := p.buf.Document().GetCursorPosition(p.renderer.col)
if endOfTextCursor.Y > cursor.Y {
endOfTextRow := p.buf.Document().TextEndPositionRow()
row := p.buf.Document().CursorPositionRow()
if endOfTextRow > row {
p.buf.CursorDown(1)
break
}
Expand Down
86 changes: 52 additions & 34 deletions renderer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package prompt

import (
"runtime"
"strings"
"unicode/utf8"

Expand Down Expand Up @@ -126,6 +125,7 @@ func (r *Renderer) renderCompletion(buf *Buffer, completions *CompletionManager)
return
}
prefix := r.prefixCallback()
prefixWidth := istrings.GetWidth(prefix)
formatted, width := formatSuggestions(
suggestions,
r.col-istrings.GetWidth(prefix)-1, // -1 means a width of scrollbar
Expand All @@ -140,7 +140,7 @@ func (r *Renderer) renderCompletion(buf *Buffer, completions *CompletionManager)
formatted = formatted[completions.verticalScroll : completions.verticalScroll+windowHeight]
r.prepareArea(windowHeight)

cursor := positionAtEndOfString(prefix+buf.Document().TextBeforeCursor(), r.col)
cursor := positionAtEndOfString(prefix+buf.Document().TextBeforeCursor(), r.col-prefixWidth)
x := cursor.X
if x+width >= r.col {
cursor = r.backward(cursor, x+width-r.col)
Expand Down Expand Up @@ -194,7 +194,6 @@ func (r *Renderer) renderCompletion(buf *Buffer, completions *CompletionManager)
r.out.SetColor(DefaultColor, DefaultColor, false)

c := cursor.Add(Position{X: width})
r.lineWrap(&c)
r.backward(c, width)
}

Expand All @@ -219,7 +218,7 @@ func (r *Renderer) Render(buffer *Buffer, completion *CompletionManager, lexer L
text := buffer.Text()
prefix := r.prefixCallback()
prefixWidth := istrings.GetWidth(prefix)
cursor := positionAtEndOfString(text, r.col)
cursor := positionAtEndOfString(text, r.col-prefixWidth)
cursor.X += prefixWidth

// prepare area
Expand All @@ -239,10 +238,9 @@ func (r *Renderer) Render(buffer *Buffer, completion *CompletionManager, lexer L

r.out.SetColor(DefaultColor, DefaultColor, false)

r.lineWrap(&cursor)

targetCursor := buffer.DisplayCursorPosition(r.col)
targetCursor := buffer.DisplayCursorPosition(r.col - prefixWidth)
targetCursor.X += prefixWidth
// Log("col: %#v, targetCursor: %#v, cursor: %#v\n", r.col-prefixWidth, targetCursor, cursor)
cursor = r.move(cursor, targetCursor)

r.renderCompletion(buffer, completion)
Expand All @@ -263,9 +261,7 @@ func (r *Renderer) Render(buffer *Buffer, completion *CompletionManager, lexer L

r.out.SetColor(DefaultColor, DefaultColor, false)

cursor = cursor.Join(positionAtEndOfString(rest, r.col))

r.lineWrap(&cursor)
cursor = cursor.Join(positionAtEndOfString(rest, r.col-prefixWidth))

cursor = r.move(cursor, endOfSuggestionPos)
}
Expand All @@ -279,21 +275,32 @@ func (r *Renderer) renderText(lexer Lexer, text string) {
}

prefix := r.prefixCallback()
prefixWidth := istrings.GetWidth(prefix)
col := r.col - prefixWidth
multilinePrefix := r.getMultilinePrefix(prefix)
firstIteration := true
var lineBuffer strings.Builder
var lineCharIndex istrings.Width

for _, char := range text {
lineBuffer.WriteRune(char)
if char != '\n' {
if lineCharIndex >= col || char == '\n' {
lineBuffer.WriteRune('\n')
r.renderLine(prefix, lineBuffer.String(), r.inputTextColor)
lineCharIndex = 0
lineBuffer.Reset()
if char != '\n' {
lineBuffer.WriteRune(char)
lineCharIndex += istrings.GetRuneWidth(char)
}
if firstIteration {
prefix = multilinePrefix
firstIteration = false
}
continue
}

r.renderLine(prefix, lineBuffer.String(), r.inputTextColor)
lineBuffer.Reset()
if firstIteration {
prefix = multilinePrefix
firstIteration = false
}
lineBuffer.WriteRune(char)
lineCharIndex += istrings.GetRuneWidth(char)
}

r.renderLine(prefix, lineBuffer.String(), r.inputTextColor)
Expand Down Expand Up @@ -353,8 +360,11 @@ func (r *Renderer) lex(lexer Lexer, input string) {
s := input

prefix := r.prefixCallback()
r.renderPrefix(prefix)
prefixWidth := istrings.GetWidth(prefix)
col := r.col - prefixWidth
multilinePrefix := r.getMultilinePrefix(prefix)
r.renderPrefix(prefix)
var lineCharIndex istrings.Width
for {
token, ok := lexer.Next()
if !ok {
Expand All @@ -365,15 +375,25 @@ func (r *Renderer) lex(lexer Lexer, input string) {
s = strings.TrimPrefix(s, text)

var lineBuffer strings.Builder

for _, char := range text {
lineBuffer.WriteRune(char)
if char != '\n' {
if lineCharIndex >= col || char == '\n' {
if char != '\n' {
lineBuffer.WriteByte('\n')
}
r.writeString(lineBuffer.String(), token.Color())
r.renderPrefix(multilinePrefix)
lineCharIndex = 0
lineBuffer.Reset()
if char != '\n' {
lineBuffer.WriteRune(char)
lineCharIndex += istrings.GetRuneWidth(char)
}
continue
}

r.writeString(lineBuffer.String(), token.Color())
r.renderPrefix(multilinePrefix)
lineBuffer.Reset()
lineBuffer.WriteRune(char)
lineCharIndex += istrings.GetRuneWidth(char)
}
r.writeString(lineBuffer.String(), token.Color())
}
Expand All @@ -382,11 +402,17 @@ func (r *Renderer) lex(lexer Lexer, input string) {
// BreakLine to break line.
func (r *Renderer) BreakLine(buffer *Buffer, lexer Lexer) {
// Erasing and Renderer
cursor := positionAtEndOfString(buffer.Document().TextBeforeCursor()+r.prefixCallback(), r.col)
prefix := r.prefixCallback()
prefixWidth := istrings.GetWidth(prefix)
cursor := positionAtEndOfString(buffer.Document().TextBeforeCursor(), r.col-prefixWidth)
cursor.X += prefixWidth
r.clear(cursor)

text := buffer.Document().Text + "\n"
text := buffer.Document().Text
r.renderText(lexer, text)
if _, err := r.out.WriteString("\n"); err != nil {
panic(err)
}

r.out.SetColor(DefaultColor, DefaultColor, false)

Expand Down Expand Up @@ -420,14 +446,6 @@ func (r *Renderer) move(from, to Position) Position {
return to
}

func (r *Renderer) lineWrap(cursor *Position) {
if runtime.GOOS != "windows" && cursor.X > 0 && cursor.X%r.col == 0 {
cursor.X = 0
cursor.Y += 1
r.out.WriteRaw([]byte{'\n'})
}
}

func clamp(high, low, x float64) float64 {
switch {
case high < x:
Expand Down

0 comments on commit f7078ba

Please sign in to comment.