summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-05-30 15:22:04 +1000
committerJesse Duffield <jessedduffield@gmail.com>2021-06-02 20:33:52 +1000
commit258eedb38c6e8692565d74a92e3590da477dea6b (patch)
treedb0a544b281184b04c704182e5a81f11172ee80e
parentbc044c64b2315c3fb8c523fa0ae7ef71bcba3b31 (diff)
refactor
-rw-r--r--pkg/gui/mergeconflicts/find_conflicts.go63
-rw-r--r--pkg/gui/mergeconflicts/find_conflicts_test.go57
-rw-r--r--pkg/gui/mergeconflicts/state.go68
-rw-r--r--pkg/utils/color.go49
-rw-r--r--pkg/utils/formatting.go39
-rw-r--r--pkg/utils/formatting_test.go81
-rw-r--r--pkg/utils/io.go25
-rw-r--r--pkg/utils/lines.go34
-rw-r--r--pkg/utils/lines_test.go94
-rw-r--r--pkg/utils/slice.go118
-rw-r--r--pkg/utils/slice_test.go135
-rw-r--r--pkg/utils/template.go30
-rw-r--r--pkg/utils/template_test.go61
-rw-r--r--pkg/utils/utils.go262
-rw-r--r--pkg/utils/utils_test.go366
15 files changed, 802 insertions, 680 deletions
diff --git a/pkg/gui/mergeconflicts/find_conflicts.go b/pkg/gui/mergeconflicts/find_conflicts.go
new file mode 100644
index 000000000..661cc5cc0
--- /dev/null
+++ b/pkg/gui/mergeconflicts/find_conflicts.go
@@ -0,0 +1,63 @@
+package mergeconflicts
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+// LineType tells us whether a given line is a start/middle/end marker of a conflict,
+// or if it's not a marker at all
+type LineType int
+
+const (
+ START LineType = iota
+ MIDDLE
+ END
+ NOT_A_MARKER
+)
+
+func findConflicts(content string) []*mergeConflict {
+ conflicts := make([]*mergeConflict, 0)
+
+ if content == "" {
+ return conflicts
+ }
+
+ var newConflict *mergeConflict
+ for i, line := range utils.SplitLines(content) {
+ switch determineLineType(line) {
+ case START:
+ newConflict = &mergeConflict{start: i}
+ case MIDDLE:
+ newConflict.middle = i
+ case END:
+ newConflict.end = i
+ conflicts = append(conflicts, newConflict)
+ default:
+ // line isn't a merge conflict marker so we just continue
+ }
+ }
+
+ return conflicts
+}
+
+func determineLineType(line string) LineType {
+ trimmedLine := strings.TrimPrefix(line, "++")
+
+ mapping := map[string]LineType{
+ "^<<<<<<< (HEAD|MERGE_HEAD|Updated upstream|ours)(:.+)?$": START,
+ "^=======$": MIDDLE,
+ "^>>>>>>> .*$": END,
+ }
+
+ for regexp_str, lineType := range mapping {
+ match, _ := regexp.MatchString(regexp_str, trimmedLine)
+ if match {
+ return lineType
+ }
+ }
+
+ return NOT_A_MARKER
+}
diff --git a/pkg/gui/mergeconflicts/find_conflicts_test.go b/pkg/gui/mergeconflicts/find_conflicts_test.go
new file mode 100644
index 000000000..c0a8e3e5b
--- /dev/null
+++ b/pkg/gui/mergeconflicts/find_conflicts_test.go
@@ -0,0 +1,57 @@
+package mergeconflicts
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDetermineLineType(t *testing.T) {
+ type scenario struct {
+ line string
+ expected LineType
+ }
+
+ scenarios := []scenario{
+ {
+ line: "",
+ expected: NOT_A_MARKER,
+ },
+ {
+ line: "blah",
+ expected: NOT_A_MARKER,
+ },
+ {
+ line: "<<<<<<< HEAD",
+ expected: START,
+ },
+ {
+ line: "<<<<<<< HEAD:my_branch",
+ expected: START,
+ },
+ {
+ line: "<<<<<<< MERGE_HEAD:my_branch",
+ expected: START,
+ },
+ {
+ line: "<<<<<<< Updated upstream:my_branch",
+ expected: START,
+ },
+ {
+ line: "<<<<<<< ours:my_branch",
+ expected: START,
+ },
+ {
+ line: "=======",
+ expected: MIDDLE,
+ },
+ {
+ line: ">>>>>>> blah",
+ expected: END,
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, determineLineType(s.line))
+ }
+}
diff --git a/pkg/gui/mergeconflicts/state.go b/pkg/gui/mergeconflicts/state.go
index 383d71a36..9d8320ffb 100644
--- a/pkg/gui/mergeconflicts/state.go
+++ b/pkg/gui/mergeconflicts/state.go
@@ -1,9 +1,6 @@
package mergeconflicts
import (
- "bufio"
- "os"
- "strings"
"sync"
"github.com/golang-collections/collections/stack"
@@ -19,7 +16,7 @@ const (
)
// mergeConflict : A git conflict with a start middle and end corresponding to line
-// numbers in the file where the conflict bars appear
+// numbers in the file where the conflict markers appear
type mergeConflict struct {
start int
middle int
@@ -88,36 +85,6 @@ func (s *State) SetConflictsFromCat(cat string) {
s.setConflicts(findConflicts(cat))
}
-func findConflicts(content string) []*mergeConflict {
- conflicts := make([]*mergeConflict, 0)
-
- if content == "" {
- return conflicts
- }
-
- var newConflict *mergeConflict
- for i, line := range utils.SplitLines(content) {
- trimmedLine := strings.TrimPrefix(line, "++")
- switch trimmedLine {
- case "<<<<<<< HEAD", "<<<<<<< MERGE_HEAD", "<<<<<<< Updated upstream", "<<<<<<< ours":
- newConflict = &mergeConflict{start: i}
- case "=======":
- newConflict.middle = i
- default:
- // Sometimes these lines look like "<<<<<<< HEAD:foo/bar/baz.go" so handle that case as well.
- if strings.HasPrefix(trimmedLine, "<<<<<<< HEAD:") {
- newConflict = &mergeConflict{start: i}
- }
- if strings.HasPrefix(trimmedLine, ">>>>>>> ") {
- newConflict.end = i
- conflicts = append(conflicts, newConflict)
- }
- }
-
- }
- return conflicts
-}
-
func (s *State) setConflicts(conflicts []*mergeConflict) {
s.conflicts = conflicts
@@ -158,32 +125,29 @@ func (s *State) ContentAfterConflictResolve(path string, selection Selection) (b
return false, "", nil
}
- file, err := os.Open(path)
- if err != nil {
- return false, "", err
- }
- defer file.Close()
-
- reader := bufio.NewReader(file)
content := ""
- for i := 0; true; i++ {
- line, err := reader.ReadString('\n')
- if err != nil {
- break
- }
+ err := utils.ForEachLineInFile(path, func(line string, i int) {
if !isIndexToDelete(i, conflict, selection) {
content += line
}
+ })
+
+ if err != nil {
+ return false, "", err
}
return true, content, nil
}
func isIndexToDelete(i int, conflict *mergeConflict, selection Selection) bool {
- return i == conflict.middle ||
- i == conflict.start ||
- i == conflict.end ||
- selection != BOTH &&
- (selection == BOTTOM && i > conflict.start && i < conflict.middle) ||
- (selection == TOP && i > conflict.middle && i < conflict.end)
+ isMarkerLine :=
+ i == conflict.middle ||
+ i == conflict.start ||
+ i == conflict.end
+
+ isUnwantedContent :=
+ (selection == BOTTOM && conflict.start < i && i < conflict.middle) ||
+ (selection == TOP && conflict.middle < i && i < conflict.end)
+
+ return isMarkerLine || isUnwantedContent
}
diff --git a/pkg/utils/color.go b/pkg/utils/color.go
new file mode 100644
index 000000000..b38dccf7f
--- /dev/null
+++ b/pkg/utils/color.go
@@ -0,0 +1,49 @@
+package utils
+
+import (
+ "fmt"
+ "regexp"
+
+ "github.com/fatih/color"
+)
+
+// ColoredString takes a string and a colour attribute and returns a colored
+// string with that attribute
+func ColoredString(str string, colorAttributes ...color.Attribute) string {
+ colour := color.New(colorAttributes...)
+ return ColoredStringDirect(str, colour)
+}
+
+// ColoredStringDirect used for aggregating a few color attributes rather than
+// just sending a single one
+func ColoredStringDirect(str string, colour *color.Color) string {
+ return colour.SprintFunc()(fmt.Sprint(str))
+}
+
+// Decolorise strips a string of color
+func Decolorise(str string) string {
+ re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
+ return re.ReplaceAllString(str, "")
+}
+
+func getPadWidths(stringArrays [][]string) []int {
+ maxWidth := 0
+ for _, stringArray := range stringArrays {
+ if len(stringArray) > maxWidth {
+ maxWidth = len(stringArray)
+ }
+ }
+ if maxWidth-1 < 0 {
+ return []int{}
+ }
+ padWidths := make([]int, maxWidth-1)
+ for i := range padWidths {
+ for _, strings := range stringArrays {
+ uncoloredString := Decolorise(strings[i])
+ if len(uncoloredString) > padWidths[i] {
+ padWidths[i] = len(uncoloredString)
+ }
+ }
+ }
+ return padWidths
+}
diff --git a/pkg/utils/formatting.go b/pkg/utils/formatting.go
new file mode 100644
index 000000000..3b4b35bb8
--- /dev/null
+++ b/pkg/utils/formatting.go
@@ -0,0 +1,39 @@
+package utils
+
+import "strings"
+
+// WithPadding pads a string as much as you want
+func WithPadding(str string, padding int) string {
+ uncoloredStr := Decolorise(str)
+ if padding < len(uncoloredStr) {
+ return str
+ }
+ return str + strings.Repeat(" ", padding-len(uncoloredStr))
+}
+
+func RenderDisplayStrings(displayStringsArr [][]string) string {
+ padWidths := getPadWidths(displayStringsArr)
+ paddedDisplayStrings := getPaddedDisplayStrings(displayStringsArr, padWidths)
+
+ return strings.Join(paddedDisplayStrings, "\n")
+}
+
+func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string {
+ paddedDisplayStrings := make([]string, len(stringArrays))
+ for i, stringArray := range stringArrays {
+ if len(stringArray) == 0 {
+ continue
+ }
+ for j, padWidth := range padWidths {
+ if len(stringArray)-1 < j {
+ continue
+ }
+ paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " "
+ }
+ if len(stringArray)-1 < len(padWidths) {
+ continue
+ }
+ paddedDisplayStrings[i] += stringArray[len(padWidths)]
+ }
+ return paddedDisplayStrings
+}
diff --git a/pkg/utils/formatting_test.go b/pkg/utils/formatting_test.go
new file mode 100644
index 000000000..3fe3f22f0
--- /dev/null
+++ b/pkg/utils/formatting_test.go
@@ -0,0 +1,81 @@
+package utils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// TestWithPadding is a function.
+func TestWithPadding(t *testing.T) {
+ type scenario struct {
+ str string
+ padding int
+ expected string
+ }
+
+ scenarios := []scenario{
+ {
+ "hello world !",
+ 1,
+ "hello world !",
+ },
+ {
+ "hello world !",
+ 14,
+ "hello world ! ",
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, WithPadding(s.str, s.padding))
+ }
+}
+
+// TestGetPaddedDisplayStrings is a function.
+func TestGetPaddedDisplayStrings(t *testing.T) {
+ type scenario struct {
+ stringArrays [][]string
+ padWidths []int
+ expected []string
+ }
+
+ scenarios := []scenario{
+ {
+ [][]string{{"a", "b"}, {"c", "d"}},
+ []int{1},
+ []string{"a b", "c d"},
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths))
+ }
+}
+
+// TestGetPadWidths is a function.
+func TestGetPadWidths(t *testing.T) {
+ type scenario struct {
+ stringArrays [][]string
+ expected []int
+ }
+
+ scenarios := []scenario{
+ {
+ [][]string{{""}, {""}},
+ []int{},
+ },
+ {
+ [][]string{{"a"}, {""}},
+ []int{},
+ },
+ {
+ [][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}},
+ []int{2, 1},
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays))
+ }
+}
diff --git a/pkg/utils/io.go b/pkg/utils/io.go
new file mode 100644
index 000000000..d31c5fc07
--- /dev/null
+++ b/pkg/utils/io.go
@@ -0,0 +1,25 @@
+package utils
+
+import (
+ "bufio"
+ "os"
+)
+
+func ForEachLineInFile(path string, f func(string, int)) error {
+ file, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ reader := bufio.NewReader(file)
+ for i := 0; true; i++ {
+ line, err := reader.ReadString('\n')
+ if err != nil {
+ break
+ }
+ f(line, i)
+ }
+
+ return nil
+}
diff --git a/pkg/utils/lines.go b/pkg/utils/lines.go
new file mode 100644
index 000000000..4c654d888
--- /dev/null
+++ b/pkg/utils/lines.go
@@ -0,0 +1,34 @@
+package utils
+
+import "strings"
+
+// SplitLines takes a multiline string and splits it on newlines
+// currently we are also stripping \r's which may have adverse effects for
+// windows users (but no issues have been raised yet)
+func SplitLines(multilineString string) []string {
+ multilineString = strings.Replace(multilineString, "\r", "", -1)
+ if multilineString == "" || multilineString == "\n" {
+ return make([]string, 0)
+ }
+ lines := strings.Split(multilineString, "\n")
+ if lines[len(lines)-1] == "" {
+ return lines[:len(lines)-1]
+ }
+ return lines
+}
+
+// TrimTrailingNewline - Trims the trailing newline
+// TODO: replace with `chomp` after refactor
+func TrimTrailingNewline(str string) string {
+ if strings.HasSuffix(str, "\n") {
+ return str[:len(str)-1]
+ }
+ return str
+}
+
+// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
+func NormalizeLinefeeds(str string) string {
+ str = strings.Replace(str, "\r\n", "\n", -1)
+ str = strings.Replace(str, "\r", "", -1)
+ return str
+}
diff --git a/pkg/utils/lines_test.go b/pkg/utils/lines_test.go
new file mode 100644
index 000000000..6069b8f93
--- /dev/null
+++ b/pkg/utils/lines_test.go
@@ -0,0 +1,94 @@
+package utils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// TestSplitLines is a function.
+func TestSplitLines(t *testing.T) {
+ type scenario struct {
+ multilineString string
+ expected []string
+ }
+
+ scenarios := []scenario{
+ {
+ "",
+ []string{},
+ },
+ {
+ "\n",
+ []string{},
+ },
+ {
+ "hello world !\nhello universe !\n",
+ []string{
+ "hello world !",
+ "hello universe !",
+ },
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, SplitLines(s.multilineString))
+ }
+}
+
+// TestTrimTrailingNewline is a function.
+func TestTrimTrailingNewline(t *testing.T) {
+ type scenario struct {
+ str string
+ expected string
+ }
+
+ scenarios := []scenario{
+ {
+ "hello world !\n",
+ "hello world !",
+ },
+ {
+ "hello world !",
+ "hello world !",
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, TrimTrailingNewline(s.str))
+ }
+}
+
+// TestNormalizeLinefeeds is a function.
+func TestNormalizeLinefeeds(t *testing.T) {
+ type scenario struct {
+ byteArray []byte
+ expected []byte
+ }
+ var scenarios = []scenario{
+ {
+ // \r\n
+ []byte{97, 115, 100, 102, 13, 10},
+ []byte{97, 115, 100, 102, 10},
+ },
+ {
+ // bash\r\nblah
+ []byte{97, 115, 100, 102, 13, 10, 97, 115, 100, 102},
+ []byte{97, 115, 100, 102, 10, 97, 115, 100, 102},
+ },
+ {
+ // \r
+ []byte{97, 115, 100, 102, 13},
+ []byte{97, 115, 100, 102},
+ },
+ {
+ // \n
+ []byte{97, 115, 100, 102, 10},
+ []byte{97, 115, 100, 102, 10},
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray)))
+ }
+}
diff --git a/pkg/utils/slice.go b/pkg/utils/slice.go
new file mode 100644
index 000000000..f536ea056
--- /dev/null
+++ b/pkg/utils/slice.go
@@ -0,0 +1,118 @@
+package utils
+
+// IncludesString if the list contains the string
+func IncludesString(list []string, a string) bool {
+ for _, b := range list {
+ if b == a {
+ return true
+ }
+ }
+ return false
+}
+
+// IncludesInt if the list contains the Int
+func IncludesInt(list []int, a int) bool {
+ for _, b := range list {
+ if b == a {
+ return true
+ }
+ }
+ return false
+}
+
+// NextIndex returns the index of the element that comes after the given number
+func NextIndex(numbers []int, currentNumber int) int {
+ for index, number := range numbers {
+ if number > currentNumber {
+ return index
+ }
+ }
+ return len(numbers) - 1
+}
+
+// PrevIndex returns the index that comes before the given number, cycling if we reach the end
+func PrevIndex(numbers []int, currentNumber int) int {
+ end := len(numbers) - 1
+ for i := end; i >= 0; i-- {
+ if numbers[i] < currentNumber {
+ return i
+ }
+ }
+ return 0
+}
+
+// UnionInt returns the union of two int arrays
+func UnionInt(a, b []int) []int {
+ m := make(map[int]bool)
+
+ for _, item := range a {
+ m[item] = true
+ }
+
+ for _, item := range b {
+ if _, ok := m[item]; !ok {
+ // this does not mutate the original a slice
+ // though it does mutate the backing array I believe
+ // but that doesn't matter because if you later want to append to the
+ // original a it must see that the backing array has been changed
+ // and create a new one
+ a = append(a, item)
+ }
+ }
+ return a
+}
+
+// DifferenceInt returns the difference of two int arrays
+func DifferenceInt(a, b []int) []int {
+ result := []int{}
+ m := make(map[int]bool)
+
+ for _, item := range b {
+ m[item] = true
+ }
+
+ for _, item := range a {
+ if _, ok := m[item]; !ok {
+ result = append(result, item)
+ }
+ }
+ return result
+}
+
+// NextIntInCycle returns the next int in a slice, returning to the first index if we've reached the end
+func NextIntInCycle(sl []int, current int) int {
+ for i, val := range sl {
+ if val == current {
+ if i == len(sl)-1 {
+ return sl[0]
+ }
+ return sl[i+1]
+ }
+ }
+ return sl[0]
+}
+
+// PrevIntInCycle returns the prev int in a slice, returning to the first index if we've reached the end
+func PrevIntInCycle(sl []int, current int) int {
+ for i, val := range sl {
+ if val == current {
+ if i > 0 {
+ return sl[i-1]
+ }
+ return sl[len(sl)-1]
+ }
+ }
+ return sl[len(sl)-1]
+}
+
+func StringArraysOverlap(strArrA []string, strArrB []string) bool {
+ for _, first := range strArrA {
+ for _, second := range strArrB {
+ if first == second {
+ return true
+ }
+ }
+ }
+
+ return false
+}
diff --git a/pkg/utils/slice_test.go b/pkg/utils/slice_test.go
new file mode 100644
index 000000000..858b1b904
--- /dev/null
+++ b/pkg/utils/slice_test.go
@@ -0,0 +1,135 @@
+package utils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// TestIncludesString is a function.
+func TestIncludesString(t *testing.T) {
+ type scenario struct {
+ list []string
+ element string
+ expected bool
+ }
+
+ scenarios := []scenario{
+ {
+ []string{"a", "b"},
+ "a",
+ true,
+ },
+ {
+ []string{"a", "b"},
+ "c",
+ false,
+ },
+ {
+ []string{"a", "b"},
+ "",
+ false,
+ },
+ {
+ []string{""},
+ "",
+ true,
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, IncludesString(s.list, s.element))
+ }
+}
+
+func TestNextIndex(t *testing.T) {
+ type scenario struct {
+ testName string
+ list []int
+ element int
+ expected int
+ }
+
+ scenarios := []scenario{
+ {
+ // I'm not really fussed about how it behaves here
+ "no elements",
+ []int{},
+ 1,
+ -1,
+ },
+ {
+ "one element",
+ []int{1},
+ 1,
+ 0,
+ },
+ {
+ "two elements",
+ []int{1, 2},
+ 1,
+ 1,
+ },
+ {
+ "two elements, giving second one",
+ []int{1, 2},
+ 2,
+ 1,
+ },
+ {
+ "three elements, giving second one",
+ []int{1, 2, 3},
+ 2,
+ 2,
+ },
+ }
+
+ for _, s := range scenarios {
+ t.Run(s.testName, func(t *testing.T) {
+ assert.EqualValues(t, s.expected, NextIndex(s.list, s.element))
+ })
+ }
+}
+
+func TestPrevIndex(t *testing.T) {
+ type scenario struct {
+ testName string
+ list []int
+ element int
+ expected int
+ }
+
+ scenarios := []scenario{
+ {
+ // I'm not really fussed about how it behaves here
+ "no elements",
+ []int{},
+ 1,
+ 0,
+ },
+ {
+ "one element",
+ []int{1},
+ 1,
+ 0,
+ },
+ {
+ "two elements",
+ []int{1, 2},
+ 1,
+ 0,
+ },
+ {
+ "three elements, giving second one",
+ []int{1, 2, 3},
+ 2,
+ 0,
+ },
+ }
+
+ for _, s := range scenarios {
+ t.Run(s.testName, func(t *testing.T) {
+ assert.EqualValues(t, s.expected, PrevIndex(s.list, s.element))
+ })
+ }
+}
diff --git a/pkg/utils/template.go b/pkg/utils/template.go
new file mode 100644
index 000000000..6c147cd38
--- /dev/null
+++ b/pkg/utils/template.go
@@ -0,0 +1,30 @@
+package utils
+
+import (
+ "bytes"
+ "strings"
+ "text/template"
+)
+
+func ResolveTemplate(templateStr string, object interface{}) (string, error) {
+ tmpl, err := template.New("template").Parse(templateStr)
+ if err != nil {
+ return "", err
+ }
+
+ var buf bytes.Buffer
+ if err := tmpl.Execute(&buf, object); err != nil {
+ return "", err
+ }
+
+ return buf.String(), nil
+}
+
+// ResolvePlaceholderString populates a template with values
+func ResolvePlaceholderString(str string, arguments map[string]string) string {
+ for key, value := range arguments {
+ str = strings.Replace(str, "{{"+key+"}}", value, -1)
+ str = strings.Replace(str, "{{."+key+"}}", value, -1)
+ }
+ return str
+}
diff --git a/pkg/utils/template_test.go b/pkg/utils/template_test.go
new file mode 100644
index 000000000..f294d115d
--- /dev/null
+++ b/pkg/utils/template_test.go
@@ -0,0 +1,61 @@
+package utils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// TestResolvePlaceholderString is a function.
+func TestResolvePlaceholderString(t *testing.T) {
+ type scenario struct {
+ templateString string
+ arguments map[string]string
+ expected string
+ }
+
+ scenarios := []scenario{
+ {
+ "",
+ map[string]string{},
+ "",
+ },
+ {
+ "hello",
+ map[string]string{},
+ "hello",
+ },
+ {
+ "hello {{arg}}",
+ map[string]string{},
+ "hello {{arg}}",
+ },
+ {
+ "hello {{arg}}",
+ map[string]string{"arg": "there"},
+ "hello there",
+ },
+ {
+ "hello",
+ map[string]string{"arg": "there"},
+ "hello",
+ },
+ {
+ "{{nothing}}",
+ map[string]string{"nothing": ""},
+ "",
+ },
+ {
+ "{{}} {{ this }} { should not throw}} an {{{{}}}} error",
+ map[string]string{
+ "blah": "blah",
+ "this": "won't match",
+ },
+ "{{}} {{ this }} { should not throw}} an {{{{}}}} error",
+ },
+ }
+
+ for _, s := range scenarios {
+ assert.EqualValues(t, s.expected, ResolvePlaceholderString(s.templateString, s.arguments))
+ }
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index 69fe711ac..4ec1c624c 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -1,7 +1,6 @@
package utils
import (
- "bytes"
"encoding/json"
"fmt"
"log"
@@ -11,50 +10,11 @@ import (
"runtime"
"strconv"
"strings"
- "text/template"
"time"
- "github.com/fatih/color"
"github.com/jesseduffield/gocui"
)
-// SplitLines takes a multiline string and splits it on newlines
-// currently we are also stripping \r's which may have adverse effects for
-// windows users (but no issues have been raised yet)
-func SplitLines(multilineString string) []string {
- multilineString = strings.Replace(multilineString, "\r", "", -1)
- if multilineString == "" || multilineString == "\n" {
- return make([]string, 0)
- }
- lines := strings.Split(multilineString, "\n")
- if lines[len(lines)-1] == "" {
- return lines[:len(lines)-1]
- }
- return lines
-}
-
-// WithPadding pads a string as much as you want
-func WithPadding(str string, padding int) string {
- uncoloredStr := Decolorise(str)
- if padding < len(uncoloredStr) {
- return str
- }
- return str + strings.Repeat(" ", padding-len(uncoloredStr))
-}
-
-// ColoredString takes a string and a colour attribute and returns a colored
-// string with that attribute
-func ColoredString(str string, colorAttributes ...color.Attribute) string {
- colour := color.New(colorAttributes...)
- return ColoredStringDirect(str, colour)
-}
-
-// ColoredStringDirect used for aggregating a few color attributes rather than
-// just sending a single one
-func ColoredStringDirect(str string, colour *color.Color) string {
- return colour.SprintFunc()(fmt.Sprint(str))
-}
-
// GetCurrentRepoName gets the repo's base name
func GetCurrentRepoName() string {
pwd, err := os.Getwd()
@@ -64,22 +24,6 @@ func GetCurrentRepoName() string {
return filepath.Base(pwd)
}
-// TrimTrailingNewline - Trims the trailing newline
-// TODO: replace with `chomp` after refactor
-func TrimTrailingNewline(str string) string {
- if strings.HasSuffix(str, "\n") {
- return str[:len(str)-1]
- }
- return str
-}
-
-// NormalizeLinefeeds - Removes all Windows and Mac style line feeds
-func NormalizeLinefeeds(str string) string {
- str = strings.Replace(str, "\r\n", "\n", -1)
- str = strings.Replace(str, "\r", "", -1)
- return str
-}
-
// GetProjectRoot returns the path to the root of the project. Only to be used
// in testing contexts, as with binaries it's unlikely this path will exist on
// the machine
@@ -100,15 +44,6 @@ func Loader() string {
return characters[index : index+1]
}