提交 2a6c091e 编写于 作者: P Pascal Corpet

First version of a formatter that works like `git log --format`.

上级 486df9ea
package ui
import (
"strconv"
"strings"
)
// Expand expands a format string using `git log` message syntax.
func Expand(format string, values map[string]string) string {
f := &expander{values: values}
return f.Expand(format)
}
// An expander is a stateful helper to expand a format string.
type expander struct {
// formatted holds the
formatted []string
// values is the map of values that should be expanded.
values map[string]string
// skipNext is true if the next placeholder is not a place holder and can be
// output directly as such.
skipNext bool
}
func (f *expander) Expand(format string) string {
parts := strings.Split(format, "%")
f.formatted = make([]string, 0, len(parts))
f.append(parts[0])
for _, p := range parts[1:] {
v, t := f.expandOneVar(p)
f.append(v, t)
}
return f.crush()
}
func (f *expander) append(formattedText ...string) {
f.formatted = append(f.formatted, formattedText...)
}
func (f *expander) crush() string {
s := strings.Join(f.formatted, "")
f.formatted = nil
return s
}
var colorMap = map[string]string{
"red": "31",
"green": "32",
"blue": "34",
"reset": "",
}
func (f *expander) expandOneVar(format string) (expand string, untouched string) {
if f.skipNext {
f.skipNext = false
return "", format
}
if format == "" {
f.skipNext = true
return "", "%"
}
if e, u, ok := f.expandSpecialChar(format[0], format[1:]); ok {
return e, u
}
if f.values != nil {
for i := 1; i <= len(format); i++ {
if v, exists := f.values[format[0:i]]; exists {
return v, format[i:]
}
}
}
return "", "%" + format
}
func (f *expander) expandSpecialChar(firstChar byte, format string) (expand string, untouched string, wasExpanded bool) {
switch firstChar {
case 'n':
return "\n", format, true
case 'C':
for k, v := range colorMap {
if strings.HasPrefix(format, k) {
return "\033[" + v + "m", format[len(k):], true
}
}
// TODO: Add custom color as specified in color.branch.* options.
// TODO: Handle auto-coloring.
case 'x':
if len(format) >= 2 {
if v, err := strconv.ParseInt(format[:2], 16, 32); err == nil {
return string(v), format[2:], true
}
}
case '+':
if e, u := f.expandOneVar(format); e != "" {
return "\n" + e, u, true
} else {
return "", u, true
}
case ' ':
if e, u := f.expandOneVar(format); e != "" {
return " " + e, u, true
} else {
return "", u, true
}
case '-':
if e, u := f.expandOneVar(format); e != "" {
return e, u, true
} else {
f.append(strings.TrimRight(f.crush(), "\n"))
return "", u, true
}
}
return "", "", false
}
package ui
import (
"testing"
)
func TestExpand(t *testing.T) {
tests := []struct {
name string
format string
values map[string]string
expect string
}{
{
name: "Simple example",
format: "The author of %h was %an, %ar%nThe title was >>%s<<%n",
values: map[string]string{
"h": "fe6e0ee",
"an": "Junio C Hamano",
"ar": "23 hours ago",
"s": "t4119: test autocomputing -p<n> for traditional diff input.",
},
expect: "The author of fe6e0ee was Junio C Hamano, 23 hours ago\nThe title was >>t4119: test autocomputing -p<n> for traditional diff input.<<\n",
},
{
name: "Percent sign, middle and trailing",
format: "%%a %%b %",
values: map[string]string{"a": "A variable that should not be used."},
expect: "%a %b %",
},
{
name: "Colors",
format: "%Cred%r %Cgreen%g %Cblue%b%Creset normal",
values: map[string]string{"r": "RED", "g": "GREEN", "b": "BLUE"},
expect: "\033[31mRED \033[32mGREEN \033[34mBLUE\033[m normal",
},
{
name: "Byte from hex code",
format: "%x00 %x3712%x61 %x%x1%xga",
expect: "\x00 \x3712a %x%x1%xga",
},
{
name: "plus modifier, conditional line",
format: "line1%+a line2%+b line3",
values: map[string]string{"a": "A", "b": ""},
expect: "line1\nA line2 line3",
},
{
name: "blank modifier, conditional blank",
format: "word1% a word2% b word3",
values: map[string]string{"a": "A", "b": ""},
expect: "word1 A word2 word3",
},
{
name: "minus modifier, crush preceding line-feeds",
format: "word1%n%n%-a",
values: map[string]string{"a": ""},
expect: "word1",
},
}
for _, test := range tests {
if got := Expand(test.format, test.values); got != test.expect {
t.Errorf("%s: Expand(%q, ...) = %q, want %q", test.name, test.format, got, test.expect)
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册