13 runewidth "github.com/mattn/go-runewidth"
16 // SliceEnd returns a byte slice where the index is a rune index
17 // Slices off the start of the slice
18 func SliceEnd(slc []byte, index int) []byte {
24 return slc[totalSize:]
27 _, size := utf8.DecodeRune(slc[totalSize:])
32 return slc[totalSize:]
35 // SliceStart returns a byte slice where the index is a rune index
36 // Slices off the end of the slice
37 func SliceStart(slc []byte, index int) []byte {
43 return slc[:totalSize]
46 _, size := utf8.DecodeRune(slc[totalSize:])
51 return slc[:totalSize]
54 // SliceVisualEnd will take a byte slice and slice off the start
55 // up to a given visual index. If the index is in the middle of a
56 // rune the number of visual columns into the rune will be returned
57 func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int) {
60 r, size := utf8.DecodeRune(b)
65 ts := tabsize - (width % tabsize)
68 w = runewidth.RuneWidth(r)
79 // Abs is a simple absolute value function for ints
87 // StringWidth returns the visual width of a byte array indexed from 0 to n (rune index)
88 // with a given tabsize
89 func StringWidth(b []byte, n, tabsize int) int {
93 r, size := utf8.DecodeRune(b)
98 ts := tabsize - (width % tabsize)
101 width += runewidth.RuneWidth(r)
113 // Min takes the min of two ints
114 func Min(a, b int) int {
121 // Max takes the max of two ints
122 func Max(a, b int) int {
129 // FSize gets the size of a file
130 func FSize(f *os.File) int64 {
135 // IsWordChar returns whether or not the string is a 'word character'
136 // If it is a unicode character, then it does not match
137 // Word characters are defined as [A-Za-z0-9_]
138 func IsWordChar(r rune) bool {
139 return (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r == '_')
142 // IsWhitespace returns true if the given rune is a space, tab, or newline
143 func IsWhitespace(c rune) bool {
144 return c == ' ' || c == '\t' || c == '\n'
147 // IsStrWhitespace returns true if the given string is all whitespace
148 func IsStrWhitespace(str string) bool {
149 // Range loop for unicode correctness
150 for _, c := range str {
151 if !IsWhitespace(c) {
158 // TODO: consider changing because of snap segfault
159 // ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
160 // home directory. Does nothing if the path does not start with '~'.
161 func ReplaceHome(path string) (string, error) {
162 if !strings.HasPrefix(path, "~") {
166 var userData *user.User
169 homeString := strings.Split(path, "/")[0]
170 if homeString == "~" {
171 userData, err = user.Current()
173 return "", errors.New("Could not find user: " + err.Error())
176 userData, err = user.Lookup(homeString[1:])
178 return "", errors.New("Could not find user: " + err.Error())
182 home := userData.HomeDir
184 return strings.Replace(path, homeString, home, 1), nil
187 // GetPathAndCursorPosition returns a filename without everything following a `:`
188 // This is used for opening files like util.go:10:5 to specify a line and column
189 // Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
190 func GetPathAndCursorPosition(path string) (string, []string) {
191 re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?`)
192 match := re.FindStringSubmatch(path)
193 // no lines/columns were specified in the path, return just the path with no cursor location
196 } else if match[len(match)-1] != "" {
197 // if the last capture group match isn't empty then both line and column were provided
198 return match[1], match[2:]
200 // if it was empty, then only a line was provided, so default to column 0
201 return match[1], []string{match[2], "0"}
204 // GetModTime returns the last modification time for a given file
205 func GetModTime(path string) (time.Time, error) {
206 info, err := os.Stat(path)
208 return time.Now(), err
210 return info.ModTime(), nil
213 // EscapePath replaces every path separator in a given path with a %
214 func EscapePath(path string) string {
215 path = filepath.ToSlash(path)
216 return strings.Replace(path, "/", "%", -1)