X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Futil.go;h=eb8a1977607df70a0291d012345c82480dc2ce38;hb=71af765b4e4f368c4bbbcb3947f3497e17271b62;hp=c81fadbf0fa569d30a4bce76879b90ace79ddc70;hpb=ccfe08bc6063ae358fc61d51668ea5e20b8dddc3;p=micro.git diff --git a/cmd/micro/util.go b/cmd/micro/util.go index c81fadbf..eb8a1977 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -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 }