├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── cmd └── gopkgs │ └── main.go ├── gopkgs.go ├── gopkgs_test.go └── x └── tools └── imports ├── LICENSE ├── export.go ├── export_test.go ├── fastwalk.go ├── fastwalk_dirent_fileno.go ├── fastwalk_dirent_ino.go ├── fastwalk_portable.go ├── fastwalk_unix.go ├── fix.go ├── run_generate.sh └── zstdlib.go /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | gopkgs 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7 5 | - 1.8 6 | - 1.9 7 | - tip 8 | 9 | os: 10 | - linux 11 | - osx 12 | 13 | before_install: 14 | - go get github.com/mitchellh/gox 15 | 16 | script: 17 | - go test -v -race ./... 18 | 19 | before_deploy: 20 | - gox -output "dist/{{.Dir}}_{{.OS}}_{{.Arch}}" ./cmd/gopkgs/ 21 | 22 | deploy: 23 | provider: releases 24 | api_key: 25 | secure: Xu5JeWMXv29Bz0VLk8lX1pFphyGLF2RciuG7umnRdNVGOEvz2jhNm+IRjNn8FCm7Y0PKaOOCGHCDH3qti2tcl8tY+MWxD8E1UpoqfvunKsVqhy5gLbJu39jBOmlSKJe/+k1s1YIkRz6Ie0+oEQEM3I/Hlruk51DA91i/2/mdA/Zea1Pp6hhO/BKvp/tQ6YJEsLAt0DLLueGoNl8X/XDPsLA501qiy4AO+CskLanvcegVoh32k9XCRauHmkfvxkHQsEIQnaG5jVuEiFISyVDZv+Oa3uXXLS37J03Gyw1/NcS8gwAtD/YpgrXKaF00NPWyRwPA8opGUeACAeOnNshyM+/9khKmVS7sS4yeNyYF6H+Tf/h6TnIEy3ltqMc6ERdkL0JB6vDU9ReR0tzoh5SjzgPvv2BnxXP+Uwqx0i9kWU2kBiklJ7Kh8KaRt0Hd0fKsLnT+uzdCUFwOHyF56cAfrUJxrS9sZitQoPkB22rhSlbwMRzn1L3WmZh8tUW+Jct1hUDlM1x4yhkgFP4FRaXF3jChiOmEUPZymArBZY2NoPGeGfuCqo05XSwyO3YIbgN63OdzcJMmGS8LTSjoy1b5epRKOosSOHzzUTieu04pxFTyzcitWUqsAKaAZrAsWsThNfnAM9+Dr+u2+xxabLfAoqo8kh6FUEWm6DAD/cGPM90= 26 | file: 27 | - dist/gopkgs_darwin_386 28 | - dist/gopkgs_darwin_amd64 29 | - dist/gopkgs_freebsd_386 30 | - dist/gopkgs_freebsd_amd64 31 | - dist/gopkgs_freebsd_arm 32 | - dist/gopkgs_linux_386 33 | - dist/gopkgs_linux_amd64 34 | - dist/gopkgs_linux_arm 35 | - dist/gopkgs_netbsd_386 36 | - dist/gopkgs_netbsd_amd64 37 | - dist/gopkgs_netbsd_arm 38 | - dist/gopkgs_openbsd_386 39 | - dist/gopkgs_openbsd_amd64 40 | - dist/gopkgs_windows_386.exe 41 | - dist/gopkgs_windows_amd64.exe 42 | skip_cleanup: true 43 | on: 44 | tags: true 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 haya14busa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | --- 24 | under x/tools/imports/ 25 | 26 | Copyright (c) 2009 The Go Authors. All rights reserved. 27 | 28 | Redistribution and use in source and binary forms, with or without 29 | modification, are permitted provided that the following conditions are 30 | met: 31 | 32 | * Redistributions of source code must retain the above copyright 33 | notice, this list of conditions and the following disclaimer. 34 | * Redistributions in binary form must reproduce the above 35 | copyright notice, this list of conditions and the following disclaimer 36 | in the documentation and/or other materials provided with the 37 | distribution. 38 | * Neither the name of Google Inc. nor the names of its 39 | contributors may be used to endorse or promote products derived from 40 | this software without specific prior written permission. 41 | 42 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 44 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 45 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 46 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 47 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 48 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 49 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 50 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 52 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gopkgs - List Go packages FAST by using the same implementation as [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) 2 | 3 | [![Travis Build Status](https://travis-ci.org/haya14busa/gopkgs.svg?branch=master)](https://travis-ci.org/haya14busa/gopkgs) 4 | [![Appveyor Build status](https://ci.appveyor.com/api/projects/status/9tr7p8hclfypvwun?svg=true)](https://ci.appveyor.com/project/haya14busa/gopkgs) 5 | [![Releases](https://img.shields.io/github/tag/haya14busa/gopkgs.svg)](https://github.com/haya14busa/gopkgs/releases) 6 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 7 | [![GoDoc](https://godoc.org/github.com/haya14busa/gopkgs?status.svg)](https://godoc.org/github.com/haya14busa/gopkgs) 8 | 9 | gopkgs outputs list of importable Go packages. 10 | 11 | By using the same implementation as [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports), 12 | it's faster than [go list ...](https://golang.org/cmd/go/#hdr-List_packages) and it also has `-f` option. 13 | 14 | gopkgs cares .goimportsignore which was introduced by https://github.com/golang/go/issues/16386 15 | since it uses the same implementation as [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports). 16 | 17 | ![gopkgs_usage.gif (890×542)](https://raw.githubusercontent.com/haya14busa/i/3bdf4c81118c4f0261073a7f3144903623240edc/gopkgs/gopkgs_usage.gif) 18 | Sample usage of gopkgs with other tools like [godoc](https://godoc.org/golang.org/x/tools/cmd/godoc) and filtering tools ([peco](https://github.com/peco/peco)). 19 | 20 | ### Installation 21 | 22 | #### Install Binary from GitHub Releases 23 | 24 | https://github.com/haya14busa/gopkgs/releases 25 | 26 | #### go get 27 | 28 | ``` 29 | go get -u github.com/haya14busa/gopkgs/cmd/gopkgs 30 | ``` 31 | 32 | ### SYNOPSIS 33 | 34 | ``` 35 | $ gopkgs -h 36 | Usage of gopkgs: 37 | -f string 38 | alternate format for the output using the syntax of template package. e.g. {{.Name}};{{ImportPathShort}} 39 | -fullpath 40 | output absolute file path to package directory. ("/usr/lib/go/src/net/http") 41 | -include-name 42 | fill Pkg.Name which can be used with -f flag 43 | -short 44 | output vendorless import path ("net/http", "foo/bar/vendor/a/b") (default true) 45 | 46 | 47 | Use -f to custom the output using template syntax. The struct being passed to template is: 48 | type Pkg struct { 49 | Dir string // absolute file path to Pkg directory ("/usr/lib/go/src/net/http") 50 | ImportPath string // full Pkg import path ("net/http", "foo/bar/vendor/a/b") 51 | ImportPathShort string // vendorless import path ("net/http", "a/b") 52 | 53 | // It can be empty. It's filled only when -include-name flag is true. 54 | Name string // package name ("http") 55 | } 56 | 57 | ``` 58 | 59 | ### Vim 60 | 61 | ![gopkgs_vim.gif (890×542)](https://raw.githubusercontent.com/haya14busa/i/3bdf4c81118c4f0261073a7f3144903623240edc/gopkgs/gopkgs_vim.gif) 62 | 63 | Sample usage of gopkgs in Vim: open godoc and import with [vim-go](https://github.com/fatih/vim-go) and [fzf](https://github.com/junegunn/fzf) 64 | 65 | ```vim 66 | augroup gopkgs 67 | autocmd! 68 | autocmd FileType go command! -buffer Import exe 'GoImport' fzf#run({'source': 'gopkgs'})[0] 69 | autocmd FileType go command! -buffer Doc exe 'GoDoc' fzf#run({'source': 'gopkgs'})[0] 70 | augroup END 71 | ``` 72 | 73 | Above Vim script is just a sample and isn't robust. I'm planning to create or contribute Vim plugins to include the same feature. 74 | 75 | - https://github.com/rhysd/unite-go-import.vim 76 | - [unite.vim](https://github.com/Shougo/unite.vim) (and 77 | [vim-go](https://github.com/fatih/vim-go)) intergration for importing 78 | package or opening godoc in Vim. 79 | 80 | ### LICENSE 81 | 82 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 83 | 84 | #### under [x/tools/imports/](x/tools/imports/) directory 85 | 86 | ``` 87 | Copyright (c) 2009 The Go Authors. All rights reserved. 88 | 89 | Redistribution and use in source and binary forms, with or without 90 | modification, are permitted provided that the following conditions are 91 | met: 92 | 93 | * Redistributions of source code must retain the above copyright 94 | notice, this list of conditions and the following disclaimer. 95 | * Redistributions in binary form must reproduce the above 96 | copyright notice, this list of conditions and the following disclaimer 97 | in the documentation and/or other materials provided with the 98 | distribution. 99 | * Neither the name of Google Inc. nor the names of its 100 | contributors may be used to endorse or promote products derived from 101 | this software without specific prior written permission. 102 | 103 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 104 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 105 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 106 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 107 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 108 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 109 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 110 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 111 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 112 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 113 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 114 | ``` 115 | 116 | gopkgs copied and modified https://godoc.org/golang.org/x/tools/imports to export some interface 117 | in accordance with LICENSE. 118 | 119 | If The Go Authors provide goimports's directory scanning code as a library, I plan to use it. 120 | ref: https://github.com/golang/go/issues/16427 121 | 122 | ### :bird: Author 123 | haya14busa (https://github.com/haya14busa) 124 | 125 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | clone_folder: c:\gopath\src\github.com\haya14busa\gopkgs 4 | 5 | environment: 6 | GOPATH: c:\gopath 7 | 8 | install: 9 | - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% 10 | - go version 11 | - go env 12 | - go get -t -d -v ./... 13 | 14 | build_script: 15 | - go build -v 16 | 17 | test_script: 18 | - go test -v -race ./... 19 | -------------------------------------------------------------------------------- /cmd/gopkgs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "sort" 9 | "strings" 10 | "text/template" 11 | 12 | "github.com/haya14busa/gopkgs" 13 | ) 14 | 15 | var ( 16 | fullpath = flag.Bool("fullpath", false, `output absolute file path to package directory. ("/usr/lib/go/src/net/http")`) 17 | short = flag.Bool("short", true, `output vendorless import path ("net/http", "foo/bar/vendor/a/b")`) 18 | f = flag.String("f", "", "alternate format for the output using the syntax of template package. e.g. {{.Name}};{{ImportPathShort}}") 19 | includeName = flag.Bool("include-name", false, "fill Pkg.Name which can be used with -f flag") 20 | ) 21 | 22 | var usageInfo = ` 23 | Use -f to custom the output using template syntax. The struct being passed to template is: 24 | type Pkg struct { 25 | Dir string // absolute file path to Pkg directory ("/usr/lib/go/src/net/http") 26 | ImportPath string // full Pkg import path ("net/http", "foo/bar/vendor/a/b") 27 | ImportPathShort string // vendorless import path ("net/http", "a/b") 28 | 29 | // It can be empty. It's filled only when -include-name flag is true. 30 | Name string // package name ("http") 31 | } 32 | ` 33 | 34 | func usage() { 35 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 36 | flag.PrintDefaults() 37 | fmt.Fprintln(os.Stderr, usageInfo) 38 | } 39 | 40 | func init() { 41 | flag.Usage = usage 42 | } 43 | 44 | type ByPath []*gopkgs.Pkg 45 | 46 | func (pkgs ByPath) Len() int { 47 | return len(pkgs) 48 | } 49 | func (pkgs ByPath) Swap(i, j int) { 50 | pkgs[i], pkgs[j] = pkgs[j], pkgs[i] 51 | } 52 | func (pkgs ByPath) Less(i, j int) bool { 53 | return strings.Compare(pkgs[i].ImportPath, pkgs[j].ImportPath) < 0 54 | } 55 | 56 | type ByShortPath []*gopkgs.Pkg 57 | 58 | func (pkgs ByShortPath) Len() int { 59 | return len(pkgs) 60 | } 61 | func (pkgs ByShortPath) Swap(i, j int) { 62 | pkgs[i], pkgs[j] = pkgs[j], pkgs[i] 63 | } 64 | func (pkgs ByShortPath) Less(i, j int) bool { 65 | return strings.Compare(pkgs[i].ImportPathShort, pkgs[j].ImportPathShort) < 0 66 | } 67 | 68 | type ByFullPath []*gopkgs.Pkg 69 | 70 | func (pkgs ByFullPath) Len() int { 71 | return len(pkgs) 72 | } 73 | func (pkgs ByFullPath) Swap(i, j int) { 74 | pkgs[i], pkgs[j] = pkgs[j], pkgs[i] 75 | } 76 | func (pkgs ByFullPath) Less(i, j int) bool { 77 | return strings.Compare(pkgs[i].Dir, pkgs[j].Dir) < 0 78 | } 79 | 80 | func uniq(s []*gopkgs.Pkg, f func(*gopkgs.Pkg) string) []*gopkgs.Pkg { 81 | l := len(s) 82 | if l == 0 || l == 1 { 83 | return s 84 | } 85 | 86 | u := make([]*gopkgs.Pkg, 0, l) 87 | prev := f(s[0]) 88 | for _, p := range s[1:] { 89 | cur := f(p) 90 | if prev == cur { 91 | continue 92 | } 93 | u = append(u, p) 94 | prev = cur 95 | } 96 | 97 | return u 98 | } 99 | 100 | func main() { 101 | flag.Parse() 102 | 103 | if len(flag.Args()) > 0 { 104 | flag.Usage() 105 | os.Exit(2) 106 | } 107 | 108 | tplFormat := "{{.ImportPath}}" 109 | if *f != "" { 110 | tplFormat = *f 111 | } else if *fullpath { 112 | tplFormat = "{{.Dir}}" 113 | } else if *short { 114 | tplFormat = "{{.ImportPathShort}}" 115 | } 116 | 117 | tpl, err := template.New("out").Parse(tplFormat) 118 | if err != nil { 119 | fmt.Fprintln(os.Stderr) 120 | os.Exit(2) 121 | } 122 | 123 | w := bufio.NewWriter(os.Stdout) 124 | defer w.Flush() 125 | 126 | opt := gopkgs.DefaultOption() 127 | opt.IncludeName = *includeName 128 | pkgs := gopkgs.Packages(opt) 129 | 130 | if *fullpath { 131 | sort.Sort(ByFullPath(pkgs)) 132 | // Fullpaths is already unique 133 | } else if *short { 134 | sort.Sort(ByShortPath(pkgs)) 135 | pkgs = uniq(pkgs, func(p *gopkgs.Pkg) string { return p.ImportPathShort }) 136 | } else { 137 | sort.Sort(ByPath(pkgs)) 138 | pkgs = uniq(pkgs, func(p *gopkgs.Pkg) string { return p.ImportPath }) 139 | } 140 | 141 | for _, pkg := range pkgs { 142 | tpl.Execute(w, pkg) 143 | fmt.Fprintln(w) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /gopkgs.go: -------------------------------------------------------------------------------- 1 | package gopkgs 2 | 3 | import ( 4 | "fmt" 5 | "go/parser" 6 | "go/token" 7 | "os" 8 | "strings" 9 | 10 | "github.com/haya14busa/gopkgs/x/tools/imports" 11 | ) 12 | 13 | // Pkg represents exported go packages. 14 | // It's based on x/tools/imports.Pkg. 15 | type Pkg struct { 16 | Dir string // absolute file path to Pkg directory ("/usr/lib/go/src/net/http") 17 | ImportPath string // full Pkg import path ("net/http", "foo/bar/vendor/a/b") 18 | ImportPathShort string // vendorless import path ("net/http", "a/b") 19 | 20 | // It can be empty. It's filled only when Option.IncludeName is true. 21 | Name string // package name ("http") 22 | } 23 | 24 | type Option struct { 25 | // Fill package name in Pkg struct if it's true. Note that it needs to parse 26 | // package directory to get package name and it takes some costs. 27 | IncludeName bool 28 | } 29 | 30 | func DefaultOption() *Option { 31 | return &Option{ 32 | IncludeName: false, 33 | } 34 | } 35 | 36 | // Packages returns all importable Go packages. 37 | // Packages uses [golang.org/x/tools/cmd/goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) implementation internally, so it's fast. 38 | // e.g. it cares .goimportsignore 39 | func Packages(opt *Option) []*Pkg { 40 | gp := imports.GoPath() 41 | pkgs := make([]*Pkg, len(gp)) 42 | i := 0 43 | for _, p := range gp { 44 | pkgs[i] = copyPkg(p) 45 | if opt.IncludeName { 46 | name, _ := packageName(p.Dir) 47 | if name != "" { 48 | pkgs[i].Name = name 49 | } 50 | } 51 | i++ 52 | } 53 | return pkgs 54 | } 55 | 56 | func copyPkg(p *imports.Pkg) *Pkg { 57 | return &Pkg{ 58 | Dir: p.Dir, 59 | ImportPath: p.ImportPath, 60 | ImportPathShort: p.ImportPathShort, 61 | } 62 | } 63 | 64 | func packageName(dir string) (string, error) { 65 | fset := token.NewFileSet() 66 | pkgs, err := parser.ParseDir(fset, dir, notGoTestFile, parser.PackageClauseOnly) 67 | if err != nil { 68 | return "", err 69 | } 70 | for name := range pkgs { 71 | return name, nil 72 | } 73 | return "", fmt.Errorf("package not found in %s", dir) 74 | } 75 | 76 | func notGoTestFile(f os.FileInfo) bool { 77 | return !strings.HasSuffix(f.Name(), "_test.go") 78 | } 79 | -------------------------------------------------------------------------------- /gopkgs_test.go: -------------------------------------------------------------------------------- 1 | package gopkgs 2 | 3 | import "testing" 4 | 5 | func TestPackages(t *testing.T) { 6 | pkgs := Packages(&Option{IncludeName: false}) 7 | if len(pkgs) == 0 { 8 | t.Fatal("len(Packages()) == 0") 9 | } 10 | if pkgs[0].Name != "" { 11 | t.Errorf("Package.Name is not empty when IncludeName==false") 12 | } 13 | } 14 | 15 | func TestPackages_IncludeName(t *testing.T) { 16 | pkgs := Packages(&Option{IncludeName: true}) 17 | if len(pkgs) == 0 { 18 | t.Fatal("len(Packages()) == 0") 19 | } 20 | if pkgs[0].Name == "" { 21 | t.Errorf("Package.Name is empty when IncludeName==true") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /x/tools/imports/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /x/tools/imports/export.go: -------------------------------------------------------------------------------- 1 | package imports 2 | 3 | import "sync" 4 | 5 | // export.go exports some type and func of golang.org/x/tools/imports 6 | 7 | var ( 8 | exportedGoPath map[string]*Pkg 9 | exportedGoPathMu sync.RWMutex 10 | ) 11 | 12 | // GoPath returns all importable packages (abs dir path => *Pkg). 13 | func GoPath() map[string]*Pkg { 14 | exportedGoPathMu.Lock() 15 | defer exportedGoPathMu.Unlock() 16 | if exportedGoPath != nil { 17 | return exportedGoPath 18 | } 19 | populateIgnoreOnce.Do(populateIgnore) 20 | scanGoRootOnce.Do(scanGoRoot) // async 21 | scanGoPathOnce.Do(scanGoPath) 22 | <-scanGoRootDone 23 | dirScanMu.Lock() 24 | defer dirScanMu.Unlock() 25 | exportedGoPath = exportDirScan(dirScan) 26 | return exportedGoPath 27 | } 28 | 29 | func exportDirScan(ds map[string]*pkg) map[string]*Pkg { 30 | r := make(map[string]*Pkg) 31 | for path, pkg := range ds { 32 | r[path] = exportPkg(pkg) 33 | } 34 | return r 35 | } 36 | 37 | // Pkg represents exported type of pkg. 38 | type Pkg struct { 39 | Dir string // absolute file path to Pkg directory ("/usr/lib/go/src/net/http") 40 | ImportPath string // full Pkg import path ("net/http", "foo/bar/vendor/a/b") 41 | ImportPathShort string // vendorless import path ("net/http", "a/b") 42 | } 43 | 44 | func exportPkg(p *pkg) *Pkg { 45 | return &Pkg{Dir: p.dir, ImportPath: p.importPath, ImportPathShort: p.importPathShort} 46 | } 47 | -------------------------------------------------------------------------------- /x/tools/imports/export_test.go: -------------------------------------------------------------------------------- 1 | package imports 2 | 3 | import ( 4 | "testing" 5 | 6 | "regexp" 7 | ) 8 | 9 | var githubReg = regexp.MustCompile(`^github\.com/`) 10 | 11 | func TestGoPath(t *testing.T) { 12 | ds := GoPath() 13 | GoPath() // confirm GoPath() can be called more than once 14 | foundStdLib := false 15 | foundGOPATH := false 16 | for _, pkg := range ds { 17 | if pkg.ImportPath == "net/http" { 18 | foundStdLib = true 19 | } 20 | if githubReg.MatchString(pkg.ImportPath) { 21 | foundGOPATH = true 22 | } 23 | if foundStdLib && foundGOPATH { 24 | break 25 | } 26 | } 27 | if !foundStdLib { 28 | t.Error("Standard Library net/http not found") 29 | } 30 | if !foundGOPATH { 31 | t.Error("GOPAH library which starts with github.com/ not found") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /x/tools/imports/fastwalk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // A faster implementation of filepath.Walk. 6 | // 7 | // filepath.Walk's design necessarily calls os.Lstat on each file, 8 | // even if the caller needs less info. And goimports only need to know 9 | // the type of each file. The kernel interface provides the type in 10 | // the Readdir call but the standard library ignored it. 11 | // fastwalk_unix.go contains a fork of the syscall routines. 12 | // 13 | // See golang.org/issue/16399 14 | 15 | package imports 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | "path/filepath" 21 | "runtime" 22 | ) 23 | 24 | // traverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir. 25 | var traverseLink = errors.New("traverse symlink, assuming target is a directory") 26 | 27 | // fastWalk walks the file tree rooted at root, calling walkFn for 28 | // each file or directory in the tree, including root. 29 | // 30 | // If fastWalk returns filepath.SkipDir, the directory is skipped. 31 | // 32 | // Unlike filepath.Walk: 33 | // * file stat calls must be done by the user. 34 | // The only provided metadata is the file type, which does not include 35 | // any permission bits. 36 | // * multiple goroutines stat the filesystem concurrently. The provided 37 | // walkFn must be safe for concurrent use. 38 | // * fastWalk can follow symlinks if walkFn returns the traverseLink 39 | // sentinel error. It is the walkFn's responsibility to prevent 40 | // fastWalk from going into symlink cycles. 41 | func fastWalk(root string, walkFn func(path string, typ os.FileMode) error) error { 42 | // TODO(bradfitz): make numWorkers configurable? We used a 43 | // minimum of 4 to give the kernel more info about multiple 44 | // things we want, in hopes its I/O scheduling can take 45 | // advantage of that. Hopefully most are in cache. Maybe 4 is 46 | // even too low of a minimum. Profile more. 47 | numWorkers := 4 48 | if n := runtime.NumCPU(); n > numWorkers { 49 | numWorkers = n 50 | } 51 | w := &walker{ 52 | fn: walkFn, 53 | enqueuec: make(chan walkItem, numWorkers), // buffered for performance 54 | workc: make(chan walkItem, numWorkers), // buffered for performance 55 | donec: make(chan struct{}), 56 | 57 | // buffered for correctness & not leaking goroutines: 58 | resc: make(chan error, numWorkers), 59 | } 60 | defer close(w.donec) 61 | // TODO(bradfitz): start the workers as needed? maybe not worth it. 62 | for i := 0; i < numWorkers; i++ { 63 | go w.doWork() 64 | } 65 | todo := []walkItem{{dir: root}} 66 | out := 0 67 | for { 68 | workc := w.workc 69 | var workItem walkItem 70 | if len(todo) == 0 { 71 | workc = nil 72 | } else { 73 | workItem = todo[len(todo)-1] 74 | } 75 | select { 76 | case workc <- workItem: 77 | todo = todo[:len(todo)-1] 78 | out++ 79 | case it := <-w.enqueuec: 80 | todo = append(todo, it) 81 | case err := <-w.resc: 82 | out-- 83 | if err != nil { 84 | return err 85 | } 86 | if out == 0 && len(todo) == 0 { 87 | // It's safe to quit here, as long as the buffered 88 | // enqueue channel isn't also readable, which might 89 | // happen if the worker sends both another unit of 90 | // work and its result before the other select was 91 | // scheduled and both w.resc and w.enqueuec were 92 | // readable. 93 | select { 94 | case it := <-w.enqueuec: 95 | todo = append(todo, it) 96 | default: 97 | return nil 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | // doWork reads directories as instructed (via workc) and runs the 105 | // user's callback function. 106 | func (w *walker) doWork() { 107 | for { 108 | select { 109 | case <-w.donec: 110 | return 111 | case it := <-w.workc: 112 | w.resc <- w.walk(it.dir, !it.callbackDone) 113 | } 114 | } 115 | } 116 | 117 | type walker struct { 118 | fn func(path string, typ os.FileMode) error 119 | 120 | donec chan struct{} // closed on fastWalk's return 121 | workc chan walkItem // to workers 122 | enqueuec chan walkItem // from workers 123 | resc chan error // from workers 124 | } 125 | 126 | type walkItem struct { 127 | dir string 128 | callbackDone bool // callback already called; don't do it again 129 | } 130 | 131 | func (w *walker) enqueue(it walkItem) { 132 | select { 133 | case w.enqueuec <- it: 134 | case <-w.donec: 135 | } 136 | } 137 | 138 | func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { 139 | joined := dirName + string(os.PathSeparator) + baseName 140 | if typ == os.ModeDir { 141 | w.enqueue(walkItem{dir: joined}) 142 | return nil 143 | } 144 | 145 | err := w.fn(joined, typ) 146 | if typ == os.ModeSymlink { 147 | if err == traverseLink { 148 | // Set callbackDone so we don't call it twice for both the 149 | // symlink-as-symlink and the symlink-as-directory later: 150 | w.enqueue(walkItem{dir: joined, callbackDone: true}) 151 | return nil 152 | } 153 | if err == filepath.SkipDir { 154 | // Permit SkipDir on symlinks too. 155 | return nil 156 | } 157 | } 158 | return err 159 | } 160 | func (w *walker) walk(root string, runUserCallback bool) error { 161 | if runUserCallback { 162 | err := w.fn(root, os.ModeDir) 163 | if err == filepath.SkipDir { 164 | return nil 165 | } 166 | if err != nil { 167 | return err 168 | } 169 | } 170 | 171 | return readDir(root, w.onDirEnt) 172 | } 173 | -------------------------------------------------------------------------------- /x/tools/imports/fastwalk_dirent_fileno.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build freebsd openbsd netbsd 6 | 7 | package imports 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Fileno) 13 | } 14 | -------------------------------------------------------------------------------- /x/tools/imports/fastwalk_dirent_ino.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,!appengine darwin 6 | 7 | package imports 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Ino) 13 | } 14 | -------------------------------------------------------------------------------- /x/tools/imports/fastwalk_portable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd 6 | 7 | package imports 8 | 9 | import ( 10 | "io/ioutil" 11 | "os" 12 | ) 13 | 14 | // readDir calls fn for each directory entry in dirName. 15 | // It does not descend into directories or follow symlinks. 16 | // If fn returns a non-nil error, readDir returns with that error 17 | // immediately. 18 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { 19 | fis, err := ioutil.ReadDir(dirName) 20 | if err != nil { 21 | return err 22 | } 23 | for _, fi := range fis { 24 | if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /x/tools/imports/fastwalk_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,!appengine darwin freebsd openbsd netbsd 6 | 7 | package imports 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "os" 13 | "syscall" 14 | "unsafe" 15 | ) 16 | 17 | const blockSize = 8 << 10 18 | 19 | // unknownFileMode is a sentinel (and bogus) os.FileMode 20 | // value used to represent a syscall.DT_UNKNOWN Dirent.Type. 21 | const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice 22 | 23 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { 24 | fd, err := syscall.Open(dirName, 0, 0) 25 | if err != nil { 26 | return err 27 | } 28 | defer syscall.Close(fd) 29 | 30 | // The buffer must be at least a block long. 31 | buf := make([]byte, blockSize) // stack-allocated; doesn't escape 32 | bufp := 0 // starting read position in buf 33 | nbuf := 0 // end valid data in buf 34 | for { 35 | if bufp >= nbuf { 36 | bufp = 0 37 | nbuf, err = syscall.ReadDirent(fd, buf) 38 | if err != nil { 39 | return os.NewSyscallError("readdirent", err) 40 | } 41 | if nbuf <= 0 { 42 | return nil 43 | } 44 | } 45 | consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) 46 | bufp += consumed 47 | if name == "" || name == "." || name == ".." { 48 | continue 49 | } 50 | // Fallback for filesystems (like old XFS) that don't 51 | // support Dirent.Type and have DT_UNKNOWN (0) there 52 | // instead. 53 | if typ == unknownFileMode { 54 | fi, err := os.Lstat(dirName + "/" + name) 55 | if err != nil { 56 | // It got deleted in the meantime. 57 | if os.IsNotExist(err) { 58 | continue 59 | } 60 | return err 61 | } 62 | typ = fi.Mode() & os.ModeType 63 | } 64 | if err := fn(dirName, name, typ); err != nil { 65 | return err 66 | } 67 | } 68 | } 69 | 70 | func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { 71 | // golang.org/issue/15653 72 | dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0])) 73 | if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { 74 | panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) 75 | } 76 | if len(buf) < int(dirent.Reclen) { 77 | panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) 78 | } 79 | consumed = int(dirent.Reclen) 80 | if direntInode(dirent) == 0 { // File absent in directory. 81 | return 82 | } 83 | switch dirent.Type { 84 | case syscall.DT_REG: 85 | typ = 0 86 | case syscall.DT_DIR: 87 | typ = os.ModeDir 88 | case syscall.DT_LNK: 89 | typ = os.ModeSymlink 90 | case syscall.DT_BLK: 91 | typ = os.ModeDevice 92 | case syscall.DT_FIFO: 93 | typ = os.ModeNamedPipe 94 | case syscall.DT_SOCK: 95 | typ = os.ModeSocket 96 | case syscall.DT_UNKNOWN: 97 | typ = unknownFileMode 98 | default: 99 | // Skip weird things. 100 | // It's probably a DT_WHT (http://lwn.net/Articles/325369/) 101 | // or something. Revisit if/when this package is moved outside 102 | // of goimports. goimports only cares about regular files, 103 | // symlinks, and directories. 104 | return 105 | } 106 | 107 | nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) 108 | nameLen := bytes.IndexByte(nameBuf[:], 0) 109 | if nameLen < 0 { 110 | panic("failed to find terminating 0 byte in dirent") 111 | } 112 | 113 | // Special cases for common things: 114 | if nameLen == 1 && nameBuf[0] == '.' { 115 | name = "." 116 | } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { 117 | name = ".." 118 | } else { 119 | name = string(nameBuf[:nameLen]) 120 | } 121 | return 122 | } 123 | -------------------------------------------------------------------------------- /x/tools/imports/fix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package imports 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "fmt" 11 | "go/ast" 12 | "go/build" 13 | "go/parser" 14 | "go/token" 15 | "io/ioutil" 16 | "log" 17 | "os" 18 | "path" 19 | "path/filepath" 20 | "sort" 21 | "strings" 22 | "sync" 23 | 24 | "golang.org/x/tools/go/ast/astutil" 25 | ) 26 | 27 | // Debug controls verbose logging. 28 | var Debug = false 29 | 30 | var ( 31 | inTests = false // set true by fix_test.go; if false, no need to use testMu 32 | testMu sync.RWMutex // guards globals reset by tests; used only if inTests 33 | ) 34 | 35 | // If set, LocalPrefix instructs Process to sort import paths with the given 36 | // prefix into another group after 3rd-party packages. 37 | var LocalPrefix string 38 | 39 | // importToGroup is a list of functions which map from an import path to 40 | // a group number. 41 | var importToGroup = []func(importPath string) (num int, ok bool){ 42 | func(importPath string) (num int, ok bool) { 43 | if LocalPrefix != "" && strings.HasPrefix(importPath, LocalPrefix) { 44 | return 3, true 45 | } 46 | return 47 | }, 48 | func(importPath string) (num int, ok bool) { 49 | if strings.HasPrefix(importPath, "appengine") { 50 | return 2, true 51 | } 52 | return 53 | }, 54 | func(importPath string) (num int, ok bool) { 55 | if strings.Contains(importPath, ".") { 56 | return 1, true 57 | } 58 | return 59 | }, 60 | } 61 | 62 | func importGroup(importPath string) int { 63 | for _, fn := range importToGroup { 64 | if n, ok := fn(importPath); ok { 65 | return n 66 | } 67 | } 68 | return 0 69 | } 70 | 71 | // packageInfo is a summary of features found in a package. 72 | type packageInfo struct { 73 | Globals map[string]bool // symbol => true 74 | } 75 | 76 | // dirPackageInfo gets information from other files in the package. 77 | func dirPackageInfo(srcDir, filename string) (*packageInfo, error) { 78 | considerTests := strings.HasSuffix(filename, "_test.go") 79 | 80 | // Handle file from stdin 81 | if _, err := os.Stat(filename); err != nil { 82 | if os.IsNotExist(err) { 83 | return &packageInfo{}, nil 84 | } 85 | return nil, err 86 | } 87 | 88 | fileBase := filepath.Base(filename) 89 | packageFileInfos, err := ioutil.ReadDir(srcDir) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | info := &packageInfo{Globals: make(map[string]bool)} 95 | for _, fi := range packageFileInfos { 96 | if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { 97 | continue 98 | } 99 | if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { 100 | continue 101 | } 102 | 103 | fileSet := token.NewFileSet() 104 | root, err := parser.ParseFile(fileSet, filepath.Join(srcDir, fi.Name()), nil, 0) 105 | if err != nil { 106 | continue 107 | } 108 | 109 | for _, decl := range root.Decls { 110 | genDecl, ok := decl.(*ast.GenDecl) 111 | if !ok { 112 | continue 113 | } 114 | 115 | for _, spec := range genDecl.Specs { 116 | valueSpec, ok := spec.(*ast.ValueSpec) 117 | if !ok { 118 | continue 119 | } 120 | info.Globals[valueSpec.Names[0].Name] = true 121 | } 122 | } 123 | } 124 | return info, nil 125 | } 126 | 127 | func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) { 128 | // refs are a set of possible package references currently unsatisfied by imports. 129 | // first key: either base package (e.g. "fmt") or renamed package 130 | // second key: referenced package symbol (e.g. "Println") 131 | refs := make(map[string]map[string]bool) 132 | 133 | // decls are the current package imports. key is base package or renamed package. 134 | decls := make(map[string]*ast.ImportSpec) 135 | 136 | abs, err := filepath.Abs(filename) 137 | if err != nil { 138 | return nil, err 139 | } 140 | srcDir := filepath.Dir(abs) 141 | if Debug { 142 | log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) 143 | } 144 | 145 | var packageInfo *packageInfo 146 | var loadedPackageInfo bool 147 | 148 | // collect potential uses of packages. 149 | var visitor visitFn 150 | visitor = visitFn(func(node ast.Node) ast.Visitor { 151 | if node == nil { 152 | return visitor 153 | } 154 | switch v := node.(type) { 155 | case *ast.ImportSpec: 156 | if v.Name != nil { 157 | decls[v.Name.Name] = v 158 | break 159 | } 160 | ipath := strings.Trim(v.Path.Value, `"`) 161 | if ipath == "C" { 162 | break 163 | } 164 | local := importPathToName(ipath, srcDir) 165 | decls[local] = v 166 | case *ast.SelectorExpr: 167 | xident, ok := v.X.(*ast.Ident) 168 | if !ok { 169 | break 170 | } 171 | if xident.Obj != nil { 172 | // if the parser can resolve it, it's not a package ref 173 | break 174 | } 175 | pkgName := xident.Name 176 | if refs[pkgName] == nil { 177 | refs[pkgName] = make(map[string]bool) 178 | } 179 | if !loadedPackageInfo { 180 | loadedPackageInfo = true 181 | packageInfo, _ = dirPackageInfo(srcDir, filename) 182 | } 183 | if decls[pkgName] == nil && (packageInfo == nil || !packageInfo.Globals[pkgName]) { 184 | refs[pkgName][v.Sel.Name] = true 185 | } 186 | } 187 | return visitor 188 | }) 189 | ast.Walk(visitor, f) 190 | 191 | // Nil out any unused ImportSpecs, to be removed in following passes 192 | unusedImport := map[string]string{} 193 | for pkg, is := range decls { 194 | if refs[pkg] == nil && pkg != "_" && pkg != "." { 195 | name := "" 196 | if is.Name != nil { 197 | name = is.Name.Name 198 | } 199 | unusedImport[strings.Trim(is.Path.Value, `"`)] = name 200 | } 201 | } 202 | for ipath, name := range unusedImport { 203 | if ipath == "C" { 204 | // Don't remove cgo stuff. 205 | continue 206 | } 207 | astutil.DeleteNamedImport(fset, f, name, ipath) 208 | } 209 | 210 | for pkgName, symbols := range refs { 211 | if len(symbols) == 0 { 212 | // skip over packages already imported 213 | delete(refs, pkgName) 214 | } 215 | } 216 | 217 | // Search for imports matching potential package references. 218 | searches := 0 219 | type result struct { 220 | ipath string // import path (if err == nil) 221 | name string // optional name to rename import as 222 | err error 223 | } 224 | results := make(chan result) 225 | for pkgName, symbols := range refs { 226 | go func(pkgName string, symbols map[string]bool) { 227 | ipath, rename, err := findImport(pkgName, symbols, filename) 228 | r := result{ipath: ipath, err: err} 229 | if rename { 230 | r.name = pkgName 231 | } 232 | results <- r 233 | }(pkgName, symbols) 234 | searches++ 235 | } 236 | for i := 0; i < searches; i++ { 237 | result := <-results 238 | if result.err != nil { 239 | return nil, result.err 240 | } 241 | if result.ipath != "" { 242 | if result.name != "" { 243 | astutil.AddNamedImport(fset, f, result.name, result.ipath) 244 | } else { 245 | astutil.AddImport(fset, f, result.ipath) 246 | } 247 | added = append(added, result.ipath) 248 | } 249 | } 250 | 251 | return added, nil 252 | } 253 | 254 | // importPathToName returns the package name for the given import path. 255 | var importPathToName func(importPath, srcDir string) (packageName string) = importPathToNameGoPath 256 | 257 | // importPathToNameBasic assumes the package name is the base of import path. 258 | func importPathToNameBasic(importPath, srcDir string) (packageName string) { 259 | return path.Base(importPath) 260 | } 261 | 262 | // importPathToNameGoPath finds out the actual package name, as declared in its .go files. 263 | // If there's a problem, it falls back to using importPathToNameBasic. 264 | func importPathToNameGoPath(importPath, srcDir string) (packageName string) { 265 | // Fast path for standard library without going to disk. 266 | if pkg, ok := stdImportPackage[importPath]; ok { 267 | return pkg 268 | } 269 | 270 | pkgName, err := importPathToNameGoPathParse(importPath, srcDir) 271 | if Debug { 272 | log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err) 273 | } 274 | if err == nil { 275 | return pkgName 276 | } 277 | return importPathToNameBasic(importPath, srcDir) 278 | } 279 | 280 | // importPathToNameGoPathParse is a faster version of build.Import if 281 | // the only thing desired is the package name. It uses build.FindOnly 282 | // to find the directory and then only parses one file in the package, 283 | // trusting that the files in the directory are consistent. 284 | func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) { 285 | buildPkg, err := build.Import(importPath, srcDir, build.FindOnly) 286 | if err != nil { 287 | return "", err 288 | } 289 | d, err := os.Open(buildPkg.Dir) 290 | if err != nil { 291 | return "", err 292 | } 293 | names, err := d.Readdirnames(-1) 294 | d.Close() 295 | if err != nil { 296 | return "", err 297 | } 298 | sort.Strings(names) // to have predictable behavior 299 | var lastErr error 300 | var nfile int 301 | for _, name := range names { 302 | if !strings.HasSuffix(name, ".go") { 303 | continue 304 | } 305 | if strings.HasSuffix(name, "_test.go") { 306 | continue 307 | } 308 | nfile++ 309 | fullFile := filepath.Join(buildPkg.Dir, name) 310 | 311 | fset := token.NewFileSet() 312 | f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) 313 | if err != nil { 314 | lastErr = err 315 | continue 316 | } 317 | pkgName := f.Name.Name 318 | if pkgName == "documentation" { 319 | // Special case from go/build.ImportDir, not 320 | // handled by ctx.MatchFile. 321 | continue 322 | } 323 | if pkgName == "main" { 324 | // Also skip package main, assuming it's a +build ignore generator or example. 325 | // Since you can't import a package main anyway, there's no harm here. 326 | continue 327 | } 328 | return pkgName, nil 329 | } 330 | if lastErr != nil { 331 | return "", lastErr 332 | } 333 | return "", fmt.Errorf("no importable package found in %d Go files", nfile) 334 | } 335 | 336 | var stdImportPackage = map[string]string{} // "net/http" => "http" 337 | 338 | func init() { 339 | // Nothing in the standard library has a package name not 340 | // matching its import base name. 341 | for _, pkg := range stdlib { 342 | if _, ok := stdImportPackage[pkg]; !ok { 343 | stdImportPackage[pkg] = path.Base(pkg) 344 | } 345 | } 346 | } 347 | 348 | // Directory-scanning state. 349 | var ( 350 | // scanGoRootOnce guards calling scanGoRoot (for $GOROOT) 351 | scanGoRootOnce sync.Once 352 | // scanGoPathOnce guards calling scanGoPath (for $GOPATH) 353 | scanGoPathOnce sync.Once 354 | 355 | // populateIgnoreOnce guards calling populateIgnore 356 | populateIgnoreOnce sync.Once 357 | ignoredDirs []os.FileInfo 358 | 359 | dirScanMu sync.RWMutex 360 | dirScan map[string]*pkg // abs dir path => *pkg 361 | ) 362 | 363 | type pkg struct { 364 | dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") 365 | importPath string // full pkg import path ("net/http", "foo/bar/vendor/a/b") 366 | importPathShort string // vendorless import path ("net/http", "a/b") 367 | } 368 | 369 | // byImportPathShortLength sorts by the short import path length, breaking ties on the 370 | // import string itself. 371 | type byImportPathShortLength []*pkg 372 | 373 | func (s byImportPathShortLength) Len() int { return len(s) } 374 | func (s byImportPathShortLength) Less(i, j int) bool { 375 | vi, vj := s[i].importPathShort, s[j].importPathShort 376 | return len(vi) < len(vj) || (len(vi) == len(vj) && vi < vj) 377 | 378 | } 379 | func (s byImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 380 | 381 | // gate is a semaphore for limiting concurrency. 382 | type gate chan struct{} 383 | 384 | func (g gate) enter() { g <- struct{}{} } 385 | func (g gate) leave() { <-g } 386 | 387 | var visitedSymlinks struct { 388 | sync.Mutex 389 | m map[string]struct{} 390 | } 391 | 392 | // guarded by populateIgnoreOnce; populates ignoredDirs. 393 | func populateIgnore() { 394 | for _, srcDir := range build.Default.SrcDirs() { 395 | if srcDir == filepath.Join(build.Default.GOROOT, "src") { 396 | continue 397 | } 398 | populateIgnoredDirs(srcDir) 399 | } 400 | } 401 | 402 | // populateIgnoredDirs reads an optional config file at /.goimportsignore 403 | // of relative directories to ignore when scanning for go files. 404 | // The provided path is one of the $GOPATH entries with "src" appended. 405 | func populateIgnoredDirs(path string) { 406 | file := filepath.Join(path, ".goimportsignore") 407 | slurp, err := ioutil.ReadFile(file) 408 | if Debug { 409 | if err != nil { 410 | log.Print(err) 411 | } else { 412 | log.Printf("Read %s", file) 413 | } 414 | } 415 | if err != nil { 416 | return 417 | } 418 | bs := bufio.NewScanner(bytes.NewReader(slurp)) 419 | for bs.Scan() { 420 | line := strings.TrimSpace(bs.Text()) 421 | if line == "" || strings.HasPrefix(line, "#") { 422 | continue 423 | } 424 | full := filepath.Join(path, line) 425 | if fi, err := os.Stat(full); err == nil { 426 | ignoredDirs = append(ignoredDirs, fi) 427 | if Debug { 428 | log.Printf("Directory added to ignore list: %s", full) 429 | } 430 | } else if Debug { 431 | log.Printf("Error statting entry in .goimportsignore: %v", err) 432 | } 433 | } 434 | } 435 | 436 | func skipDir(fi os.FileInfo) bool { 437 | for _, ignoredDir := range ignoredDirs { 438 | if os.SameFile(fi, ignoredDir) { 439 | return true 440 | } 441 | } 442 | return false 443 | } 444 | 445 | // shouldTraverse reports whether the symlink fi should, found in dir, 446 | // should be followed. It makes sure symlinks were never visited 447 | // before to avoid symlink loops. 448 | func shouldTraverse(dir string, fi os.FileInfo) bool { 449 | path := filepath.Join(dir, fi.Name()) 450 | target, err := filepath.EvalSymlinks(path) 451 | if err != nil { 452 | if !os.IsNotExist(err) { 453 | fmt.Fprintln(os.Stderr, err) 454 | } 455 | return false 456 | } 457 | ts, err := os.Stat(target) 458 | if err != nil { 459 | fmt.Fprintln(os.Stderr, err) 460 | return false 461 | } 462 | if !ts.IsDir() { 463 | return false 464 | } 465 | if skipDir(ts) { 466 | return false 467 | } 468 | 469 | realParent, err := filepath.EvalSymlinks(dir) 470 | if err != nil { 471 | fmt.Fprint(os.Stderr, err) 472 | return false 473 | } 474 | realPath := filepath.Join(realParent, fi.Name()) 475 | visitedSymlinks.Lock() 476 | defer visitedSymlinks.Unlock() 477 | if visitedSymlinks.m == nil { 478 | visitedSymlinks.m = make(map[string]struct{}) 479 | } 480 | if _, ok := visitedSymlinks.m[realPath]; ok { 481 | return false 482 | } 483 | visitedSymlinks.m[realPath] = struct{}{} 484 | return true 485 | } 486 | 487 | var testHookScanDir = func(dir string) {} 488 | 489 | var scanGoRootDone = make(chan struct{}) // closed when scanGoRoot is done 490 | 491 | func scanGoRoot() { 492 | go func() { 493 | scanGoDirs(true) 494 | close(scanGoRootDone) 495 | }() 496 | } 497 | 498 | func scanGoPath() { scanGoDirs(false) } 499 | 500 | func scanGoDirs(goRoot bool) { 501 | if Debug { 502 | which := "$GOROOT" 503 | if !goRoot { 504 | which = "$GOPATH" 505 | } 506 | log.Printf("scanning " + which) 507 | defer log.Printf("scanned " + which) 508 | } 509 | dirScanMu.Lock() 510 | if dirScan == nil { 511 | dirScan = make(map[string]*pkg) 512 | } 513 | dirScanMu.Unlock() 514 | 515 | for _, srcDir := range build.Default.SrcDirs() { 516 | isGoroot := srcDir == filepath.Join(build.Default.GOROOT, "src") 517 | if isGoroot != goRoot { 518 | continue 519 | } 520 | testHookScanDir(srcDir) 521 | walkFn := func(path string, typ os.FileMode) error { 522 | dir := filepath.Dir(path) 523 | if typ.IsRegular() { 524 | if dir == srcDir { 525 | // Doesn't make sense to have regular files 526 | // directly in your $GOPATH/src or $GOROOT/src. 527 | return nil 528 | } 529 | if !strings.HasSuffix(path, ".go") { 530 | return nil 531 | } 532 | dirScanMu.Lock() 533 | if _, dup := dirScan[dir]; !dup { 534 | importpath := filepath.ToSlash(dir[len(srcDir)+len("/"):]) 535 | dirScan[dir] = &pkg{ 536 | importPath: importpath, 537 | importPathShort: vendorlessImportPath(importpath), 538 | dir: dir, 539 | } 540 | } 541 | dirScanMu.Unlock() 542 | return nil 543 | } 544 | if typ == os.ModeDir { 545 | base := filepath.Base(path) 546 | if base == "" || base[0] == '.' || base[0] == '_' || 547 | base == "testdata" || base == "node_modules" { 548 | return filepath.SkipDir 549 | } 550 | fi, err := os.Lstat(path) 551 | if err == nil && skipDir(fi) { 552 | if Debug { 553 | log.Printf("skipping directory %q under %s", fi.Name(), dir) 554 | } 555 | return filepath.SkipDir 556 | } 557 | return nil 558 | } 559 | if typ == os.ModeSymlink { 560 | base := filepath.Base(path) 561 | if strings.HasPrefix(base, ".#") { 562 | // Emacs noise. 563 | return nil 564 | } 565 | fi, err := os.Lstat(path) 566 | if err != nil { 567 | // Just ignore it. 568 | return nil 569 | } 570 | if shouldTraverse(dir, fi) { 571 | return traverseLink 572 | } 573 | } 574 | return nil 575 | } 576 | if err := fastWalk(srcDir, walkFn); err != nil { 577 | log.Printf("goimports: scanning directory %v: %v", srcDir, err) 578 | } 579 | } 580 | } 581 | 582 | // vendorlessImportPath returns the devendorized version of the provided import path. 583 | // e.g. "foo/bar/vendor/a/b" => "a/b" 584 | func vendorlessImportPath(ipath string) string { 585 | // Devendorize for use in import statement. 586 | if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { 587 | return ipath[i+len("/vendor/"):] 588 | } 589 | if strings.HasPrefix(ipath, "vendor/") { 590 | return ipath[len("vendor/"):] 591 | } 592 | return ipath 593 | } 594 | 595 | // loadExports returns the set of exported symbols in the package at dir. 596 | // It returns nil on error or if the package name in dir does not match expectPackage. 597 | var loadExports func(expectPackage, dir string) map[string]bool = loadExportsGoPath 598 | 599 | func loadExportsGoPath(expectPackage, dir string) map[string]bool { 600 | if Debug { 601 | log.Printf("loading exports in dir %s (seeking package %s)", dir, expectPackage) 602 | } 603 | exports := make(map[string]bool) 604 | 605 | ctx := build.Default 606 | 607 | // ReadDir is like ioutil.ReadDir, but only returns *.go files 608 | // and filters out _test.go files since they're not relevant 609 | // and only slow things down. 610 | ctx.ReadDir = func(dir string) (notTests []os.FileInfo, err error) { 611 | all, err := ioutil.ReadDir(dir) 612 | if err != nil { 613 | return nil, err 614 | } 615 | notTests = all[:0] 616 | for _, fi := range all { 617 | name := fi.Name() 618 | if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { 619 | notTests = append(notTests, fi) 620 | } 621 | } 622 | return notTests, nil 623 | } 624 | 625 | files, err := ctx.ReadDir(dir) 626 | if err != nil { 627 | log.Print(err) 628 | return nil 629 | } 630 | 631 | fset := token.NewFileSet() 632 | 633 | for _, fi := range files { 634 | match, err := ctx.MatchFile(dir, fi.Name()) 635 | if err != nil || !match { 636 | continue 637 | } 638 | fullFile := filepath.Join(dir, fi.Name()) 639 | f, err := parser.ParseFile(fset, fullFile, nil, 0) 640 | if err != nil { 641 | if Debug { 642 | log.Printf("Parsing %s: %v", fullFile, err) 643 | } 644 | return nil 645 | } 646 | pkgName := f.Name.Name 647 | if pkgName == "documentation" { 648 | // Special case from go/build.ImportDir, not 649 | // handled by ctx.MatchFile. 650 | continue 651 | } 652 | if pkgName != expectPackage { 653 | if Debug { 654 | log.Printf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName) 655 | } 656 | return nil 657 | } 658 | for name := range f.Scope.Objects { 659 | if ast.IsExported(name) { 660 | exports[name] = true 661 | } 662 | } 663 | } 664 | 665 | if Debug { 666 | exportList := make([]string, 0, len(exports)) 667 | for k := range exports { 668 | exportList = append(exportList, k) 669 | } 670 | sort.Strings(exportList) 671 | log.Printf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", ")) 672 | } 673 | return exports 674 | } 675 | 676 | // findImport searches for a package with the given symbols. 677 | // If no package is found, findImport returns ("", false, nil) 678 | // 679 | // This is declared as a variable rather than a function so goimports 680 | // can be easily extended by adding a file with an init function. 681 | // 682 | // The rename value tells goimports whether to use the package name as 683 | // a local qualifier in an import. For example, if findImports("pkg", 684 | // "X") returns ("foo/bar", rename=true), then goimports adds the 685 | // import line: 686 | // import pkg "foo/bar" 687 | // to satisfy uses of pkg.X in the file. 688 | var findImport func(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) = findImportGoPath 689 | 690 | // findImportGoPath is the normal implementation of findImport. 691 | // (Some companies have their own internally.) 692 | func findImportGoPath(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) { 693 | if inTests { 694 | testMu.RLock() 695 | defer testMu.RUnlock() 696 | } 697 | 698 | // Fast path for the standard library. 699 | // In the common case we hopefully never have to scan the GOPATH, which can 700 | // be slow with moving disks. 701 | if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok { 702 | return pkg, rename, nil 703 | } 704 | if pkgName == "rand" && symbols["Read"] { 705 | // Special-case rand.Read. 706 | // 707 | // If findImportStdlib didn't find it above, don't go 708 | // searching for it, lest it find and pick math/rand 709 | // in GOROOT (new as of Go 1.6) 710 | // 711 | // crypto/rand is the safer choice. 712 | return "", false, nil 713 | } 714 | 715 | // TODO(sameer): look at the import lines for other Go files in the 716 | // local directory, since the user is likely to import the same packages 717 | // in the current Go file. Return rename=true when the other Go files 718 | // use a renamed package that's also used in the current file. 719 | 720 | // Read all the $GOPATH/src/.goimportsignore files before scanning directories. 721 | populateIgnoreOnce.Do(populateIgnore) 722 | 723 | // Start scanning the $GOROOT asynchronously, then run the 724 | // GOPATH scan synchronously if needed, and then wait for the 725 | // $GOROOT to finish. 726 | // 727 | // TODO(bradfitz): run each $GOPATH entry async. But nobody 728 | // really has more than one anyway, so low priority. 729 | scanGoRootOnce.Do(scanGoRoot) // async 730 | if !fileInDir(filename, build.Default.GOROOT) { 731 | scanGoPathOnce.Do(scanGoPath) // blocking 732 | } 733 | <-scanGoRootDone 734 | 735 | // Find candidate packages, looking only at their directory names first. 736 | var candidates []*pkg 737 | for _, pkg := range dirScan { 738 | if pkgIsCandidate(filename, pkgName, pkg) { 739 | candidates = append(candidates, pkg) 740 | } 741 | } 742 | 743 | // Sort the candidates by their import package length, 744 | // assuming that shorter package names are better than long 745 | // ones. Note that this sorts by the de-vendored name, so 746 | // there's no "penalty" for vendoring. 747 | sort.Sort(byImportPathShortLength(candidates)) 748 | if Debug { 749 | for i, pkg := range candidates { 750 | log.Printf("%s candidate %d/%d: %v", pkgName, i+1, len(candidates), pkg.importPathShort) 751 | } 752 | } 753 | 754 | // Collect exports for packages with matching names. 755 | 756 | done := make(chan struct{}) // closed when we find the answer 757 | defer close(done) 758 | 759 | rescv := make([]chan *pkg, len(candidates)) 760 | for i := range candidates { 761 | rescv[i] = make(chan *pkg) 762 | } 763 | const maxConcurrentPackageImport = 4 764 | loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) 765 | 766 | go func() { 767 | for i, pkg := range candidates { 768 | select { 769 | case loadExportsSem <- struct{}{}: 770 | select { 771 | case <-done: 772 | default: 773 | } 774 | case <-done: 775 | return 776 | } 777 | pkg := pkg 778 | resc := rescv[i] 779 | go func() { 780 | if inTests { 781 | testMu.RLock() 782 | defer testMu.RUnlock() 783 | } 784 | defer func() { <-loadExportsSem }() 785 | exports := loadExports(pkgName, pkg.dir) 786 | 787 | // If it doesn't have the right 788 | // symbols, send nil to mean no match. 789 | for symbol := range symbols { 790 | if !exports[symbol] { 791 | pkg = nil 792 | break 793 | } 794 | } 795 | select { 796 | case resc <- pkg: 797 | case <-done: 798 | } 799 | }() 800 | } 801 | }() 802 | for _, resc := range rescv { 803 | pkg := <-resc 804 | if pkg == nil { 805 | continue 806 | } 807 | // If the package name in the source doesn't match the import path's base, 808 | // return true so the rewriter adds a name (import foo "github.com/bar/go-foo") 809 | needsRename := path.Base(pkg.importPath) != pkgName 810 | return pkg.importPathShort, needsRename, nil 811 | } 812 | return "", false, nil 813 | } 814 | 815 | // pkgIsCandidate reports whether pkg is a candidate for satisfying the 816 | // finding which package pkgIdent in the file named by filename is trying 817 | // to refer to. 818 | // 819 | // This check is purely lexical and is meant to be as fast as possible 820 | // because it's run over all $GOPATH directories to filter out poor 821 | // candidates in order to limit the CPU and I/O later parsing the 822 | // exports in candidate packages. 823 | // 824 | // filename is the file being formatted. 825 | // pkgIdent is the package being searched for, like "client" (if 826 | // searching for "client.New") 827 | func pkgIsCandidate(filename, pkgIdent string, pkg *pkg) bool { 828 | // Check "internal" and "vendor" visibility: 829 | if !canUse(filename, pkg.dir) { 830 | return false 831 | } 832 | 833 | // Speed optimization to minimize disk I/O: 834 | // the last two components on disk must contain the 835 | // package name somewhere. 836 | // 837 | // This permits mismatch naming like directory 838 | // "go-foo" being package "foo", or "pkg.v3" being "pkg", 839 | // or directory "google.golang.org/api/cloudbilling/v1" 840 | // being package "cloudbilling", but doesn't 841 | // permit a directory "foo" to be package 842 | // "bar", which is strongly discouraged 843 | // anyway. There's no reason goimports needs 844 | // to be slow just to accomodate that. 845 | lastTwo := lastTwoComponents(pkg.importPathShort) 846 | if strings.Contains(lastTwo, pkgIdent) { 847 | return true 848 | } 849 | if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { 850 | lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) 851 | if strings.Contains(lastTwo, pkgIdent) { 852 | return true 853 | } 854 | } 855 | 856 | return false 857 | } 858 | 859 | func hasHyphenOrUpperASCII(s string) bool { 860 | for i := 0; i < len(s); i++ { 861 | b := s[i] 862 | if b == '-' || ('A' <= b && b <= 'Z') { 863 | return true 864 | } 865 | } 866 | return false 867 | } 868 | 869 | func lowerASCIIAndRemoveHyphen(s string) (ret string) { 870 | buf := make([]byte, 0, len(s)) 871 | for i := 0; i < len(s); i++ { 872 | b := s[i] 873 | switch { 874 | case b == '-': 875 | continue 876 | case 'A' <= b && b <= 'Z': 877 | buf = append(buf, b+('a'-'A')) 878 | default: 879 | buf = append(buf, b) 880 | } 881 | } 882 | return string(buf) 883 | } 884 | 885 | // canUse reports whether the package in dir is usable from filename, 886 | // respecting the Go "internal" and "vendor" visibility rules. 887 | func canUse(filename, dir string) bool { 888 | // Fast path check, before any allocations. If it doesn't contain vendor 889 | // or internal, it's not tricky: 890 | // Note that this can false-negative on directories like "notinternal", 891 | // but we check it correctly below. This is just a fast path. 892 | if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { 893 | return true 894 | } 895 | 896 | dirSlash := filepath.ToSlash(dir) 897 | if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { 898 | return true 899 | } 900 | // Vendor or internal directory only visible from children of parent. 901 | // That means the path from the current directory to the target directory 902 | // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal 903 | // or bar/vendor or bar/internal. 904 | // After stripping all the leading ../, the only okay place to see vendor or internal 905 | // is at the very beginning of the path. 906 | absfile, err := filepath.Abs(filename) 907 | if err != nil { 908 | return false 909 | } 910 | absdir, err := filepath.Abs(dir) 911 | if err != nil { 912 | return false 913 | } 914 | rel, err := filepath.Rel(absfile, absdir) 915 | if err != nil { 916 | return false 917 | } 918 | relSlash := filepath.ToSlash(rel) 919 | if i := strings.LastIndex(relSlash, "../"); i >= 0 { 920 | relSlash = relSlash[i+len("../"):] 921 | } 922 | return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") 923 | } 924 | 925 | // lastTwoComponents returns at most the last two path components 926 | // of v, using either / or \ as the path separator. 927 | func lastTwoComponents(v string) string { 928 | nslash := 0 929 | for i := len(v) - 1; i >= 0; i-- { 930 | if v[i] == '/' || v[i] == '\\' { 931 | nslash++ 932 | if nslash == 2 { 933 | return v[i:] 934 | } 935 | } 936 | } 937 | return v 938 | } 939 | 940 | type visitFn func(node ast.Node) ast.Visitor 941 | 942 | func (fn visitFn) Visit(node ast.Node) ast.Visitor { 943 | return fn(node) 944 | } 945 | 946 | func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) { 947 | for symbol := range symbols { 948 | key := shortPkg + "." + symbol 949 | path := stdlib[key] 950 | if path == "" { 951 | if key == "rand.Read" { 952 | continue 953 | } 954 | return "", false, false 955 | } 956 | if importPath != "" && importPath != path { 957 | // Ambiguous. Symbols pointed to different things. 958 | return "", false, false 959 | } 960 | importPath = path 961 | } 962 | if importPath == "" && shortPkg == "rand" && symbols["Read"] { 963 | return "crypto/rand", false, true 964 | } 965 | return importPath, false, importPath != "" 966 | } 967 | 968 | // fileInDir reports whether the provided file path looks like 969 | // it's in dir. (without hitting the filesystem) 970 | func fileInDir(file, dir string) bool { 971 | rest := strings.TrimPrefix(file, dir) 972 | if len(rest) == len(file) { 973 | // dir is not a prefix of file. 974 | return false 975 | } 976 | // Check for boundary: either nothing (file == dir), or a slash. 977 | return len(rest) == 0 || rest[0] == '/' || rest[0] == '\\' 978 | } 979 | -------------------------------------------------------------------------------- /x/tools/imports/run_generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ls $GOPATH/src/golang.org/x/tools/imports/*.go | grep --invert-match -e '_test.go$' | grep -e 'fastwalk*' -e 'fix.go' -e 'zstdlib.go' | xargs -i cp {} ./ 3 | cp $GOPATH/src/golang.org/x/tools/LICENSE ./ 4 | --------------------------------------------------------------------------------