wide/vendor/github.com/nsf/gocode/utils.go

297 lines
6.8 KiB
Go

package main
import (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"unicode/utf8"
)
// our own readdir, which skips the files it cannot lstat
func readdir_lstat(name string) ([]os.FileInfo, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
return nil, err
}
out := make([]os.FileInfo, 0, len(names))
for _, lname := range names {
s, err := os.Lstat(filepath.Join(name, lname))
if err != nil {
continue
}
out = append(out, s)
}
return out, nil
}
// our other readdir function, only opens and reads
func readdir(dirname string) []os.FileInfo {
f, err := os.Open(dirname)
if err != nil {
return nil
}
fi, err := f.Readdir(-1)
f.Close()
if err != nil {
panic(err)
}
return fi
}
// returns truncated 'data' and amount of bytes skipped (for cursor pos adjustment)
func filter_out_shebang(data []byte) ([]byte, int) {
if len(data) > 2 && data[0] == '#' && data[1] == '!' {
newline := bytes.Index(data, []byte("\n"))
if newline != -1 && len(data) > newline+1 {
return data[newline+1:], newline + 1
}
}
return data, 0
}
func file_exists(filename string) bool {
_, err := os.Stat(filename)
if err != nil {
return false
}
return true
}
func is_dir(path string) bool {
fi, err := os.Stat(path)
return err == nil && fi.IsDir()
}
func char_to_byte_offset(s []byte, offset_c int) (offset_b int) {
for offset_b = 0; offset_c > 0 && offset_b < len(s); offset_b++ {
if utf8.RuneStart(s[offset_b]) {
offset_c--
}
}
return offset_b
}
func xdg_home_dir() string {
xdghome := os.Getenv("XDG_CONFIG_HOME")
if xdghome == "" {
xdghome = filepath.Join(os.Getenv("HOME"), ".config")
}
return xdghome
}
func has_prefix(s, prefix string, ignorecase bool) bool {
if ignorecase {
s = strings.ToLower(s)
prefix = strings.ToLower(prefix)
}
return strings.HasPrefix(s, prefix)
}
func find_bzl_project_root(libpath, path string) (string, error) {
if libpath == "" {
return "", fmt.Errorf("could not find project root, libpath is empty")
}
pathMap := map[string]struct{}{}
for _, lp := range strings.Split(libpath, ":") {
lp := strings.TrimSpace(lp)
pathMap[filepath.Clean(lp)] = struct{}{}
}
path = filepath.Dir(path)
if path == "" {
return "", fmt.Errorf("project root is blank")
}
start := path
for path != "/" {
if _, ok := pathMap[filepath.Clean(path)]; ok {
return path, nil
}
path = filepath.Dir(path)
}
return "", fmt.Errorf("could not find project root in %q or its parents", start)
}
// Code taken directly from `gb`, I hope author doesn't mind.
func find_gb_project_root(path string) (string, error) {
path = filepath.Dir(path)
if path == "" {
return "", fmt.Errorf("project root is blank")
}
start := path
for path != "/" {
root := filepath.Join(path, "src")
if _, err := os.Stat(root); err != nil {
if os.IsNotExist(err) {
path = filepath.Dir(path)
continue
}
return "", err
}
path, err := filepath.EvalSymlinks(path)
if err != nil {
return "", err
}
return path, nil
}
return "", fmt.Errorf("could not find project root in %q or its parents", start)
}
// vendorlessImportPath returns the devendorized version of the provided import path.
// e.g. "foo/bar/vendor/a/b" => "a/b"
func vendorlessImportPath(ipath string, currentPackagePath string) (string, bool) {
split := strings.Split(ipath, "vendor/")
// no vendor in path
if len(split) == 1 {
return ipath, true
}
// this import path does not belong to the current package
if currentPackagePath != "" && !strings.Contains(currentPackagePath, split[0]) {
return "", false
}
// Devendorize for use in import statement.
if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
return ipath[i+len("/vendor/"):], true
}
if strings.HasPrefix(ipath, "vendor/") {
return ipath[len("vendor/"):], true
}
return ipath, true
}
//-------------------------------------------------------------------------
// print_backtrace
//
// a nicer backtrace printer than the default one
//-------------------------------------------------------------------------
var g_backtrace_mutex sync.Mutex
func print_backtrace(err interface{}) {
g_backtrace_mutex.Lock()
defer g_backtrace_mutex.Unlock()
fmt.Printf("panic: %v\n", err)
i := 2
for {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
f := runtime.FuncForPC(pc)
fmt.Printf("%d(%s): %s:%d\n", i-1, f.Name(), file, line)
i++
}
fmt.Println("")
}
//-------------------------------------------------------------------------
// File reader goroutine
//
// It's a bad idea to block multiple goroutines on file I/O. Creates many
// threads which fight for HDD. Therefore only single goroutine should read HDD
// at the same time.
//-------------------------------------------------------------------------
type file_read_request struct {
filename string
out chan file_read_response
}
type file_read_response struct {
data []byte
error error
}
type file_reader_type struct {
in chan file_read_request
}
func new_file_reader() *file_reader_type {
this := new(file_reader_type)
this.in = make(chan file_read_request)
go func() {
var rsp file_read_response
for {
req := <-this.in
rsp.data, rsp.error = ioutil.ReadFile(req.filename)
req.out <- rsp
}
}()
return this
}
func (this *file_reader_type) read_file(filename string) ([]byte, error) {
req := file_read_request{
filename,
make(chan file_read_response),
}
this.in <- req
rsp := <-req.out
return rsp.data, rsp.error
}
var file_reader = new_file_reader()
//-------------------------------------------------------------------------
// copy of the build.Context without func fields
//-------------------------------------------------------------------------
type go_build_context struct {
GOARCH string
GOOS string
GOROOT string
GOPATH string
CgoEnabled bool
UseAllFiles bool
Compiler string
BuildTags []string
ReleaseTags []string
InstallSuffix string
}
func pack_build_context(ctx *build.Context) go_build_context {
return go_build_context{
GOARCH: ctx.GOARCH,
GOOS: ctx.GOOS,
GOROOT: ctx.GOROOT,
GOPATH: ctx.GOPATH,
CgoEnabled: ctx.CgoEnabled,
UseAllFiles: ctx.UseAllFiles,
Compiler: ctx.Compiler,
BuildTags: ctx.BuildTags,
ReleaseTags: ctx.ReleaseTags,
InstallSuffix: ctx.InstallSuffix,
}
}
func unpack_build_context(ctx *go_build_context) package_lookup_context {
return package_lookup_context{
Context: build.Context{
GOARCH: ctx.GOARCH,
GOOS: ctx.GOOS,
GOROOT: ctx.GOROOT,
GOPATH: ctx.GOPATH,
CgoEnabled: ctx.CgoEnabled,
UseAllFiles: ctx.UseAllFiles,
Compiler: ctx.Compiler,
BuildTags: ctx.BuildTags,
ReleaseTags: ctx.ReleaseTags,
InstallSuffix: ctx.InstallSuffix,
},
}
}