2017-07-14 01:51:19 +00:00
|
|
|
package prompt
|
|
|
|
|
2017-07-17 14:17:08 +00:00
|
|
|
import "strings"
|
|
|
|
|
2017-07-14 01:51:19 +00:00
|
|
|
type Render struct {
|
2017-07-17 17:01:24 +00:00
|
|
|
out ConsoleWriter
|
|
|
|
prefix string
|
|
|
|
title string
|
|
|
|
row uint16
|
|
|
|
col uint16
|
2017-07-16 19:32:42 +00:00
|
|
|
// colors
|
2017-07-17 13:32:13 +00:00
|
|
|
prefixTextColor Color
|
|
|
|
prefixBGColor Color
|
|
|
|
inputTextColor Color
|
|
|
|
inputBGColor Color
|
2017-07-16 19:55:05 +00:00
|
|
|
outputTextColor Color
|
2017-07-17 13:32:13 +00:00
|
|
|
outputBGColor Color
|
|
|
|
previewSuggestionTextColor Color
|
|
|
|
previewSuggestionBGColor Color
|
|
|
|
suggestionTextColor Color
|
|
|
|
suggestionBGColor Color
|
|
|
|
selectedSuggestionTextColor Color
|
|
|
|
selectedSuggestionBGColor Color
|
2017-07-15 11:22:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Render) Setup() {
|
2017-07-16 17:18:11 +00:00
|
|
|
if r.title != "" {
|
|
|
|
r.out.SetTitle(r.title)
|
2017-07-17 16:14:03 +00:00
|
|
|
r.out.Flush()
|
2017-07-15 11:22:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-15 16:18:40 +00:00
|
|
|
func (r *Render) renderPrefix() {
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.prefixTextColor, r.prefixBGColor)
|
2017-07-16 17:11:52 +00:00
|
|
|
r.out.WriteStr(r.prefix)
|
2017-07-16 19:32:42 +00:00
|
|
|
r.out.SetColor(DefaultColor, DefaultColor)
|
2017-07-15 16:18:40 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 11:22:56 +00:00
|
|
|
func (r *Render) TearDown() {
|
|
|
|
r.out.ClearTitle()
|
|
|
|
r.out.EraseDown()
|
|
|
|
r.out.Flush()
|
2017-07-15 08:37:54 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 12:43:04 +00:00
|
|
|
func (r *Render) prepareArea(lines int) {
|
2017-07-15 08:37:54 +00:00
|
|
|
for i := 0; i < lines; i++ {
|
|
|
|
r.out.ScrollDown()
|
|
|
|
}
|
|
|
|
for i := 0; i < lines; i++ {
|
|
|
|
r.out.ScrollUp()
|
|
|
|
}
|
|
|
|
return
|
2017-07-14 01:51:19 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 09:03:18 +00:00
|
|
|
func (r *Render) UpdateWinSize(ws *WinSize) {
|
2017-07-14 01:51:19 +00:00
|
|
|
r.row = ws.Row
|
|
|
|
r.col = ws.Col
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-17 16:14:03 +00:00
|
|
|
func (r *Render) renderWindowTooSmall() {
|
|
|
|
r.out.CursorGoTo(0, 0)
|
|
|
|
r.out.EraseScreen()
|
|
|
|
r.out.SetColor(DarkRed, White)
|
|
|
|
r.out.WriteStr("Your console window is too small...")
|
|
|
|
r.out.Flush()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-17 15:52:55 +00:00
|
|
|
func (r *Render) renderCompletion(buf *Buffer, words []string, max uint16, selected int) {
|
|
|
|
if max > r.row {
|
|
|
|
max = r.row
|
2017-07-17 12:54:39 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 16:04:18 +00:00
|
|
|
if l := len(words); l == 0 {
|
2017-07-15 11:22:56 +00:00
|
|
|
return
|
2017-07-17 15:52:55 +00:00
|
|
|
} else if l > int(max) {
|
2017-07-17 12:54:39 +00:00
|
|
|
words = words[:max]
|
2017-07-15 11:22:56 +00:00
|
|
|
}
|
2017-07-15 16:04:18 +00:00
|
|
|
|
2017-07-17 14:17:08 +00:00
|
|
|
formatted, width := formatCompletions(
|
|
|
|
words,
|
2017-07-17 17:01:24 +00:00
|
|
|
int(r.col)-len(r.prefix),
|
2017-07-17 14:17:08 +00:00
|
|
|
" ",
|
|
|
|
" ",
|
|
|
|
)
|
2017-07-15 09:51:33 +00:00
|
|
|
l := len(formatted)
|
2017-07-15 08:37:54 +00:00
|
|
|
|
2017-07-16 17:11:52 +00:00
|
|
|
d := (len(r.prefix) + len(buf.Document().TextBeforeCursor())) % int(r.col)
|
2017-07-17 19:06:32 +00:00
|
|
|
if d == 0 { // the cursor is on right end.
|
|
|
|
r.out.CursorBackward(width)
|
2017-07-17 20:32:14 +00:00
|
|
|
} else if d+width > int(r.col) {
|
2017-07-17 15:35:10 +00:00
|
|
|
r.out.CursorBackward(d + width - int(r.col))
|
2017-07-15 16:04:18 +00:00
|
|
|
}
|
|
|
|
|
2017-07-16 19:32:42 +00:00
|
|
|
r.out.SetColor(White, Cyan)
|
2017-07-15 09:51:33 +00:00
|
|
|
for i := 0; i < l; i++ {
|
|
|
|
r.out.CursorDown(1)
|
2017-07-17 15:52:55 +00:00
|
|
|
if i == selected {
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.selectedSuggestionTextColor, r.selectedSuggestionBGColor)
|
2017-07-15 13:44:10 +00:00
|
|
|
} else {
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor)
|
2017-07-15 13:44:10 +00:00
|
|
|
}
|
2017-07-17 14:17:08 +00:00
|
|
|
r.out.WriteStr(formatted[i])
|
2017-07-17 15:35:10 +00:00
|
|
|
r.out.CursorBackward(width)
|
2017-07-15 09:51:33 +00:00
|
|
|
}
|
2017-07-17 19:06:32 +00:00
|
|
|
if d == 0 { // the cursor is on right end.
|
|
|
|
// DON'T CURSOR DOWN HERE. Because the line doesn't erase properly.
|
|
|
|
r.out.CursorForward(width + 1)
|
|
|
|
} else if d+width > int(r.col) {
|
2017-07-17 15:35:10 +00:00
|
|
|
r.out.CursorForward(d + width - int(r.col))
|
2017-07-15 16:04:18 +00:00
|
|
|
}
|
2017-07-15 08:37:54 +00:00
|
|
|
|
2017-07-15 09:51:33 +00:00
|
|
|
r.out.CursorUp(l)
|
2017-07-16 19:32:42 +00:00
|
|
|
r.out.SetColor(DefaultColor, DefaultColor)
|
2017-07-15 09:51:33 +00:00
|
|
|
return
|
|
|
|
}
|
2017-07-15 08:37:54 +00:00
|
|
|
|
2017-07-17 16:14:03 +00:00
|
|
|
func (r *Render) Render(buffer *Buffer, completions []string, maxCompletions uint16, selected int) {
|
|
|
|
// Erasing
|
2017-07-17 14:37:40 +00:00
|
|
|
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
|
2017-07-15 11:22:56 +00:00
|
|
|
r.out.EraseDown()
|
|
|
|
|
2017-07-17 16:14:03 +00:00
|
|
|
// prepare area
|
2017-07-17 16:19:29 +00:00
|
|
|
line := buffer.Text()
|
2017-07-17 16:14:03 +00:00
|
|
|
h := ((len(r.prefix) + len(line)) / int(r.col)) + 1 + int(maxCompletions)
|
|
|
|
if h > int(r.row) {
|
|
|
|
r.renderWindowTooSmall()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.prepareArea(h)
|
|
|
|
|
|
|
|
// Rendering
|
|
|
|
r.renderPrefix()
|
|
|
|
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.inputTextColor, r.inputBGColor)
|
2017-07-15 11:22:56 +00:00
|
|
|
r.out.WriteStr(line)
|
2017-07-16 19:55:05 +00:00
|
|
|
r.out.SetColor(DefaultColor, DefaultColor)
|
2017-07-15 11:22:56 +00:00
|
|
|
r.out.CursorBackward(len(line) - buffer.CursorPosition)
|
2017-07-17 15:52:55 +00:00
|
|
|
r.renderCompletion(buffer, completions, maxCompletions, selected)
|
|
|
|
if selected != -1 {
|
|
|
|
c := completions[selected]
|
2017-07-15 14:27:49 +00:00
|
|
|
r.out.CursorBackward(len([]rune(buffer.Document().GetWordBeforeCursor())))
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor)
|
2017-07-15 14:27:49 +00:00
|
|
|
r.out.WriteStr(c)
|
2017-07-16 19:55:05 +00:00
|
|
|
r.out.SetColor(DefaultColor, DefaultColor)
|
2017-07-15 14:27:49 +00:00
|
|
|
}
|
2017-07-15 11:22:56 +00:00
|
|
|
r.out.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Render) BreakLine(buffer *Buffer, result string) {
|
2017-07-17 16:14:03 +00:00
|
|
|
// Erasing
|
|
|
|
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
|
|
|
|
r.out.EraseDown()
|
|
|
|
r.renderPrefix()
|
|
|
|
|
2017-07-17 16:47:50 +00:00
|
|
|
// Render Line Break
|
2017-07-17 13:32:13 +00:00
|
|
|
r.out.SetColor(r.inputTextColor, r.inputBGColor)
|
2017-07-16 19:55:05 +00:00
|
|
|
r.out.WriteStr(buffer.Document().Text + "\n")
|
2017-07-17 16:47:50 +00:00
|
|
|
|
|
|
|
// Render Result
|
|
|
|
if result != "" {
|
|
|
|
r.out.SetColor(r.outputTextColor, r.outputBGColor)
|
2017-07-18 10:06:38 +00:00
|
|
|
r.out.WriteRawStr(result + "\n")
|
2017-07-17 16:47:50 +00:00
|
|
|
}
|
2017-07-16 19:55:05 +00:00
|
|
|
r.out.SetColor(DefaultColor, DefaultColor)
|
2017-07-15 11:22:56 +00:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:17:08 +00:00
|
|
|
func formatCompletions(words []string, max int, prefix string, suffix string) (new []string, width int) {
|
2017-07-15 09:51:33 +00:00
|
|
|
num := len(words)
|
2017-07-15 14:27:49 +00:00
|
|
|
new = make([]string, num)
|
|
|
|
width = 0
|
2017-07-15 08:37:54 +00:00
|
|
|
|
2017-07-15 09:51:33 +00:00
|
|
|
for i := 0; i < num; i++ {
|
|
|
|
if width < len([]rune(words[i])) {
|
|
|
|
width = len([]rune(words[i]))
|
|
|
|
}
|
|
|
|
}
|
2017-07-15 08:37:54 +00:00
|
|
|
|
2017-07-17 17:01:24 +00:00
|
|
|
if len(prefix)+width+len(suffix) > max {
|
2017-07-17 14:17:08 +00:00
|
|
|
width = max - len(prefix) - len(suffix)
|
2017-07-16 14:11:16 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 09:51:33 +00:00
|
|
|
for i := 0; i < num; i++ {
|
2017-07-16 14:11:16 +00:00
|
|
|
if l := len(words[i]); l > width {
|
2017-07-17 17:01:24 +00:00
|
|
|
new[i] = prefix + words[i][:width-len("...")] + "..." + suffix
|
|
|
|
} else if l < width {
|
|
|
|
spaces := strings.Repeat(" ", width-len([]rune(words[i])))
|
2017-07-17 14:17:08 +00:00
|
|
|
new[i] = prefix + words[i] + spaces + suffix
|
2017-07-16 14:11:16 +00:00
|
|
|
} else {
|
2017-07-17 14:17:08 +00:00
|
|
|
new[i] = prefix + words[i] + suffix
|
2017-07-15 09:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-17 14:17:08 +00:00
|
|
|
width += len(prefix) + len(suffix)
|
2017-07-15 14:27:49 +00:00
|
|
|
return
|
2017-07-14 01:51:19 +00:00
|
|
|
}
|