14 "github.com/mattn/go-runewidth"
17 // Util.go is a collection of utility functions that are used throughout
20 // Count returns the length of a string in runes
21 // This is exactly equivalent to utf8.RuneCountInString(), just less characters
22 func Count(s string) int {
23 return utf8.RuneCountInString(s)
26 // Convert byte array to rune array
27 func toRunes(b []byte) []rune {
28 runes := make([]rune, 0, utf8.RuneCount(b))
31 r, size := utf8.DecodeRune(b)
32 runes = append(runes, r)
40 func sliceStart(slc []byte, index int) []byte {
46 return slc[totalSize:]
49 _, size := utf8.DecodeRune(slc[totalSize:])
54 return slc[totalSize:]
57 func sliceEnd(slc []byte, index int) []byte {
63 return slc[:totalSize]
66 _, size := utf8.DecodeRune(slc[totalSize:])
71 return slc[:totalSize]
74 // NumOccurrences counts the number of occurrences of a byte in a string
75 func NumOccurrences(s string, c byte) int {
77 for i := 0; i < len(s); i++ {
85 // Spaces returns a string with n spaces
86 func Spaces(n int) string {
87 return strings.Repeat(" ", n)
90 // Min takes the min of two ints
91 func Min(a, b int) int {
98 // Max takes the max of two ints
99 func Max(a, b int) int {
106 // FSize gets the size of a file
107 func FSize(f *os.File) int64 {
113 // IsWordChar returns whether or not the string is a 'word character'
114 // If it is a unicode character, then it does not match
115 // Word characters are defined as [A-Za-z0-9_]
116 func IsWordChar(str string) bool {
122 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
125 // IsWhitespace returns true if the given rune is a space, tab, or newline
126 func IsWhitespace(c rune) bool {
127 return c == ' ' || c == '\t' || c == '\n'
130 // IsStrWhitespace returns true if the given string is all whitespace
131 func IsStrWhitespace(str string) bool {
132 for _, c := range str {
133 if !IsWhitespace(c) {
140 // Contains returns whether or not a string array contains a given string
141 func Contains(list []string, a string) bool {
142 for _, b := range list {
150 // Insert makes a simple insert into a string at the given position
151 func Insert(str string, pos int, value string) string {
152 return string([]rune(str)[:pos]) + value + string([]rune(str)[pos:])
155 // MakeRelative will attempt to make a relative path between path and base
156 func MakeRelative(path, base string) (string, error) {
158 rel, err := filepath.Rel(base, path)
167 // GetLeadingWhitespace returns the leading whitespace of the given string
168 func GetLeadingWhitespace(str string) string {
170 for _, c := range str {
171 if c == ' ' || c == '\t' {
180 // IsSpaces checks if a given string is only spaces
181 func IsSpaces(str []byte) bool {
182 for _, c := range str {
191 // IsSpacesOrTabs checks if a given string contains only spaces and tabs
192 func IsSpacesOrTabs(str string) bool {
193 for _, c := range str {
194 if c != ' ' && c != '\t' {
202 // ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off'
203 // as 'true' and 'false' respectively
204 func ParseBool(str string) (bool, error) {
211 return strconv.ParseBool(str)
214 // EscapePath replaces every path separator in a given path with a %
215 func EscapePath(path string) string {
216 path = filepath.ToSlash(path)
217 return strings.Replace(path, "/", "%", -1)
220 // GetModTime returns the last modification time for a given file
221 // It also returns a boolean if there was a problem accessing the file
222 func GetModTime(path string) (time.Time, bool) {
223 info, err := os.Stat(path)
225 return time.Now(), false
227 return info.ModTime(), true
230 // StringWidth returns the width of a string where tabs count as `tabsize` width
231 func StringWidth(str string, tabsize int) int {
232 sw := runewidth.StringWidth(str)
234 for _, ch := range str {
237 ts := tabsize - (lineIdx % tabsize)
249 // WidthOfLargeRunes searches all the runes in a string and counts up all the widths of runes
250 // that have a width larger than 1 (this also counts tabs as `tabsize` width)
251 func WidthOfLargeRunes(str string, tabsize int) int {
254 for _, ch := range str {
257 w = tabsize - (lineIdx % tabsize)
259 w = runewidth.RuneWidth(ch)
273 // RunePos returns the rune index of a given byte index
274 // This could cause problems if the byte index is between code points
275 func runePos(p int, str string) int {
276 return utf8.RuneCountInString(str[:p])
279 func lcs(a, b string) string {
284 for i, r := range arunes {
285 if i >= len(brunes) {
297 // CommonSubstring gets a common substring among the inputs
298 func CommonSubstring(arr ...string) string {
301 for _, str := range arr[1:] {
302 commonStr = lcs(commonStr, str)
308 // Abs is a simple absolute value function for ints
309 func Abs(n int) int {
316 // FuncName returns the full name of a given function object
317 func FuncName(i interface{}) string {
318 return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
321 // ShortFuncName returns the name only of a given function object
322 func ShortFuncName(i interface{}) string {
323 return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
326 // ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
327 // home directory. Does nothing if the path does not start with '~'.
328 func ReplaceHome(path string) string {
329 if !strings.HasPrefix(path, "~") {
333 var userData *user.User
336 homeString := strings.Split(path, "/")[0]
337 if homeString == "~" {
338 userData, err = user.Current()
340 messenger.Error("Could not find user: ", err)
343 userData, err = user.Lookup(homeString[1:])
345 if messenger != nil {
346 messenger.Error("Could not find user: ", err)
348 TermMessage("Could not find user: ", err)
354 home := userData.HomeDir
356 return strings.Replace(path, homeString, home, 1)
359 // GetPath returns a filename without everything following a `:`
360 // This is used for opening files like util.go:10:5 to specify a line and column
361 func GetPath(path string) string {
362 if strings.Contains(path, ":") {
363 path = strings.Split(path, ":")[0]