Complete with description

This commit is contained in:
c-bata 2017-07-19 00:36:16 +09:00
parent c7333f7c05
commit 2e1d7d8eb0
6 changed files with 163 additions and 62 deletions

View File

@ -71,6 +71,10 @@ func main() {
#### `OptionSelectedSuggestionTextColor(x Color)`
#### `OptionSelectedSuggestionBGColor(x Color)`
#### `OptionMaxCompletions(x uint16)`
#### `OptionDescriptionTextColor(x Color)`
#### `OptionDescriptionBGColor(x Color)`
#### `OptionSelectedDescriptionTextColor(x Color)`
#### `OptionSelectedDescriptionBGColor(x Color)`
## LICENSE

View File

@ -11,12 +11,12 @@ func executor(t string) string {
return r
}
func completer(t string) []*prompt.Suggestion {
return []*prompt.Suggestion{
{Text: "users"},
{Text: "sites"},
{Text: "articles"},
{Text: "comments"},
func completer(t string) []prompt.Suggestion {
return []prompt.Suggestion{
{Text: "users", Description: "user table"},
{Text: "sites", Description: "sites table"},
{Text: "articles", Description: "articles table"},
{Text: "comments", Description: "comments table"},
}
}

View File

@ -116,6 +116,34 @@ func OptionSelectedSuggestionBGColor(x Color) option {
}
}
func OptionDescriptionTextColor(x Color) option {
return func(p *Prompt) error {
p.renderer.descriptionTextColor = x
return nil
}
}
func OptionDescriptionBGColor(x Color) option {
return func(p *Prompt) error {
p.renderer.descriptionBGColor = x
return nil
}
}
func OptionSelectedDescriptionTextColor(x Color) option {
return func(p *Prompt) error {
p.renderer.selectedDescriptionTextColor = x
return nil
}
}
func OptionSelectedDescriptionBGColor(x Color) option {
return func(p *Prompt) error {
p.renderer.selectedDescriptionBGColor = x
return nil
}
}
func OptionMaxCompletions(x uint16) option {
return func(p *Prompt) error {
p.maxCompletions = x
@ -127,20 +155,24 @@ func NewPrompt(executor Executor, completer Completer, opts ...option) *Prompt {
pt := &Prompt{
in: &VT100Parser{fd: syscall.Stdin},
renderer: &Render{
prefix: "> ",
out: &VT100Writer{fd: syscall.Stdout},
prefixTextColor: Blue,
prefixBGColor: DefaultColor,
inputTextColor: DefaultColor,
inputBGColor: DefaultColor,
outputTextColor: DefaultColor,
outputBGColor: DefaultColor,
previewSuggestionTextColor: Green,
previewSuggestionBGColor: DefaultColor,
suggestionTextColor: White,
suggestionBGColor: Cyan,
selectedSuggestionTextColor: Black,
selectedSuggestionBGColor: Turquoise,
prefix: "> ",
out: &VT100Writer{fd: syscall.Stdout},
prefixTextColor: Blue,
prefixBGColor: DefaultColor,
inputTextColor: DefaultColor,
inputBGColor: DefaultColor,
outputTextColor: DefaultColor,
outputBGColor: DefaultColor,
previewSuggestionTextColor: Green,
previewSuggestionBGColor: DefaultColor,
suggestionTextColor: White,
suggestionBGColor: Cyan,
selectedSuggestionTextColor: Black,
selectedSuggestionBGColor: Turquoise,
descriptionTextColor: Black,
descriptionBGColor: Turquoise,
selectedDescriptionTextColor: White,
selectedDescriptionBGColor: Cyan,
},
buf: NewBuffer(),
executor: executor,

View File

@ -8,7 +8,7 @@ import (
)
type Executor func(string) string
type Completer func(string) []*Suggestion
type Completer func(string) []Suggestion
type Suggestion struct {
Text string
Description string
@ -97,7 +97,7 @@ func (p *Prompt) Run() {
}
}
func (p *Prompt) updateSelectedCompletion(completions []*Suggestion) {
func (p *Prompt) updateSelectedCompletion(completions []Suggestion) {
max := int(p.maxCompletions)
if len(completions) < max {
max = len(completions)

108
render.go
View File

@ -2,6 +2,16 @@ package prompt
import "strings"
const (
leftPrefix = " "
leftSuffix = " "
rightPrefix = " "
rightSuffix = " "
leftMargin = len(leftPrefix + leftSuffix)
rightMargin = len(rightPrefix + rightSuffix)
completionMargin = leftMargin + rightMargin
)
type Render struct {
out ConsoleWriter
prefix string
@ -9,18 +19,22 @@ type Render struct {
row uint16
col uint16
// colors
prefixTextColor Color
prefixBGColor Color
inputTextColor Color
inputBGColor Color
outputTextColor Color
outputBGColor Color
previewSuggestionTextColor Color
previewSuggestionBGColor Color
suggestionTextColor Color
suggestionBGColor Color
selectedSuggestionTextColor Color
selectedSuggestionBGColor Color
prefixTextColor Color
prefixBGColor Color
inputTextColor Color
inputBGColor Color
outputTextColor Color
outputBGColor Color
previewSuggestionTextColor Color
previewSuggestionBGColor Color
suggestionTextColor Color
suggestionBGColor Color
selectedSuggestionTextColor Color
selectedSuggestionBGColor Color
descriptionTextColor Color
descriptionBGColor Color
selectedDescriptionTextColor Color
selectedDescriptionBGColor Color
}
func (r *Render) Setup() {
@ -67,7 +81,7 @@ func (r *Render) renderWindowTooSmall() {
return
}
func (r *Render) renderCompletion(buf *Buffer, suggestions []*Suggestion, max uint16, selected int) {
func (r *Render) renderCompletion(buf *Buffer, suggestions []Suggestion, max uint16, selected int) {
if max > r.row {
max = r.row
}
@ -81,8 +95,6 @@ func (r *Render) renderCompletion(buf *Buffer, suggestions []*Suggestion, max ui
formatted, width := formatCompletions(
suggestions,
int(r.col)-len(r.prefix),
" ",
" ",
)
l := len(formatted)
r.prepareArea(l)
@ -102,7 +114,14 @@ func (r *Render) renderCompletion(buf *Buffer, suggestions []*Suggestion, max ui
} else {
r.out.SetColor(r.suggestionTextColor, r.suggestionBGColor)
}
r.out.WriteStr(formatted[i])
r.out.WriteStr(formatted[i].Text)
if i == selected {
r.out.SetColor(r.selectedDescriptionTextColor, r.selectedDescriptionBGColor)
} else {
r.out.SetColor(r.descriptionTextColor, r.descriptionBGColor)
}
r.out.WriteStr(formatted[i].Description)
r.out.CursorBackward(width)
}
if d == 0 { // the cursor is on right end.
@ -117,7 +136,7 @@ func (r *Render) renderCompletion(buf *Buffer, suggestions []*Suggestion, max ui
return
}
func (r *Render) Render(buffer *Buffer, completions []*Suggestion, maxCompletions uint16, selected int) {
func (r *Render) Render(buffer *Buffer, completions []Suggestion, maxCompletions uint16, selected int) {
// Erasing
r.out.CursorBackward(int(r.col) + len(buffer.Text()) + len(r.prefix))
r.out.EraseDown()
@ -125,7 +144,7 @@ func (r *Render) Render(buffer *Buffer, completions []*Suggestion, maxCompletion
// prepare area
line := buffer.Text()
h := ((len(r.prefix) + len(line)) / int(r.col)) + 1 + int(maxCompletions)
if h > int(r.row) {
if h > int(r.row) || completionMargin > int(r.col) {
r.renderWindowTooSmall()
return
}
@ -169,31 +188,58 @@ func (r *Render) RenderResult(result string) {
r.out.SetColor(DefaultColor, DefaultColor)
}
func formatCompletions(suggestions []*Suggestion, max int, prefix string, suffix string) (new []string, width int) {
func formatCompletions(suggestions []Suggestion, max int) (new []Suggestion, width int) {
num := len(suggestions)
new = make([]string, num)
width = 0
new = make([]Suggestion, num)
leftWidth := 0
rightWidth := 0
for i := 0; i < num; i++ {
if width < len([]rune(suggestions[i].Text)) {
width = len([]rune(suggestions[i].Text))
if leftWidth < len([]rune(suggestions[i].Text)) {
leftWidth = len([]rune(suggestions[i].Text))
}
if rightWidth < len([]rune(suggestions[i].Description)) {
rightWidth = len([]rune(suggestions[i].Description))
}
}
if len(prefix)+width+len(suffix) > max {
width = max - len(prefix) - len(suffix)
if diff := max - completionMargin - leftWidth - rightWidth; diff < 0 {
if rightWidth > diff {
rightWidth -= diff
} else if rightWidth+rightMargin > diff {
leftWidth += rightWidth + rightMargin - diff
rightWidth = 0
}
}
if rightWidth == 0 {
width = leftWidth + leftMargin
} else {
width = leftWidth + leftMargin + rightWidth + rightMargin
}
for i := 0; i < num; i++ {
if l := len(suggestions[i].Text); l > width {
new[i] = prefix + suggestions[i].Text[:width-len("...")] + "..." + suffix
var newText string
var newDescription string
if l := len(suggestions[i].Text); l > leftWidth {
newText = leftPrefix + suggestions[i].Text[:leftWidth-len("...")] + "..." + leftSuffix
} else if l < width {
spaces := strings.Repeat(" ", width-len([]rune(suggestions[i].Text)))
new[i] = prefix + suggestions[i].Text + spaces + suffix
spaces := strings.Repeat(" ", leftWidth-len([]rune(suggestions[i].Text)))
newText = leftPrefix + suggestions[i].Text + spaces + leftSuffix
} else {
new[i] = prefix + suggestions[i].Text + suffix
newText = leftPrefix + suggestions[i].Text + leftSuffix
}
if rightWidth == 0 {
newDescription = ""
} else if l := len(suggestions[i].Description); l > rightWidth {
newDescription = rightPrefix + suggestions[i].Description[:rightWidth-len("...")] + "..." + rightSuffix
} else if l < width {
spaces := strings.Repeat(" ", rightWidth-len([]rune(suggestions[i].Description)))
newDescription = rightPrefix + suggestions[i].Description + spaces + rightSuffix
} else {
newDescription = rightPrefix + suggestions[i].Description + rightSuffix
}
new[i] = Suggestion{Text: newText, Description: newDescription}
}
width += len(prefix) + len(suffix)
return
}

View File

@ -8,16 +8,16 @@ import (
func TestFormatCompletion(t *testing.T) {
scenarioTable := []struct {
scenario string
completions []*Suggestion
completions []Suggestion
prefix string
suffix string
expected []string
expected []Suggestion
maxWidth int
expectedWidth int
}{
{
scenario: "",
completions: []*Suggestion{
completions: []Suggestion{
{Text: "select"},
{Text: "from"},
{Text: "insert"},
@ -25,19 +25,38 @@ func TestFormatCompletion(t *testing.T) {
},
prefix: " ",
suffix: " ",
expected: []string{
" select ",
" from ",
" insert ",
" where ",
expected: []Suggestion{
{Text: " select "},
{Text: " from "},
{Text: " insert "},
{Text: " where "},
},
maxWidth: 20,
expectedWidth: 8,
},
{
scenario: "",
completions: []Suggestion{
{Text: "select", Description: "select description"},
{Text: "from", Description: "from description"},
{Text: "insert", Description: "insert description"},
{Text: "where", Description: "where description"},
},
prefix: " ",
suffix: " ",
expected: []Suggestion{
{Text: " select ", Description: " select description "},
{Text: " from ", Description: " from description "},
{Text: " insert ", Description: " insert description "},
{Text: " where ", Description: " where description "},
},
maxWidth: 40,
expectedWidth: 28,
},
}
for _, s := range scenarioTable {
ac, width := formatCompletions(s.completions, s.maxWidth, s.prefix, s.suffix)
ac, width := formatCompletions(s.completions, s.maxWidth)
if !reflect.DeepEqual(ac, s.expected) {
t.Errorf("Should be %#v, but got %#v", s.expected, ac)
}