]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/util.go
Code optimisation (#1117)
[micro.git] / cmd / micro / util.go
index c81fadbf0fa569d30a4bce76879b90ace79ddc70..eb8a1977607df70a0291d012345c82480dc2ce38 100644 (file)
@@ -1,9 +1,11 @@
 package main
 
 import (
-       "bytes"
        "os"
+       "os/user"
        "path/filepath"
+       "reflect"
+       "runtime"
        "strconv"
        "strings"
        "time"
@@ -21,7 +23,55 @@ func Count(s string) int {
        return utf8.RuneCountInString(s)
 }
 
-// NumOccurrences counts the number of occurences of a byte in a string
+// Convert byte array to rune array
+func toRunes(b []byte) []rune {
+       runes := make([]rune, 0, utf8.RuneCount(b))
+
+       for len(b) > 0 {
+               r, size := utf8.DecodeRune(b)
+               runes = append(runes, r)
+
+               b = b[size:]
+       }
+
+       return runes
+}
+
+func sliceStart(slc []byte, index int) []byte {
+       len := len(slc)
+       i := 0
+       totalSize := 0
+       for totalSize < len {
+               if i >= index {
+                       return slc[totalSize:]
+               }
+
+               _, size := utf8.DecodeRune(slc[totalSize:])
+               totalSize += size
+               i++
+       }
+
+       return slc[totalSize:]
+}
+
+func sliceEnd(slc []byte, index int) []byte {
+       len := len(slc)
+       i := 0
+       totalSize := 0
+       for totalSize < len {
+               if i >= index {
+                       return slc[:totalSize]
+               }
+
+               _, size := utf8.DecodeRune(slc[totalSize:])
+               totalSize += size
+               i++
+       }
+
+       return slc[:totalSize]
+}
+
+// NumOccurrences counts the number of occurrences of a byte in a string
 func NumOccurrences(s string, c byte) int {
        var n int
        for i := 0; i < len(s); i++ {
@@ -34,11 +84,7 @@ func NumOccurrences(s string, c byte) int {
 
 // Spaces returns a string with n spaces
 func Spaces(n int) string {
-       var str string
-       for i := 0; i < n; i++ {
-               str += " "
-       }
-       return str
+       return strings.Repeat(" ", n)
 }
 
 // Min takes the min of two ints
@@ -57,13 +103,20 @@ func Max(a, b int) int {
        return b
 }
 
+// FSize gets the size of a file
+func FSize(f *os.File) int64 {
+       fi, _ := f.Stat()
+       // get the size
+       return fi.Size()
+}
+
 // IsWordChar returns whether or not the string is a 'word character'
 // If it is a unicode character, then it does not match
 // Word characters are defined as [A-Za-z0-9_]
 func IsWordChar(str string) bool {
        if len(str) > 1 {
                // Unicode
-               return false
+               return true
        }
        c := str[0]
        return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
@@ -74,6 +127,16 @@ func IsWhitespace(c rune) bool {
        return c == ' ' || c == '\t' || c == '\n'
 }
 
+// IsStrWhitespace returns true if the given string is all whitespace
+func IsStrWhitespace(str string) bool {
+       for _, c := range str {
+               if !IsWhitespace(c) {
+                       return false
+               }
+       }
+       return true
+}
+
 // Contains returns whether or not a string array contains a given string
 func Contains(list []string, a string) bool {
        for _, b := range list {
@@ -89,6 +152,18 @@ func Insert(str string, pos int, value string) string {
        return string([]rune(str)[:pos]) + value + string([]rune(str)[pos:])
 }
 
+// MakeRelative will attempt to make a relative path between path and base
+func MakeRelative(path, base string) (string, error) {
+       if len(path) > 0 {
+               rel, err := filepath.Rel(base, path)
+               if err != nil {
+                       return path, err
+               }
+               return rel, nil
+       }
+       return path, nil
+}
+
 // GetLeadingWhitespace returns the leading whitespace of the given string
 func GetLeadingWhitespace(str string) string {
        ws := ""
@@ -103,7 +178,7 @@ func GetLeadingWhitespace(str string) string {
 }
 
 // IsSpaces checks if a given string is only spaces
-func IsSpaces(str string) bool {
+func IsSpaces(str []byte) bool {
        for _, c := range str {
                if c != ' ' {
                        return false
@@ -155,7 +230,19 @@ func GetModTime(path string) (time.Time, bool) {
 // StringWidth returns the width of a string where tabs count as `tabsize` width
 func StringWidth(str string, tabsize int) int {
        sw := runewidth.StringWidth(str)
-       sw += NumOccurrences(str, '\t') * (tabsize - 1)
+       lineIdx := 0
+       for _, ch := range str {
+               switch ch {
+               case '\t':
+                       ts := tabsize - (lineIdx % tabsize)
+                       sw += ts
+                       lineIdx += ts
+               case '\n':
+                       lineIdx = 0
+               default:
+                       lineIdx++
+               }
+       }
        return sw
 }
 
@@ -163,16 +250,22 @@ func StringWidth(str string, tabsize int) int {
 // that have a width larger than 1 (this also counts tabs as `tabsize` width)
 func WidthOfLargeRunes(str string, tabsize int) int {
        count := 0
+       lineIdx := 0
        for _, ch := range str {
                var w int
                if ch == '\t' {
-                       w = tabsize
+                       w = tabsize - (lineIdx % tabsize)
                } else {
                        w = runewidth.RuneWidth(ch)
                }
                if w > 1 {
                        count += (w - 1)
                }
+               if ch == '\n' {
+                       lineIdx = 0
+               } else {
+                       lineIdx += w
+               }
        }
        return count
 }
@@ -201,6 +294,7 @@ func lcs(a, b string) string {
        return lcs
 }
 
+// CommonSubstring gets a common substring among the inputs
 func CommonSubstring(arr ...string) string {
        commonStr := arr[0]
 
@@ -219,61 +313,54 @@ func Abs(n int) int {
        return n
 }
 
-// SplitCommandArgs seperates multiple command arguments which may be quoted.
-// The returned slice contains at least one string
-func SplitCommandArgs(input string) []string {
-       var result []string
-
-       inQuote := false
-       escape := false
-       curArg := new(bytes.Buffer)
-       for _, r := range input {
-               if !escape {
-                       switch {
-                       case r == '\\' && inQuote:
-                               escape = true
-                               continue
-                       case r == '"' && inQuote:
-                               inQuote = false
-                               continue
-                       case r == '"' && !inQuote && curArg.Len() == 0:
-                               inQuote = true
-                               continue
-                       case r == ' ' && !inQuote:
-                               result = append(result, curArg.String())
-                               curArg.Reset()
-                               continue
-                       }
-               }
-               escape = false
-               curArg.WriteRune(r)
-       }
-       if curArg.Len() > 0 || len(result) == 0 {
-               result = append(result, curArg.String())
-       }
-       return result
+// FuncName returns the full name of a given function object
+func FuncName(i interface{}) string {
+       return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
+}
+
+// ShortFuncName returns the name only of a given function object
+func ShortFuncName(i interface{}) string {
+       return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
 }
 
-// JoinCommandArgs joins multiple command arguments and quote the strings if needed.
-func JoinCommandArgs(args ...string) string {
-       buf := new(bytes.Buffer)
-       for _, arg := range args {
-               if buf.Len() > 0 {
-                       buf.WriteRune(' ')
+// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
+// home directory. Does nothing if the path does not start with '~'.
+func ReplaceHome(path string) string {
+       if !strings.HasPrefix(path, "~") {
+               return path
+       }
+
+       var userData *user.User
+       var err error
+
+       homeString := strings.Split(path, "/")[0]
+       if homeString == "~" {
+               userData, err = user.Current()
+               if err != nil {
+                       messenger.Error("Could not find user: ", err)
                }
-               if !strings.Contains(arg, " ") {
-                       buf.WriteString(arg)
-               } else {
-                       buf.WriteRune('"')
-                       for _, r := range arg {
-                               if r == '"' || r == '\\' {
-                                       buf.WriteRune('\\')
-                               }
-                               buf.WriteRune(r)
+       } else {
+               userData, err = user.Lookup(homeString[1:])
+               if err != nil {
+                       if messenger != nil {
+                               messenger.Error("Could not find user: ", err)
+                       } else {
+                               TermMessage("Could not find user: ", err)
                        }
-                       buf.WriteRune('"')
+                       return ""
                }
        }
 
-       return buf.String()
+       home := userData.HomeDir
+
+       return strings.Replace(path, homeString, home, 1)
+}
+
+// GetPath returns a filename without everything following a `:`
+// This is used for opening files like util.go:10:5 to specify a line and column
+func GetPath(path string) string {
+       if strings.Contains(path, ":") {
+               path = strings.Split(path, ":")[0]
+       }
+       return path
 }