Reorganize file structure

This commit is contained in:
Zachary Yedidia
2018-08-27 15:53:10 -04:00
parent a2a14c7e11
commit 8cf9aa216a
34 changed files with 5822 additions and 4386 deletions

40
cmd/micro/util/message.go Normal file
View File

@@ -0,0 +1,40 @@
package util
import (
"bufio"
"fmt"
"os"
"strconv"
"github.com/zyedidia/micro/cmd/micro/screen"
)
// TermMessage sends a message to the user in the terminal. This usually occurs before
// micro has been fully initialized -- ie if there is an error in the syntax highlighting
// regular expressions
// The function must be called when the Screen is not initialized
// This will write the message, and wait for the user
// to press and key to continue
func TermMessage(msg ...interface{}) {
screenWasNil := screen.Screen == nil
if !screenWasNil {
screen.Screen.Fini()
screen.Screen = nil
}
fmt.Println(msg...)
fmt.Print("\nPress enter to continue")
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
if !screenWasNil {
screen.Init()
}
}
// TermError sends an error to the user in the terminal. Like TermMessage except formatted
// as an error
func TermError(filename string, lineNum int, err string) {
TermMessage(filename + ", " + strconv.Itoa(lineNum) + ": " + err)
}

28
cmd/micro/util/profile.go Normal file
View File

@@ -0,0 +1,28 @@
package util
import (
"fmt"
"log"
"runtime"
"time"
humanize "github.com/dustin/go-humanize"
)
func GetMemStats() string {
var memstats runtime.MemStats
runtime.ReadMemStats(&memstats)
return fmt.Sprintf("Alloc: %s, Sys: %s, GC: %d, PauseTotalNs: %dns", humanize.Bytes(memstats.Alloc), humanize.Bytes(memstats.Sys), memstats.NumGC, memstats.PauseTotalNs)
}
var start time.Time
func tic(s string) {
log.Println("START:", s)
start = time.Now()
}
func toc() {
end := time.Now()
log.Println("END: ElapsedTime in seconds:", end.Sub(start))
}

222
cmd/micro/util/util.go Normal file
View File

@@ -0,0 +1,222 @@
package util
import (
"errors"
"os"
"os/user"
"path/filepath"
"regexp"
"strings"
"time"
"unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
)
// SliceEnd returns a byte slice where the index is a rune index
// Slices off the start of the slice
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:]
}
// SliceStart returns a byte slice where the index is a rune index
// Slices off the end of the slice
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]
}
// SliceVisualEnd will take a byte slice and slice off the start
// up to a given visual index. If the index is in the middle of a
// rune the number of visual columns into the rune will be returned
func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int) {
width := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
w := 0
switch r {
case '\t':
ts := tabsize - (width % tabsize)
w = ts
default:
w = runewidth.RuneWidth(r)
}
if width+w > n {
return b, n - width
}
width += w
b = b[size:]
}
return b, width
}
// Abs is a simple absolute value function for ints
func Abs(n int) int {
if n < 0 {
return -n
}
return n
}
// StringWidth returns the visual width of a byte array indexed from 0 to n (rune index)
// with a given tabsize
func StringWidth(b []byte, n, tabsize int) int {
i := 0
width := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
b = b[size:]
switch r {
case '\t':
ts := tabsize - (width % tabsize)
width += ts
default:
width += runewidth.RuneWidth(r)
}
i++
if i == n {
return width
}
}
return width
}
// 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
}
// FSize gets the size of a file
func FSize(f *os.File) int64 {
fi, _ := f.Stat()
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 true
}
c := str[0]
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
}
// 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 {
// Range loop for unicode correctness
for _, c := range str {
if !IsWhitespace(c) {
return false
}
}
return true
}
// TODO: consider changing because of snap segfault
// 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, error) {
if !strings.HasPrefix(path, "~") {
return path, nil
}
var userData *user.User
var err error
homeString := strings.Split(path, "/")[0]
if homeString == "~" {
userData, err = user.Current()
if err != nil {
return "", errors.New("Could not find user: " + err.Error())
}
} else {
userData, err = user.Lookup(homeString[1:])
if err != nil {
return "", errors.New("Could not find user: " + err.Error())
}
}
home := userData.HomeDir
return strings.Replace(path, homeString, home, 1), nil
}
// GetPathAndCursorPosition returns a filename without everything following a `:`
// This is used for opening files like util.go:10:5 to specify a line and column
// Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
func GetPathAndCursorPosition(path string) (string, []string) {
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?`)
match := re.FindStringSubmatch(path)
// no lines/columns were specified in the path, return just the path with no cursor location
if len(match) == 0 {
return path, nil
} else if match[len(match)-1] != "" {
// if the last capture group match isn't empty then both line and column were provided
return match[1], match[2:]
}
// if it was empty, then only a line was provided, so default to column 0
return match[1], []string{match[2], "0"}
}
// GetModTime returns the last modification time for a given file
func GetModTime(path string) (time.Time, error) {
info, err := os.Stat(path)
if err != nil {
return time.Now(), err
}
return info.ModTime(), nil
}
// 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)
}

View File

@@ -0,0 +1,33 @@
package util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestStringWidth(t *testing.T) {
bytes := []byte("\tPot să \tmănânc sticlă și ea nu mă rănește.")
n := StringWidth(bytes, 23, 4)
assert.Equal(t, 26, n)
}
func TestSliceVisualEnd(t *testing.T) {
s := []byte("\thello")
slc, n := SliceVisualEnd(s, 2, 4)
assert.Equal(t, []byte("\thello"), slc)
assert.Equal(t, 2, n)
slc, n = SliceVisualEnd(s, 1, 4)
assert.Equal(t, []byte("\thello"), slc)
assert.Equal(t, 1, n)
slc, n = SliceVisualEnd(s, 4, 4)
assert.Equal(t, []byte("hello"), slc)
assert.Equal(t, 0, n)
slc, n = SliceVisualEnd(s, 5, 4)
assert.Equal(t, []byte("ello"), slc)
assert.Equal(t, 0, n)
}