micro/cmd/micro/util.go

340 lines
6.9 KiB
Go
Raw Normal View History

2016-03-17 21:27:57 +00:00
package main
import (
"os"
"path/filepath"
"reflect"
"runtime"
2016-04-24 19:35:16 +00:00
"strconv"
"strings"
"time"
2016-03-17 21:27:57 +00:00
"unicode/utf8"
"github.com/mattn/go-runewidth"
homedir "github.com/mitchellh/go-homedir"
2016-03-17 21:27:57 +00:00
)
2016-03-25 16:14:22 +00:00
// Util.go is a collection of utility functions that are used throughout
// the program
2016-03-19 00:40:00 +00:00
// Count returns the length of a string in runes
2016-03-25 16:14:22 +00:00
// This is exactly equivalent to utf8.RuneCountInString(), just less characters
2016-03-19 00:40:00 +00:00
func Count(s string) int {
2016-03-17 21:27:57 +00:00
return utf8.RuneCountInString(s)
}
// 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
}
2018-01-29 21:02:15 +00:00
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]
}
2016-10-18 15:12:28 +00:00
// NumOccurrences counts the number of occurrences of a byte in a string
func NumOccurrences(s string, c byte) int {
2016-03-17 21:27:57 +00:00
var n int
for i := 0; i < len(s); i++ {
if s[i] == c {
n++
}
}
return n
}
2016-03-25 16:14:22 +00:00
// Spaces returns a string with n spaces
func Spaces(n int) string {
return strings.Repeat(" ", n)
2016-03-17 21:27:57 +00:00
}
2016-03-25 16:14:22 +00:00
// Min takes the min of two ints
func Min(a, b int) int {
if a > b {
return b
}
return a
}
// Max takes the max of two ints
func Max(a, b int) int {
if a > b {
return a
}
return b
}
2016-03-28 12:43:08 +00:00
2017-10-01 16:42:23 +00:00
// FSize gets the size of a file
func FSize(f *os.File) int64 {
fi, _ := f.Stat()
// get the size
return fi.Size()
}
2016-03-28 12:43:08 +00:00
// 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 true
2016-03-28 12:43:08 +00:00
}
c := str[0]
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
}
2016-04-27 14:44:36 +00:00
// IsWhitespace returns true if the given rune is a space, tab, or newline
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 {
if b == a {
return true
}
}
return false
}
2016-04-08 13:30:36 +00:00
// Insert makes a simple insert into a string at the given position
func Insert(str string, pos int, value string) string {
return string([]rune(str)[:pos]) + value + string([]rune(str)[pos:])
}
2016-04-21 01:47:52 +00:00
// 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
}
2016-04-21 01:47:52 +00:00
// GetLeadingWhitespace returns the leading whitespace of the given string
func GetLeadingWhitespace(str string) string {
ws := ""
for _, c := range str {
if c == ' ' || c == '\t' {
ws += string(c)
} else {
break
}
}
return ws
}
2016-04-21 23:15:46 +00:00
// IsSpaces checks if a given string is only spaces
2018-01-29 21:02:15 +00:00
func IsSpaces(str []byte) bool {
2016-04-21 23:15:46 +00:00
for _, c := range str {
if c != ' ' {
return false
}
}
return true
}
2016-04-24 19:35:16 +00:00
// 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
}
2016-04-24 19:35:16 +00:00
// 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) {
if str == "on" {
return true, nil
}
if str == "off" {
return false, nil
}
return strconv.ParseBool(str)
}
// EscapePath replaces every path separator in a given path with a %
func EscapePath(path string) string {
path = filepath.ToSlash(path)
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
}
2016-07-10 17:26:05 +00:00
// StringWidth returns the width of a string where tabs count as `tabsize` width
2016-08-24 23:55:44 +00:00
func StringWidth(str string, tabsize int) int {
sw := runewidth.StringWidth(str)
2016-09-28 19:54:34 +00:00
lineIdx := 0
for _, ch := range str {
switch ch {
case '\t':
ts := tabsize - (lineIdx % tabsize)
2017-02-03 12:32:48 +00:00
sw += ts
2016-09-28 19:54:34 +00:00
lineIdx += ts
case '\n':
lineIdx = 0
default:
lineIdx++
}
}
return sw
}
2016-07-10 17:26:05 +00:00
// 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)
2016-08-24 23:55:44 +00:00
func WidthOfLargeRunes(str string, tabsize int) int {
count := 0
2016-09-28 19:54:34 +00:00
lineIdx := 0
for _, ch := range str {
var w int
if ch == '\t' {
2016-09-28 19:54:34 +00:00
w = tabsize - (lineIdx % tabsize)
} else {
w = runewidth.RuneWidth(ch)
}
if w > 1 {
count += (w - 1)
}
2016-09-28 19:54:34 +00:00
if ch == '\n' {
lineIdx = 0
} else {
lineIdx += w
}
}
return count
}
2016-07-10 17:26:05 +00:00
// 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])
}
2016-06-07 15:43:28 +00:00
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
}
2017-10-01 16:42:23 +00:00
// 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
}
2016-06-07 15:43:28 +00:00
// Abs is a simple absolute value function for ints
func Abs(n int) int {
if n < 0 {
return -n
}
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
}
home, err := homedir.Dir()
if err != nil {
messenger.Error("Could not find home directory: ", err)
return path
}
return strings.Replace(path, "~", home, 1)
}