]> git.lizzy.rs Git - micro.git/blob - cmd/micro/util.go
Proper support for double width characters
[micro.git] / cmd / micro / util.go
1 package main
2
3 import (
4         "os"
5         "path/filepath"
6         "strconv"
7         "strings"
8         "time"
9         "unicode/utf8"
10
11         "github.com/mattn/go-runewidth"
12 )
13
14 // Util.go is a collection of utility functions that are used throughout
15 // the program
16
17 // Count returns the length of a string in runes
18 // This is exactly equivalent to utf8.RuneCountInString(), just less characters
19 func Count(s string) int {
20         return utf8.RuneCountInString(s)
21 }
22
23 // NumOccurences counts the number of occurences of a byte in a string
24 func NumOccurences(s string, c byte) int {
25         var n int
26         for i := 0; i < len(s); i++ {
27                 if s[i] == c {
28                         n++
29                 }
30         }
31         return n
32 }
33
34 // Spaces returns a string with n spaces
35 func Spaces(n int) string {
36         var str string
37         for i := 0; i < n; i++ {
38                 str += " "
39         }
40         return str
41 }
42
43 // Min takes the min of two ints
44 func Min(a, b int) int {
45         if a > b {
46                 return b
47         }
48         return a
49 }
50
51 // Max takes the max of two ints
52 func Max(a, b int) int {
53         if a > b {
54                 return a
55         }
56         return b
57 }
58
59 // IsWordChar returns whether or not the string is a 'word character'
60 // If it is a unicode character, then it does not match
61 // Word characters are defined as [A-Za-z0-9_]
62 func IsWordChar(str string) bool {
63         if len(str) > 1 {
64                 // Unicode
65                 return false
66         }
67         c := str[0]
68         return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
69 }
70
71 // IsWhitespace returns true if the given rune is a space, tab, or newline
72 func IsWhitespace(c rune) bool {
73         return c == ' ' || c == '\t' || c == '\n'
74 }
75
76 // Contains returns whether or not a string array contains a given string
77 func Contains(list []string, a string) bool {
78         for _, b := range list {
79                 if b == a {
80                         return true
81                 }
82         }
83         return false
84 }
85
86 // Insert makes a simple insert into a string at the given position
87 func Insert(str string, pos int, value string) string {
88         return string([]rune(str)[:pos]) + value + string([]rune(str)[pos:])
89 }
90
91 // GetLeadingWhitespace returns the leading whitespace of the given string
92 func GetLeadingWhitespace(str string) string {
93         ws := ""
94         for _, c := range str {
95                 if c == ' ' || c == '\t' {
96                         ws += string(c)
97                 } else {
98                         break
99                 }
100         }
101         return ws
102 }
103
104 // IsSpaces checks if a given string is only spaces
105 func IsSpaces(str string) bool {
106         for _, c := range str {
107                 if c != ' ' {
108                         return false
109                 }
110         }
111
112         return true
113 }
114
115 // ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off'
116 // as 'true' and 'false' respectively
117 func ParseBool(str string) (bool, error) {
118         if str == "on" {
119                 return true, nil
120         }
121         if str == "off" {
122                 return false, nil
123         }
124         return strconv.ParseBool(str)
125 }
126
127 // EscapePath replaces every path separator in a given path with a %
128 func EscapePath(path string) string {
129         path = filepath.ToSlash(path)
130         return strings.Replace(path, "/", "%", -1)
131 }
132
133 // GetModTime returns the last modification time for a given file
134 // It also returns a boolean if there was a problem accessing the file
135 func GetModTime(path string) (time.Time, bool) {
136         info, err := os.Stat(path)
137         if err != nil {
138                 return time.Now(), false
139         }
140         return info.ModTime(), true
141 }
142
143 func StringWidth(str string) int {
144         sw := runewidth.StringWidth(str)
145         sw += NumOccurences(str, '\t') * (int(settings["tabsize"].(float64)) - 1)
146         return sw
147 }
148
149 func WidthOfLargeRunes(str string) int {
150         count := 0
151         for _, ch := range str {
152                 var w int
153                 if ch == '\t' {
154                         w = int(settings["tabsize"].(float64))
155                 } else {
156                         w = runewidth.RuneWidth(ch)
157                 }
158                 if w > 1 {
159                         count += (w - 1)
160                 }
161         }
162         return count
163 }
164
165 func runePos(p int, str string) int {
166         return utf8.RuneCountInString(str[:p])
167 }