X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Futil.go;h=1888155a8370536fd1cdab3733546cdbd2a53a39;hb=59251ee5d02f0320051a7df32f7220a3d3de442c;hp=63b0a040942d00c6a268c7deb548a9f36cddcabd;hpb=a92a7dc4e6e2426b081d70f1ff21e438c67b8da9;p=micro.git diff --git a/cmd/micro/util.go b/cmd/micro/util.go index 63b0a040..1888155a 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -1,10 +1,17 @@ package main import ( + "bytes" + "os" "path/filepath" + "reflect" + "runtime" "strconv" "strings" + "time" "unicode/utf8" + + "github.com/mattn/go-runewidth" ) // Util.go is a collection of utility functions that are used throughout @@ -16,8 +23,8 @@ func Count(s string) int { return utf8.RuneCountInString(s) } -// NumOccurences counts the number of occurences of a byte in a string -func NumOccurences(s string, c byte) int { +// 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++ { if s[i] == c { @@ -58,7 +65,7 @@ func Max(a, b int) int { 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 == '_') @@ -84,6 +91,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 := "" @@ -108,6 +127,17 @@ func IsSpaces(str string) bool { return true } +// IsSpacesOrTabs checks if a given string contains only spaces and tabs +func IsSpacesOrTabs(str string) bool { + for _, c := range str { + if c != ' ' && c != '\t' { + return false + } + } + + return true +} + // ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off' // as 'true' and 'false' respectively func ParseBool(str string) (bool, error) { @@ -126,6 +156,193 @@ func EscapePath(path string) string { return strings.Replace(path, "/", "%", -1) } +// GetModTime returns the last modification time for a given file +// It also returns a boolean if there was a problem accessing the file +func GetModTime(path string) (time.Time, bool) { + info, err := os.Stat(path) + if err != nil { + return time.Now(), false + } + return info.ModTime(), true +} + +// StringWidth returns the width of a string where tabs count as `tabsize` width +func StringWidth(str string, tabsize int) int { + sw := runewidth.StringWidth(str) + lineIdx := 0 + for _, ch := range str { + switch ch { + case '\t': + ts := tabsize - (lineIdx % tabsize) + sw += ts - 1 + lineIdx += ts + case '\n': + lineIdx = 0 + default: + lineIdx++ + } + } + return sw +} + +// WidthOfLargeRunes searches all the runes in a string and counts up all the widths of runes +// 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 - (lineIdx % tabsize) + } else { + w = runewidth.RuneWidth(ch) + } + if w > 1 { + count += (w - 1) + } + if ch == '\n' { + lineIdx = 0 + } else { + lineIdx += w + } + } + return count +} + +// RunePos returns the rune index of a given byte index +// This could cause problems if the byte index is between code points func runePos(p int, str string) int { return utf8.RuneCountInString(str[:p]) } + +func lcs(a, b string) string { + arunes := []rune(a) + brunes := []rune(b) + + lcs := "" + for i, r := range arunes { + if i >= len(brunes) { + break + } + if r == brunes[i] { + lcs += string(r) + } else { + break + } + } + return lcs +} + +func CommonSubstring(arr ...string) string { + commonStr := arr[0] + + for _, str := range arr[1:] { + commonStr = lcs(commonStr, str) + } + + return commonStr +} + +// Abs is a simple absolute value function for ints +func Abs(n int) int { + if n < 0 { + return -n + } + return n +} + +// FuncName returns the name of a given function object +func FuncName(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} + +// SplitCommandArgs separates multiple command arguments which may be quoted. +// The returned slice contains at least one string +func SplitCommandArgs(input string) []string { + var result []string + var curQuote *bytes.Buffer + + curArg := new(bytes.Buffer) + escape := false + + finishQuote := func() { + if curQuote == nil { + return + } + str := curQuote.String() + if unquoted, err := strconv.Unquote(str); err == nil { + str = unquoted + } + curArg.WriteString(str) + curQuote = nil + } + + appendResult := func() { + finishQuote() + escape = false + + str := curArg.String() + result = append(result, str) + curArg.Reset() + } + + for _, r := range input { + if r == ' ' && curQuote == nil { + appendResult() + } else { + runeHandled := false + appendRuneToBuff := func() { + if curQuote != nil { + curQuote.WriteRune(r) + } else { + curArg.WriteRune(r) + } + runeHandled = true + } + + if r == '"' && curQuote == nil { + curQuote = new(bytes.Buffer) + appendRuneToBuff() + } else { + if curQuote != nil && !escape { + if r == '"' { + appendRuneToBuff() + finishQuote() + } else if r == '\\' { + appendRuneToBuff() + escape = true + continue + } + } + } + if !runeHandled { + appendRuneToBuff() + } + } + + escape = false + } + appendResult() + return result +} + +// JoinCommandArgs joins multiple command arguments and quote the strings if needed. +func JoinCommandArgs(args ...string) string { + buf := new(bytes.Buffer) + first := true + for _, arg := range args { + if first { + first = false + } else { + buf.WriteRune(' ') + } + quoted := strconv.Quote(arg) + if quoted[1:len(quoted)-1] != arg || strings.ContainsRune(arg, ' ') { + buf.WriteString(quoted) + } else { + buf.WriteString(arg) + } + } + + return buf.String() +}