import (
"os"
+ "os/user"
"path/filepath"
+ "reflect"
+ "runtime"
"strconv"
"strings"
"time"
return utf8.RuneCountInString(s)
}
-// NumOccurences counts the number of occurences of a byte in a string
-func NumOccurences(s string, c byte) int {
+// 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++ {
if s[i] == c {
// 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
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 == '_')
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 {
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 := ""
}
// 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
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) {
}
// StringWidth returns the width of a string where tabs count as `tabsize` width
-func StringWidth(str string) int {
+func StringWidth(str string, tabsize int) int {
sw := runewidth.StringWidth(str)
- sw += NumOccurences(str, '\t') * (int(settings["tabsize"].(float64)) - 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
}
// 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) int {
+func WidthOfLargeRunes(str string, tabsize int) int {
count := 0
+ lineIdx := 0
for _, ch := range str {
var w int
if ch == '\t' {
- w = int(settings["tabsize"].(float64))
+ 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
}
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
+}
+
+// CommonSubstring gets a common substring among the inputs
+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
}
+
+// 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).")
+}
+
+// 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)
+ }
+ } 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)
+ }
+ return ""
+ }
+ }
+
+ 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
+}