Merge pull request #1 from c-bata/suggest-with-description
Add Suggestion struct with Description
This commit is contained in:
commit
58b2a9c539
@ -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
|
||||
|
@ -11,12 +11,12 @@ func executor(t string) string {
|
||||
return r
|
||||
}
|
||||
|
||||
func completer(t string) []string {
|
||||
return []string{
|
||||
"users",
|
||||
"sites",
|
||||
"articles",
|
||||
"comments",
|
||||
func completer(t string) []prompt.Completion {
|
||||
return []prompt.Completion{
|
||||
{Text: "users", Description: "user table"},
|
||||
{Text: "sites", Description: "sites table"},
|
||||
{Text: "articles", Description: "articles table"},
|
||||
{Text: "comments", Description: "comments table"},
|
||||
}
|
||||
}
|
||||
|
||||
|
32
option.go
32
option.go
@ -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
|
||||
@ -141,6 +169,10 @@ func NewPrompt(executor Executor, completer Completer, opts ...option) *Prompt {
|
||||
suggestionBGColor: Cyan,
|
||||
selectedSuggestionTextColor: Black,
|
||||
selectedSuggestionBGColor: Turquoise,
|
||||
descriptionTextColor: Black,
|
||||
descriptionBGColor: Turquoise,
|
||||
selectedDescriptionTextColor: White,
|
||||
selectedDescriptionBGColor: Cyan,
|
||||
},
|
||||
buf: NewBuffer(),
|
||||
executor: executor,
|
||||
|
12
prompt.go
12
prompt.go
@ -8,7 +8,11 @@ import (
|
||||
)
|
||||
|
||||
type Executor func(string) string
|
||||
type Completer func(string) []string
|
||||
type Completer func(string) []Completion
|
||||
type Completion struct {
|
||||
Text string
|
||||
Description string
|
||||
}
|
||||
|
||||
type Prompt struct {
|
||||
in ConsoleParser
|
||||
@ -43,7 +47,7 @@ func (p *Prompt) Run() {
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(c, false, true)
|
||||
p.buf.InsertText(c.Text, false, true)
|
||||
}
|
||||
p.selected = -1
|
||||
p.buf.InsertText(string(b), false, true)
|
||||
@ -54,7 +58,7 @@ func (p *Prompt) Run() {
|
||||
if w != "" {
|
||||
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||
}
|
||||
p.buf.InsertText(c, false, true)
|
||||
p.buf.InsertText(c.Text, false, true)
|
||||
}
|
||||
p.renderer.BreakLine(p.buf)
|
||||
res := p.executor(p.buf.Text())
|
||||
@ -93,7 +97,7 @@ func (p *Prompt) Run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prompt) updateSelectedCompletion(completions []string) {
|
||||
func (p *Prompt) updateSelectedCompletion(completions []Completion) {
|
||||
max := int(p.maxCompletions)
|
||||
if len(completions) < max {
|
||||
max = len(completions)
|
||||
|
100
render.go
100
render.go
@ -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
|
||||
@ -21,6 +31,10 @@ type Render struct {
|
||||
suggestionBGColor Color
|
||||
selectedSuggestionTextColor Color
|
||||
selectedSuggestionBGColor Color
|
||||
descriptionTextColor Color
|
||||
descriptionBGColor Color
|
||||
selectedDescriptionTextColor Color
|
||||
selectedDescriptionBGColor Color
|
||||
}
|
||||
|
||||
func (r *Render) Setup() {
|
||||
@ -67,22 +81,20 @@ func (r *Render) renderWindowTooSmall() {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Render) renderCompletion(buf *Buffer, words []string, max uint16, selected int) {
|
||||
func (r *Render) renderCompletion(buf *Buffer, completions []Completion, max uint16, selected int) {
|
||||
if max > r.row {
|
||||
max = r.row
|
||||
}
|
||||
|
||||
if l := len(words); l == 0 {
|
||||
if l := len(completions); l == 0 {
|
||||
return
|
||||
} else if l > int(max) {
|
||||
words = words[:max]
|
||||
completions = completions[:max]
|
||||
}
|
||||
|
||||
formatted, width := formatCompletions(
|
||||
words,
|
||||
completions,
|
||||
int(r.col)-len(r.prefix),
|
||||
" ",
|
||||
" ",
|
||||
)
|
||||
l := len(formatted)
|
||||
r.prepareArea(l)
|
||||
@ -102,7 +114,14 @@ func (r *Render) renderCompletion(buf *Buffer, words []string, max uint16, selec
|
||||
} 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, words []string, max uint16, selec
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Render) Render(buffer *Buffer, completions []string, maxCompletions uint16, selected int) {
|
||||
func (r *Render) Render(buffer *Buffer, completions []Completion, 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 []string, maxCompletions uin
|
||||
// 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
|
||||
}
|
||||
@ -142,7 +161,7 @@ func (r *Render) Render(buffer *Buffer, completions []string, maxCompletions uin
|
||||
c := completions[selected]
|
||||
r.out.CursorBackward(len([]rune(buffer.Document().GetWordBeforeCursor())))
|
||||
r.out.SetColor(r.previewSuggestionTextColor, r.previewSuggestionBGColor)
|
||||
r.out.WriteStr(c)
|
||||
r.out.WriteStr(c.Text)
|
||||
r.out.SetColor(DefaultColor, DefaultColor)
|
||||
}
|
||||
r.out.Flush()
|
||||
@ -169,31 +188,58 @@ func (r *Render) RenderResult(result string) {
|
||||
r.out.SetColor(DefaultColor, DefaultColor)
|
||||
}
|
||||
|
||||
func formatCompletions(words []string, max int, prefix string, suffix string) (new []string, width int) {
|
||||
num := len(words)
|
||||
new = make([]string, num)
|
||||
width = 0
|
||||
func formatCompletions(completions []Completion, max int) (new []Completion, width int) {
|
||||
num := len(completions)
|
||||
new = make([]Completion, num)
|
||||
leftWidth := 0
|
||||
rightWidth := 0
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
if width < len([]rune(words[i])) {
|
||||
width = len([]rune(words[i]))
|
||||
if leftWidth < len([]rune(completions[i].Text)) {
|
||||
leftWidth = len([]rune(completions[i].Text))
|
||||
}
|
||||
if rightWidth < len([]rune(completions[i].Description)) {
|
||||
rightWidth = len([]rune(completions[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
|
||||
}
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
if l := len(words[i]); l > width {
|
||||
new[i] = prefix + words[i][:width-len("...")] + "..." + suffix
|
||||
} else if l < width {
|
||||
spaces := strings.Repeat(" ", width-len([]rune(words[i])))
|
||||
new[i] = prefix + words[i] + spaces + suffix
|
||||
}
|
||||
if rightWidth == 0 {
|
||||
width = leftWidth + leftMargin
|
||||
} else {
|
||||
new[i] = prefix + words[i] + suffix
|
||||
width = leftWidth + leftMargin + rightWidth + rightMargin
|
||||
}
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
var newText string
|
||||
var newDescription string
|
||||
if l := len(completions[i].Text); l > leftWidth {
|
||||
newText = leftPrefix + completions[i].Text[:leftWidth-len("...")] + "..." + leftSuffix
|
||||
} else if l < width {
|
||||
spaces := strings.Repeat(" ", leftWidth-len([]rune(completions[i].Text)))
|
||||
newText = leftPrefix + completions[i].Text + spaces + leftSuffix
|
||||
} else {
|
||||
newText = leftPrefix + completions[i].Text + leftSuffix
|
||||
}
|
||||
|
||||
if rightWidth == 0 {
|
||||
newDescription = ""
|
||||
} else if l := len(completions[i].Description); l > rightWidth {
|
||||
newDescription = rightPrefix + completions[i].Description[:rightWidth-len("...")] + "..." + rightSuffix
|
||||
} else if l < width {
|
||||
spaces := strings.Repeat(" ", rightWidth-len([]rune(completions[i].Description)))
|
||||
newDescription = rightPrefix + completions[i].Description + spaces + rightSuffix
|
||||
} else {
|
||||
newDescription = rightPrefix + completions[i].Description + rightSuffix
|
||||
}
|
||||
new[i] = Completion{Text: newText, Description: newDescription}
|
||||
}
|
||||
width += len(prefix) + len(suffix)
|
||||
return
|
||||
}
|
||||
|
@ -8,36 +8,55 @@ import (
|
||||
func TestFormatCompletion(t *testing.T) {
|
||||
scenarioTable := []struct {
|
||||
scenario string
|
||||
completions []string
|
||||
completions []Completion
|
||||
prefix string
|
||||
suffix string
|
||||
expected []string
|
||||
expected []Completion
|
||||
maxWidth int
|
||||
expectedWidth int
|
||||
}{
|
||||
{
|
||||
scenario: "",
|
||||
completions: []string{
|
||||
"select",
|
||||
"from",
|
||||
"insert",
|
||||
"where",
|
||||
completions: []Completion{
|
||||
{Text: "select"},
|
||||
{Text: "from"},
|
||||
{Text: "insert"},
|
||||
{Text: "where"},
|
||||
},
|
||||
prefix: " ",
|
||||
suffix: " ",
|
||||
expected: []string{
|
||||
" select ",
|
||||
" from ",
|
||||
" insert ",
|
||||
" where ",
|
||||
expected: []Completion{
|
||||
{Text: " select "},
|
||||
{Text: " from "},
|
||||
{Text: " insert "},
|
||||
{Text: " where "},
|
||||
},
|
||||
maxWidth: 20,
|
||||
expectedWidth: 8,
|
||||
},
|
||||
{
|
||||
scenario: "",
|
||||
completions: []Completion{
|
||||
{Text: "select", Description: "select description"},
|
||||
{Text: "from", Description: "from description"},
|
||||
{Text: "insert", Description: "insert description"},
|
||||
{Text: "where", Description: "where description"},
|
||||
},
|
||||
prefix: " ",
|
||||
suffix: " ",
|
||||
expected: []Completion{
|
||||
{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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user