12 "github.com/mattn/go-runewidth"
15 // Util.go is a collection of utility functions that are used throughout
18 // Count returns the length of a string in runes
19 // This is exactly equivalent to utf8.RuneCountInString(), just less characters
20 func Count(s string) int {
21 return utf8.RuneCountInString(s)
24 // NumOccurrences counts the number of occurences of a byte in a string
25 func NumOccurrences(s string, c byte) int {
27 for i := 0; i < len(s); i++ {
35 // Spaces returns a string with n spaces
36 func Spaces(n int) string {
38 for i := 0; i < n; i++ {
44 // Min takes the min of two ints
45 func Min(a, b int) int {
52 // Max takes the max of two ints
53 func Max(a, b int) int {
60 // IsWordChar returns whether or not the string is a 'word character'
61 // If it is a unicode character, then it does not match
62 // Word characters are defined as [A-Za-z0-9_]
63 func IsWordChar(str string) bool {
69 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
72 // IsWhitespace returns true if the given rune is a space, tab, or newline
73 func IsWhitespace(c rune) bool {
74 return c == ' ' || c == '\t' || c == '\n'
77 // Contains returns whether or not a string array contains a given string
78 func Contains(list []string, a string) bool {
79 for _, b := range list {
87 // Insert makes a simple insert into a string at the given position
88 func Insert(str string, pos int, value string) string {
89 return string([]rune(str)[:pos]) + value + string([]rune(str)[pos:])
92 // GetLeadingWhitespace returns the leading whitespace of the given string
93 func GetLeadingWhitespace(str string) string {
95 for _, c := range str {
96 if c == ' ' || c == '\t' {
105 // IsSpaces checks if a given string is only spaces
106 func IsSpaces(str string) bool {
107 for _, c := range str {
116 // IsSpacesOrTabs checks if a given string contains only spaces and tabs
117 func IsSpacesOrTabs(str string) bool {
118 for _, c := range str {
119 if c != ' ' && c != '\t' {
127 // ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off'
128 // as 'true' and 'false' respectively
129 func ParseBool(str string) (bool, error) {
136 return strconv.ParseBool(str)
139 // EscapePath replaces every path separator in a given path with a %
140 func EscapePath(path string) string {
141 path = filepath.ToSlash(path)
142 return strings.Replace(path, "/", "%", -1)
145 // GetModTime returns the last modification time for a given file
146 // It also returns a boolean if there was a problem accessing the file
147 func GetModTime(path string) (time.Time, bool) {
148 info, err := os.Stat(path)
150 return time.Now(), false
152 return info.ModTime(), true
155 // StringWidth returns the width of a string where tabs count as `tabsize` width
156 func StringWidth(str string, tabsize int) int {
157 sw := runewidth.StringWidth(str)
158 sw += NumOccurrences(str, '\t') * (tabsize - 1)
162 // WidthOfLargeRunes searches all the runes in a string and counts up all the widths of runes
163 // that have a width larger than 1 (this also counts tabs as `tabsize` width)
164 func WidthOfLargeRunes(str string, tabsize int) int {
166 for _, ch := range str {
171 w = runewidth.RuneWidth(ch)
180 // RunePos returns the rune index of a given byte index
181 // This could cause problems if the byte index is between code points
182 func runePos(p int, str string) int {
183 return utf8.RuneCountInString(str[:p])
186 func lcs(a, b string) string {
191 for i, r := range arunes {
192 if i >= len(brunes) {
204 func CommonSubstring(arr ...string) string {
207 for _, str := range arr[1:] {
208 commonStr = lcs(commonStr, str)
214 // Abs is a simple absolute value function for ints
215 func Abs(n int) int {
222 // SplitCommandArgs seperates multiple command arguments which may be quoted.
223 // The returned slice contains at least one string
224 func SplitCommandArgs(input string) []string {
229 curArg := new(bytes.Buffer)
230 for _, r := range input {
233 case r == '\\' && inQuote:
236 case r == '"' && inQuote:
239 case r == '"' && !inQuote && curArg.Len() == 0:
242 case r == ' ' && !inQuote:
243 result = append(result, curArg.String())
251 if curArg.Len() > 0 || len(result) == 0 {
252 result = append(result, curArg.String())
257 // JoinCommandArgs joins multiple command arguments and quote the strings if needed.
258 func JoinCommandArgs(args ...string) string {
259 buf := new(bytes.Buffer)
260 for _, arg := range args {
264 if !strings.Contains(arg, " ") {
268 for _, r := range arg {
269 if r == '"' || r == '\\' {