├── gopresent ├── static │ ├── favicon.ico │ ├── print.css │ ├── dir.js │ ├── article.css │ ├── dir.css │ ├── styles.css │ └── slides.js ├── templates │ ├── action.tmpl │ ├── article.tmpl │ └── slides.tmpl └── gopresent.go ├── doc.go ├── go.mod ├── .gitignore ├── pkg ├── stdlib │ ├── go13.go │ ├── go14.go │ ├── mkstdlib.go │ ├── mkpkglist.go │ └── pkglist.go ├── command │ ├── version.go │ └── command.go ├── buildctx │ └── context.go ├── godiff │ └── godiff.go ├── gomod │ └── gomod.go ├── pkgwalk │ └── allpackages.go ├── srcimporter │ └── srcimporter.go └── pkgutil │ └── pkgutil.go ├── README.md ├── terminal ├── command.go ├── command_windows.go └── terminal.go ├── .github └── workflows │ └── go.yml ├── astview ├── ast_go117.go ├── ast_go118.go └── astview.go ├── types ├── types_go117.go ├── types_go118.go └── types_test.go ├── godoc └── godoc.go ├── LICENSE ├── main.go ├── pkgcheck └── check.go ├── runcmd └── runcmd.go ├── docview ├── filesystem.go ├── docview.go └── dirtrees.go ├── debugflags └── debugflags.go ├── gotest └── gotest.go ├── finddecl └── finddecl.go ├── go.sum ├── jsonfmt └── jsonfmt.go ├── gofmt └── gofmt.go ├── pkgs └── pkgs.go └── finddoc └── finddoc.go /gopresent/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visualfc/gotools/HEAD/gopresent/static/favicon.ico -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2017 visualfc . 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 | /* 6 | gotools document 7 | */ 8 | package main 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/visualfc/gotools 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/creack/pty v1.1.21 7 | github.com/pmezard/go-difflib v1.0.0 8 | github.com/visualfc/gomod v0.1.2 9 | github.com/visualfc/goversion v1.1.0 10 | golang.org/x/tools v0.5.0 11 | ) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /pkg/stdlib/go13.go: -------------------------------------------------------------------------------- 1 | // +build !go1.4 2 | 3 | package stdlib 4 | 5 | import ( 6 | "go/build" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func ImportStdPkg(context *build.Context, path string, mode build.ImportMode) (*build.Package, error) { 12 | realpath := filepath.Join(context.GOROOT, "src", "pkg", path) 13 | if _, err := os.Stat(realpath); err != nil { 14 | realpath = filepath.Join(context.GOROOT, "src", path) 15 | } 16 | pkg, err := context.ImportDir(realpath, 0) 17 | pkg.ImportPath = path 18 | return pkg, err 19 | } 20 | -------------------------------------------------------------------------------- /pkg/stdlib/go14.go: -------------------------------------------------------------------------------- 1 | // +build go1.4 2 | 3 | package stdlib 4 | 5 | import ( 6 | "go/build" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | func ImportStdPkg(context *build.Context, path string, mode build.ImportMode) (*build.Package, error) { 12 | realpath := filepath.Join(context.GOROOT, "src", path) 13 | if _, err := os.Stat(realpath); err != nil { 14 | realpath = filepath.Join(context.GOROOT, "src/pkg", path) 15 | } 16 | pkg, err := context.ImportDir(realpath, 0) 17 | pkg.ImportPath = path 18 | return pkg, err 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LiteIDE Golang Tools 2 | ========= 3 | 4 | ### LiteIDE 5 | 6 | _LiteIDE is a simple, open source, cross-platform Go IDE._ 7 | 8 | ### GoTools 9 | _GoTools is a golang tools support for LiteIDE._ 10 | 11 | ``` 12 | go install github.com/visualfc/gotools@latest 13 | 14 | Windows/Linux: copy GOPATH/bin gotools to liteide/bin 15 | MacOS: copy GOPATH/bin gotools to LiteIDE.app/Contents/MacOS 16 | ``` 17 | 18 | ### Website 19 | * LiteIDE Source code 20 | 21 | * Gotools Source code 22 | 23 | -------------------------------------------------------------------------------- /terminal/command.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package terminal 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "os/exec" 9 | 10 | "github.com/creack/pty" 11 | ) 12 | 13 | func GetShell() (cmd string, args []string) { 14 | return "/bin/sh", []string{"-l", "-i"} 15 | } 16 | 17 | func Execute(c *exec.Cmd) error { 18 | f, err := pty.Start(c) 19 | if err != nil { 20 | return nil 21 | } 22 | go func() { 23 | for { 24 | io.Copy(f, os.Stdin) 25 | } 26 | }() 27 | go func() { 28 | for { 29 | io.Copy(os.Stdout, f) 30 | } 31 | }() 32 | return c.Wait() 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: gotools 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | Test: 11 | strategy: 12 | matrix: 13 | go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x] 14 | os: [ubuntu-latest, windows-latest, macos-11] 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: ${{ matrix.go-version }} 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | 27 | -------------------------------------------------------------------------------- /terminal/command_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package terminal 4 | 5 | import ( 6 | "os" 7 | "os/exec" 8 | ) 9 | 10 | func checkFiles(names ...string) string { 11 | for _, name := range names { 12 | _, err := os.Stat(name) 13 | if err == nil { 14 | return name 15 | } 16 | } 17 | return "" 18 | } 19 | 20 | func GetShell() (cmd string, args []string) { 21 | windir := os.Getenv("windir") 22 | if windir == "" { 23 | windir = "c:\\windows" 24 | } 25 | cmd = checkFiles(windir+"\\Sysnative\\cmd.exe", windir+"\\System32\\cmd.exe") 26 | return 27 | } 28 | 29 | func Execute(c *exec.Cmd) error { 30 | c.Stdin = os.Stdin 31 | c.Stdout = os.Stdout 32 | c.Stderr = os.Stderr 33 | return c.Run() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/command/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 command 6 | 7 | import ( 8 | "os" 9 | "runtime" 10 | ) 11 | 12 | func init() { 13 | Register(cmdVersion) 14 | } 15 | 16 | var AppVersion string = "1.0" 17 | 18 | var cmdVersion = &Command{ 19 | Run: runVersion, 20 | UsageLine: "version", 21 | Short: "print tool version", 22 | Long: `Version prints the version.`, 23 | } 24 | 25 | func runVersion(cmd *Command, args []string) error { 26 | if len(args) != 0 { 27 | cmd.PrintUsage() 28 | return os.ErrInvalid 29 | } 30 | 31 | cmd.Printf("%s version %s [%s %s/%s]\n", AppName, AppVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH) 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /astview/ast_go117.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.18 2 | // +build !go1.18 3 | 4 | package astview 5 | 6 | import "go/ast" 7 | 8 | func docBaseTypeName(typ ast.Expr, showAll bool) string { 9 | name, _ := recvTypeName(typ, showAll) 10 | return name 11 | } 12 | 13 | func recvTypeName(typ ast.Expr, showAll bool) (string, bool) { 14 | switch t := typ.(type) { 15 | case *ast.Ident: 16 | // if the type is not exported, the effect to 17 | // a client is as if there were no type name 18 | if showAll || t.IsExported() { 19 | return t.Name, false 20 | } 21 | case *ast.StarExpr: 22 | return docBaseTypeName(t.X, showAll), true 23 | } 24 | return "", false 25 | } 26 | 27 | func typeName(ts *ast.TypeSpec, showTypeParams bool) string { 28 | return ts.Name.String() 29 | } 30 | 31 | func funcName(d *ast.FuncDecl, showTypeParams bool) string { 32 | return d.Name.String() 33 | } 34 | -------------------------------------------------------------------------------- /terminal/terminal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2017 visualfc . 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 | package terminal 5 | 6 | import ( 7 | "os" 8 | "os/exec" 9 | 10 | "github.com/visualfc/gotools/pkg/command" 11 | ) 12 | 13 | var Command = &command.Command{ 14 | Run: runTerminal, 15 | UsageLine: "terminal [program_name arguments...]", 16 | Short: "terminal [program]", 17 | Long: `terminal program and arguments`, 18 | CustomFlags: true, 19 | } 20 | 21 | func runTerminal(cmd *command.Command, args []string) (err error) { 22 | var c *exec.Cmd 23 | if len(args) >= 1 { 24 | var carg []string 25 | if len(args) >= 2 { 26 | carg = append(carg, args[1:]...) 27 | } 28 | c = exec.Command(args[0], carg...) 29 | } else { 30 | shellCmd, shellArgs := GetShell() 31 | c = exec.Command(shellCmd, shellArgs...) 32 | } 33 | if c == nil { 34 | return os.ErrInvalid 35 | } 36 | err = Execute(c) 37 | 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /gopresent/static/print.css: -------------------------------------------------------------------------------- 1 | /* set page layout */ 2 | @page { 3 | size: A4 landscape; 4 | } 5 | 6 | body { 7 | display: block !important; 8 | } 9 | 10 | .slides { 11 | left: 0; 12 | top: 0; 13 | } 14 | 15 | .slides > article { 16 | position: relative; 17 | 18 | left: 0; 19 | top: 0; 20 | 21 | margin: 0 !important; 22 | page-break-inside: avoid; 23 | 24 | text-shadow: none; /* disable shadow */ 25 | 26 | display: block !important; 27 | transform: translate(0) !important; 28 | -o-transform: translate(0) !important; 29 | -moz-transform: translate(0) !important; 30 | -webkit-transform: translate3d(0, 0, 0) !important; 31 | } 32 | 33 | div.code { 34 | background: rgb(240, 240, 240); 35 | } 36 | 37 | /* hide click areas */ 38 | .slide-area, #prev-slide-area, #next-slide-area { 39 | display: none; 40 | } 41 | 42 | /* add explicit links */ 43 | a:link:after, a:visited:after { 44 | content: " (" attr(href) ") "; 45 | font-size: 50%; 46 | } 47 | 48 | /* white background */ 49 | body { 50 | background: rgb(255,255,255) !important; 51 | } 52 | -------------------------------------------------------------------------------- /types/types_go117.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.18 2 | // +build !go1.18 3 | 4 | package types 5 | 6 | import ( 7 | "go/ast" 8 | "go/types" 9 | ) 10 | 11 | const enableTypeParams = false 12 | 13 | func DefaultPkgConfig() *PkgConfig { 14 | conf := &PkgConfig{IgnoreFuncBodies: false, AllowBinary: true, WithTestFiles: true} 15 | conf.Info = &types.Info{ 16 | Uses: make(map[*ast.Ident]types.Object), 17 | Defs: make(map[*ast.Ident]types.Object), 18 | Selections: make(map[*ast.SelectorExpr]*types.Selection), 19 | Types: make(map[ast.Expr]types.TypeAndValue), 20 | Scopes: make(map[ast.Node]*types.Scope), 21 | Implicits: make(map[ast.Node]types.Object), 22 | } 23 | conf.XInfo = &types.Info{ 24 | Uses: make(map[*ast.Ident]types.Object), 25 | Defs: make(map[*ast.Ident]types.Object), 26 | Selections: make(map[*ast.SelectorExpr]*types.Selection), 27 | Types: make(map[ast.Expr]types.TypeAndValue), 28 | Scopes: make(map[ast.Node]*types.Scope), 29 | Implicits: make(map[ast.Node]types.Object), 30 | } 31 | return conf 32 | } 33 | 34 | func sameNamed(n1, n2 *types.Named) bool { 35 | return n1 == n2 36 | } 37 | -------------------------------------------------------------------------------- /pkg/buildctx/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2018 visualfc . 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 buildctx 6 | 7 | import ( 8 | "go/build" 9 | "os" 10 | ) 11 | 12 | var ( 13 | fnLookupEnv = os.LookupEnv 14 | ) 15 | 16 | func SetLookupEnv(fn func(key string) (string, bool)) { 17 | if fn != nil { 18 | fnLookupEnv = fn 19 | } else { 20 | fnLookupEnv = os.LookupEnv 21 | } 22 | } 23 | 24 | func Default() *build.Context { 25 | return &build.Default 26 | } 27 | 28 | func System() *build.Context { 29 | return NewContext(fnLookupEnv) 30 | } 31 | 32 | func NewContext(env func(key string) (string, bool)) *build.Context { 33 | c := build.Default 34 | if v, ok := env("GOARCH"); ok { 35 | c.GOARCH = v 36 | } 37 | if v, ok := env("GOOS"); ok { 38 | c.GOOS = v 39 | } 40 | if v, ok := env("GOROOT"); ok { 41 | c.GOROOT = v 42 | } 43 | if v, ok := env("GOPATH"); ok { 44 | c.GOPATH = v 45 | } 46 | if v, ok := env("CGO_ENABLED"); ok { 47 | switch v { 48 | case "1": 49 | c.CgoEnabled = true 50 | case "0": 51 | c.CgoEnabled = false 52 | } 53 | } 54 | return &c 55 | } 56 | -------------------------------------------------------------------------------- /gopresent/static/dir.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 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 | // copied from $GOROOT/doc/godocs.js 6 | 7 | function bindEvent(el, e, fn) { 8 | if (el.addEventListener){ 9 | el.addEventListener(e, fn, false); 10 | } else if (el.attachEvent){ 11 | el.attachEvent('on'+e, fn); 12 | } 13 | } 14 | 15 | function godocs_bindSearchEvents() { 16 | var search = document.getElementById('search'); 17 | if (!search) { 18 | // no search box (index disabled) 19 | return; 20 | } 21 | function clearInactive() { 22 | if (search.className == "inactive") { 23 | search.value = ""; 24 | search.className = ""; 25 | } 26 | } 27 | function restoreInactive() { 28 | if (search.value !== "") { 29 | return; 30 | } 31 | if (search.type != "search") { 32 | search.value = search.getAttribute("placeholder"); 33 | } 34 | search.className = "inactive"; 35 | } 36 | restoreInactive(); 37 | bindEvent(search, 'focus', clearInactive); 38 | bindEvent(search, 'blur', restoreInactive); 39 | } 40 | 41 | bindEvent(window, 'load', godocs_bindSearchEvents); 42 | -------------------------------------------------------------------------------- /types/types_go118.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package types 5 | 6 | import ( 7 | "go/ast" 8 | "go/types" 9 | ) 10 | 11 | const enableTypeParams = true 12 | 13 | func DefaultPkgConfig() *PkgConfig { 14 | conf := &PkgConfig{IgnoreFuncBodies: false, AllowBinary: true, WithTestFiles: true} 15 | conf.Info = &types.Info{ 16 | Uses: make(map[*ast.Ident]types.Object), 17 | Defs: make(map[*ast.Ident]types.Object), 18 | Selections: make(map[*ast.SelectorExpr]*types.Selection), 19 | Types: make(map[ast.Expr]types.TypeAndValue), 20 | Scopes: make(map[ast.Node]*types.Scope), 21 | Implicits: make(map[ast.Node]types.Object), 22 | Instances: make(map[*ast.Ident]types.Instance), 23 | } 24 | conf.XInfo = &types.Info{ 25 | Uses: make(map[*ast.Ident]types.Object), 26 | Defs: make(map[*ast.Ident]types.Object), 27 | Selections: make(map[*ast.SelectorExpr]*types.Selection), 28 | Types: make(map[ast.Expr]types.TypeAndValue), 29 | Scopes: make(map[ast.Node]*types.Scope), 30 | Implicits: make(map[ast.Node]types.Object), 31 | Instances: make(map[*ast.Ident]types.Instance), 32 | } 33 | return conf 34 | } 35 | 36 | func sameNamed(n1, n2 *types.Named) bool { 37 | return n1 != nil && n2 != nil && n1.Origin().String() == n2.Origin().String() 38 | } 39 | -------------------------------------------------------------------------------- /gopresent/templates/action.tmpl: -------------------------------------------------------------------------------- 1 | {/* 2 | This is the action template. 3 | It determines how the formatting actions are rendered. 4 | */} 5 | 6 | {{define "section"}} 7 | {{.FormattedNumber}} {{.Title}} 8 | {{range .Elem}}{{elem $.Template .}}{{end}} 9 | {{end}} 10 | 11 | {{define "list"}} 12 |
    13 | {{range .Bullet}} 14 |
  • {{style .}}
  • 15 | {{end}} 16 |
17 | {{end}} 18 | 19 | {{define "text"}} 20 | {{if .Pre}} 21 |
{{range .Lines}}{{.}}{{end}}
22 | {{else}} 23 |

24 | {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}} 25 | {{end}}{{style $l}}{{end}} 26 |

27 | {{end}} 28 | {{end}} 29 | 30 | {{define "code"}} 31 |
{{.Text}}
32 | {{end}} 33 | 34 | {{define "image"}} 35 |
36 | 37 |
38 | {{end}} 39 | 40 | {{define "iframe"}} 41 | 42 | {{end}} 43 | 44 | {{define "link"}}{{end}} 45 | 46 | {{define "html"}}{{.HTML}}{{end}} 47 | -------------------------------------------------------------------------------- /godoc/godoc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc . 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 godoc 6 | 7 | import ( 8 | "errors" 9 | "os/exec" 10 | 11 | "github.com/visualfc/gotools/pkg/command" 12 | "github.com/visualfc/goversion" 13 | ) 14 | 15 | var Command = &command.Command{ 16 | Run: runDoc, 17 | UsageLine: "godoc [pkg]", 18 | Short: "golang doc print", 19 | Long: "golang document print", 20 | } 21 | 22 | var ( 23 | GoVer112 = goversion.GoVersion{1, 12, -1, 0, 0, ""} 24 | ) 25 | 26 | func runDoc(cmd *command.Command, args []string) error { 27 | if len(args) < 1 { 28 | return nil 29 | } 30 | ver, _, ok := goversion.Installed() 31 | if !ok { 32 | return errors.New("could not parse output of go version") 33 | } 34 | gocmd, err := exec.LookPath("go") 35 | if err != nil { 36 | return err 37 | } 38 | var godoc_html bool 39 | godoc, err := exec.LookPath("godoc") 40 | if err == nil { 41 | godoc_html = true 42 | } 43 | if ver.AfterOrEqual(GoVer112) { 44 | godoc_html = false 45 | } 46 | var command *exec.Cmd 47 | if godoc_html { 48 | command = exec.Command(godoc, "-html", args[0]) 49 | } else { 50 | command = exec.Command(gocmd, "doc", "-all", args[0]) 51 | } 52 | command.Stdin = cmd.Stdin 53 | command.Stdout = cmd.Stdout 54 | command.Stderr = cmd.Stderr 55 | return command.Run() 56 | } 57 | -------------------------------------------------------------------------------- /gopresent/templates/article.tmpl: -------------------------------------------------------------------------------- 1 | {/* This is the article template. It defines how articles are formatted. */} 2 | 3 | {{define "root"}} 4 | 5 | 6 | 7 | {{.Title}} 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
{{.Title}} 16 | {{with .Subtitle}}{{.}}{{end}} 17 |
18 |
19 |
20 |
21 |
22 | {{with .Sections}} 23 |
24 | {{template "TOC" .}} 25 |
26 | {{end}} 27 | 28 | {{range .Sections}} 29 | {{elem $.Template .}} 30 | {{end}}{{/* of Section block */}} 31 | 32 |

Authors

33 | {{range .Authors}} 34 |
35 | {{range .Elem}}{{elem $.Template .}}{{end}} 36 |
37 | {{end}} 38 |
39 |
40 | 41 | 42 | 43 | {{end}} 44 | 45 | {{define "TOC"}} 46 |
    47 | {{range .}} 48 |
  • {{.Title}}
  • 49 | {{with .Sections}}{{template "TOC" .}}{{end}} 50 | {{end}} 51 |
52 | {{end}} 53 | 54 | {{define "newline"}} 55 | {{/* No automatic line break. Paragraphs are free-form. */}} 56 | {{end}} 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2017, visualfc 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of gotools nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (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 | 29 | -------------------------------------------------------------------------------- /gopresent/templates/slides.tmpl: -------------------------------------------------------------------------------- 1 | {/* This is the slide template. It defines how presentations are formatted. */} 2 | 3 | {{define "root"}} 4 | 5 | 6 | 7 | {{.Title}} 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 |

{{.Title}}

18 | {{with .Subtitle}}

{{.}}

{{end}} 19 | {{if not .Time.IsZero}}

{{.Time.Format "2 January 2006"}}

{{end}} 20 | {{range .Authors}} 21 |
22 | {{range .TextElem}}{{elem $.Template .}}{{end}} 23 |
24 | {{end}} 25 |
26 | 27 | {{range $i, $s := .Sections}} 28 | 29 |
30 | {{if $s.Elem}} 31 |

{{$s.Title}}

32 | {{range $s.Elem}}{{elem $.Template .}}{{end}} 33 | {{else}} 34 |

{{$s.Title}}

35 | {{end}} 36 |
37 | 38 | {{end}}{{/* of Slide block */}} 39 | 40 |
41 |

Thank you

42 | {{range .Authors}} 43 |
44 | {{range .Elem}}{{elem $.Template .}}{{end}} 45 |
46 | {{end}} 47 |
48 | 49 | 50 | {{if .PlayEnabled}} 51 | 52 | {{end}} 53 | 54 | {{end}} 55 | 56 | {{define "newline"}} 57 |
58 | {{end}} 59 | -------------------------------------------------------------------------------- /pkg/godiff/godiff.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 godiff 6 | 7 | import ( 8 | "io/ioutil" 9 | "os" 10 | "os/exec" 11 | 12 | "github.com/pmezard/go-difflib/difflib" 13 | ) 14 | 15 | func UnifiedDiffLines(a []string, b []string) (string, error) { 16 | diff := difflib.UnifiedDiff{ 17 | A: a, 18 | B: b, 19 | FromFile: "Original", 20 | ToFile: "Current", 21 | Context: 3, 22 | Eol: "\n", 23 | } 24 | return difflib.GetUnifiedDiffString(diff) 25 | } 26 | 27 | func UnifiedDiffString(a string, b string) (string, error) { 28 | diff := difflib.UnifiedDiff{ 29 | A: difflib.SplitLines(a), 30 | B: difflib.SplitLines(b), 31 | FromFile: "Original", 32 | ToFile: "Current", 33 | Context: 3, 34 | Eol: "\n", 35 | } 36 | return difflib.GetUnifiedDiffString(diff) 37 | } 38 | 39 | func UnifiedDiffBytesByCmd(b1, b2 []byte) (data []byte, err error) { 40 | f1, err := ioutil.TempFile("", "godiff") 41 | if err != nil { 42 | return 43 | } 44 | defer os.Remove(f1.Name()) 45 | defer f1.Close() 46 | 47 | f2, err := ioutil.TempFile("", "godiff") 48 | if err != nil { 49 | return 50 | } 51 | defer os.Remove(f2.Name()) 52 | defer f2.Close() 53 | 54 | f1.Write(b1) 55 | f2.Write(b2) 56 | 57 | data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() 58 | if len(data) > 0 { 59 | // diff exits with a non-zero status when the files don't match. 60 | // Ignore that failure as long as we get output. 61 | err = nil 62 | } 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2023 visualfc . 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 main 6 | 7 | import ( 8 | "github.com/visualfc/gotools/astview" 9 | "github.com/visualfc/gotools/debugflags" 10 | "github.com/visualfc/gotools/docview" 11 | "github.com/visualfc/gotools/finddecl" 12 | "github.com/visualfc/gotools/finddoc" 13 | "github.com/visualfc/gotools/goapi" 14 | "github.com/visualfc/gotools/godoc" 15 | "github.com/visualfc/gotools/gofmt" 16 | "github.com/visualfc/gotools/gopresent" 17 | "github.com/visualfc/gotools/gotest" 18 | "github.com/visualfc/gotools/jsonfmt" 19 | "github.com/visualfc/gotools/pkg/command" 20 | "github.com/visualfc/gotools/pkgcheck" 21 | "github.com/visualfc/gotools/pkgs" 22 | "github.com/visualfc/gotools/runcmd" 23 | "github.com/visualfc/gotools/terminal" 24 | "github.com/visualfc/gotools/types" 25 | ) 26 | 27 | func init() { 28 | command.Register(types.Command) 29 | command.Register(jsonfmt.Command) 30 | command.Register(finddoc.Command) 31 | command.Register(runcmd.Command) 32 | command.Register(docview.Command) 33 | command.Register(astview.Command) 34 | command.Register(gopresent.Command) 35 | command.Register(goapi.Command) 36 | command.Register(pkgs.Command) 37 | command.Register(gofmt.Command) 38 | command.Register(gotest.Command) 39 | command.Register(finddecl.Command) 40 | command.Register(terminal.Command) 41 | command.Register(debugflags.Command) 42 | command.Register(pkgcheck.Command) 43 | command.Register(godoc.Command) 44 | } 45 | 46 | func main() { 47 | command.AppName = "gotools" 48 | command.AppVersion = "1.4" 49 | command.AppInfo = "Go tools for liteide." 50 | command.Main() 51 | } 52 | -------------------------------------------------------------------------------- /types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestTypes(t *testing.T) { 11 | w := NewPkgWalker(&build.Default) 12 | w.SetOutput(os.Stdout, os.Stderr) 13 | w.SetFindMode(&FindMode{Info: true, Doc: true, Define: true}) 14 | conf := DefaultPkgConfig() 15 | dir, _ := os.Getwd() 16 | cursor := NewFileCursor(nil, dir, "types_test.go", 126) 17 | pkg, conf, err := w.Check(dir, conf, cursor) 18 | if err != nil { 19 | t.Fatalf("error %v\n", err) 20 | } 21 | w.LookupCursor(pkg, conf, cursor) 22 | } 23 | 24 | func test_error() { 25 | e := fmt.Errorf("error_test") 26 | _ = e.Error() 27 | } 28 | 29 | func TestError(t *testing.T) { 30 | w := NewPkgWalker(&build.Default) 31 | w.SetOutput(os.Stdout, os.Stderr) 32 | w.SetFindMode(&FindMode{Info: true, Doc: true, Define: true, Usage: true, UsageAll: true}) 33 | conf := DefaultPkgConfig() 34 | dir, _ := os.Getwd() 35 | cursor := NewFileCursor(nil, dir, "types_test.go", 530) 36 | pkg, conf, err := w.Check(dir, conf, cursor) 37 | if err != nil { 38 | t.Fatalf("error %v\n", err) 39 | } 40 | w.LookupCursor(pkg, conf, cursor) 41 | } 42 | 43 | func TestOS(t *testing.T) { 44 | w := NewPkgWalker(&build.Default) 45 | 46 | w.SetOutput(os.Stdout, os.Stderr) 47 | w.SetFindMode(&FindMode{Info: true, Doc: true, Define: true}) 48 | conf := DefaultPkgConfig() 49 | fn1 := func() { 50 | cursor := NewFileCursor(nil, "", "dir_windows.go", 235) 51 | pkg, conf, _ := w.Check("os", conf, cursor) 52 | w.LookupCursor(pkg, conf, cursor) 53 | } 54 | fn2 := func() { 55 | cursor := NewFileCursor(nil, "", "dir_unix.go", 1040) 56 | pkg, conf, _ := w.Check("os", conf, cursor) 57 | w.LookupCursor(pkg, conf, cursor) 58 | } 59 | for i := 0; i < 2; i++ { 60 | fn1() 61 | fn2() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /astview/ast_go118.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package astview 5 | 6 | import ( 7 | "go/ast" 8 | "go/types" 9 | "strings" 10 | ) 11 | 12 | func docBaseTypeName(typ ast.Expr, showAll bool) string { 13 | name, _ := recvTypeName(typ, showAll) 14 | return name 15 | } 16 | 17 | func recvTypeName(typ ast.Expr, showAll bool) (string, bool) { 18 | switch t := typ.(type) { 19 | case *ast.Ident: 20 | // if the type is not exported, the effect to 21 | // a client is as if there were no type name 22 | if showAll || t.IsExported() { 23 | return t.Name, false 24 | } 25 | case *ast.StarExpr: 26 | return docBaseTypeName(t.X, showAll), true 27 | case *ast.IndexExpr: 28 | return docBaseTypeName(t.X, showAll), false 29 | case *ast.IndexListExpr: 30 | return docBaseTypeName(t.X, showAll), false 31 | } 32 | return "", false 33 | } 34 | 35 | func typeName(d *ast.TypeSpec, showTypeParams bool) string { 36 | if showTypeParams && d.TypeParams != nil { 37 | tparams := d.TypeParams 38 | var fs []string 39 | n := len(tparams.List) 40 | for i := 0; i < n; i++ { 41 | f := tparams.List[i] 42 | for _, name := range f.Names { 43 | fs = append(fs, name.String()+" "+types.ExprString(f.Type)) 44 | } 45 | } 46 | return d.Name.String() + "[" + strings.Join(fs, ", ") + "]" 47 | } 48 | return d.Name.String() 49 | } 50 | 51 | func funcName(d *ast.FuncDecl, showTypeParams bool) string { 52 | if showTypeParams && d.Type.TypeParams != nil { 53 | tparams := d.Type.TypeParams 54 | var fs []string 55 | n := len(tparams.List) 56 | for i := 0; i < n; i++ { 57 | f := tparams.List[i] 58 | for _, name := range f.Names { 59 | fs = append(fs, name.String()+" "+types.ExprString(f.Type)) 60 | } 61 | } 62 | return d.Name.String() + "[" + strings.Join(fs, ", ") + "]" 63 | } 64 | return d.Name.String() 65 | } 66 | -------------------------------------------------------------------------------- /pkgcheck/check.go: -------------------------------------------------------------------------------- 1 | package pkgcheck 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/visualfc/gomod" 10 | "github.com/visualfc/gotools/pkg/command" 11 | "github.com/visualfc/gotools/pkg/pkgutil" 12 | ) 13 | 14 | var Command = &command.Command{ 15 | Run: runCheck, 16 | UsageLine: "pkgcheck [-pkg | -name] -w .", 17 | Short: "pkg check utils", 18 | Long: "check pkg mod or vendor path", 19 | } 20 | 21 | var ( 22 | flagCheckPkg string 23 | flagCheckDir string = "." 24 | flagCheckName bool 25 | ) 26 | 27 | func init() { 28 | Command.Flag.StringVar(&flagCheckPkg, "pkg", "", "check pkg name") 29 | Command.Flag.BoolVar(&flagCheckName, "name", false, "check module name") 30 | Command.Flag.StringVar(&flagCheckDir, "w", "", "work path") 31 | } 32 | 33 | func runCheck(cmd *command.Command, args []string) error { 34 | if flagCheckPkg == "" && !flagCheckName { 35 | cmd.Usage() 36 | return os.ErrInvalid 37 | } 38 | if flagCheckDir == "" || flagCheckDir == "." { 39 | flagCheckDir, _ = os.Getwd() 40 | } 41 | mod, _ := gomod.Load(flagCheckDir, &build.Default) 42 | if flagCheckName { 43 | if mod != nil { 44 | fmt.Println(mod.Root().Path) 45 | } else { 46 | _, fname := filepath.Split(flagCheckDir) 47 | fmt.Println(fname) 48 | } 49 | return nil 50 | } 51 | // check mod, check vendor 52 | if mod != nil { 53 | _, dir, _ := mod.Lookup(flagCheckPkg) 54 | if dir != "" { 55 | fmt.Printf("%s,mod\n", dir) 56 | return nil 57 | } 58 | } else { 59 | pkg := pkgutil.ImportDir(flagCheckDir) 60 | if pkg != nil { 61 | found, _ := pkgutil.VendoredImportPath(pkg, flagCheckPkg) 62 | if found != "" && found != flagCheckPkg { 63 | fmt.Printf("%s,vendor\n", found) 64 | return nil 65 | } 66 | } 67 | } 68 | fmt.Printf("%s,pkg\n", flagCheckPkg) 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /runcmd/runcmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 runcmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | 13 | "github.com/visualfc/gotools/pkg/command" 14 | ) 15 | 16 | var Command = &command.Command{ 17 | Run: runCmd, 18 | UsageLine: "runcmd [-w work_path] [arguments...]", 19 | Short: "run program", 20 | Long: `run program and arguments`, 21 | } 22 | 23 | var execWorkPath string 24 | var execWaitEnter bool 25 | 26 | func init() { 27 | Command.Flag.StringVar(&execWorkPath, "w", "", "work path") 28 | Command.Flag.BoolVar(&execWaitEnter, "e", true, "wait enter and continue") 29 | } 30 | 31 | func runCmd(cmd *command.Command, args []string) error { 32 | if len(args) == 0 { 33 | cmd.Usage() 34 | return os.ErrInvalid 35 | } 36 | if execWorkPath == "" { 37 | var err error 38 | execWorkPath, err = os.Getwd() 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | fileName := args[0] 44 | 45 | filePath, err := exec.LookPath(fileName) 46 | if err != nil { 47 | filePath, err = exec.LookPath("./" + fileName) 48 | } 49 | if err != nil { 50 | return err 51 | } 52 | 53 | fmt.Println("Starting Process", filePath, strings.Join(args[1:], " "), "...") 54 | 55 | command := exec.Command(filePath, args[1:]...) 56 | command.Dir = execWorkPath 57 | command.Stdin = os.Stdin 58 | command.Stdout = os.Stdout 59 | command.Stderr = os.Stderr 60 | 61 | err = command.Run() 62 | 63 | if err != nil { 64 | fmt.Println("\nEnd Process", err) 65 | } else { 66 | fmt.Println("\nEnd Process", "exit status 0") 67 | } 68 | 69 | exitWaitEnter() 70 | return nil 71 | } 72 | 73 | func exitWaitEnter() { 74 | if !execWaitEnter { 75 | return 76 | } 77 | fmt.Println("\nPress enter key to continue") 78 | var s = [256]byte{} 79 | os.Stdin.Read(s[:]) 80 | } 81 | -------------------------------------------------------------------------------- /docview/filesystem.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | // This file defines types for abstract file system access and 6 | // provides an implementation accessing the file system of the 7 | // underlying OS. 8 | 9 | package docview 10 | 11 | import ( 12 | "fmt" 13 | "io" 14 | "io/ioutil" 15 | "os" 16 | ) 17 | 18 | // The FileSystem interface specifies the methods godoc is using 19 | // to access the file system for which it serves documentation. 20 | type FileSystem interface { 21 | Open(path string) (io.ReadCloser, error) 22 | Lstat(path string) (os.FileInfo, error) 23 | Stat(path string) (os.FileInfo, error) 24 | ReadDir(path string) ([]os.FileInfo, error) 25 | } 26 | 27 | // ReadFile reads the file named by path from fs and returns the contents. 28 | func ReadFile(fs FileSystem, path string) ([]byte, error) { 29 | rc, err := fs.Open(path) 30 | if err != nil { 31 | return nil, err 32 | } 33 | defer rc.Close() 34 | return ioutil.ReadAll(rc) 35 | } 36 | 37 | // ---------------------------------------------------------------------------- 38 | // OS-specific FileSystem implementation 39 | 40 | var OS FileSystem = osFS{} 41 | 42 | // osFS is the OS-specific implementation of FileSystem 43 | type osFS struct{} 44 | 45 | func (osFS) Open(path string) (io.ReadCloser, error) { 46 | f, err := os.Open(path) 47 | if err != nil { 48 | return nil, err 49 | } 50 | fi, err := f.Stat() 51 | if err != nil { 52 | return nil, err 53 | } 54 | if fi.IsDir() { 55 | return nil, fmt.Errorf("Open: %s is a directory", path) 56 | } 57 | return f, nil 58 | } 59 | 60 | func (osFS) Lstat(path string) (os.FileInfo, error) { 61 | return os.Lstat(path) 62 | } 63 | 64 | func (osFS) Stat(path string) (os.FileInfo, error) { 65 | return os.Stat(path) 66 | } 67 | 68 | func (osFS) ReadDir(path string) ([]os.FileInfo, error) { 69 | return ioutil.ReadDir(path) // is sorted 70 | } 71 | -------------------------------------------------------------------------------- /debugflags/debugflags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2018 visualfc . 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 debugflags 6 | 7 | import ( 8 | "errors" 9 | "runtime" 10 | 11 | "github.com/visualfc/gotools/pkg/command" 12 | "github.com/visualfc/goversion" 13 | ) 14 | 15 | var Command = &command.Command{ 16 | Run: runDebugFlags, 17 | UsageLine: "debugflags", 18 | Short: "print go debug flags", 19 | Long: `print go debug flags`, 20 | CustomFlags: true, 21 | } 22 | 23 | func runDebugFlags(cmd *command.Command, args []string) error { 24 | var buildFlagsDefault string 25 | if runtime.GOOS == "windows" { 26 | ver, _, ok := goversion.Installed() 27 | if !ok { 28 | return errors.New("could not parse output of go version") 29 | } 30 | if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { 31 | // Work-around for https://github.com/golang/go/issues/13154 32 | buildFlagsDefault = "-ldflags='-linkmode internal'" 33 | } 34 | } 35 | flags := debugflags() 36 | if buildFlagsDefault != "" { 37 | flags += " " + buildFlagsDefault 38 | } 39 | cmd.Println(flags) 40 | return nil 41 | } 42 | 43 | // copy from github.com/derekparker/delve/cmd/dlv/cmds/commands.go 44 | func debugflags() string { 45 | // after go1.9 building with -gcflags='-N -l' and -a simultaneously works. 46 | // after go1.10 specifying -a is unnecessary because of the new caching strategy, but we should pass -gcflags=all=-N -l to have it applied to all packages 47 | // see https://github.com/golang/go/commit/5993251c015dfa1e905bdf44bdb41572387edf90 48 | 49 | var flags string 50 | ver, _, ok := goversion.Installed() 51 | switch { 52 | case !ok: 53 | flags = "" 54 | case ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}): 55 | flags = "-gcflags=\"all=-N -l\"" 56 | case ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}): 57 | flags = "-gcflags=\"-N -l\" -a" 58 | default: 59 | flags = "-gcflags=\"-N -l\" -a" 60 | } 61 | return flags 62 | } 63 | -------------------------------------------------------------------------------- /gopresent/static/article.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: Helvetica, Arial, sans-serif; 4 | font-size: 16px; 5 | } 6 | pre, 7 | code { 8 | font-family: Menlo, monospace; 9 | font-size: 14px; 10 | } 11 | pre { 12 | line-height: 18px; 13 | margin: 0; 14 | padding: 0; 15 | } 16 | a { 17 | color: #375EAB; 18 | text-decoration: none; 19 | } 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | p, ul, ol { 24 | margin: 20px; 25 | } 26 | 27 | h1, h2, h3, h4 { 28 | margin: 20px 0; 29 | padding: 0; 30 | color: #375EAB; 31 | font-weight: bold; 32 | } 33 | h1 { 34 | font-size: 24px; 35 | } 36 | h2 { 37 | font-size: 20px; 38 | background: #E0EBF5; 39 | padding: 2px 5px; 40 | } 41 | h3 { 42 | font-size: 20px; 43 | } 44 | h3, h4 { 45 | margin: 20px 5px; 46 | } 47 | h4 { 48 | font-size: 16px; 49 | } 50 | 51 | div#heading { 52 | float: left; 53 | margin: 0 0 10px 0; 54 | padding: 21px 0; 55 | font-size: 20px; 56 | font-weight: normal; 57 | } 58 | 59 | div#topbar { 60 | background: #E0EBF5; 61 | height: 64px; 62 | overflow: hidden; 63 | } 64 | 65 | body { 66 | text-align: center; 67 | } 68 | div#page { 69 | width: 100%; 70 | } 71 | div#page > .container, 72 | div#topbar > .container { 73 | text-align: left; 74 | margin-left: auto; 75 | margin-right: auto; 76 | padding: 0 20px; 77 | width: 900px; 78 | } 79 | div#page.wide > .container, 80 | div#topbar.wide > .container { 81 | width: auto; 82 | } 83 | 84 | div#footer { 85 | text-align: center; 86 | color: #666; 87 | font-size: 14px; 88 | margin: 40px 0; 89 | } 90 | 91 | .author p { 92 | margin: 20, 0, 0, 0px; 93 | } 94 | 95 | div.code, 96 | div.output { 97 | margin: 20px; 98 | padding: 10px; 99 | -webkit-border-radius: 5px; 100 | -moz-border-radius: 5px; 101 | border-radius: 5px; 102 | } 103 | 104 | div.code { background: #e9e9e9; } 105 | div.output { background: black; } 106 | div.output .stdout { color: #e6e6e6; } 107 | div.output .stderr { color: rgb(244, 74, 63); } 108 | div.output .system { color: rgb(255, 209, 77) } 109 | 110 | .buttons { 111 | margin-left: 20px; 112 | } 113 | div.output .buttons { 114 | margin-left: 0; 115 | margin-bottom: 10px; 116 | } 117 | 118 | #toc { 119 | float: right; 120 | margin: 0px 10px; 121 | padding: 10px; 122 | border: 1px solid #e5ecf9; 123 | background-color: white; 124 | max-width: 33%; 125 | 126 | -webkit-border-radius: 5px; 127 | -moz-border-radius: 5px; 128 | border-radius: 5px; 129 | } 130 | 131 | #toc ul, #toc a { 132 | list-style-type: none; 133 | padding-left: 10px; 134 | color: black; 135 | margin: 0px; 136 | } 137 | -------------------------------------------------------------------------------- /pkg/stdlib/mkstdlib.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | // +build ignore 3 | 4 | // mkstdlib generates the zstdlib.go file, containing the Go standard 5 | // library API symbols. It's baked into the binary to avoid scanning 6 | // GOPATH in the common case. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "bytes" 12 | "fmt" 13 | "go/format" 14 | "io" 15 | "io/ioutil" 16 | "log" 17 | "os" 18 | "path" 19 | "path/filepath" 20 | "regexp" 21 | "sort" 22 | "strings" 23 | ) 24 | 25 | func mustOpen(name string) io.Reader { 26 | f, err := os.Open(name) 27 | if err != nil { 28 | log.Println(err) 29 | return nil 30 | } 31 | return f 32 | } 33 | 34 | func api(base string) string { 35 | return filepath.Join(os.Getenv("GOROOT"), "api", base) 36 | } 37 | 38 | var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`) 39 | 40 | func main() { 41 | var buf bytes.Buffer 42 | outf := func(format string, args ...interface{}) { 43 | fmt.Fprintf(&buf, format, args...) 44 | } 45 | outf("// AUTO-GENERATED BY mkstdlib.go\n\n") 46 | outf("package stdlib\n") 47 | outf("var Symbols = map[string]string{\n") 48 | f := io.MultiReader( 49 | mustOpen(api("go1.txt")), 50 | mustOpen(api("go1.1.txt")), 51 | mustOpen(api("go1.2.txt")), 52 | mustOpen(api("go1.3.txt")), 53 | mustOpen(api("go1.4.txt")), 54 | mustOpen(api("go1.5.txt")), 55 | mustOpen(api("go1.6.txt")), 56 | mustOpen(api("go1.7.txt")), 57 | mustOpen(api("go1.8.txt")), 58 | mustOpen(api("go1.9.txt")), 59 | mustOpen(api("go1.10.txt")), 60 | mustOpen(api("go1.11.txt")), 61 | mustOpen(api("go1.12.txt")), 62 | mustOpen(api("go1.13.txt")), 63 | mustOpen(api("go1.14.txt")), 64 | mustOpen(api("go1.15.txt")), 65 | mustOpen(api("go1.16.txt")), 66 | mustOpen(api("go1.17.txt")), 67 | mustOpen(api("go1.18.txt")), 68 | mustOpen(api("go1.19.txt")), 69 | mustOpen(api("go1.20.txt")), 70 | ) 71 | sc := bufio.NewScanner(f) 72 | fullImport := map[string]string{} // "zip.NewReader" => "archive/zip" 73 | ambiguous := map[string]bool{} 74 | var keys []string 75 | for sc.Scan() { 76 | l := sc.Text() 77 | has := func(v string) bool { return strings.Contains(l, v) } 78 | if has("struct, ") || has("interface, ") || has(", method (") { 79 | continue 80 | } 81 | if m := sym.FindStringSubmatch(l); m != nil { 82 | full := m[1] 83 | key := path.Base(full) + "." + m[2] 84 | if exist, ok := fullImport[key]; ok { 85 | if exist != full { 86 | ambiguous[key] = true 87 | } 88 | } else { 89 | fullImport[key] = full 90 | keys = append(keys, key) 91 | } 92 | } 93 | } 94 | if err := sc.Err(); err != nil { 95 | log.Fatal(err) 96 | } 97 | sort.Strings(keys) 98 | for _, key := range keys { 99 | if ambiguous[key] { 100 | outf("\t// %q is ambiguous\n", key) 101 | } else { 102 | outf("\t%q: %q,\n", key, fullImport[key]) 103 | } 104 | } 105 | outf("}\n") 106 | fmtbuf, err := format.Source(buf.Bytes()) 107 | if err != nil { 108 | log.Fatal(err) 109 | } 110 | //os.Stdout.Write(fmtbuf) 111 | ioutil.WriteFile("./zstdlib.go", fmtbuf, 0777) 112 | } 113 | -------------------------------------------------------------------------------- /gotest/gotest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 gotest 6 | 7 | import ( 8 | "fmt" 9 | "go/ast" 10 | "go/build" 11 | "go/parser" 12 | "go/token" 13 | "os" 14 | "os/exec" 15 | "path/filepath" 16 | "strings" 17 | 18 | "github.com/visualfc/gotools/pkg/command" 19 | "golang.org/x/tools/go/buildutil" 20 | ) 21 | 22 | var Command = &command.Command{ 23 | Run: runGotest, 24 | UsageLine: "gotest -f filename [build/test flags]", 25 | Short: "go test go filename", 26 | Long: `go test go filename`, 27 | CustomFlags: true, 28 | } 29 | 30 | var testFileName string 31 | var testFileArgs string 32 | 33 | func init() { 34 | //Command.Flag.StringVar(&testFileName, "f", "", "test go filename") 35 | ApplyBuildTags() 36 | } 37 | 38 | func runGotest(cmd *command.Command, args []string) error { 39 | index := -1 40 | for n, arg := range args { 41 | if arg == "-f" { 42 | index = n 43 | break 44 | } 45 | } 46 | if index >= 0 && index < len(args) { 47 | testFileName = args[index+1] 48 | var r []string 49 | r = append(r, args[0:index]...) 50 | r = append(r, args[index+2:]...) 51 | args = r 52 | } 53 | 54 | if testFileName == "" { 55 | cmd.Usage() 56 | return os.ErrInvalid 57 | } 58 | if !strings.HasSuffix(testFileName, "_test.go") { 59 | fmt.Println("The test filename must xxx_test.go") 60 | return os.ErrInvalid 61 | } 62 | fset := token.NewFileSet() 63 | f, err := parser.ParseFile(fset, testFileName, nil, parser.ParseComments) 64 | if err != nil { 65 | return err 66 | } 67 | var fnList []string 68 | for _, decl := range f.Decls { 69 | if fn, ok := decl.(*ast.FuncDecl); ok { 70 | name := fn.Name.Name 71 | if strings.HasPrefix(name, "Test") || strings.HasPrefix(name, "Benchmark") { 72 | fnList = append(fnList, name) 73 | } 74 | } 75 | } 76 | if len(fnList) == 0 { 77 | return fmt.Errorf("testing: warning: no tests to run") 78 | } 79 | 80 | if filepath.IsAbs(testFileName) { 81 | dir, _ := filepath.Split(testFileName) 82 | os.Chdir(dir) 83 | } 84 | 85 | gobin, err := exec.LookPath("go") 86 | if err != nil { 87 | return fmt.Errorf("error lookup go: %v", err) 88 | } 89 | 90 | var testArgs []string 91 | testArgs = append(testArgs, "test") 92 | if len(args) > 0 { 93 | testArgs = append(testArgs, args...) 94 | } 95 | testArgs = append(testArgs, "-run", fmt.Sprintf("^(%v)$", strings.Join(fnList, "|"))) 96 | 97 | command := exec.Command(gobin, testArgs...) 98 | command.Stdin = os.Stdin 99 | command.Stdout = os.Stdout 100 | command.Stderr = os.Stderr 101 | 102 | return command.Run() 103 | } 104 | 105 | func ApplyBuildTags() { 106 | nexttag := false 107 | for _, arg := range os.Args[1:] { 108 | if nexttag { 109 | var tags buildutil.TagsFlag 110 | tags.Set(arg) 111 | 112 | build.Default.BuildTags = tags 113 | nexttag = false 114 | continue 115 | } 116 | if arg == "-tags" { 117 | nexttag = true 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /pkg/gomod/gomod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2018 visualfc . 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 gomod 6 | 7 | import ( 8 | "encoding/json" 9 | "os/exec" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | func LooupModList(dir string) *ModuleList { 15 | data := ListModuleJson(dir) 16 | if data == nil { 17 | return nil 18 | } 19 | ms := parseModuleJson(data) 20 | ms.Dir = dir 21 | return &ms 22 | } 23 | 24 | func LookupModFile(dir string) string { 25 | command := exec.Command("go", "env", "GOMOD") 26 | command.Dir = dir 27 | data, err := command.Output() 28 | if err != nil { 29 | return "" 30 | } 31 | return strings.TrimSpace(string(data)) 32 | } 33 | 34 | func ListModuleJson(dir string) []byte { 35 | command := exec.Command("go", "list", "-m", "-json", "all") 36 | command.Dir = dir 37 | data, err := command.Output() 38 | if err != nil { 39 | return nil 40 | } 41 | return data 42 | } 43 | 44 | type ModuleList struct { 45 | Dir string 46 | Module Module 47 | Require []*Module 48 | } 49 | 50 | func makePath(path, dir string, addin string) string { 51 | dir = filepath.ToSlash(dir) 52 | pos := strings.Index(dir, "mod/"+path+"@") 53 | if pos == -1 { 54 | return path 55 | } 56 | return filepath.Join(dir[pos:], addin) 57 | } 58 | 59 | type Package struct { 60 | Dir string 61 | ImportPath string 62 | Name string 63 | Module *Module 64 | } 65 | 66 | func (m *ModuleList) LookupModule(pkgname string) (require *Module, path string, dir string) { 67 | for _, r := range m.Require { 68 | if strings.HasPrefix(pkgname, r.Path) { 69 | addin := pkgname[len(r.Path):] 70 | if r.Replace != nil { 71 | path = makePath(r.Replace.Path, r.Dir, addin) 72 | } else { 73 | path = makePath(r.Path, r.Dir, addin) 74 | } 75 | return r, path, filepath.Join(r.Dir, addin) 76 | } 77 | } 78 | c := exec.Command("go", "list", "-json", "-e", pkgname) 79 | c.Dir = m.Dir 80 | data, err := c.Output() 81 | if err == nil { 82 | var p Package 83 | err = json.Unmarshal(data, &p) 84 | if err == nil { 85 | add := &Module{Path: p.ImportPath, Dir: p.Dir} 86 | m.Require = append(m.Require, add) 87 | return add, p.ImportPath, p.Dir 88 | } 89 | } 90 | 91 | return nil, "", "" 92 | } 93 | 94 | type Module struct { 95 | Path string 96 | Version string 97 | Time string 98 | Dir string 99 | Main bool 100 | Replace *Module 101 | } 102 | 103 | func parseModuleJson(data []byte) ModuleList { 104 | var ms ModuleList 105 | var index int 106 | var tag int 107 | for i, v := range data { 108 | switch v { 109 | case '{': 110 | if tag == 0 { 111 | index = i 112 | } 113 | tag++ 114 | case '}': 115 | tag-- 116 | if tag == 0 { 117 | var m Module 118 | err := json.Unmarshal(data[index:i+1], &m) 119 | if err == nil { 120 | if m.Main { 121 | ms.Module = m 122 | } else { 123 | ms.Require = append(ms.Require, &m) 124 | } 125 | } 126 | } 127 | } 128 | } 129 | return ms 130 | } 131 | -------------------------------------------------------------------------------- /gopresent/static/dir.css: -------------------------------------------------------------------------------- 1 | /* copied from $GOROOT/doc/style.css */ 2 | 3 | body { 4 | margin: 0; 5 | font-family: Helvetica, Arial, sans-serif; 6 | font-size: 16px; 7 | } 8 | pre, 9 | code { 10 | font-family: Menlo, monospace; 11 | font-size: 14px; 12 | } 13 | pre { 14 | line-height: 18px; 15 | } 16 | pre .comment { 17 | color: #375EAB; 18 | } 19 | pre .highlight, 20 | pre .highlight-comment, 21 | pre .selection-highlight, 22 | pre .selection-highlight-comment { 23 | background: #FFFF00; 24 | } 25 | pre .selection, 26 | pre .selection-comment { 27 | background: #FF9632; 28 | } 29 | pre .ln { 30 | color: #999; 31 | } 32 | body { 33 | color: #222; 34 | } 35 | a, 36 | .exampleHeading .text { 37 | color: #375EAB; 38 | text-decoration: none; 39 | } 40 | a:hover, 41 | .exampleHeading .text:hover { 42 | text-decoration: underline; 43 | } 44 | p, 45 | pre, 46 | ul, 47 | ol { 48 | margin: 20px; 49 | } 50 | pre { 51 | background: #e9e9e9; 52 | padding: 10px; 53 | 54 | -webkit-border-radius: 5px; 55 | -moz-border-radius: 5px; 56 | border-radius: 5px; 57 | } 58 | 59 | h1, 60 | h2, 61 | h3, 62 | h4, 63 | .rootHeading { 64 | margin: 20px 0; 65 | padding: 0; 66 | color: #375EAB; 67 | font-weight: bold; 68 | } 69 | h1 { 70 | font-size: 24px; 71 | } 72 | h2 { 73 | font-size: 20px; 74 | background: #E0EBF5; 75 | padding: 2px 5px; 76 | } 77 | h3 { 78 | font-size: 20px; 79 | } 80 | h3, 81 | h4 { 82 | margin: 20px 5px; 83 | } 84 | h4 { 85 | font-size: 16px; 86 | } 87 | 88 | dl { 89 | margin: 20px; 90 | } 91 | dd { 92 | margin: 2px 20px; 93 | } 94 | dl, 95 | dd { 96 | font-size: 14px; 97 | } 98 | div#nav table td { 99 | vertical-align: top; 100 | } 101 | 102 | div#heading { 103 | float: left; 104 | margin: 0 0 10px 0; 105 | padding: 21px 0; 106 | font-size: 20px; 107 | font-weight: normal; 108 | } 109 | div#heading a { 110 | color: #222; 111 | text-decoration: none; 112 | } 113 | 114 | div#topbar { 115 | background: #E0EBF5; 116 | height: 64px; 117 | } 118 | 119 | body { 120 | text-align: center; 121 | } 122 | div#page, 123 | div#topbar > .container { 124 | clear: both; 125 | text-align: left; 126 | margin-left: auto; 127 | margin-right: auto; 128 | padding: 0 20px; 129 | width: 900px; 130 | } 131 | div#page.wide, 132 | div#topbar > .wide { 133 | width: auto; 134 | } 135 | div#plusone { 136 | float: right; 137 | } 138 | 139 | div#footer { 140 | color: #666; 141 | font-size: 14px; 142 | margin: 40px 0; 143 | } 144 | 145 | div#menu > a, 146 | div#menu > input { 147 | padding: 10px; 148 | 149 | text-decoration: none; 150 | font-size: 16px; 151 | 152 | -webkit-border-radius: 5px; 153 | -moz-border-radius: 5px; 154 | border-radius: 5px; 155 | } 156 | div#menu > a, 157 | div#menu > input { 158 | border: 1px solid #375EAB; 159 | } 160 | div#menu > a { 161 | color: white; 162 | background: #375EAB; 163 | } 164 | 165 | div#menu { 166 | float: right; 167 | min-width: 590px; 168 | padding: 10px 0; 169 | text-align: right; 170 | } 171 | div#menu > a { 172 | margin-right: 5px; 173 | margin-bottom: 10px; 174 | 175 | padding: 10px; 176 | } 177 | div#menu > input { 178 | position: relative; 179 | top: 1px; 180 | width: 60px; 181 | background: white; 182 | color: #222; 183 | } 184 | div#menu > input.inactive { 185 | color: #999; 186 | } 187 | -------------------------------------------------------------------------------- /finddecl/finddecl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 finddecl 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "go/ast" 11 | "go/parser" 12 | "go/token" 13 | "os" 14 | "path/filepath" 15 | "strconv" 16 | 17 | "github.com/visualfc/gotools/pkg/command" 18 | ) 19 | 20 | var Command = &command.Command{ 21 | Run: runFindDecl, 22 | UsageLine: "finddecl", 23 | Short: "golang finddecl util", 24 | Long: `golang finddecl util.`, 25 | } 26 | var ( 27 | filePath string 28 | fileLine int 29 | ) 30 | 31 | func init() { 32 | Command.Flag.StringVar(&filePath, "file", "", "file path") 33 | Command.Flag.IntVar(&fileLine, "line", -1, "file line") 34 | } 35 | 36 | func runFindDecl(cmd *command.Command, args []string) error { 37 | if len(filePath) == 0 || fileLine == -1 { 38 | cmd.Usage() 39 | return os.ErrInvalid 40 | } 41 | if !filepath.IsAbs(filePath) { 42 | dir, err := os.Getwd() 43 | if err != nil { 44 | return err 45 | } 46 | filePath = filepath.Join(dir, filePath) 47 | } 48 | 49 | fset := token.NewFileSet() 50 | f, err := parser.ParseFile(fset, filePath, nil, 0) 51 | if err != nil { 52 | return err 53 | } 54 | decl := findDecl(fset, f, fileLine) 55 | if decl == nil { 56 | fmt.Println("-") 57 | return errors.New("error find decl") 58 | } 59 | printDecl(fset, decl, fileLine) 60 | return nil 61 | } 62 | 63 | type Info struct { 64 | Type string 65 | Name string 66 | BeginLine int 67 | EndLine int 68 | } 69 | 70 | func printDecl(fset *token.FileSet, decl ast.Decl, line int) { 71 | var tag string 72 | var name string 73 | 74 | tag = "-" 75 | name = "-" 76 | switch d := decl.(type) { 77 | case *ast.GenDecl: 78 | switch d.Tok { 79 | case token.IMPORT: 80 | tag = "import" 81 | if len(d.Specs) > 0 { 82 | if ts := d.Specs[0].(*ast.ImportSpec); ts != nil { 83 | name, _ = strconv.Unquote(ts.Path.Value) 84 | } 85 | } 86 | case token.TYPE: 87 | tag = "type" 88 | if len(d.Specs) > 0 { 89 | if ts := d.Specs[0].(*ast.TypeSpec); ts != nil { 90 | name = ts.Name.Name 91 | switch ts.Type.(type) { 92 | case *ast.StructType: 93 | tag = "struct" 94 | case *ast.InterfaceType: 95 | tag = "interface" 96 | default: 97 | tag = "type" 98 | } 99 | } 100 | } 101 | case token.VAR, token.CONST: 102 | tag = d.Tok.String() 103 | var testName string 104 | for _, ds := range d.Specs { 105 | if ts := ds.(*ast.ValueSpec); ts != nil { 106 | name = ts.Names[0].Name 107 | for _, n := range ts.Names { 108 | if line >= fset.Position(n.Pos()).Line && line <= fset.Position(n.End()).Line { 109 | testName = n.Name 110 | break 111 | } 112 | } 113 | } 114 | } 115 | if testName != "" { 116 | name = testName 117 | } 118 | default: 119 | tag = d.Tok.String() 120 | } 121 | case *ast.FuncDecl: 122 | tag = "func" 123 | name = d.Name.Name 124 | } 125 | fmt.Println(tag, name, fset.Position(decl.Pos()).Line, fset.Position(decl.End()).Line) 126 | } 127 | 128 | func findDecl(fset *token.FileSet, file *ast.File, line int) ast.Decl { 129 | for _, decl := range file.Decls { 130 | if line >= fset.Position(decl.Pos()).Line && line <= fset.Position(decl.End()).Line { 131 | return decl 132 | } 133 | } 134 | return nil 135 | } 136 | -------------------------------------------------------------------------------- /pkg/stdlib/mkpkglist.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "go/format" 9 | "io/ioutil" 10 | "log" 11 | "os/exec" 12 | "strings" 13 | ) 14 | 15 | var basePkgList = ` 16 | archive/tar 17 | archive/zip 18 | bufio 19 | bytes 20 | compress/bzip2 21 | compress/flate 22 | compress/gzip 23 | compress/lzw 24 | compress/zlib 25 | container/heap 26 | container/list 27 | container/ring 28 | context 29 | crypto 30 | crypto/aes 31 | crypto/cipher 32 | crypto/des 33 | crypto/dsa 34 | crypto/ecdsa 35 | crypto/elliptic 36 | crypto/hmac 37 | crypto/md5 38 | crypto/rand 39 | crypto/rc4 40 | crypto/rsa 41 | crypto/sha1 42 | crypto/sha256 43 | crypto/sha512 44 | crypto/subtle 45 | crypto/tls 46 | crypto/x509 47 | crypto/x509/pkix 48 | database/sql 49 | database/sql/driver 50 | debug/dwarf 51 | debug/elf 52 | debug/gosym 53 | debug/macho 54 | debug/pe 55 | debug/plan9obj 56 | encoding 57 | encoding/ascii85 58 | encoding/asn1 59 | encoding/base32 60 | encoding/base64 61 | encoding/binary 62 | encoding/csv 63 | encoding/gob 64 | encoding/hex 65 | encoding/json 66 | encoding/pem 67 | encoding/xml 68 | errors 69 | expvar 70 | flag 71 | fmt 72 | go/ast 73 | go/build 74 | go/constant 75 | go/doc 76 | go/format 77 | go/importer 78 | go/parser 79 | go/printer 80 | go/scanner 81 | go/token 82 | go/types 83 | hash 84 | hash/adler32 85 | hash/crc32 86 | hash/crc64 87 | hash/fnv 88 | html 89 | html/template 90 | image 91 | image/color 92 | image/color/palette 93 | image/draw 94 | image/gif 95 | image/jpeg 96 | image/png 97 | index/suffixarray 98 | io 99 | io/ioutil 100 | log 101 | log/syslog 102 | math 103 | math/big 104 | math/bits 105 | math/cmplx 106 | math/rand 107 | mime 108 | mime/multipart 109 | mime/quotedprintable 110 | net 111 | net/http 112 | net/http/cgi 113 | net/http/cookiejar 114 | net/http/fcgi 115 | net/http/httptest 116 | net/http/httptrace 117 | net/http/httputil 118 | net/http/pprof 119 | net/mail 120 | net/rpc 121 | net/rpc/jsonrpc 122 | net/smtp 123 | net/textproto 124 | net/url 125 | os 126 | os/exec 127 | os/signal 128 | os/user 129 | path 130 | path/filepath 131 | plugin 132 | reflect 133 | regexp 134 | regexp/syntax 135 | runtime 136 | runtime/cgo 137 | runtime/debug 138 | runtime/pprof 139 | runtime/race 140 | runtime/trace 141 | sort 142 | strconv 143 | strings 144 | sync 145 | sync/atomic 146 | syscall 147 | testing 148 | testing/iotest 149 | testing/quick 150 | text/scanner 151 | text/tabwriter 152 | text/template 153 | text/template/parse 154 | time 155 | unicode 156 | unicode/utf16 157 | unicode/utf8 158 | unsafe 159 | ` 160 | 161 | func main() { 162 | cmd := exec.Command("go", "list", "std") 163 | out, err := cmd.Output() 164 | if err != nil { 165 | log.Fatalln(err) 166 | } 167 | var pkgList []string 168 | for _, v := range strings.Split(string(out), "\n") { 169 | if v == "" { 170 | continue 171 | } 172 | if strings.HasPrefix(v, "vendor/") { 173 | continue 174 | } 175 | pkgList = append(pkgList, v) 176 | } 177 | 178 | var list []string 179 | index := 0 180 | for _, v := range pkgList { 181 | v = strings.TrimSpace(v) 182 | if v == "" { 183 | continue 184 | } 185 | v = "\"" + v + "\"" 186 | if index%4 == 0 && index != 0 { 187 | v = "\n" + v 188 | } 189 | list = append(list, v) 190 | index++ 191 | } 192 | var buf bytes.Buffer 193 | outf := func(format string, args ...interface{}) { 194 | fmt.Fprintf(&buf, format, args...) 195 | } 196 | outf("// AUTO-GENERATED BY mkpkglist.go\n\n") 197 | outf("package stdlib\n") 198 | outf("var Packages = []string{\n") 199 | outf(strings.Join(list, ",")) 200 | outf("}\n") 201 | outf(` 202 | func IsStdPkg(pkg string) bool { 203 | for _, v := range Packages { 204 | if v == pkg { 205 | return true 206 | } 207 | } 208 | return false 209 | }`) 210 | fmtbuf, err := format.Source(buf.Bytes()) 211 | if err != nil { 212 | log.Fatal(err) 213 | } 214 | //os.Stdout.Write(fmtbuf) 215 | ioutil.WriteFile("./pkglist.go", fmtbuf, 0777) 216 | } 217 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= 2 | github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/visualfc/gomod v0.1.2 h1:7qfPmifcA8r/0ZTpTPZQqsm5aJUiQ/EeyHEENPyywDg= 6 | github.com/visualfc/gomod v0.1.2/go.mod h1:rV5goiA/Ul6yT8X2eDnc/dl0dVy0cDHJLZVOuJ8PdmM= 7 | github.com/visualfc/goversion v1.1.0 h1:EN0YQGRkeGoWTPxPNTnbhyNQyas5leKH5U5lL4t8lRE= 8 | github.com/visualfc/goversion v1.1.0/go.mod h1:Gr3s6bW8NTomhheImwAttqno97Mw6pAnFn2dU8/EMa8= 9 | github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= 10 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 12 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 13 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 14 | golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= 15 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 16 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 17 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 18 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 19 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 20 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 21 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 22 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 23 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 24 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 25 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 26 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 28 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= 30 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 32 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 33 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= 34 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 35 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 36 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 37 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 40 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 41 | golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= 42 | golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= 43 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | -------------------------------------------------------------------------------- /pkg/stdlib/pkglist.go: -------------------------------------------------------------------------------- 1 | // AUTO-GENERATED BY mkpkglist.go 2 | 3 | package stdlib 4 | 5 | var Packages = []string{ 6 | "archive/tar", "archive/zip", "bufio", "bytes", 7 | "compress/bzip2", "compress/flate", "compress/gzip", "compress/lzw", 8 | "compress/zlib", "container/heap", "container/list", "container/ring", 9 | "context", "crypto", "crypto/aes", "crypto/cipher", 10 | "crypto/des", "crypto/dsa", "crypto/ecdh", "crypto/ecdsa", 11 | "crypto/ed25519", "crypto/elliptic", "crypto/hmac", "crypto/internal/alias", 12 | "crypto/internal/bigmod", "crypto/internal/boring", "crypto/internal/boring/bbig", "crypto/internal/boring/bcache", 13 | "crypto/internal/boring/sig", "crypto/internal/edwards25519", "crypto/internal/edwards25519/field", "crypto/internal/nistec", 14 | "crypto/internal/nistec/fiat", "crypto/internal/randutil", "crypto/md5", "crypto/rand", 15 | "crypto/rc4", "crypto/rsa", "crypto/sha1", "crypto/sha256", 16 | "crypto/sha512", "crypto/subtle", "crypto/tls", "crypto/x509", 17 | "crypto/x509/internal/macos", "crypto/x509/pkix", "database/sql", "database/sql/driver", 18 | "debug/buildinfo", "debug/dwarf", "debug/elf", "debug/gosym", 19 | "debug/macho", "debug/pe", "debug/plan9obj", "embed", 20 | "embed/internal/embedtest", "encoding", "encoding/ascii85", "encoding/asn1", 21 | "encoding/base32", "encoding/base64", "encoding/binary", "encoding/csv", 22 | "encoding/gob", "encoding/hex", "encoding/json", "encoding/pem", 23 | "encoding/xml", "errors", "expvar", "flag", 24 | "fmt", "go/ast", "go/build", "go/build/constraint", 25 | "go/constant", "go/doc", "go/doc/comment", "go/format", 26 | "go/importer", "go/internal/gccgoimporter", "go/internal/gcimporter", "go/internal/srcimporter", 27 | "go/internal/typeparams", "go/parser", "go/printer", "go/scanner", 28 | "go/token", "go/types", "hash", "hash/adler32", 29 | "hash/crc32", "hash/crc64", "hash/fnv", "hash/maphash", 30 | "html", "html/template", "image", "image/color", 31 | "image/color/palette", "image/draw", "image/gif", "image/internal/imageutil", 32 | "image/jpeg", "image/png", "index/suffixarray", "internal/abi", 33 | "internal/buildcfg", "internal/bytealg", "internal/cfg", "internal/coverage", 34 | "internal/coverage/calloc", "internal/coverage/cformat", "internal/coverage/cmerge", "internal/coverage/decodecounter", 35 | "internal/coverage/decodemeta", "internal/coverage/encodecounter", "internal/coverage/encodemeta", "internal/coverage/pods", 36 | "internal/coverage/rtcov", "internal/coverage/slicereader", "internal/coverage/slicewriter", "internal/coverage/stringtab", 37 | "internal/coverage/test", "internal/coverage/uleb128", "internal/cpu", "internal/dag", 38 | "internal/diff", "internal/fmtsort", "internal/fuzz", "internal/goarch", 39 | "internal/godebug", "internal/goexperiment", "internal/goos", "internal/goroot", 40 | "internal/goversion", "internal/intern", "internal/itoa", "internal/lazyregexp", 41 | "internal/lazytemplate", "internal/nettrace", "internal/obscuretestdata", "internal/oserror", 42 | "internal/pkgbits", "internal/platform", "internal/poll", "internal/profile", 43 | "internal/race", "internal/reflectlite", "internal/safefilepath", "internal/saferio", 44 | "internal/singleflight", "internal/syscall/execenv", "internal/syscall/unix", "internal/sysinfo", 45 | "internal/testenv", "internal/testlog", "internal/testpty", "internal/trace", 46 | "internal/txtar", "internal/types/errors", "internal/unsafeheader", "internal/xcoff", 47 | "io", "io/fs", "io/ioutil", "log", 48 | "log/syslog", "math", "math/big", "math/bits", 49 | "math/cmplx", "math/rand", "mime", "mime/multipart", 50 | "mime/quotedprintable", "net", "net/http", "net/http/cgi", 51 | "net/http/cookiejar", "net/http/fcgi", "net/http/httptest", "net/http/httptrace", 52 | "net/http/httputil", "net/http/internal", "net/http/internal/ascii", "net/http/internal/testcert", 53 | "net/http/pprof", "net/internal/socktest", "net/mail", "net/netip", 54 | "net/rpc", "net/rpc/jsonrpc", "net/smtp", "net/textproto", 55 | "net/url", "os", "os/exec", "os/exec/internal/fdtest", 56 | "os/signal", "os/user", "path", "path/filepath", 57 | "plugin", "reflect", "reflect/internal/example1", "reflect/internal/example2", 58 | "regexp", "regexp/syntax", "runtime", "runtime/cgo", 59 | "runtime/coverage", "runtime/debug", "runtime/internal/atomic", "runtime/internal/math", 60 | "runtime/internal/startlinetest", "runtime/internal/sys", "runtime/metrics", "runtime/pprof", 61 | "runtime/race", "runtime/race/internal/amd64v1", "runtime/trace", "sort", 62 | "strconv", "strings", "sync", "sync/atomic", 63 | "syscall", "testing", "testing/fstest", "testing/internal/testdeps", 64 | "testing/iotest", "testing/quick", "text/scanner", "text/tabwriter", 65 | "text/template", "text/template/parse", "time", "time/tzdata", 66 | "unicode", "unicode/utf16", "unicode/utf8", "unsafe"} 67 | 68 | func IsStdPkg(pkg string) bool { 69 | for _, v := range Packages { 70 | if v == pkg { 71 | return true 72 | } 73 | } 74 | return false 75 | } 76 | -------------------------------------------------------------------------------- /jsonfmt/jsonfmt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 jsonfmt 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | 17 | "github.com/visualfc/gotools/pkg/command" 18 | "github.com/visualfc/gotools/pkg/godiff" 19 | ) 20 | 21 | var Command = &command.Command{ 22 | Run: runJsonFmt, 23 | UsageLine: "jsonfmt", 24 | Short: "json format util", 25 | Long: `json format util.`, 26 | } 27 | 28 | var ( 29 | jsonFmtList bool 30 | jsonFmtCompact bool 31 | jsonFmtWrite bool 32 | jsonFmtDiff bool 33 | jsonTabWidth int 34 | jsonTabIndent bool 35 | ) 36 | 37 | func init() { 38 | Command.Flag.BoolVar(&jsonFmtList, "l", false, "list files whose formatting differs") 39 | Command.Flag.BoolVar(&jsonFmtCompact, "c", false, "compact json") 40 | Command.Flag.BoolVar(&jsonFmtWrite, "w", false, "write result to (source) file instead of stdout") 41 | Command.Flag.BoolVar(&jsonFmtDiff, "d", false, "display diffs instead of rewriting files") 42 | Command.Flag.IntVar(&jsonTabWidth, "tabwidth", 4, "tab width") 43 | Command.Flag.BoolVar(&jsonTabIndent, "tabs", false, "indent with tabs") 44 | } 45 | 46 | func runJsonFmt(cmd *command.Command, args []string) error { 47 | opt := &JsonFmtOption{} 48 | opt.List = jsonFmtList 49 | opt.Compact = jsonFmtCompact 50 | opt.IndentTab = jsonTabIndent 51 | opt.TabWidth = jsonTabWidth 52 | opt.Write = jsonFmtWrite 53 | opt.Diff = jsonFmtDiff 54 | 55 | if len(args) == 0 { 56 | if err := processJsonFile("", cmd.Stdin, cmd.Stdout, true, opt); err != nil { 57 | return err 58 | } 59 | } else { 60 | for _, path := range args { 61 | switch dir, err := os.Stat(path); { 62 | case err != nil: 63 | reportJsonError(err) 64 | case dir.IsDir(): 65 | filepath.Walk(path, func(path string, f os.FileInfo, err error) error { 66 | if err == nil && isJsonFile(f) { 67 | err = processJsonFile(path, nil, cmd.Stdout, false, opt) 68 | } 69 | if err != nil { 70 | reportJsonError(err) 71 | } 72 | return nil 73 | }) 74 | default: 75 | if err := processJsonFile(path, nil, cmd.Stdout, false, opt); err != nil { 76 | reportJsonError(err) 77 | } 78 | } 79 | } 80 | } 81 | return nil 82 | } 83 | 84 | type JsonFmtOption struct { 85 | List bool 86 | Compact bool 87 | Format bool 88 | Write bool 89 | Diff bool 90 | IndentTab bool 91 | TabWidth int 92 | } 93 | 94 | func isJsonFile(f os.FileInfo) bool { 95 | // ignore non-Go files 96 | name := f.Name() 97 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".json") 98 | } 99 | 100 | func reportJsonError(err error) { 101 | fmt.Fprintf(os.Stderr, "%s\n", err) 102 | } 103 | 104 | func processJson(filename string, src []byte, opt *JsonFmtOption) ([]byte, error) { 105 | if opt.Compact { 106 | var out bytes.Buffer 107 | err := json.Compact(&out, src) 108 | if err != nil { 109 | return nil, err 110 | } 111 | return out.Bytes(), nil 112 | } else { 113 | var out bytes.Buffer 114 | var err error 115 | if opt.IndentTab { 116 | err = json.Indent(&out, src, "", "\t") 117 | } else { 118 | var indent string 119 | for i := 0; i < opt.TabWidth; i++ { 120 | indent += " " 121 | } 122 | err = json.Indent(&out, src, "", indent) 123 | } 124 | if err != nil { 125 | return nil, err 126 | } 127 | return out.Bytes(), nil 128 | } 129 | return src, nil 130 | } 131 | 132 | func processJsonFile(filename string, in io.Reader, out io.Writer, stdin bool, opt *JsonFmtOption) error { 133 | if in == nil { 134 | f, err := os.Open(filename) 135 | if err != nil { 136 | return err 137 | } 138 | defer f.Close() 139 | in = f 140 | } 141 | 142 | src, err := ioutil.ReadAll(in) 143 | if err != nil { 144 | return err 145 | } 146 | 147 | res, err := processJson(filename, src, opt) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | if !bytes.Equal(src, res) { 153 | // formatting has changed 154 | if opt.List { 155 | fmt.Fprintln(out, filename) 156 | } 157 | if opt.Write { 158 | err = ioutil.WriteFile(filename, res, 0) 159 | if err != nil { 160 | return err 161 | } 162 | } 163 | if opt.Diff { 164 | data, err := diffJson(src, res) 165 | if err != nil { 166 | return fmt.Errorf("computing diff: %s", err) 167 | } 168 | fmt.Fprintf(out, "diff %s json/%s\n", filename, filename) 169 | out.Write(data) 170 | } 171 | } 172 | 173 | if !opt.List && !opt.Write && !opt.Diff { 174 | _, err = out.Write(res) 175 | } 176 | 177 | return err 178 | } 179 | 180 | func diffJson(src, res []byte) (data []byte, err error) { 181 | var dataTmp string // because godiff.UnifiedDiffString returns string 182 | dataTmp, err = godiff.UnifiedDiffString(string(src), string(res)) 183 | data = []byte(dataTmp) 184 | return 185 | } 186 | -------------------------------------------------------------------------------- /gofmt/gofmt.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 gofmt 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "go/token" 11 | "io" 12 | "io/ioutil" 13 | "os" 14 | "path/filepath" 15 | "runtime" 16 | "strings" 17 | "sync" 18 | 19 | "github.com/visualfc/gotools/pkg/command" 20 | "github.com/visualfc/gotools/pkg/godiff" 21 | "golang.org/x/tools/imports" 22 | ) 23 | 24 | var Command = &command.Command{ 25 | Run: runGofmt, 26 | UsageLine: "gofmt [flags] [path ...]", 27 | Short: "gofmt formats Go source.", 28 | Long: `gofmt formats Go source`, 29 | } 30 | 31 | var ( 32 | gofmtList bool 33 | gofmtWrite bool 34 | gofmtDiff bool 35 | gofmtAllErrors bool 36 | gofmtFixImports bool 37 | gofmtSortImports bool 38 | gofmtUseGodiffLib bool 39 | 40 | // layout control 41 | gofmtComments bool 42 | gofmtTabWidth int 43 | gofmtTabIndent bool 44 | ) 45 | 46 | //func init 47 | func init() { 48 | Command.Flag.BoolVar(&gofmtList, "l", false, "list files whose formatting differs from goimport's") 49 | Command.Flag.BoolVar(&gofmtWrite, "w", false, "write result to (source) file instead of stdout") 50 | Command.Flag.BoolVar(&gofmtDiff, "d", false, "display diffs instead of rewriting files") 51 | Command.Flag.BoolVar(&gofmtAllErrors, "e", false, "report all errors (not just the first 10 on different lines)") 52 | Command.Flag.BoolVar(&gofmtFixImports, "fiximports", false, "updates Go import lines, adding missing ones and removing unreferenced ones") 53 | Command.Flag.BoolVar(&gofmtSortImports, "sortimports", false, "sort Go import lines use goimports style") 54 | Command.Flag.BoolVar(&gofmtUseGodiffLib, "godiff", true, "diff use godiff library") 55 | 56 | // layout control 57 | Command.Flag.BoolVar(&gofmtComments, "comments", true, "print comments") 58 | Command.Flag.IntVar(&gofmtTabWidth, "tabwidth", 8, "tab width") 59 | Command.Flag.BoolVar(&gofmtTabIndent, "tabs", true, "indent with tabs") 60 | } 61 | 62 | var ( 63 | fileSet = token.NewFileSet() // per process FileSet 64 | exitCode = 0 65 | 66 | initModesOnce sync.Once // guards calling initModes 67 | //parserMode parser.Mode 68 | //printerMode printer.Mode 69 | options *imports.Options 70 | ) 71 | 72 | func runGofmt(cmd *command.Command, args []string) error { 73 | runtime.GOMAXPROCS(runtime.NumCPU()) 74 | 75 | if gofmtTabWidth < 0 { 76 | return os.ErrInvalid 77 | } 78 | 79 | if gofmtFixImports { 80 | gofmtSortImports = true 81 | } 82 | 83 | options = &imports.Options{ 84 | FormatOnly: !gofmtFixImports, 85 | TabWidth: gofmtTabWidth, 86 | TabIndent: gofmtTabIndent, 87 | Comments: gofmtComments, 88 | AllErrors: gofmtAllErrors, 89 | Fragment: true, 90 | } 91 | 92 | if len(args) == 0 { 93 | return processFile("", cmd.Stdin, cmd.Stdout, true) 94 | } 95 | for _, path := range args { 96 | switch dir, err := os.Stat(path); { 97 | case err != nil: 98 | fmt.Fprintln(cmd.Stderr, err) 99 | case dir.IsDir(): 100 | walkDir(path) 101 | default: 102 | if err := processFile(path, nil, cmd.Stdout, false); err != nil { 103 | fmt.Fprintln(cmd.Stderr, err) 104 | } 105 | } 106 | } 107 | return nil 108 | } 109 | 110 | func isGoFile(f os.FileInfo) bool { 111 | // ignore non-Go files 112 | name := f.Name() 113 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") 114 | } 115 | 116 | func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error { 117 | var src []byte 118 | var err error 119 | if in == nil { 120 | src, err = ioutil.ReadFile(filename) 121 | } else { 122 | src, err = ioutil.ReadAll(in) 123 | } 124 | if err != nil { 125 | return err 126 | } 127 | 128 | res, err := imports.Process(filename, src, options) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | if !bytes.Equal(src, res) { 134 | // formatting has changed 135 | if gofmtList { 136 | fmt.Fprintln(out, filename) 137 | } 138 | if gofmtWrite { 139 | err = ioutil.WriteFile(filename, res, 0) 140 | if err != nil { 141 | return err 142 | } 143 | } 144 | if gofmtDiff { 145 | var data []byte 146 | var err error 147 | if gofmtUseGodiffLib { 148 | var dataTmp string // because godiff.UnifiedDiffString returns string 149 | dataTmp, err = godiff.UnifiedDiffString(string(src), string(res)) 150 | data = []byte(dataTmp) 151 | } else { 152 | data, err = godiff.UnifiedDiffBytesByCmd(src, res) 153 | } 154 | if err != nil { 155 | return fmt.Errorf("computing diff: %s", err) 156 | } 157 | fmt.Fprintf(out, "diff %s gofmt/%s\n", filename, filename) 158 | out.Write(data) 159 | } 160 | } 161 | 162 | if !gofmtList && !gofmtWrite && !gofmtDiff { 163 | _, err = out.Write(res) 164 | } 165 | 166 | return err 167 | } 168 | 169 | func visitFile(path string, f os.FileInfo, err error) error { 170 | if err == nil && isGoFile(f) { 171 | err = processFile(path, nil, os.Stdout, false) 172 | } 173 | return err 174 | } 175 | 176 | func walkDir(path string) { 177 | filepath.Walk(path, visitFile) 178 | } 179 | -------------------------------------------------------------------------------- /pkg/pkgwalk/allpackages.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 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 buildutil provides utilities related to the go/build 6 | // package in the standard library. 7 | // 8 | // All I/O is done via the build.Context file system interface, which must 9 | // be concurrency-safe. 10 | package pkgwalk 11 | 12 | import ( 13 | "go/build" 14 | "io/ioutil" 15 | "os" 16 | "path/filepath" 17 | "sort" 18 | "strings" 19 | "sync" 20 | ) 21 | 22 | // AllPackages returns the package path of each Go package in any source 23 | // directory of the specified build context (e.g. $GOROOT or an element 24 | // of $GOPATH). Errors are ignored. The results are sorted. 25 | // All package paths are canonical, and thus may contain "/vendor/". 26 | // 27 | // The result may include import paths for directories that contain no 28 | // *.go files, such as "archive" (in $GOROOT/src). 29 | // 30 | // All I/O is done via the build.Context file system interface, 31 | // which must be concurrency-safe. 32 | // 33 | func AllPackages(ctxt *build.Context) []string { 34 | var list []string 35 | ForEachPackage(ctxt, func(pkg string, _ error) { 36 | list = append(list, pkg) 37 | }) 38 | sort.Strings(list) 39 | return list 40 | } 41 | 42 | // ForEachPackage calls the found function with the package path of 43 | // each Go package it finds in any source directory of the specified 44 | // build context (e.g. $GOROOT or an element of $GOPATH). 45 | // All package paths are canonical, and thus may contain "/vendor/". 46 | // 47 | // If the package directory exists but could not be read, the second 48 | // argument to the found function provides the error. 49 | // 50 | // All I/O is done via the build.Context file system interface, 51 | // which must be concurrency-safe. 52 | // 53 | func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { 54 | ch := make(chan item) 55 | 56 | var wg sync.WaitGroup 57 | for _, root := range ctxt.SrcDirs() { 58 | root := root 59 | wg.Add(1) 60 | go func() { 61 | allPackages(ctxt, root, ch) 62 | wg.Done() 63 | }() 64 | } 65 | go func() { 66 | wg.Wait() 67 | close(ch) 68 | }() 69 | 70 | // All calls to found occur in the caller's goroutine. 71 | for i := range ch { 72 | found(i.importPath, i.err) 73 | } 74 | } 75 | 76 | type item struct { 77 | importPath string 78 | err error // (optional) 79 | } 80 | 81 | // We use a process-wide counting semaphore to limit 82 | // the number of parallel calls to ReadDir. 83 | var ioLimit = make(chan bool, 20) 84 | 85 | func allPackages(ctxt *build.Context, root string, ch chan<- item) { 86 | root = filepath.Clean(root) + string(os.PathSeparator) 87 | 88 | var wg sync.WaitGroup 89 | 90 | var walkDir func(dir string) 91 | walkDir = func(dir string) { 92 | // Avoid .foo, _foo, and testdata directory trees. 93 | base := filepath.Base(dir) 94 | if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" { 95 | return 96 | } 97 | 98 | pkg := filepath.ToSlash(strings.TrimPrefix(dir, root)) 99 | 100 | // Prune search if we encounter any of these import paths. 101 | switch pkg { 102 | case "builtin": 103 | return 104 | } 105 | 106 | ioLimit <- true 107 | files, err := ReadDir(ctxt, dir) 108 | <-ioLimit 109 | if pkg != "" || err != nil { 110 | ch <- item{pkg, err} 111 | } 112 | for _, fi := range files { 113 | fi := fi 114 | if fi.IsDir() { 115 | wg.Add(1) 116 | go func() { 117 | walkDir(filepath.Join(dir, fi.Name())) 118 | wg.Done() 119 | }() 120 | } 121 | } 122 | } 123 | 124 | walkDir(root) 125 | wg.Wait() 126 | } 127 | 128 | // ExpandPatterns returns the set of packages matched by patterns, 129 | // which may have the following forms: 130 | // 131 | // golang.org/x/tools/cmd/guru # a single package 132 | // golang.org/x/tools/... # all packages beneath dir 133 | // ... # the entire workspace. 134 | // 135 | // Order is significant: a pattern preceded by '-' removes matching 136 | // packages from the set. For example, these patterns match all encoding 137 | // packages except encoding/xml: 138 | // 139 | // encoding/... -encoding/xml 140 | // 141 | func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool { 142 | // TODO(adonovan): support other features of 'go list': 143 | // - "std"/"cmd"/"all" meta-packages 144 | // - "..." not at the end of a pattern 145 | // - relative patterns using "./" or "../" prefix 146 | 147 | pkgs := make(map[string]bool) 148 | doPkg := func(pkg string, neg bool) { 149 | if neg { 150 | delete(pkgs, pkg) 151 | } else { 152 | pkgs[pkg] = true 153 | } 154 | } 155 | 156 | // Scan entire workspace if wildcards are present. 157 | // TODO(adonovan): opt: scan only the necessary subtrees of the workspace. 158 | var all []string 159 | for _, arg := range patterns { 160 | if strings.HasSuffix(arg, "...") { 161 | all = AllPackages(ctxt) 162 | break 163 | } 164 | } 165 | 166 | for _, arg := range patterns { 167 | if arg == "" { 168 | continue 169 | } 170 | 171 | neg := arg[0] == '-' 172 | if neg { 173 | arg = arg[1:] 174 | } 175 | 176 | if arg == "..." { 177 | // ... matches all packages 178 | for _, pkg := range all { 179 | doPkg(pkg, neg) 180 | } 181 | } else if dir := strings.TrimSuffix(arg, "/..."); dir != arg { 182 | // dir/... matches all packages beneath dir 183 | for _, pkg := range all { 184 | if strings.HasPrefix(pkg, dir) && 185 | (len(pkg) == len(dir) || pkg[len(dir)] == '/') { 186 | doPkg(pkg, neg) 187 | } 188 | } 189 | } else { 190 | // single package 191 | doPkg(arg, neg) 192 | } 193 | } 194 | 195 | return pkgs 196 | } 197 | 198 | func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) { 199 | if ctxt.ReadDir != nil { 200 | return ctxt.ReadDir(path) 201 | } 202 | return ioutil.ReadDir(path) 203 | } 204 | -------------------------------------------------------------------------------- /pkg/srcimporter/srcimporter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 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 srcimporter implements importing directly 6 | // from source files rather than installed packages. 7 | package srcimporter 8 | 9 | import ( 10 | "fmt" 11 | "go/ast" 12 | "go/build" 13 | "go/importer" 14 | "go/parser" 15 | "go/token" 16 | "go/types" 17 | "io" 18 | "os" 19 | "path/filepath" 20 | "sync" 21 | ) 22 | 23 | // An Importer provides the context for importing packages from source code. 24 | type Importer struct { 25 | ctxt *build.Context 26 | fset *token.FileSet 27 | sizes types.Sizes 28 | packages map[string]*types.Package 29 | } 30 | 31 | // NewImporter returns a new Importer for the given context, file set, and map 32 | // of packages. The context is used to resolve import paths to package paths, 33 | // and identifying the files belonging to the package. If the context provides 34 | // non-nil file system functions, they are used instead of the regular package 35 | // os functions. The file set is used to track position information of package 36 | // files; and imported packages are added to the packages map. 37 | func New(ctxt *build.Context, fset *token.FileSet, packages map[string]*types.Package) *Importer { 38 | return &Importer{ 39 | ctxt: ctxt, 40 | fset: fset, 41 | sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH), // uses go/types default if GOARCH not found 42 | packages: packages, 43 | } 44 | } 45 | 46 | // Importing is a sentinel taking the place in Importer.packages 47 | // for a package that is in the process of being imported. 48 | var importing types.Package 49 | 50 | // Import(path) is a shortcut for ImportFrom(path, ".", 0). 51 | func (p *Importer) Import(path string) (*types.Package, error) { 52 | return p.ImportFrom(path, ".", 0) // use "." rather than "" (see issue #24441) 53 | } 54 | 55 | // ImportFrom imports the package with the given import path resolved from the given srcDir, 56 | // adds the new package to the set of packages maintained by the importer, and returns the 57 | // package. Package path resolution and file system operations are controlled by the context 58 | // maintained with the importer. The import mode must be zero but is otherwise ignored. 59 | // Packages that are not comprised entirely of pure Go files may fail to import because the 60 | // type checker may not be able to determine all exported entities (e.g. due to cgo dependencies). 61 | func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) { 62 | if mode != 0 { 63 | panic("non-zero import mode") 64 | } 65 | 66 | // if abs, err := p.absPath(srcDir); err == nil { // see issue #14282 67 | // srcDir = abs 68 | // } 69 | var bp *build.Package 70 | var err error 71 | if srcDir != "" && srcDir != "." { 72 | bp, err = p.ctxt.ImportDir(srcDir, 0) 73 | bp.ImportPath = path 74 | } else { 75 | bp, err = p.ctxt.Import(path, srcDir, 0) 76 | } 77 | if err != nil { 78 | return nil, err // err may be *build.NoGoError - return as is 79 | } 80 | 81 | // package unsafe is known to the type checker 82 | if bp.ImportPath == "unsafe" { 83 | return types.Unsafe, nil 84 | } 85 | 86 | // no need to re-import if the package was imported completely before 87 | pkg := p.packages[bp.ImportPath] 88 | if pkg != nil { 89 | if pkg == &importing { 90 | return nil, fmt.Errorf("import cycle through package %q", bp.ImportPath) 91 | } 92 | if !pkg.Complete() { 93 | // Package exists but is not complete - we cannot handle this 94 | // at the moment since the source importer replaces the package 95 | // wholesale rather than augmenting it (see #19337 for details). 96 | // Return incomplete package with error (see #16088). 97 | return pkg, fmt.Errorf("reimported partially imported package %q", bp.ImportPath) 98 | } 99 | return pkg, nil 100 | } 101 | 102 | p.packages[bp.ImportPath] = &importing 103 | defer func() { 104 | // clean up in case of error 105 | // TODO(gri) Eventually we may want to leave a (possibly empty) 106 | // package in the map in all cases (and use that package to 107 | // identify cycles). See also issue 16088. 108 | if p.packages[bp.ImportPath] == &importing { 109 | p.packages[bp.ImportPath] = nil 110 | } 111 | }() 112 | 113 | var filenames []string 114 | filenames = append(filenames, bp.GoFiles...) 115 | filenames = append(filenames, bp.CgoFiles...) 116 | 117 | files, err := p.parseFiles(bp.Dir, filenames) 118 | if err != nil { 119 | return nil, err 120 | } 121 | 122 | // type-check package files 123 | var firstHardErr error 124 | conf := types.Config{ 125 | IgnoreFuncBodies: true, 126 | FakeImportC: true, 127 | // continue type-checking after the first error 128 | Error: func(err error) { 129 | if firstHardErr == nil && !err.(types.Error).Soft { 130 | firstHardErr = err 131 | } 132 | }, 133 | Importer: importer.Default(), 134 | Sizes: p.sizes, 135 | } 136 | pkg, err = conf.Check(bp.ImportPath, p.fset, files, nil) 137 | if pkg == nil { 138 | // If there was a hard error it is possibly unsafe 139 | // to use the package as it may not be fully populated. 140 | // Do not return it (see also #20837, #20855). 141 | if firstHardErr != nil { 142 | err = firstHardErr // give preference to first hard error over any soft error 143 | } 144 | return pkg, fmt.Errorf("type-checking package %q failed (%v)", bp.ImportPath, err) 145 | } 146 | // if firstHardErr != nil { 147 | // // this can only happen if we have a bug in go/types 148 | // panic("package is not safe yet no error was returned") 149 | // } 150 | 151 | p.packages[bp.ImportPath] = pkg 152 | return pkg, nil 153 | } 154 | 155 | func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) { 156 | // use build.Context's OpenFile if there is one 157 | open := p.ctxt.OpenFile 158 | if open == nil { 159 | open = func(name string) (io.ReadCloser, error) { return os.Open(name) } 160 | } 161 | 162 | files := make([]*ast.File, len(filenames)) 163 | errors := make([]error, len(filenames)) 164 | 165 | var wg sync.WaitGroup 166 | wg.Add(len(filenames)) 167 | for i, filename := range filenames { 168 | go func(i int, filepath string) { 169 | defer wg.Done() 170 | src, err := open(filepath) 171 | if err != nil { 172 | errors[i] = err // open provides operation and filename in error 173 | return 174 | } 175 | files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0) 176 | src.Close() // ignore Close error - parsing may have succeeded which is all we need 177 | }(i, p.joinPath(dir, filename)) 178 | } 179 | wg.Wait() 180 | 181 | // if there are errors, return the first one for deterministic results 182 | for _, err := range errors { 183 | if err != nil { 184 | return nil, err 185 | } 186 | } 187 | 188 | return files, nil 189 | } 190 | 191 | // context-controlled file system operations 192 | 193 | func (p *Importer) absPath(path string) (string, error) { 194 | // TODO(gri) This should be using p.ctxt.AbsPath which doesn't 195 | // exist but probably should. See also issue #14282. 196 | return filepath.Abs(path) 197 | } 198 | 199 | func (p *Importer) isAbsPath(path string) bool { 200 | if f := p.ctxt.IsAbsPath; f != nil { 201 | return f(path) 202 | } 203 | return filepath.IsAbs(path) 204 | } 205 | 206 | func (p *Importer) joinPath(elem ...string) string { 207 | if f := p.ctxt.JoinPath; f != nil { 208 | return f(elem...) 209 | } 210 | return filepath.Join(elem...) 211 | } 212 | -------------------------------------------------------------------------------- /pkg/command/command.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 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 | //modify 2013-2014 visualfc 6 | 7 | package command 8 | 9 | import ( 10 | "bytes" 11 | "flag" 12 | "fmt" 13 | "io" 14 | "log" 15 | "os" 16 | "strings" 17 | "text/template" 18 | "unicode" 19 | "unicode/utf8" 20 | ) 21 | 22 | // A Command is an implementation of a go command 23 | // like go build or go fix. 24 | type Command struct { 25 | // Run runs the command. 26 | // The args are the arguments after the command name. 27 | Run func(cmd *Command, args []string) error 28 | 29 | // UsageLine is the one-line usage message. 30 | // The first word in the line is taken to be the command name. 31 | UsageLine string 32 | 33 | // Short is the short description shown in the 'go help' output. 34 | Short string 35 | 36 | // Long is the long message shown in the 'go help ' output. 37 | Long string 38 | 39 | // Flag is a set of flags specific to this command. 40 | Flag flag.FlagSet 41 | 42 | // CustomFlags indicates that the command will do its own 43 | // flag parsing. 44 | CustomFlags bool 45 | 46 | Stdin io.Reader 47 | Stdout io.Writer 48 | Stderr io.Writer 49 | } 50 | 51 | // Name returns the command's name: the first word in the usage line. 52 | func (c *Command) Name() string { 53 | name := c.UsageLine 54 | i := strings.Index(name, " ") 55 | if i >= 0 { 56 | name = name[:i] 57 | } 58 | return name 59 | } 60 | 61 | func (c *Command) Usage() { 62 | fmt.Fprintf(c.Stderr, "usage: %s %s\n", AppName, c.UsageLine) 63 | c.Flag.SetOutput(c.Stderr) 64 | c.Flag.PrintDefaults() 65 | //fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) 66 | //os.Exit(2) 67 | } 68 | 69 | func (c *Command) PrintUsage() { 70 | fmt.Fprintf(c.Stderr, "usage: %s %s\n", AppName, c.UsageLine) 71 | c.Flag.SetOutput(c.Stderr) 72 | c.Flag.PrintDefaults() 73 | } 74 | 75 | // Runnable reports whether the command can be run; otherwise 76 | // it is a documentation pseudo-command such as importpath. 77 | func (c *Command) Runnable() bool { 78 | return c.Run != nil 79 | } 80 | 81 | func (c *Command) Println(args ...interface{}) { 82 | fmt.Fprintln(c.Stdout, args...) 83 | } 84 | 85 | func (c *Command) Printf(format string, args ...interface{}) { 86 | fmt.Fprintf(c.Stdout, format, args...) 87 | } 88 | 89 | var commands []*Command 90 | 91 | func Register(cmd *Command) { 92 | commands = append(commands, cmd) 93 | } 94 | 95 | func CommandList() (cmds []string) { 96 | for _, cmd := range commands { 97 | cmds = append(cmds, cmd.Name()) 98 | } 99 | return 100 | } 101 | 102 | var ( 103 | Stdout io.Writer = os.Stdout 104 | Stderr io.Writer = os.Stderr 105 | Stdin io.Reader = os.Stdin 106 | ) 107 | 108 | func RunArgs(arguments []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 109 | flag.CommandLine.VisitAll(func(f *flag.Flag) { 110 | f.Value.Set(f.DefValue) 111 | }) 112 | flag.CommandLine.Parse(arguments) 113 | args := flag.Args() 114 | if len(args) < 1 { 115 | printUsage(os.Stderr) 116 | return os.ErrInvalid 117 | } 118 | 119 | if len(args) == 1 && strings.TrimSpace(args[0]) == "" { 120 | printUsage(os.Stderr) 121 | return os.ErrInvalid 122 | } 123 | 124 | if args[0] == "help" { 125 | if !help(args[1:]) { 126 | return os.ErrInvalid 127 | } 128 | return nil 129 | } 130 | 131 | for _, cmd := range commands { 132 | if cmd.Name() == args[0] && cmd.Run != nil { 133 | cmd.Flag.VisitAll(func(f *flag.Flag) { 134 | f.Value.Set(f.DefValue) 135 | }) 136 | cmd.Flag.Usage = func() { cmd.Usage() } 137 | cmd.Stdin = stdin 138 | cmd.Stdout = stdout 139 | cmd.Stderr = stderr 140 | if cmd.CustomFlags { 141 | args = args[1:] 142 | } else { 143 | err := cmd.Flag.Parse(args[1:]) 144 | if err != nil { 145 | return err 146 | } 147 | args = cmd.Flag.Args() 148 | } 149 | return cmd.Run(cmd, args) 150 | } 151 | } 152 | 153 | fmt.Fprintf(os.Stderr, "%s: unknown subcommand %q\nRun '%s help' for usage.\n", 154 | AppName, args[0], AppName) 155 | return os.ErrInvalid 156 | } 157 | 158 | func Main() { 159 | flag.Usage = func() { 160 | printUsage(os.Stderr) 161 | } 162 | flag.Parse() 163 | log.SetFlags(0) 164 | 165 | args := flag.Args() 166 | if len(args) < 1 { 167 | flag.Usage() 168 | Exit(2) 169 | } 170 | 171 | if len(args) == 1 && strings.TrimSpace(args[0]) == "" { 172 | flag.Usage() 173 | Exit(2) 174 | } 175 | 176 | if args[0] == "help" { 177 | if !help(args[1:]) { 178 | os.Exit(2) 179 | } 180 | return 181 | } 182 | 183 | for _, cmd := range commands { 184 | if cmd.Name() == args[0] && cmd.Run != nil { 185 | cmd.Stdin = Stdin 186 | cmd.Stdout = Stdout 187 | cmd.Stderr = Stderr 188 | if cmd.CustomFlags { 189 | args = args[1:] 190 | } else { 191 | err := cmd.Flag.Parse(args[1:]) 192 | if err != nil { 193 | Exit(2) 194 | } 195 | args = cmd.Flag.Args() 196 | } 197 | cmd.Flag.Usage = func() { cmd.Usage() } 198 | err := cmd.Run(cmd, args) 199 | if err != nil { 200 | fmt.Fprintln(cmd.Stderr, err) 201 | Exit(2) 202 | } 203 | Exit(0) 204 | return 205 | } 206 | } 207 | 208 | fmt.Fprintf(os.Stderr, "%s: unknown subcommand %q\nRun '%s help' for usage.\n", 209 | AppName, args[0], AppName) 210 | Exit(2) 211 | } 212 | 213 | var AppInfo string = "LiteIDE golang tool." 214 | var AppName string = "tools" 215 | 216 | var usageTemplate = ` 217 | Usage: 218 | 219 | {{AppName}} command [arguments] 220 | 221 | The commands are: 222 | {{range .}}{{if .Runnable}} 223 | {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} 224 | 225 | Use "{{AppName}} help [command]" for more information about a command. 226 | 227 | Additional help topics: 228 | {{range .}}{{if not .Runnable}} 229 | {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} 230 | 231 | Use "{{AppName}} help [topic]" for more information about that topic. 232 | 233 | ` 234 | 235 | var helpTemplate = `{{if .Runnable}}usage: {{AppName}} {{.UsageLine}} 236 | 237 | {{end}}{{.Long | trim}} 238 | ` 239 | 240 | var documentationTemplate = `// 241 | /* 242 | {{range .}}{{if .Short}}{{.Short | capitalize}} 243 | 244 | {{end}}{{if .Runnable}}Usage: 245 | 246 | {{AppName}} {{.UsageLine}} 247 | 248 | {{end}}{{.Long | trim}} 249 | 250 | 251 | {{end}}*/ 252 | package main 253 | ` 254 | 255 | // tmpl executes the given template text on data, writing the result to w. 256 | func tmpl(w io.Writer, text string, data interface{}) { 257 | t := template.New("top") 258 | t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) 259 | template.Must(t.Parse(text)) 260 | if err := t.Execute(w, data); err != nil { 261 | panic(err) 262 | } 263 | } 264 | 265 | func capitalize(s string) string { 266 | if s == "" { 267 | return s 268 | } 269 | r, n := utf8.DecodeRuneInString(s) 270 | return string(unicode.ToTitle(r)) + s[n:] 271 | } 272 | 273 | func printUsage(w io.Writer) { 274 | if len(AppInfo) > 0 { 275 | fmt.Fprintln(w, AppInfo) 276 | } 277 | tmpl(w, strings.Replace(usageTemplate, "{{AppName}}", AppName, -1), commands) 278 | } 279 | 280 | // help implements the 'help' command. 281 | func help(args []string) bool { 282 | if len(args) == 0 { 283 | printUsage(os.Stdout) 284 | // not exit 2: succeeded at 'go help'. 285 | return true 286 | } 287 | if len(args) != 1 { 288 | fmt.Fprintf(os.Stderr, "usage: %s help command\n\nToo many arguments given.\n", AppName) 289 | return false 290 | } 291 | 292 | arg := args[0] 293 | 294 | // 'go help documentation' generates doc.go. 295 | if arg == "documentation" { 296 | buf := new(bytes.Buffer) 297 | printUsage(buf) 298 | usage := &Command{Long: buf.String()} 299 | tmpl(os.Stdout, strings.Replace(documentationTemplate, "{{AppName}}", AppName, -1), append([]*Command{usage}, commands...)) 300 | return false 301 | } 302 | 303 | for _, cmd := range commands { 304 | if cmd.Name() == arg { 305 | tmpl(os.Stdout, strings.Replace(helpTemplate, "{{AppName}}", AppName, -1), cmd) 306 | // not exit 2: succeeded at 'go help cmd'. 307 | return true 308 | } 309 | } 310 | 311 | fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run '%s help'.\n", arg, AppName) 312 | return false 313 | } 314 | 315 | func Exit(code int) { 316 | os.Exit(code) 317 | } 318 | -------------------------------------------------------------------------------- /pkg/pkgutil/pkgutil.go: -------------------------------------------------------------------------------- 1 | package pkgutil 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "regexp" 10 | "strings" 11 | ) 12 | 13 | //var go15VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") == "1" 14 | 15 | func IsVendorExperiment() bool { 16 | return true 17 | } 18 | 19 | // matchPattern(pattern)(name) reports whether 20 | // name matches pattern. Pattern is a limited glob 21 | // pattern in which '...' means 'any string' and there 22 | // is no other special syntax. 23 | func matchPattern(pattern string) func(name string) bool { 24 | re := regexp.QuoteMeta(pattern) 25 | re = strings.Replace(re, `\.\.\.`, `.*`, -1) 26 | // Special case: foo/... matches foo too. 27 | if strings.HasSuffix(re, `/.*`) { 28 | re = re[:len(re)-len(`/.*`)] + `(/.*)?` 29 | } 30 | reg := regexp.MustCompile(`^` + re + `$`) 31 | return func(name string) bool { 32 | return reg.MatchString(name) 33 | } 34 | } 35 | 36 | // hasPathPrefix reports whether the path s begins with the 37 | // elements in prefix. 38 | func hasPathPrefix(s, prefix string) bool { 39 | switch { 40 | default: 41 | return false 42 | case len(s) == len(prefix): 43 | return s == prefix 44 | case len(s) > len(prefix): 45 | if prefix != "" && prefix[len(prefix)-1] == '/' { 46 | return strings.HasPrefix(s, prefix) 47 | } 48 | return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 49 | } 50 | } 51 | 52 | // hasFilePathPrefix reports whether the filesystem path s begins with the 53 | // elements in prefix. 54 | func hasFilePathPrefix(s, prefix string) bool { 55 | sv := strings.ToUpper(filepath.VolumeName(s)) 56 | pv := strings.ToUpper(filepath.VolumeName(prefix)) 57 | s = s[len(sv):] 58 | prefix = prefix[len(pv):] 59 | switch { 60 | default: 61 | return false 62 | case sv != pv: 63 | return false 64 | case len(s) == len(prefix): 65 | return s == prefix 66 | case len(s) > len(prefix): 67 | if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { 68 | return strings.HasPrefix(s, prefix) 69 | } 70 | return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix 71 | } 72 | } 73 | 74 | // treeCanMatchPattern(pattern)(name) reports whether 75 | // name or children of name can possibly match pattern. 76 | // Pattern is the same limited glob accepted by matchPattern. 77 | func treeCanMatchPattern(pattern string) func(name string) bool { 78 | wildCard := false 79 | if i := strings.Index(pattern, "..."); i >= 0 { 80 | wildCard = true 81 | pattern = pattern[:i] 82 | } 83 | return func(name string) bool { 84 | return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 85 | wildCard && strings.HasPrefix(name, pattern) 86 | } 87 | } 88 | 89 | var isDirCache = map[string]bool{} 90 | 91 | func isDir(path string) bool { 92 | result, ok := isDirCache[path] 93 | if ok { 94 | return result 95 | } 96 | 97 | fi, err := os.Stat(path) 98 | result = err == nil && fi.IsDir() 99 | isDirCache[path] = result 100 | return result 101 | } 102 | 103 | type Package struct { 104 | Root string 105 | Dir string 106 | ImportPath string 107 | } 108 | 109 | func ImportFile(fileName string) *Package { 110 | return ImportDir(filepath.Dir(fileName)) 111 | } 112 | 113 | func ImportDir(dir string) *Package { 114 | pkg, err := build.ImportDir(dir, build.FindOnly) 115 | if err != nil { 116 | return &Package{"", dir, ""} 117 | } 118 | return &Package{pkg.Root, pkg.Dir, pkg.ImportPath} 119 | } 120 | 121 | func ImportDirEx(ctx *build.Context, dir string) *Package { 122 | pkg, err := ctx.ImportDir(dir, build.FindOnly) 123 | if err != nil { 124 | return &Package{"", dir, ""} 125 | } 126 | return &Package{pkg.Root, pkg.Dir, pkg.ImportPath} 127 | } 128 | 129 | // expandPath returns the symlink-expanded form of path. 130 | func expandPath(p string) string { 131 | x, err := filepath.EvalSymlinks(p) 132 | if err == nil { 133 | return x 134 | } 135 | return p 136 | } 137 | 138 | // vendoredImportPath returns the expansion of path when it appears in parent. 139 | // If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path, 140 | // x/vendor/path, vendor/path, or else stay path if none of those exist. 141 | // vendoredImportPath returns the expanded path or, if no expansion is found, the original. 142 | func VendoredImportPath(parent *Package, path string) (found string, err error) { 143 | if parent == nil || parent.Root == "" { 144 | return path, nil 145 | } 146 | 147 | dir := filepath.Clean(parent.Dir) 148 | root := filepath.Join(parent.Root, "src") 149 | if !hasFilePathPrefix(dir, root) { 150 | // Look for symlinks before reporting error. 151 | dir = expandPath(dir) 152 | root = expandPath(root) 153 | } 154 | if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator { 155 | return "", fmt.Errorf("invalid vendoredImportPath: dir=%q root=%q separator=%q", dir, root, string(filepath.Separator)) 156 | } 157 | 158 | vpath := "vendor/" + path 159 | for i := len(dir); i >= len(root); i-- { 160 | if i < len(dir) && dir[i] != filepath.Separator { 161 | continue 162 | } 163 | // Note: checking for the vendor directory before checking 164 | // for the vendor/path directory helps us hit the 165 | // isDir cache more often. It also helps us prepare a more useful 166 | // list of places we looked, to report when an import is not found. 167 | if !isDir(filepath.Join(dir[:i], "vendor")) { 168 | continue 169 | } 170 | targ := filepath.Join(dir[:i], vpath) 171 | if isDir(targ) && hasGoFiles(targ) { 172 | importPath := parent.ImportPath 173 | if importPath == "command-line-arguments" { 174 | // If parent.ImportPath is 'command-line-arguments'. 175 | // set to relative directory to root (also chopped root directory) 176 | importPath = dir[len(root)+1:] 177 | } 178 | // We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy. 179 | // We know the import path for parent's dir. 180 | // We chopped off some number of path elements and 181 | // added vendor\path to produce c:\gopath\src\foo\bar\baz\vendor\path. 182 | // Now we want to know the import path for that directory. 183 | // Construct it by chopping the same number of path elements 184 | // (actually the same number of bytes) from parent's import path 185 | // and then append /vendor/path. 186 | chopped := len(dir) - i 187 | if chopped == len(importPath)+1 { 188 | // We walked up from c:\gopath\src\foo\bar 189 | // and found c:\gopath\src\vendor\path. 190 | // We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7). 191 | // Use "vendor/path" without any prefix. 192 | return vpath, nil 193 | } 194 | return importPath[:len(importPath)-chopped] + "/" + vpath, nil 195 | } 196 | } 197 | return path, nil 198 | } 199 | 200 | // hasGoFiles reports whether dir contains any files with names ending in .go. 201 | // For a vendor check we must exclude directories that contain no .go files. 202 | // Otherwise it is not possible to vendor just a/b/c and still import the 203 | // non-vendored a/b. See golang.org/issue/13832. 204 | func hasGoFiles(dir string) bool { 205 | fis, _ := ioutil.ReadDir(dir) 206 | for _, fi := range fis { 207 | if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".go") { 208 | return true 209 | } 210 | } 211 | return false 212 | } 213 | 214 | // findVendor looks for the last non-terminating "vendor" path element in the given import path. 215 | // If there isn't one, findVendor returns ok=false. 216 | // Otherwise, findVendor returns ok=true and the index of the "vendor". 217 | // 218 | // Note that terminating "vendor" elements don't count: "x/vendor" is its own package, 219 | // not the vendored copy of an import "" (the empty import path). 220 | // This will allow people to have packages or commands named vendor. 221 | // This may help reduce breakage, or it may just be confusing. We'll see. 222 | func findVendor(path string) (index int, ok bool) { 223 | // Two cases, depending on internal at start of string or not. 224 | // The order matters: we must return the index of the final element, 225 | // because the final one is where the effective import path starts. 226 | switch { 227 | case strings.Contains(path, "/vendor/"): 228 | return strings.LastIndex(path, "/vendor/") + 1, true 229 | case strings.HasPrefix(path, "vendor/"): 230 | return 0, true 231 | } 232 | return 0, false 233 | } 234 | 235 | func VendorPathToImportPath(path string) string { 236 | if i, ok := findVendor(path); ok { 237 | return path[i+len("vendor/"):] 238 | } 239 | return path 240 | } 241 | -------------------------------------------------------------------------------- /docview/docview.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 docview 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "go/build" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "runtime" 15 | "strings" 16 | "text/template" 17 | 18 | "github.com/visualfc/gotools/pkg/command" 19 | ) 20 | 21 | var Command = &command.Command{ 22 | Run: runDocView, 23 | UsageLine: "docview [-mode] [-list|-find]", 24 | Short: "golang docview util", 25 | Long: `golang docview util`, 26 | } 27 | 28 | var goroot = runtime.GOROOT() 29 | 30 | var docViewFind string 31 | var docViewList string 32 | var docViewMode string 33 | 34 | func init() { 35 | Command.Flag.StringVar(&docViewFind, "find", "", "find package list, :pkg flag is best match") 36 | Command.Flag.StringVar(&docViewList, "list", "", "Print go packages list [pkg|cmd]") 37 | Command.Flag.StringVar(&docViewMode, "mode", "text", "Print mode [text|html|lite]") 38 | } 39 | 40 | func runDocView(cmd *command.Command, args []string) error { 41 | if docViewFind == "" && docViewList == "" { 42 | cmd.Usage() 43 | return os.ErrInvalid 44 | } 45 | 46 | var template string 47 | var info *Info 48 | if len(docViewList) > 0 { 49 | pkgPath := filepath.Join(goroot, "src", docViewList) 50 | if docViewList == "pkg" { 51 | _, err := os.Stat(pkgPath) 52 | if err != nil { 53 | pkgPath = filepath.Join(goroot, "src") 54 | } 55 | } 56 | info = NewListInfo(pkgPath) 57 | if info != nil { 58 | if docViewList == "pkg" { 59 | var filterList []DirEntry 60 | for _, v := range info.Dirs.List { 61 | if v.Path == "cmd" { 62 | continue 63 | } 64 | if strings.HasPrefix(v.Path, "cmd/") { 65 | continue 66 | } 67 | if strings.Contains(v.Path, "/testdata") { 68 | continue 69 | } 70 | filterList = append(filterList, v) 71 | } 72 | info.Dirs.List = filterList 73 | } else if docViewList == "cmd" { 74 | var filterList []DirEntry 75 | for _, v := range info.Dirs.List { 76 | if strings.Contains(v.Path, "/") { 77 | continue 78 | } 79 | if strings.Contains(v.Path, "internal") { 80 | continue 81 | } 82 | filterList = append(filterList, v) 83 | } 84 | info.Dirs.List = filterList 85 | } 86 | } 87 | switch docViewMode { 88 | case "html": 89 | template = listHTML 90 | case "lite": 91 | template = listLite 92 | case "text": 93 | template = listText 94 | default: 95 | template = listText 96 | } 97 | } else if len(docViewFind) > 0 { 98 | dir := NewSourceDir(goroot) 99 | info = dir.FindInfo(docViewFind) 100 | switch docViewMode { 101 | case "html": 102 | template = findHTML 103 | case "lite": 104 | template = findLite 105 | case "text": 106 | template = findText 107 | default: 108 | template = findText 109 | } 110 | } 111 | if info == nil { 112 | return os.ErrNotExist 113 | } 114 | contents := info.GetPkgList(docViewMode, template) 115 | fmt.Fprintf(cmd.Stdout, "%s", contents) 116 | return nil 117 | } 118 | 119 | var ( 120 | fs FileSystem = OS // the underlying file system 121 | ) 122 | 123 | // Fake package file and name for commands. Contains the command documentation. 124 | const fakePkgName = "documentation" 125 | 126 | var fmap = template.FuncMap{ 127 | "repeat": strings.Repeat, 128 | } 129 | 130 | func readTemplateData(name, data string) *template.Template { 131 | return template.Must(template.New(name).Funcs(fmap).Parse(data)) 132 | } 133 | 134 | func readTemplateFile(name, path string) *template.Template { 135 | return template.Must(template.New(name).Funcs(fmap).ParseFiles(path)) 136 | } 137 | 138 | func applyTemplate(t *template.Template, name string, data interface{}) []byte { 139 | var buf bytes.Buffer 140 | if err := t.Execute(&buf, data); err != nil { 141 | log.Printf("%s.Execute: %s", name, err) 142 | } 143 | return buf.Bytes() 144 | } 145 | 146 | type Info struct { 147 | Find string 148 | Best *DirEntry 149 | Dirs *DirList 150 | } 151 | 152 | type GodocDir struct { 153 | pkg *Directory 154 | cmd *Directory 155 | gopath []*Directory 156 | } 157 | 158 | func NewSourceDir(goroot string) *GodocDir { 159 | pkgPath := filepath.Join(goroot, "src/pkg") 160 | _, err := os.Stat(pkgPath) 161 | var cmd *Directory 162 | if err != nil { 163 | pkgPath = filepath.Join(goroot, "src") 164 | } else { 165 | cmd = newDirectory(filepath.Join(goroot, "src", "cmd"), nil, -1) 166 | } 167 | pkg := newDirectory(pkgPath, nil, -1) 168 | ctx := build.Default 169 | ctx.GOROOT = "" 170 | var gopath []*Directory 171 | for _, v := range ctx.SrcDirs() { 172 | gopath = append(gopath, newDirectory(v, nil, -1)) 173 | } 174 | return &GodocDir{pkg, cmd, gopath} 175 | } 176 | 177 | func (dir *GodocDir) FindInfo(name string) *Info { 178 | max1, best1, list1 := FindDir(dir.pkg, name) 179 | max2, best2, list2 := FindDir(dir.cmd, name) 180 | var maxHeight int 181 | if max1 >= max2 { 182 | maxHeight = max1 183 | } else { 184 | maxHeight = max2 185 | } 186 | var best *DirEntry 187 | if best1 != nil { 188 | best = best1 189 | if best2 != nil { 190 | list2 = append(list2, *best2) 191 | } 192 | } else { 193 | best = best2 194 | } 195 | var list []DirEntry 196 | list = append(list, list1...) 197 | list = append(list, list2...) 198 | for _, v := range dir.gopath { 199 | max3, best3, list3 := FindDir(v, name) 200 | if max3 > maxHeight { 201 | maxHeight = max3 202 | } 203 | if best == nil { 204 | best = best3 205 | } 206 | list = append(list, list3...) 207 | } 208 | return &Info{name, best, &DirList{maxHeight, list}} 209 | } 210 | 211 | func FindDir(dir *Directory, pkgname string) (maxHeight int, best *DirEntry, list []DirEntry) { 212 | if dir == nil { 213 | return 214 | } 215 | dirList := dir.listing(true) 216 | max := len(dirList.List) 217 | maxHeight = dirList.MaxHeight 218 | 219 | for i := 0; i < max; i++ { 220 | name := dirList.List[i].Name 221 | path := filepath.ToSlash(dirList.List[i].Path) 222 | if name == pkgname || path == pkgname { 223 | best = &dirList.List[i] 224 | } else if strings.Contains(path, pkgname) { 225 | list = append(list, dirList.List[i]) 226 | } 227 | } 228 | return 229 | } 230 | 231 | func appendList(list1, list2 []DirEntry) []DirEntry { 232 | list := list1 233 | max := len(list2) 234 | for i := 0; i < max; i++ { 235 | list = append(list, list2[i]) 236 | } 237 | return list 238 | } 239 | 240 | func NewListInfo(root string) *Info { 241 | dir := newDirectory(root, nil, -1) 242 | if dir == nil { 243 | return nil 244 | } 245 | return &Info{"", nil, dir.listing(true)} 246 | } 247 | 248 | func FindPkgInfo(root string, pkgname string) *Info { 249 | dir := newDirectory(root, nil, -1) 250 | if dir == nil { 251 | return nil 252 | } 253 | dirList := dir.listing(true) 254 | if pkgname == "*" { 255 | return &Info{pkgname, nil, dirList} 256 | } 257 | var best DirEntry 258 | var list []DirEntry 259 | max := len(dirList.List) 260 | for i := 0; i < max; i++ { 261 | name := dirList.List[i].Name 262 | path := filepath.ToSlash(dirList.List[i].Path) 263 | if name == pkgname || path == pkgname { 264 | best = dirList.List[i] 265 | } else if strings.Contains(path, pkgname) { 266 | list = append(list, dirList.List[i]) 267 | } 268 | } 269 | return &Info{pkgname, &best, &DirList{dirList.MaxHeight, list}} 270 | } 271 | 272 | func (info *Info) GetPkgList(name, templateData string) []byte { 273 | data := readTemplateData(name, templateData) 274 | return applyTemplate(data, "pkglist", info) 275 | } 276 | 277 | var listHTML = ` 278 |

279 | Need more packages? The 280 | Package Dashboard 281 | provides a list of goinstallable packages. 282 |

283 |

Subdirectories

284 |

285 | {{with .Dirs}} 286 |

287 | 288 | 289 | 290 | 291 | 292 | 293 | {{range .List}} 294 | 295 | {{repeat "" .Depth}} 296 | 297 | 298 | 299 | 300 | {{end}} 301 |
Name Synopsis
{{html .Name}}{{html .Synopsis}}
302 |

303 | {{end}}` 304 | 305 | var listText = `$list 306 | {{with .Dirs}} 307 | {{range .List}}{{.Path }} 308 | {{end}} 309 | {{end}}` 310 | 311 | var listLite = `$list{{with .Dirs}}{{range .List}},{{.Path}}{{end}}{{end}}` 312 | 313 | var findHTML = ` 314 |

315 | Need more packages? The 316 | Package Dashboard 317 | provides a list of goinstallable packages. 318 |

319 |

Subdirectories

320 | 321 | 322 | 323 | 324 | 325 | {{with .Best}} 326 | 327 | 328 | 329 | 330 | 331 | {{end}} 332 | {{with .Dirs}} 333 | 334 | 335 | 336 | 337 | 338 | {{range .List}} 339 | 340 | 341 | 342 | 343 | 344 | {{end}} 345 |
Best Synopsis
{{.Path}}{{html .Synopsis}}
Match Synopsis
{{.Path}}{{html .Synopsis}}
346 |

347 | {{end}}` 348 | 349 | var findText = `$best 350 | {{with .Best}}{{.Path}}{{end}} 351 | $list 352 | {{with .Dirs}}{{range .List}}{{.Path}} 353 | {{end}}{{end}}` 354 | 355 | var findLite = `$find,{{with .Best}}{{.Path}}{{end}}{{with .Dirs}}{{range .List}},{{.Path}}{{end}}{{end}}` 356 | -------------------------------------------------------------------------------- /gopresent/gopresent.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 | //modify 2013-2014 visualfc 6 | 7 | package gopresent 8 | 9 | import ( 10 | "fmt" 11 | "html/template" 12 | "io" 13 | "os" 14 | "path/filepath" 15 | 16 | "github.com/visualfc/gotools/pkg/command" 17 | "golang.org/x/tools/present" 18 | ) 19 | 20 | var Command = &command.Command{ 21 | Run: runPresent, 22 | UsageLine: "gopresent", 23 | Short: "golang present util", 24 | Long: `golang present util`, 25 | } 26 | 27 | var presentVerifyOnly bool 28 | var presentInput string 29 | var presentStdout bool 30 | var presentOutput string 31 | 32 | func init() { 33 | Command.Flag.BoolVar(&presentVerifyOnly, "v", false, "verify present only") 34 | Command.Flag.BoolVar(&presentStdout, "stdout", false, "output use std output") 35 | Command.Flag.StringVar(&presentInput, "i", "", "input golang present file") 36 | Command.Flag.StringVar(&presentOutput, "o", "", "output html file name") 37 | } 38 | 39 | func runPresent(cmd *command.Command, args []string) error { 40 | if presentInput == "" || !isDoc(presentInput) { 41 | cmd.Usage() 42 | return os.ErrInvalid 43 | } 44 | 45 | if presentVerifyOnly { 46 | err := VerifyDoc(presentInput) 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | w := cmd.Stdout 53 | if !presentStdout { 54 | if presentOutput == "" { 55 | presentOutput = presentInput + ".html" 56 | } 57 | ext := filepath.Ext(presentOutput) 58 | if ext != ".htm" && ext != ".html" { 59 | presentOutput += ".html" 60 | } 61 | var err error 62 | w, err = os.Create(presentOutput) 63 | if err != nil { 64 | return err 65 | } 66 | } 67 | err := RenderDoc(w, presentInput) 68 | return err 69 | } 70 | 71 | var extensions = map[string]string{ 72 | ".slide": "slides.tmpl", 73 | ".article": "article.tmpl", 74 | } 75 | 76 | var extensions_tmpl = map[string]string{ 77 | ".slide": slides_tmpl, 78 | ".article": article_tmpl, 79 | } 80 | 81 | func isDoc(path string) bool { 82 | _, ok := extensions[filepath.Ext(path)] 83 | return ok 84 | } 85 | 86 | func VerifyDoc(docFile string) error { 87 | doc, err := parse(docFile, 0) 88 | if err != nil { 89 | return err 90 | } 91 | dir := filepath.Dir(docFile) 92 | return verify_doc(dir, doc) 93 | } 94 | 95 | // renderDoc reads the present file, builds its template representation, 96 | // and executes the template, sending output to w. 97 | func renderDoc(w io.Writer, base, docFile string) error { 98 | // Read the input and build the doc structure. 99 | doc, err := parse(docFile, 0) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | // Find which template should be executed. 105 | ext := filepath.Ext(docFile) 106 | contentTmpl, ok := extensions[ext] 107 | if !ok { 108 | return fmt.Errorf("no template for extension %v", ext) 109 | } 110 | 111 | // Locate the template file. 112 | actionTmpl := filepath.Join(base, "templates/action.tmpl") 113 | contentTmpl = filepath.Join(base, "templates", contentTmpl) 114 | 115 | // Read and parse the input. 116 | tmpl := present.Template() 117 | tmpl = tmpl.Funcs(template.FuncMap{"playable": playable}) 118 | if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil { 119 | return err 120 | } 121 | // Execute the template. 122 | return doc.Render(w, tmpl) 123 | } 124 | 125 | func RenderDoc(w io.Writer, docFile string) error { 126 | // Read the input and build the doc structure. 127 | doc, err := parse(docFile, 0) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | // Find which template should be executed. 133 | ext := filepath.Ext(docFile) 134 | contentTmpl, ok := extensions_tmpl[ext] 135 | if !ok { 136 | return fmt.Errorf("no template for extension %v", ext) 137 | } 138 | 139 | // Locate the template file. 140 | actionTmpl := action_tmpl //filepath.Join(base, "templates/action.tmpl") 141 | // Read and parse the input. 142 | tmpl := present.Template() 143 | tmpl = tmpl.Funcs(template.FuncMap{"playable": playable}) 144 | if tmpl, err = tmpl.New("action").Parse(actionTmpl); err != nil { 145 | return err 146 | } 147 | if tmpl, err = tmpl.New("content").Parse(contentTmpl); err != nil { 148 | return err 149 | } 150 | 151 | // Execute the template. 152 | return doc.Render(w, tmpl) 153 | } 154 | 155 | func parse(name string, mode present.ParseMode) (*present.Doc, error) { 156 | f, err := os.Open(name) 157 | if err != nil { 158 | return nil, err 159 | } 160 | defer f.Close() 161 | return present.Parse(f, name, 0) 162 | } 163 | 164 | func playable(c present.Code) bool { 165 | return present.PlayEnabled && c.Play 166 | } 167 | 168 | func isSkipURL(url string) bool { 169 | if filepath.HasPrefix(url, "http://") { 170 | return true 171 | } 172 | if filepath.HasPrefix(url, "https://") { 173 | return true 174 | } 175 | return false 176 | } 177 | 178 | func verify_path(root string, url string) error { 179 | if isSkipURL(url) { 180 | return nil 181 | } 182 | path := url 183 | if !filepath.IsAbs(url) { 184 | path = filepath.Join(root, path) 185 | } 186 | _, err := os.Stat(path) 187 | if err != nil { 188 | return err 189 | } 190 | return nil 191 | } 192 | 193 | func verify_doc(root string, doc *present.Doc) error { 194 | for _, section := range doc.Sections { 195 | for _, elem := range section.Elem { 196 | switch i := elem.(type) { 197 | case present.Image: 198 | if err := verify_path(root, i.URL); err != nil { 199 | return fmt.Errorf("! .image %s not exist", i.URL) 200 | } 201 | } 202 | } 203 | } 204 | return nil 205 | } 206 | 207 | var action_tmpl = ` 208 | {/* 209 | This is the action template. 210 | It determines how the formatting actions are rendered. 211 | */} 212 | 213 | {{define "section"}} 214 | {{.FormattedNumber}} {{.Title}} 215 | {{range .Elem}}{{elem $.Template .}}{{end}} 216 | {{end}} 217 | 218 | {{define "list"}} 219 |
    220 | {{range .Bullet}} 221 |
  • {{style .}}
  • 222 | {{end}} 223 |
224 | {{end}} 225 | 226 | {{define "text"}} 227 | {{if .Pre}} 228 |
{{range .Lines}}{{.}}{{end}}
229 | {{else}} 230 |

231 | {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}} 232 | {{end}}{{style $l}}{{end}} 233 |

234 | {{end}} 235 | {{end}} 236 | 237 | {{define "code"}} 238 |
{{.Text}}
239 | {{end}} 240 | 241 | {{define "image"}} 242 |
243 | 244 |
245 | {{end}} 246 | 247 | {{define "iframe"}} 248 | 249 | {{end}} 250 | 251 | {{define "link"}}{{end}} 252 | 253 | {{define "html"}}{{.HTML}}{{end}} 254 | ` 255 | 256 | var article_tmpl = ` 257 | {/* This is the article template. It defines how articles are formatted. */} 258 | 259 | {{define "root"}} 260 | 261 | 262 | 263 | {{.Title}} 264 | 265 | 266 | 267 | 268 | 269 |
270 |
271 |
{{.Title}} 272 | {{with .Subtitle}}{{.}}{{end}} 273 |
274 |
275 |
276 |
277 |
278 | {{with .Sections}} 279 |
280 | {{template "TOC" .}} 281 |
282 | {{end}} 283 | 284 | {{range .Sections}} 285 | {{elem $.Template .}} 286 | {{end}}{{/* of Section block */}} 287 | 288 |

Authors

289 | {{range .Authors}} 290 |
291 | {{range .Elem}}{{elem $.Template .}}{{end}} 292 |
293 | {{end}} 294 |
295 |
296 | 297 | 298 | 299 | {{end}} 300 | 301 | {{define "TOC"}} 302 |
    303 | {{range .}} 304 |
  • {{.Title}}
  • 305 | {{with .Sections}}{{template "TOC" .}}{{end}} 306 | {{end}} 307 |
308 | {{end}} 309 | 310 | {{define "newline"}} 311 | {{/* No automatic line break. Paragraphs are free-form. */}} 312 | {{end}} 313 | ` 314 | 315 | var slides_tmpl = ` 316 | {/* This is the slide template. It defines how presentations are formatted. */} 317 | 318 | {{define "root"}} 319 | 320 | 321 | 322 | {{.Title}} 323 | 324 | 325 | 326 | 327 | 328 | 329 |
330 | 331 |
332 |

{{.Title}}

333 | {{with .Subtitle}}

{{.}}

{{end}} 334 | {{if not .Time.IsZero}}

{{.Time.Format "2 January 2006"}}

{{end}} 335 | {{range .Authors}} 336 |
337 | {{range .TextElem}}{{elem $.Template .}}{{end}} 338 |
339 | {{end}} 340 |
341 | 342 | {{range $i, $s := .Sections}} 343 | 344 |
345 | {{if $s.Elem}} 346 |

{{$s.Title}}

347 | {{range $s.Elem}}{{elem $.Template .}}{{end}} 348 | {{else}} 349 |

{{$s.Title}}

350 | {{end}} 351 |
352 | 353 | {{end}}{{/* of Slide block */}} 354 | 355 |
356 |

Thank you

357 | {{range .Authors}} 358 |
359 | {{range .Elem}}{{elem $.Template .}}{{end}} 360 |
361 | {{end}} 362 |
363 | 364 | 365 | {{if .PlayEnabled}} 366 | 367 | {{end}} 368 | 369 | {{end}} 370 | 371 | {{define "newline"}} 372 |
373 | {{end}} 374 | ` 375 | -------------------------------------------------------------------------------- /gopresent/static/styles.css: -------------------------------------------------------------------------------- 1 | /* Framework */ 2 | 3 | html { 4 | height: 100%; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | padding: 0; 10 | 11 | display: block !important; 12 | 13 | height: 100%; 14 | min-height: 740px; 15 | 16 | overflow-x: hidden; 17 | overflow-y: auto; 18 | 19 | background: rgb(215, 215, 215); 20 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 21 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 22 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); 23 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190))); 24 | 25 | -webkit-font-smoothing: antialiased; 26 | } 27 | 28 | .slides { 29 | width: 100%; 30 | height: 100%; 31 | left: 0; 32 | top: 0; 33 | 34 | position: absolute; 35 | 36 | -webkit-transform: translate3d(0, 0, 0); 37 | } 38 | 39 | .slides > article { 40 | display: block; 41 | 42 | position: absolute; 43 | overflow: hidden; 44 | 45 | width: 900px; 46 | height: 700px; 47 | 48 | left: 50%; 49 | top: 50%; 50 | 51 | margin-left: -450px; 52 | margin-top: -350px; 53 | 54 | padding: 40px 60px; 55 | 56 | box-sizing: border-box; 57 | -o-box-sizing: border-box; 58 | -moz-box-sizing: border-box; 59 | -webkit-box-sizing: border-box; 60 | 61 | border-radius: 10px; 62 | -o-border-radius: 10px; 63 | -moz-border-radius: 10px; 64 | -webkit-border-radius: 10px; 65 | 66 | background-color: white; 67 | 68 | border: 1px solid rgba(0, 0, 0, .3); 69 | 70 | transition: transform .3s ease-out; 71 | -o-transition: -o-transform .3s ease-out; 72 | -moz-transition: -moz-transform .3s ease-out; 73 | -webkit-transition: -webkit-transform .3s ease-out; 74 | } 75 | .slides.layout-widescreen > article { 76 | margin-left: -550px; 77 | width: 1100px; 78 | } 79 | .slides.layout-faux-widescreen > article { 80 | margin-left: -550px; 81 | width: 1100px; 82 | 83 | padding: 40px 160px; 84 | } 85 | 86 | .slides.layout-widescreen > article:not(.nobackground):not(.biglogo), 87 | .slides.layout-faux-widescreen > article:not(.nobackground):not(.biglogo) { 88 | background-position-x: 0, 840px; 89 | } 90 | 91 | /* Clickable/tappable areas */ 92 | 93 | .slide-area { 94 | z-index: 1000; 95 | 96 | position: absolute; 97 | left: 0; 98 | top: 0; 99 | width: 150px; 100 | height: 700px; 101 | 102 | left: 50%; 103 | top: 50%; 104 | 105 | cursor: pointer; 106 | margin-top: -350px; 107 | 108 | tap-highlight-color: transparent; 109 | -o-tap-highlight-color: transparent; 110 | -moz-tap-highlight-color: transparent; 111 | -webkit-tap-highlight-color: transparent; 112 | } 113 | #prev-slide-area { 114 | margin-left: -550px; 115 | } 116 | #next-slide-area { 117 | margin-left: 400px; 118 | } 119 | .slides.layout-widescreen #prev-slide-area, 120 | .slides.layout-faux-widescreen #prev-slide-area { 121 | margin-left: -650px; 122 | } 123 | .slides.layout-widescreen #next-slide-area, 124 | .slides.layout-faux-widescreen #next-slide-area { 125 | margin-left: 500px; 126 | } 127 | 128 | /* Slides */ 129 | 130 | .slides > article { 131 | display: none; 132 | } 133 | .slides > article.far-past { 134 | display: block; 135 | transform: translate(-2040px); 136 | -o-transform: translate(-2040px); 137 | -moz-transform: translate(-2040px); 138 | -webkit-transform: translate3d(-2040px, 0, 0); 139 | } 140 | .slides > article.past { 141 | display: block; 142 | transform: translate(-1020px); 143 | -o-transform: translate(-1020px); 144 | -moz-transform: translate(-1020px); 145 | -webkit-transform: translate3d(-1020px, 0, 0); 146 | } 147 | .slides > article.current { 148 | display: block; 149 | transform: translate(0); 150 | -o-transform: translate(0); 151 | -moz-transform: translate(0); 152 | -webkit-transform: translate3d(0, 0, 0); 153 | } 154 | .slides > article.next { 155 | display: block; 156 | transform: translate(1020px); 157 | -o-transform: translate(1020px); 158 | -moz-transform: translate(1020px); 159 | -webkit-transform: translate3d(1020px, 0, 0); 160 | } 161 | .slides > article.far-next { 162 | display: block; 163 | transform: translate(2040px); 164 | -o-transform: translate(2040px); 165 | -moz-transform: translate(2040px); 166 | -webkit-transform: translate3d(2040px, 0, 0); 167 | } 168 | 169 | .slides.layout-widescreen > article.far-past, 170 | .slides.layout-faux-widescreen > article.far-past { 171 | display: block; 172 | transform: translate(-2260px); 173 | -o-transform: translate(-2260px); 174 | -moz-transform: translate(-2260px); 175 | -webkit-transform: translate3d(-2260px, 0, 0); 176 | } 177 | .slides.layout-widescreen > article.past, 178 | .slides.layout-faux-widescreen > article.past { 179 | display: block; 180 | transform: translate(-1130px); 181 | -o-transform: translate(-1130px); 182 | -moz-transform: translate(-1130px); 183 | -webkit-transform: translate3d(-1130px, 0, 0); 184 | } 185 | .slides.layout-widescreen > article.current, 186 | .slides.layout-faux-widescreen > article.current { 187 | display: block; 188 | transform: translate(0); 189 | -o-transform: translate(0); 190 | -moz-transform: translate(0); 191 | -webkit-transform: translate3d(0, 0, 0); 192 | } 193 | .slides.layout-widescreen > article.next, 194 | .slides.layout-faux-widescreen > article.next { 195 | display: block; 196 | transform: translate(1130px); 197 | -o-transform: translate(1130px); 198 | -moz-transform: translate(1130px); 199 | -webkit-transform: translate3d(1130px, 0, 0); 200 | } 201 | .slides.layout-widescreen > article.far-next, 202 | .slides.layout-faux-widescreen > article.far-next { 203 | display: block; 204 | transform: translate(2260px); 205 | -o-transform: translate(2260px); 206 | -moz-transform: translate(2260px); 207 | -webkit-transform: translate3d(2260px, 0, 0); 208 | } 209 | 210 | /* Styles for slides */ 211 | 212 | .slides > article { 213 | font-family: 'Open Sans', Arial, sans-serif; 214 | 215 | color: black; 216 | text-shadow: 0 1px 1px rgba(0, 0, 0, .1); 217 | 218 | font-size: 26px; 219 | line-height: 36px; 220 | 221 | letter-spacing: -1px; 222 | } 223 | 224 | b { 225 | font-weight: 600; 226 | } 227 | 228 | a { 229 | color: rgb(0, 102, 204); 230 | text-decoration: none; 231 | } 232 | a:visited { 233 | color: rgba(0, 102, 204, .75); 234 | } 235 | a:hover { 236 | color: black; 237 | } 238 | 239 | p { 240 | margin: 0; 241 | padding: 0; 242 | 243 | margin-top: 20px; 244 | } 245 | p:first-child { 246 | margin-top: 0; 247 | } 248 | 249 | h1 { 250 | font-size: 60px; 251 | line-height: 60px; 252 | 253 | padding: 0; 254 | margin: 0; 255 | margin-top: 200px; 256 | margin-bottom: 5px; 257 | padding-right: 40px; 258 | 259 | font-weight: 600; 260 | 261 | letter-spacing: -3px; 262 | 263 | color: rgb(51, 51, 51); 264 | } 265 | 266 | h2 { 267 | font-size: 45px; 268 | line-height: 45px; 269 | 270 | position: absolute; 271 | bottom: 150px; 272 | 273 | padding: 0; 274 | margin: 0; 275 | padding-right: 40px; 276 | 277 | font-weight: 600; 278 | 279 | letter-spacing: -2px; 280 | 281 | color: rgb(51, 51, 51); 282 | } 283 | 284 | h3 { 285 | font-size: 30px; 286 | line-height: 36px; 287 | 288 | padding: 0; 289 | margin: 0; 290 | padding-right: 40px; 291 | 292 | font-weight: 600; 293 | 294 | letter-spacing: -1px; 295 | 296 | color: rgb(51, 51, 51); 297 | } 298 | 299 | ul { 300 | margin: 0; 301 | padding: 0; 302 | margin-top: 20px; 303 | margin-left: 1.5em; 304 | } 305 | li { 306 | padding: 0; 307 | margin: 0 0 .5em 0; 308 | } 309 | 310 | div.code { 311 | padding: 5px 10px; 312 | margin-top: 20px; 313 | margin-bottom: 20px; 314 | overflow: hidden; 315 | 316 | background: rgb(240, 240, 240); 317 | border: 1px solid rgb(224, 224, 224); 318 | } 319 | pre { 320 | margin: 0; 321 | padding: 0; 322 | 323 | font-family: 'Droid Sans Mono', 'Courier New', monospace; 324 | font-size: 18px; 325 | line-height: 24px; 326 | letter-spacing: -1px; 327 | 328 | color: black; 329 | } 330 | 331 | code { 332 | font-size: 95%; 333 | font-family: 'Droid Sans Mono', 'Courier New', monospace; 334 | 335 | color: black; 336 | } 337 | 338 | article > .image { 339 | text-align: center; 340 | margin-top: 40px; 341 | } 342 | 343 | table { 344 | width: 100%; 345 | border-collapse: collapse; 346 | margin-top: 40px; 347 | } 348 | th { 349 | font-weight: 600; 350 | text-align: left; 351 | } 352 | td, 353 | th { 354 | border: 1px solid rgb(224, 224, 224); 355 | padding: 5px 10px; 356 | vertical-align: top; 357 | } 358 | 359 | p.link { 360 | margin-left: 20px; 361 | } 362 | 363 | /* Code */ 364 | div.code { 365 | outline: 0px solid transparent; 366 | } 367 | div.playground { 368 | position: relative; 369 | } 370 | div.output { 371 | position: absolute; 372 | left: 50%; 373 | top: 50%; 374 | right: 40px; 375 | bottom: 40px; 376 | background: #202020; 377 | padding: 5px 10px; 378 | z-index: 2; 379 | 380 | border-radius: 10px; 381 | -o-border-radius: 10px; 382 | -moz-border-radius: 10px; 383 | -webkit-border-radius: 10px; 384 | 385 | } 386 | div.output pre { 387 | margin: 0; 388 | padding: 0; 389 | background: none; 390 | border: none; 391 | width: 100%; 392 | height: 100%; 393 | overflow: auto; 394 | } 395 | div.output .stdout, div.output pre { 396 | color: #e6e6e6; 397 | } 398 | div.output .stderr, div.output .error { 399 | color: rgb(244, 74, 63); 400 | } 401 | div.output .system, div.output .exit { 402 | color: rgb(255, 209, 77) 403 | } 404 | .buttons { 405 | position: relative; 406 | float: right; 407 | top: -60px; 408 | right: 10px; 409 | } 410 | div.output .buttons { 411 | position: absolute; 412 | float: none; 413 | top: auto; 414 | right: 5px; 415 | bottom: 5px; 416 | } 417 | 418 | /* Presenter details */ 419 | .presenter { 420 | margin-top: 20px; 421 | } 422 | .presenter p, 423 | .presenter .link { 424 | margin: 0; 425 | font-size: 28px; 426 | line-height: 1.2em; 427 | } 428 | 429 | /* Output resize details */ 430 | .ui-resizable-handle { 431 | position: absolute; 432 | } 433 | .ui-resizable-n { 434 | cursor: n-resize; 435 | height: 7px; 436 | width: 100%; 437 | top: -5px; 438 | left: 0; 439 | } 440 | .ui-resizable-w { 441 | cursor: w-resize; 442 | width: 7px; 443 | left: -5px; 444 | top: 0; 445 | height: 100%; 446 | } 447 | .ui-resizable-nw { 448 | cursor: nw-resize; 449 | width: 9px; 450 | height: 9px; 451 | left: -5px; 452 | top: -5px; 453 | } 454 | -------------------------------------------------------------------------------- /docview/dirtrees.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 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 | // This file contains the code dealing with package directory trees. 6 | 7 | package docview 8 | 9 | import ( 10 | "bytes" 11 | "go/doc" 12 | "go/parser" 13 | "go/token" 14 | "log" 15 | "os" 16 | "path/filepath" 17 | "strings" 18 | "unicode" 19 | ) 20 | 21 | type Directory struct { 22 | Depth int 23 | Path string // includes Name 24 | Name string 25 | Text string // package documentation, if any 26 | Dirs []*Directory // subdirectories 27 | } 28 | 29 | //func isGoFile(fi os.FileInfo) bool { 30 | // name := fi.Name() 31 | // return !fi.IsDir() && 32 | // len(name) > 0 && name[0] != '.' && // ignore .files 33 | // filepath.Ext(name) == ".go" 34 | //} 35 | 36 | func isGoFile(f os.FileInfo) bool { 37 | // ignore non-Go files 38 | name := f.Name() 39 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") 40 | } 41 | 42 | func isPkgFile(fi os.FileInfo) bool { 43 | return isGoFile(fi) && 44 | !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files 45 | } 46 | 47 | func isPkgDir(fi os.FileInfo) bool { 48 | name := fi.Name() 49 | return fi.IsDir() && len(name) > 0 && 50 | name[0] != '_' && name[0] != '.' // ignore _files and .files 51 | } 52 | 53 | func firstSentence(s string) string { 54 | i := -1 // index+1 of first terminator (punctuation ending a sentence) 55 | j := -1 // index+1 of first terminator followed by white space 56 | prev := 'A' 57 | for k, ch := range s { 58 | k1 := k + 1 59 | if ch == '.' || ch == '!' || ch == '?' { 60 | if i < 0 { 61 | i = k1 // first terminator 62 | } 63 | if k1 < len(s) && s[k1] <= ' ' { 64 | if j < 0 { 65 | j = k1 // first terminator followed by white space 66 | } 67 | if !unicode.IsUpper(prev) { 68 | j = k1 69 | break 70 | } 71 | } 72 | } 73 | prev = ch 74 | } 75 | 76 | if j < 0 { 77 | // use the next best terminator 78 | j = i 79 | if j < 0 { 80 | // no terminator at all, use the entire string 81 | j = len(s) 82 | } 83 | } 84 | 85 | return s[0:j] 86 | } 87 | 88 | type treeBuilder struct { 89 | pathFilter func(string) bool 90 | maxDepth int 91 | } 92 | 93 | func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory { 94 | if b.pathFilter != nil && !b.pathFilter(path) { 95 | return nil 96 | } 97 | 98 | if depth >= b.maxDepth { 99 | // return a dummy directory so that the parent directory 100 | // doesn't get discarded just because we reached the max 101 | // directory depth 102 | return &Directory{depth, path, name, "", nil} 103 | } 104 | 105 | list, err := fs.ReadDir(path) 106 | if err != nil { 107 | // newDirTree is called with a path that should be a package 108 | // directory; errors here should not happen, but if they do, 109 | // we want to know about them 110 | log.Printf("ReadDir(%s): %s", path, err) 111 | } 112 | 113 | // determine number of subdirectories and if there are package files 114 | ndirs := 0 115 | hasPkgFiles := false 116 | var synopses [4]string // prioritized package documentation (0 == highest priority) 117 | for _, d := range list { 118 | switch { 119 | case isPkgDir(d): 120 | ndirs++ 121 | case isPkgFile(d): 122 | // looks like a package file, but may just be a file ending in ".go"; 123 | // don't just count it yet (otherwise we may end up with hasPkgFiles even 124 | // though the directory doesn't contain any real package files - was bug) 125 | if synopses[0] == "" { 126 | // no "optimal" package synopsis yet; continue to collect synopses 127 | //file, err := parseFile(fset, filepath.Join(path, d.Name()), 128 | //parser.ParseComments|parser.PackageClauseOnly) 129 | file, err := parser.ParseFile(fset, filepath.Join(path, d.Name()), nil, 130 | parser.ParseComments|parser.PackageClauseOnly) 131 | 132 | if err == nil { 133 | hasPkgFiles = true 134 | if file.Doc != nil { 135 | // prioritize documentation 136 | i := -1 137 | switch file.Name.Name { 138 | case name: 139 | i = 0 // normal case: directory name matches package name 140 | case fakePkgName: 141 | i = 1 // synopses for commands 142 | case "main": 143 | i = 2 // directory contains a main package 144 | default: 145 | i = 3 // none of the above 146 | } 147 | if 0 <= i && i < len(synopses) && synopses[i] == "" { 148 | synopses[i] = doc.Synopsis(file.Doc.Text()) 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } 155 | 156 | // create subdirectory tree 157 | var dirs []*Directory 158 | if ndirs > 0 { 159 | dirs = make([]*Directory, ndirs) 160 | i := 0 161 | for _, d := range list { 162 | if isPkgDir(d) { 163 | name := d.Name() 164 | dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1) 165 | if dd != nil { 166 | dirs[i] = dd 167 | i++ 168 | } 169 | } 170 | } 171 | dirs = dirs[0:i] 172 | } 173 | 174 | // if there are no package files and no subdirectories 175 | // containing package files, ignore the directory 176 | if !hasPkgFiles && len(dirs) == 0 { 177 | return nil 178 | } 179 | 180 | // select the highest-priority synopsis for the directory entry, if any 181 | synopsis := "" 182 | for _, synopsis = range synopses { 183 | if synopsis != "" { 184 | break 185 | } 186 | } 187 | 188 | return &Directory{depth, path, name, synopsis, dirs} 189 | } 190 | 191 | // newDirectory creates a new package directory tree with at most maxDepth 192 | // levels, anchored at root. The result tree is pruned such that it only 193 | // contains directories that contain package files or that contain 194 | // subdirectories containing package files (transitively). If a non-nil 195 | // pathFilter is provided, directory paths additionally must be accepted 196 | // by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is 197 | // provided for maxDepth, nodes at larger depths are pruned as well; they 198 | // are assumed to contain package files even if their contents are not known 199 | // (i.e., in this case the tree may contain directories w/o any package files). 200 | // 201 | func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory { 202 | // The root could be a symbolic link so use Stat not Lstat. 203 | d, err := fs.Stat(root) 204 | // If we fail here, report detailed error messages; otherwise 205 | // is is hard to see why a directory tree was not built. 206 | switch { 207 | case err != nil: 208 | log.Printf("newDirectory(%s): %s", root, err) 209 | return nil 210 | case !isPkgDir(d): 211 | log.Printf("newDirectory(%s): not a package directory", root) 212 | return nil 213 | } 214 | if maxDepth < 0 { 215 | maxDepth = 1e6 // "infinity" 216 | } 217 | b := treeBuilder{pathFilter, maxDepth} 218 | // the file set provided is only for local parsing, no position 219 | // information escapes and thus we don't need to save the set 220 | return b.newDirTree(token.NewFileSet(), root, d.Name(), 0) 221 | } 222 | 223 | func (dir *Directory) writeLeafs(buf *bytes.Buffer) { 224 | if dir != nil { 225 | if len(dir.Dirs) == 0 { 226 | buf.WriteString(dir.Path) 227 | buf.WriteByte('\n') 228 | return 229 | } 230 | 231 | for _, d := range dir.Dirs { 232 | d.writeLeafs(buf) 233 | } 234 | } 235 | } 236 | 237 | func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) { 238 | if dir != nil { 239 | if !skipRoot { 240 | c <- dir 241 | } 242 | for _, d := range dir.Dirs { 243 | d.walk(c, false) 244 | } 245 | } 246 | } 247 | 248 | func (dir *Directory) iter(skipRoot bool) <-chan *Directory { 249 | c := make(chan *Directory) 250 | go func() { 251 | dir.walk(c, skipRoot) 252 | close(c) 253 | }() 254 | return c 255 | } 256 | 257 | func (dir *Directory) lookupLocal(name string) *Directory { 258 | for _, d := range dir.Dirs { 259 | if d.Name == name { 260 | return d 261 | } 262 | } 263 | return nil 264 | } 265 | 266 | // lookup looks for the *Directory for a given path, relative to dir. 267 | func (dir *Directory) lookup(path string) *Directory { 268 | d := strings.Split(dir.Path, string(filepath.Separator)) 269 | p := strings.Split(path, string(filepath.Separator)) 270 | i := 0 271 | for i < len(d) { 272 | if i >= len(p) || d[i] != p[i] { 273 | return nil 274 | } 275 | i++ 276 | } 277 | for dir != nil && i < len(p) { 278 | dir = dir.lookupLocal(p[i]) 279 | i++ 280 | } 281 | return dir 282 | } 283 | 284 | // DirEntry describes a directory entry. The Depth and Height values 285 | // are useful for presenting an entry in an indented fashion. 286 | // 287 | type DirEntry struct { 288 | Depth int // >= 0 289 | Height int // = DirList.MaxHeight - Depth, > 0 290 | Path string // includes Name, relative to DirList root 291 | Name string 292 | Synopsis string 293 | } 294 | 295 | type DirList struct { 296 | MaxHeight int // directory tree height, > 0 297 | List []DirEntry 298 | } 299 | 300 | // listing creates a (linear) directory listing from a directory tree. 301 | // If skipRoot is set, the root directory itself is excluded from the list. 302 | // 303 | func (root *Directory) listing(skipRoot bool) *DirList { 304 | if root == nil { 305 | return nil 306 | } 307 | 308 | // determine number of entries n and maximum height 309 | n := 0 310 | minDepth := 1 << 30 // infinity 311 | maxDepth := 0 312 | for d := range root.iter(skipRoot) { 313 | n++ 314 | if minDepth > d.Depth { 315 | minDepth = d.Depth 316 | } 317 | if maxDepth < d.Depth { 318 | maxDepth = d.Depth 319 | } 320 | } 321 | maxHeight := maxDepth - minDepth + 1 322 | 323 | if n == 0 { 324 | return nil 325 | } 326 | 327 | // create list 328 | list := make([]DirEntry, n) 329 | i := 0 330 | for d := range root.iter(skipRoot) { 331 | p := &list[i] 332 | p.Depth = d.Depth - minDepth 333 | p.Height = maxHeight - p.Depth 334 | // the path is relative to root.Path - remove the root.Path 335 | // prefix (the prefix should always be present but avoid 336 | // crashes and check) 337 | path := d.Path 338 | if strings.HasPrefix(d.Path, root.Path) { 339 | path = d.Path[len(root.Path):] 340 | } 341 | // remove trailing separator if any - path must be relative 342 | if len(path) > 0 && path[0] == filepath.Separator { 343 | path = path[1:] 344 | } 345 | p.Path = filepath.ToSlash(path) 346 | p.Name = d.Name 347 | p.Synopsis = d.Text 348 | i++ 349 | } 350 | 351 | return &DirList{maxHeight, list} 352 | } 353 | -------------------------------------------------------------------------------- /pkgs/pkgs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 pkgs 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "go/build" 11 | "os" 12 | "path/filepath" 13 | "runtime" 14 | "sort" 15 | "strings" 16 | "sync" 17 | 18 | "github.com/visualfc/gotools/pkg/command" 19 | ) 20 | 21 | var Command = &command.Command{ 22 | Run: runPkgs, 23 | UsageLine: "pkgs [-list|-json] [-std]", 24 | Short: "print go package", 25 | Long: `print go package.`, 26 | } 27 | 28 | var ( 29 | pkgsList bool 30 | pkgsJson bool 31 | pkgsSimple bool 32 | pkgsFind string 33 | pkgsStd bool 34 | pkgsPkgOnly bool 35 | pkgsSkipGoroot bool 36 | ) 37 | 38 | func init() { 39 | Command.Flag.BoolVar(&pkgsList, "list", false, "list all package") 40 | Command.Flag.BoolVar(&pkgsJson, "json", false, "json format") 41 | Command.Flag.BoolVar(&pkgsSimple, "simple", false, "simple format") 42 | Command.Flag.BoolVar(&pkgsStd, "std", false, "std library") 43 | Command.Flag.BoolVar(&pkgsPkgOnly, "pkg", false, "pkg only") 44 | Command.Flag.BoolVar(&pkgsSkipGoroot, "skip_goroot", false, "skip goroot") 45 | Command.Flag.StringVar(&pkgsFind, "find", "", "find package by name") 46 | } 47 | 48 | func runPkgs(cmd *command.Command, args []string) error { 49 | runtime.GOMAXPROCS(runtime.NumCPU()) 50 | if len(args) != 0 { 51 | cmd.Usage() 52 | return os.ErrInvalid 53 | } 54 | //pkgIndexOnce.Do(loadPkgsList) 55 | var flag LoadFlag 56 | if pkgsStd { 57 | flag = LoadGoroot 58 | } else if pkgsSkipGoroot { 59 | flag = LoadSkipGoroot 60 | } else { 61 | flag = LoadAll 62 | } 63 | var pp PathPkgsIndex 64 | pp.LoadIndex(build.Default, flag) 65 | pp.Sort() 66 | export := func(pkg *build.Package) { 67 | if pkgsJson { 68 | var p GoPackage 69 | p.copyBuild(pkg) 70 | b, err := json.MarshalIndent(&p, "", "\t") 71 | if err == nil { 72 | cmd.Stdout.Write(b) 73 | cmd.Stdout.Write([]byte{'\n'}) 74 | } 75 | } else if pkgsSimple { 76 | cmd.Println(pkg.Name + "::" + pkg.ImportPath + "::" + pkg.Dir) 77 | } else { 78 | cmd.Println(pkg.ImportPath) 79 | } 80 | } 81 | if pkgsList { 82 | for _, pi := range pp.Indexs { 83 | for _, pkg := range pi.Pkgs { 84 | if pkgsPkgOnly && pkg.IsCommand() { 85 | continue 86 | } 87 | export(pkg) 88 | } 89 | } 90 | } else if pkgsFind != "" { 91 | for _, pi := range pp.Indexs { 92 | for _, pkg := range pi.Pkgs { 93 | if pkgsPkgOnly && pkg.IsCommand() { 94 | continue 95 | } 96 | if pkg.Name == pkgsFind { 97 | export(pkg) 98 | break 99 | } 100 | } 101 | } 102 | } 103 | return nil 104 | } 105 | 106 | // A Package describes a single package found in a directory. 107 | type GoPackage struct { 108 | // Note: These fields are part of the go command's public API. 109 | // See list.go. It is okay to add fields, but not to change or 110 | // remove existing ones. Keep in sync with list.go 111 | Dir string `json:",omitempty"` // directory containing package sources 112 | ImportPath string `json:",omitempty"` // import path of package in dir 113 | Name string `json:",omitempty"` // package name 114 | Doc string `json:",omitempty"` // package documentation string 115 | Target string `json:",omitempty"` // install path 116 | Goroot bool `json:",omitempty"` // is this package found in the Go root? 117 | Standard bool `json:",omitempty"` // is this package part of the standard Go library? 118 | Stale bool `json:",omitempty"` // would 'go install' do anything for this package? 119 | Root string `json:",omitempty"` // Go root or Go path dir containing this package 120 | ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory 121 | 122 | // Source files 123 | GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 124 | CgoFiles []string `json:",omitempty"` // .go sources files that import "C" 125 | IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints 126 | CFiles []string `json:",omitempty"` // .c source files 127 | CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files 128 | MFiles []string `json:",omitempty"` // .m source files 129 | HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files 130 | SFiles []string `json:",omitempty"` // .s source files 131 | SwigFiles []string `json:",omitempty"` // .swig files 132 | SwigCXXFiles []string `json:",omitempty"` // .swigcxx files 133 | SysoFiles []string `json:",omitempty"` // .syso system object files added to package 134 | 135 | // Cgo directives 136 | CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler 137 | CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor 138 | CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler 139 | CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker 140 | CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names 141 | 142 | // Dependency information 143 | Imports []string `json:",omitempty"` // import paths used by this package 144 | Deps []string `json:",omitempty"` // all (recursively) imported dependencies 145 | 146 | // Error information 147 | Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies? 148 | 149 | // Test information 150 | TestGoFiles []string `json:",omitempty"` // _test.go files in package 151 | TestImports []string `json:",omitempty"` // imports from TestGoFiles 152 | XTestGoFiles []string `json:",omitempty"` // _test.go files outside package 153 | XTestImports []string `json:",omitempty"` // imports from XTestGoFiles 154 | 155 | // Unexported fields are not part of the public API. 156 | build *build.Package 157 | pkgdir string // overrides build.PkgDir 158 | // imports []*goapi.Package 159 | // deps []*goapi.Package 160 | gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths 161 | sfiles []string 162 | allgofiles []string // gofiles + IgnoredGoFiles, absolute paths 163 | target string // installed file for this package (may be executable) 164 | fake bool // synthesized package 165 | forceBuild bool // this package must be rebuilt 166 | forceLibrary bool // this package is a library (even if named "main") 167 | cmdline bool // defined by files listed on command line 168 | local bool // imported via local path (./ or ../) 169 | localPrefix string // interpret ./ and ../ imports relative to this prefix 170 | exeName string // desired name for temporary executable 171 | coverMode string // preprocess Go source files with the coverage tool in this mode 172 | coverVars map[string]*CoverVar // variables created by coverage analysis 173 | omitDWARF bool // tell linker not to write DWARF information 174 | } 175 | 176 | // CoverVar holds the name of the generated coverage variables targeting the named file. 177 | type CoverVar struct { 178 | File string // local file name 179 | Var string // name of count struct 180 | } 181 | 182 | func (p *GoPackage) copyBuild(pp *build.Package) { 183 | p.build = pp 184 | 185 | p.Dir = pp.Dir 186 | p.ImportPath = pp.ImportPath 187 | p.Name = pp.Name 188 | p.Doc = pp.Doc 189 | p.Root = pp.Root 190 | p.ConflictDir = pp.ConflictDir 191 | // TODO? Target 192 | p.Goroot = pp.Goroot 193 | p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") 194 | p.GoFiles = pp.GoFiles 195 | p.CgoFiles = pp.CgoFiles 196 | p.IgnoredGoFiles = pp.IgnoredGoFiles 197 | p.CFiles = pp.CFiles 198 | p.CXXFiles = pp.CXXFiles 199 | p.MFiles = pp.MFiles 200 | p.HFiles = pp.HFiles 201 | p.SFiles = pp.SFiles 202 | p.SwigFiles = pp.SwigFiles 203 | p.SwigCXXFiles = pp.SwigCXXFiles 204 | p.SysoFiles = pp.SysoFiles 205 | p.CgoCFLAGS = pp.CgoCFLAGS 206 | p.CgoCPPFLAGS = pp.CgoCPPFLAGS 207 | p.CgoCXXFLAGS = pp.CgoCXXFLAGS 208 | p.CgoLDFLAGS = pp.CgoLDFLAGS 209 | p.CgoPkgConfig = pp.CgoPkgConfig 210 | p.Imports = pp.Imports 211 | p.TestGoFiles = pp.TestGoFiles 212 | p.TestImports = pp.TestImports 213 | p.XTestGoFiles = pp.XTestGoFiles 214 | p.XTestImports = pp.XTestImports 215 | } 216 | 217 | type PathPkgsIndex struct { 218 | Indexs []*PkgsIndex 219 | } 220 | 221 | type LoadFlag int 222 | 223 | const ( 224 | LoadGoroot LoadFlag = iota 225 | LoadSkipGoroot 226 | LoadAll 227 | ) 228 | 229 | func (p *PathPkgsIndex) LoadIndex(context build.Context, flag LoadFlag) { 230 | var wg sync.WaitGroup 231 | if flag == LoadGoroot { 232 | context.GOPATH = "" 233 | } 234 | var srcDirs []string 235 | goroot := context.GOROOT 236 | gopath := context.GOPATH 237 | context.GOPATH = "" 238 | 239 | if flag != LoadSkipGoroot { 240 | //go1.4 go/src/ 241 | //go1.3 go/src/pkg; go/src/cmd 242 | _, err := os.Stat(filepath.Join(goroot, "src/pkg/runtime")) 243 | if err == nil { 244 | for _, v := range context.SrcDirs() { 245 | if strings.HasSuffix(v, "pkg") { 246 | srcDirs = append(srcDirs, v[:len(v)-3]+"cmd") 247 | } 248 | srcDirs = append(srcDirs, v) 249 | } 250 | } else { 251 | srcDirs = append(srcDirs, filepath.Join(goroot, "src")) 252 | } 253 | } 254 | iRoot := len(srcDirs) 255 | context.GOPATH = gopath 256 | context.GOROOT = "" 257 | for _, v := range context.SrcDirs() { 258 | srcDirs = append(srcDirs, v) 259 | } 260 | context.GOROOT = goroot 261 | for i, path := range srcDirs { 262 | pi := &PkgsIndex{} 263 | pi.Root = path 264 | pi.Goroot = (i < iRoot) 265 | p.Indexs = append(p.Indexs, pi) 266 | pkgsGate.enter() 267 | f, err := os.Open(path) 268 | if err != nil { 269 | pkgsGate.leave() 270 | continue 271 | } 272 | children, err := f.Readdir(-1) 273 | f.Close() 274 | pkgsGate.leave() 275 | if err != nil { 276 | fmt.Fprint(os.Stderr, err) 277 | continue 278 | } 279 | for _, child := range children { 280 | if child.IsDir() { 281 | wg.Add(1) 282 | go func(path, name string) { 283 | defer wg.Done() 284 | pi.loadPkgsPath(&wg, path, name) 285 | }(path, child.Name()) 286 | } 287 | } 288 | } 289 | wg.Wait() 290 | } 291 | 292 | func (p *PathPkgsIndex) Sort() { 293 | for _, v := range p.Indexs { 294 | v.sort() 295 | } 296 | } 297 | 298 | type PkgsIndex struct { 299 | sync.Mutex 300 | Pkgs []*build.Package 301 | Root string 302 | Goroot bool 303 | } 304 | 305 | func (p *PkgsIndex) sort() { 306 | sort.Sort(PkgSlice(p.Pkgs)) 307 | } 308 | 309 | type PkgSlice []*build.Package 310 | 311 | func (p PkgSlice) Len() int { 312 | return len([]*build.Package(p)) 313 | } 314 | 315 | func (p PkgSlice) Less(i, j int) bool { 316 | if p[i].IsCommand() && !p[j].IsCommand() { 317 | return true 318 | } else if !p[i].IsCommand() && p[j].IsCommand() { 319 | return false 320 | } 321 | return p[i].ImportPath < p[j].ImportPath 322 | } 323 | 324 | func (p PkgSlice) Swap(i, j int) { 325 | p[i], p[j] = p[j], p[i] 326 | } 327 | 328 | // pkgsgate protects the OS & filesystem from too much concurrency. 329 | // Too much disk I/O -> too many threads -> swapping and bad scheduling. 330 | // gate is a semaphore for limiting concurrency. 331 | type gate chan struct{} 332 | 333 | func (g gate) enter() { g <- struct{}{} } 334 | func (g gate) leave() { <-g } 335 | 336 | var pkgsGate = make(gate, 8) 337 | 338 | func (p *PkgsIndex) loadPkgsPath(wg *sync.WaitGroup, root, pkgrelpath string) { 339 | importpath := filepath.ToSlash(pkgrelpath) 340 | dir := filepath.Join(root, importpath) 341 | 342 | pkgsGate.enter() 343 | defer pkgsGate.leave() 344 | pkgDir, err := os.Open(dir) 345 | if err != nil { 346 | return 347 | } 348 | children, err := pkgDir.Readdir(-1) 349 | pkgDir.Close() 350 | if err != nil { 351 | return 352 | } 353 | // hasGo tracks whether a directory actually appears to be a 354 | // Go source code directory. If $GOPATH == $HOME, and 355 | // $HOME/src has lots of other large non-Go projects in it, 356 | // then the calls to importPathToName below can be expensive. 357 | hasGo := false 358 | for _, child := range children { 359 | name := child.Name() 360 | if name == "" { 361 | continue 362 | } 363 | if c := name[0]; c == '.' || ('0' <= c && c <= '9') { 364 | continue 365 | } 366 | if strings.HasSuffix(name, ".go") { 367 | hasGo = true 368 | } 369 | if child.IsDir() { 370 | if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" { 371 | continue 372 | } 373 | wg.Add(1) 374 | go func(root, name string) { 375 | defer wg.Done() 376 | p.loadPkgsPath(wg, root, name) 377 | }(root, filepath.Join(importpath, name)) 378 | } 379 | } 380 | if hasGo { 381 | buildPkg, err := build.ImportDir(dir, 0) 382 | if err == nil { 383 | if buildPkg.ImportPath == "." { 384 | buildPkg.ImportPath = filepath.ToSlash(pkgrelpath) 385 | buildPkg.Root = root 386 | buildPkg.Goroot = p.Goroot 387 | } 388 | p.Lock() 389 | p.Pkgs = append(p.Pkgs, buildPkg) 390 | p.Unlock() 391 | } 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /gopresent/static/slides.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 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 | var PERMANENT_URL_PREFIX = 'static/'; 6 | 7 | var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next']; 8 | 9 | var PM_TOUCH_SENSITIVITY = 15; 10 | 11 | var curSlide; 12 | 13 | /* ---------------------------------------------------------------------- */ 14 | /* classList polyfill by Eli Grey 15 | * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */ 16 | 17 | if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { 18 | 19 | (function (view) { 20 | 21 | var 22 | classListProp = "classList" 23 | , protoProp = "prototype" 24 | , elemCtrProto = (view.HTMLElement || view.Element)[protoProp] 25 | , objCtr = Object 26 | strTrim = String[protoProp].trim || function () { 27 | return this.replace(/^\s+|\s+$/g, ""); 28 | } 29 | , arrIndexOf = Array[protoProp].indexOf || function (item) { 30 | for (var i = 0, len = this.length; i < len; i++) { 31 | if (i in this && this[i] === item) { 32 | return i; 33 | } 34 | } 35 | return -1; 36 | } 37 | // Vendors: please allow content code to instantiate DOMExceptions 38 | , DOMEx = function (type, message) { 39 | this.name = type; 40 | this.code = DOMException[type]; 41 | this.message = message; 42 | } 43 | , checkTokenAndGetIndex = function (classList, token) { 44 | if (token === "") { 45 | throw new DOMEx( 46 | "SYNTAX_ERR" 47 | , "An invalid or illegal string was specified" 48 | ); 49 | } 50 | if (/\s/.test(token)) { 51 | throw new DOMEx( 52 | "INVALID_CHARACTER_ERR" 53 | , "String contains an invalid character" 54 | ); 55 | } 56 | return arrIndexOf.call(classList, token); 57 | } 58 | , ClassList = function (elem) { 59 | var 60 | trimmedClasses = strTrim.call(elem.className) 61 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] 62 | ; 63 | for (var i = 0, len = classes.length; i < len; i++) { 64 | this.push(classes[i]); 65 | } 66 | this._updateClassName = function () { 67 | elem.className = this.toString(); 68 | }; 69 | } 70 | , classListProto = ClassList[protoProp] = [] 71 | , classListGetter = function () { 72 | return new ClassList(this); 73 | } 74 | ; 75 | // Most DOMException implementations don't allow calling DOMException's toString() 76 | // on non-DOMExceptions. Error's toString() is sufficient here. 77 | DOMEx[protoProp] = Error[protoProp]; 78 | classListProto.item = function (i) { 79 | return this[i] || null; 80 | }; 81 | classListProto.contains = function (token) { 82 | token += ""; 83 | return checkTokenAndGetIndex(this, token) !== -1; 84 | }; 85 | classListProto.add = function (token) { 86 | token += ""; 87 | if (checkTokenAndGetIndex(this, token) === -1) { 88 | this.push(token); 89 | this._updateClassName(); 90 | } 91 | }; 92 | classListProto.remove = function (token) { 93 | token += ""; 94 | var index = checkTokenAndGetIndex(this, token); 95 | if (index !== -1) { 96 | this.splice(index, 1); 97 | this._updateClassName(); 98 | } 99 | }; 100 | classListProto.toggle = function (token) { 101 | token += ""; 102 | if (checkTokenAndGetIndex(this, token) === -1) { 103 | this.add(token); 104 | } else { 105 | this.remove(token); 106 | } 107 | }; 108 | classListProto.toString = function () { 109 | return this.join(" "); 110 | }; 111 | 112 | if (objCtr.defineProperty) { 113 | var classListPropDesc = { 114 | get: classListGetter 115 | , enumerable: true 116 | , configurable: true 117 | }; 118 | try { 119 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 120 | } catch (ex) { // IE 8 doesn't support enumerable:true 121 | if (ex.number === -0x7FF5EC54) { 122 | classListPropDesc.enumerable = false; 123 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 124 | } 125 | } 126 | } else if (objCtr[protoProp].__defineGetter__) { 127 | elemCtrProto.__defineGetter__(classListProp, classListGetter); 128 | } 129 | 130 | }(self)); 131 | 132 | } 133 | /* ---------------------------------------------------------------------- */ 134 | 135 | /* Slide movement */ 136 | 137 | function getSlideEl(no) { 138 | if ((no < 0) || (no >= slideEls.length)) { 139 | return null; 140 | } else { 141 | return slideEls[no]; 142 | } 143 | }; 144 | 145 | function updateSlideClass(slideNo, className) { 146 | var el = getSlideEl(slideNo); 147 | 148 | if (!el) { 149 | return; 150 | } 151 | 152 | if (className) { 153 | el.classList.add(className); 154 | } 155 | 156 | for (var i in SLIDE_CLASSES) { 157 | if (className != SLIDE_CLASSES[i]) { 158 | el.classList.remove(SLIDE_CLASSES[i]); 159 | } 160 | } 161 | }; 162 | 163 | function updateSlides() { 164 | for (var i = 0; i < slideEls.length; i++) { 165 | switch (i) { 166 | case curSlide - 2: 167 | updateSlideClass(i, 'far-past'); 168 | break; 169 | case curSlide - 1: 170 | updateSlideClass(i, 'past'); 171 | break; 172 | case curSlide: 173 | updateSlideClass(i, 'current'); 174 | break; 175 | case curSlide + 1: 176 | updateSlideClass(i, 'next'); 177 | break; 178 | case curSlide + 2: 179 | updateSlideClass(i, 'far-next'); 180 | break; 181 | default: 182 | updateSlideClass(i); 183 | break; 184 | } 185 | } 186 | 187 | triggerLeaveEvent(curSlide - 1); 188 | triggerEnterEvent(curSlide); 189 | 190 | window.setTimeout(function() { 191 | // Hide after the slide 192 | disableSlideFrames(curSlide - 2); 193 | }, 301); 194 | 195 | enableSlideFrames(curSlide - 1); 196 | enableSlideFrames(curSlide + 2); 197 | 198 | updateHash(); 199 | }; 200 | 201 | function prevSlide() { 202 | if (curSlide > 0) { 203 | curSlide--; 204 | 205 | updateSlides(); 206 | } 207 | }; 208 | 209 | function nextSlide() { 210 | if (curSlide < slideEls.length - 1) { 211 | curSlide++; 212 | 213 | updateSlides(); 214 | } 215 | }; 216 | 217 | /* Slide events */ 218 | 219 | function triggerEnterEvent(no) { 220 | var el = getSlideEl(no); 221 | if (!el) { 222 | return; 223 | } 224 | 225 | var onEnter = el.getAttribute('onslideenter'); 226 | if (onEnter) { 227 | new Function(onEnter).call(el); 228 | } 229 | 230 | var evt = document.createEvent('Event'); 231 | evt.initEvent('slideenter', true, true); 232 | evt.slideNumber = no + 1; // Make it readable 233 | 234 | el.dispatchEvent(evt); 235 | }; 236 | 237 | function triggerLeaveEvent(no) { 238 | var el = getSlideEl(no); 239 | if (!el) { 240 | return; 241 | } 242 | 243 | var onLeave = el.getAttribute('onslideleave'); 244 | if (onLeave) { 245 | new Function(onLeave).call(el); 246 | } 247 | 248 | var evt = document.createEvent('Event'); 249 | evt.initEvent('slideleave', true, true); 250 | evt.slideNumber = no + 1; // Make it readable 251 | 252 | el.dispatchEvent(evt); 253 | }; 254 | 255 | /* Touch events */ 256 | 257 | function handleTouchStart(event) { 258 | if (event.touches.length == 1) { 259 | touchDX = 0; 260 | touchDY = 0; 261 | 262 | touchStartX = event.touches[0].pageX; 263 | touchStartY = event.touches[0].pageY; 264 | 265 | document.body.addEventListener('touchmove', handleTouchMove, true); 266 | document.body.addEventListener('touchend', handleTouchEnd, true); 267 | } 268 | }; 269 | 270 | function handleTouchMove(event) { 271 | if (event.touches.length > 1) { 272 | cancelTouch(); 273 | } else { 274 | touchDX = event.touches[0].pageX - touchStartX; 275 | touchDY = event.touches[0].pageY - touchStartY; 276 | event.preventDefault(); 277 | } 278 | }; 279 | 280 | function handleTouchEnd(event) { 281 | var dx = Math.abs(touchDX); 282 | var dy = Math.abs(touchDY); 283 | 284 | if ((dx > PM_TOUCH_SENSITIVITY) && (dy < (dx * 2 / 3))) { 285 | if (touchDX > 0) { 286 | prevSlide(); 287 | } else { 288 | nextSlide(); 289 | } 290 | } 291 | 292 | cancelTouch(); 293 | }; 294 | 295 | function cancelTouch() { 296 | document.body.removeEventListener('touchmove', handleTouchMove, true); 297 | document.body.removeEventListener('touchend', handleTouchEnd, true); 298 | }; 299 | 300 | /* Preloading frames */ 301 | 302 | function disableSlideFrames(no) { 303 | var el = getSlideEl(no); 304 | if (!el) { 305 | return; 306 | } 307 | 308 | var frames = el.getElementsByTagName('iframe'); 309 | for (var i = 0, frame; frame = frames[i]; i++) { 310 | disableFrame(frame); 311 | } 312 | }; 313 | 314 | function enableSlideFrames(no) { 315 | var el = getSlideEl(no); 316 | if (!el) { 317 | return; 318 | } 319 | 320 | var frames = el.getElementsByTagName('iframe'); 321 | for (var i = 0, frame; frame = frames[i]; i++) { 322 | enableFrame(frame); 323 | } 324 | }; 325 | 326 | function disableFrame(frame) { 327 | frame.src = 'about:blank'; 328 | }; 329 | 330 | function enableFrame(frame) { 331 | var src = frame._src; 332 | 333 | if (frame.src != src && src != 'about:blank') { 334 | frame.src = src; 335 | } 336 | }; 337 | 338 | function setupFrames() { 339 | var frames = document.querySelectorAll('iframe'); 340 | for (var i = 0, frame; frame = frames[i]; i++) { 341 | frame._src = frame.src; 342 | disableFrame(frame); 343 | } 344 | 345 | enableSlideFrames(curSlide); 346 | enableSlideFrames(curSlide + 1); 347 | enableSlideFrames(curSlide + 2); 348 | }; 349 | 350 | function setupInteraction() { 351 | /* Clicking and tapping */ 352 | 353 | var el = document.createElement('div'); 354 | el.className = 'slide-area'; 355 | el.id = 'prev-slide-area'; 356 | el.addEventListener('click', prevSlide, false); 357 | document.querySelector('section.slides').appendChild(el); 358 | 359 | var el = document.createElement('div'); 360 | el.className = 'slide-area'; 361 | el.id = 'next-slide-area'; 362 | el.addEventListener('click', nextSlide, false); 363 | document.querySelector('section.slides').appendChild(el); 364 | 365 | /* Swiping */ 366 | 367 | document.body.addEventListener('touchstart', handleTouchStart, false); 368 | } 369 | 370 | /* Hash functions */ 371 | 372 | function getCurSlideFromHash() { 373 | var slideNo = parseInt(location.hash.substr(1)); 374 | 375 | if (slideNo) { 376 | curSlide = slideNo - 1; 377 | } else { 378 | curSlide = 0; 379 | } 380 | }; 381 | 382 | function updateHash() { 383 | location.replace('#' + (curSlide + 1)); 384 | }; 385 | 386 | /* Event listeners */ 387 | 388 | function handleBodyKeyDown(event) { 389 | // If we're in a code element, only handle pgup/down. 390 | var inCode = event.target.classList.contains("code"); 391 | 392 | switch (event.keyCode) { 393 | case 39: // right arrow 394 | case 13: // Enter 395 | case 32: // space 396 | if (inCode) break; 397 | case 34: // PgDn 398 | nextSlide(); 399 | event.preventDefault(); 400 | break; 401 | 402 | case 37: // left arrow 403 | case 8: // Backspace 404 | if (inCode) break; 405 | case 33: // PgUp 406 | prevSlide(); 407 | event.preventDefault(); 408 | break; 409 | 410 | case 40: // down arrow 411 | if (inCode) break; 412 | nextSlide(); 413 | event.preventDefault(); 414 | break; 415 | 416 | case 38: // up arrow 417 | if (inCode) break; 418 | prevSlide(); 419 | event.preventDefault(); 420 | break; 421 | } 422 | }; 423 | 424 | function addEventListeners() { 425 | document.addEventListener('keydown', handleBodyKeyDown, false); 426 | }; 427 | 428 | /* Initialization */ 429 | 430 | function addFontStyle() { 431 | var el = document.createElement('link'); 432 | el.rel = 'stylesheet'; 433 | el.type = 'text/css'; 434 | el.href = '//fonts.googleapis.com/css?family=' + 435 | 'Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono'; 436 | 437 | document.body.appendChild(el); 438 | }; 439 | 440 | function addGeneralStyle() { 441 | var el = document.createElement('link'); 442 | el.rel = 'stylesheet'; 443 | el.type = 'text/css'; 444 | el.href = PERMANENT_URL_PREFIX + 'styles.css'; 445 | document.body.appendChild(el); 446 | 447 | var el = document.createElement('meta'); 448 | el.name = 'viewport'; 449 | el.content = 'width=1100,height=750'; 450 | document.querySelector('head').appendChild(el); 451 | 452 | var el = document.createElement('meta'); 453 | el.name = 'apple-mobile-web-app-capable'; 454 | el.content = 'yes'; 455 | document.querySelector('head').appendChild(el); 456 | }; 457 | 458 | function addPrintStyle() { 459 | var el = document.createElement('link'); 460 | el.rel = 'stylesheet'; 461 | el.type = 'text/css'; 462 | el.media = "print"; 463 | el.href = PERMANENT_URL_PREFIX + 'print.css'; 464 | document.body.appendChild(el); 465 | }; 466 | 467 | function handleDomLoaded() { 468 | slideEls = document.querySelectorAll('section.slides > article'); 469 | 470 | setupFrames(); 471 | 472 | addFontStyle(); 473 | addGeneralStyle(); 474 | addPrintStyle(); 475 | addEventListeners(); 476 | 477 | updateSlides(); 478 | 479 | setupInteraction(); 480 | 481 | document.body.classList.add('loaded'); 482 | }; 483 | 484 | function initialize() { 485 | getCurSlideFromHash(); 486 | 487 | if (window['_DEBUG']) { 488 | PERMANENT_URL_PREFIX = '../'; 489 | } 490 | 491 | if (window['_DCL']) { 492 | handleDomLoaded(); 493 | } else { 494 | document.addEventListener('DOMContentLoaded', handleDomLoaded, false); 495 | } 496 | } 497 | 498 | // If ?debug exists then load the script relative instead of absolute 499 | if (!window['_DEBUG'] && document.location.href.indexOf('?debug') !== -1) { 500 | document.addEventListener('DOMContentLoaded', function() { 501 | // Avoid missing the DomContentLoaded event 502 | window['_DCL'] = true 503 | }, false); 504 | 505 | window['_DEBUG'] = true; 506 | var script = document.createElement('script'); 507 | script.type = 'text/javascript'; 508 | script.src = '../slides.js'; 509 | var s = document.getElementsByTagName('script')[0]; 510 | s.parentNode.insertBefore(script, s); 511 | 512 | // Remove this script 513 | s.parentNode.removeChild(s); 514 | } else { 515 | initialize(); 516 | } 517 | -------------------------------------------------------------------------------- /astview/astview.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2015 visualfc . 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 astview 6 | 7 | import ( 8 | "fmt" 9 | "go/ast" 10 | "go/parser" 11 | "go/token" 12 | "go/types" 13 | "io" 14 | "io/ioutil" 15 | "os" 16 | "path/filepath" 17 | "sort" 18 | "strconv" 19 | "strings" 20 | 21 | "github.com/visualfc/gotools/pkg/command" 22 | "github.com/visualfc/gotools/pkg/pkgutil" 23 | ) 24 | 25 | var Command = &command.Command{ 26 | Run: runAstView, 27 | UsageLine: "astview [-stdin] files...", 28 | Short: "print go files astview", 29 | Long: `print go files astview`, 30 | } 31 | 32 | var ( 33 | astViewStdin bool 34 | astViewShowEndPos bool 35 | astViewShowTodo bool 36 | astViewOutline bool 37 | astViewShowTypeParams bool 38 | astViewSep string 39 | ) 40 | 41 | func init() { 42 | Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin") 43 | Command.Flag.BoolVar(&astViewShowEndPos, "end", false, "show decl end pos") 44 | Command.Flag.BoolVar(&astViewShowTodo, "todo", false, "show todo list") 45 | Command.Flag.BoolVar(&astViewShowTypeParams, "tp", false, "show typeparams") 46 | Command.Flag.BoolVar(&astViewOutline, "outline", false, "set outline mode") 47 | Command.Flag.StringVar(&astViewSep, "sep", ",", "set output seperator") 48 | } 49 | 50 | func runAstView(cmd *command.Command, args []string) error { 51 | if len(args) == 0 { 52 | cmd.Usage() 53 | return os.ErrInvalid 54 | } 55 | if astViewStdin { 56 | view, err := NewFilePackageSource(args[0], cmd.Stdin, true) 57 | if err != nil { 58 | return err 59 | } 60 | view.PrintTree(cmd.Stdout) 61 | } else { 62 | if len(args) == 1 && astViewOutline { 63 | err := PrintFileOutline(args[0], cmd.Stdout, astViewSep, true) 64 | if err != nil { 65 | return err 66 | } 67 | } else { 68 | err := PrintFilesTree(args, cmd.Stdout, true) 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | const ( 78 | tag_package = "p" 79 | tag_imports_folder = "+m" 80 | tag_import = "mm" 81 | tag_type = "t" 82 | tag_struct = "s" 83 | tag_interface = "i" 84 | tag_value = "v" 85 | tag_const = "c" 86 | tag_func = "f" 87 | tag_value_folder = "+v" 88 | tag_const_folder = "+c" 89 | tag_func_folder = "+f" 90 | tag_factor_folder = "+tf" 91 | tag_type_method = "tm" 92 | tag_type_factor = "tf" 93 | tag_type_value = "tv" 94 | tag_todo = "b" 95 | tag_todo_folder = "+b" 96 | ) 97 | 98 | type PackageView struct { 99 | fset *token.FileSet 100 | pdoc *PackageDoc 101 | pkg *ast.Package 102 | expr bool 103 | } 104 | 105 | var AllFiles []string 106 | 107 | func (p *PackageView) posFileIndex(pos token.Position) int { 108 | var index = -1 109 | for i := 0; i < len(AllFiles); i++ { 110 | if AllFiles[i] == pos.Filename { 111 | index = i 112 | break 113 | } 114 | } 115 | if index == -1 { 116 | AllFiles = append(AllFiles, pos.Filename) 117 | index = len(AllFiles) - 1 118 | } 119 | return index 120 | } 121 | 122 | func (p *PackageView) posText(node ast.Node) string { 123 | pos := p.fset.Position(node.Pos()) 124 | index := p.posFileIndex(pos) 125 | if astViewShowEndPos { 126 | end := p.fset.Position(node.End()) 127 | return fmt.Sprintf("%d:%d:%d:%d:%d", index, pos.Line, pos.Column, end.Line, end.Column) 128 | } 129 | return fmt.Sprintf("%d:%d:%d", index, pos.Line, pos.Column) 130 | } 131 | 132 | func NewFilePackage(filename string) (*PackageView, error) { 133 | p := new(PackageView) 134 | p.fset = token.NewFileSet() 135 | file, err := parser.ParseFile(p.fset, filename, nil, parser.AllErrors) 136 | if file == nil { 137 | return nil, err 138 | } 139 | m := make(map[string]*ast.File) 140 | m[filename] = file 141 | pkg, err := ast.NewPackage(p.fset, m, nil, nil) 142 | if err != nil { 143 | return nil, err 144 | } 145 | p.pkg = pkg 146 | p.pdoc = NewPackageDoc(pkg, pkg.Name, true) 147 | return p, nil 148 | } 149 | 150 | func NewPackageView(pkg *ast.Package, fset *token.FileSet, expr bool) (*PackageView, error) { 151 | p := new(PackageView) 152 | p.fset = fset 153 | p.pkg = pkg 154 | p.pdoc = NewPackageDoc(pkg, pkg.Name, true) 155 | p.expr = expr 156 | return p, nil 157 | } 158 | 159 | func ParseFiles(fset *token.FileSet, filenames []string, mode parser.Mode) (pkgs map[string]*ast.Package, pkgsfiles []string, first error) { 160 | pkgs = make(map[string]*ast.Package) 161 | for _, filename := range filenames { 162 | if src, err := parser.ParseFile(fset, filename, nil, mode); src != nil { 163 | name := src.Name.Name 164 | pkg, found := pkgs[name] 165 | if !found { 166 | pkg = &ast.Package{ 167 | Name: name, 168 | Files: make(map[string]*ast.File), 169 | } 170 | pkgs[name] = pkg 171 | } 172 | pkg.Files[filename] = src 173 | pkgsfiles = append(pkgsfiles, filename) 174 | } else { 175 | first = err 176 | return 177 | } 178 | } 179 | return 180 | } 181 | 182 | func PrintFilesTree(filenames []string, w io.Writer, expr bool) error { 183 | fset := token.NewFileSet() 184 | mode := parser.AllErrors 185 | if astViewShowTodo { 186 | mode |= parser.ParseComments 187 | } 188 | pkgs, pkgsfiles, err := ParseFiles(fset, filenames, mode) 189 | if err != nil { 190 | return err 191 | } 192 | AllFiles = pkgsfiles 193 | for i := 0; i < len(AllFiles); i++ { 194 | fmt.Fprintf(w, "@%s\n", AllFiles[i]) 195 | } 196 | for _, pkg := range pkgs { 197 | view, err := NewPackageView(pkg, fset, expr) 198 | if err != nil { 199 | return err 200 | } 201 | view.PrintTree(w) 202 | } 203 | return nil 204 | } 205 | 206 | func NewFilePackageSource(filename string, f io.Reader, expr bool) (*PackageView, error) { 207 | src, err := ioutil.ReadAll(f) 208 | if err != nil { 209 | return nil, err 210 | } 211 | p := new(PackageView) 212 | p.fset = token.NewFileSet() 213 | p.expr = expr 214 | mode := parser.AllErrors 215 | if astViewShowTodo { 216 | mode |= parser.ParseComments 217 | } 218 | file, err := parser.ParseFile(p.fset, filename, src, mode) 219 | if err != nil { 220 | return nil, err 221 | } 222 | m := make(map[string]*ast.File) 223 | m[filename] = file 224 | pkg, err := ast.NewPackage(p.fset, m, nil, nil) 225 | if err != nil { 226 | return nil, err 227 | } 228 | 229 | p.pdoc = NewPackageDoc(pkg, pkg.Name, true) 230 | return p, nil 231 | } 232 | 233 | func (p *PackageView) out0(w io.Writer, level int, text ...string) { 234 | fmt.Fprintf(w, "%v%s%s\n", level, astViewSep, strings.Join(text, astViewSep)) 235 | } 236 | func (p *PackageView) out1(w io.Writer, level int, pos ast.Node, text ...string) { 237 | fmt.Fprintf(w, "%v%s%s%s%s\n", level, astViewSep, strings.Join(text, astViewSep), astViewSep, p.posText(pos)) 238 | } 239 | func (p *PackageView) out2(w io.Writer, level int, pos ast.Node, expr ast.Expr, text ...string) { 240 | fmt.Fprintf(w, "%v%s%s%s%s@%v\n", level, astViewSep, strings.Join(text, astViewSep), astViewSep, p.posText(pos), types.ExprString(expr)) 241 | } 242 | func (p *PackageView) out2s(w io.Writer, level int, pos ast.Node, expr string, text ...string) { 243 | fmt.Fprintf(w, "%v%s%s%s%s@%v\n", level, astViewSep, strings.Join(text, astViewSep), astViewSep, p.posText(pos), expr) 244 | } 245 | 246 | func (p *PackageView) printFuncsHelper(w io.Writer, funcs []*FuncDoc, level int, tag string, tag_folder string) { 247 | for _, f := range funcs { 248 | if p.expr { 249 | p.out2(w, level, f.Decl, f.Decl.Type, tag, funcName(f.Decl, astViewShowTypeParams)) 250 | } else { 251 | p.out1(w, level, f.Decl, tag, funcName(f.Decl, astViewShowTypeParams)) 252 | } 253 | } 254 | } 255 | 256 | func (p *PackageView) PrintVars(w io.Writer, vars []*ValueDoc, level int, tag string, tag_folder string) { 257 | if len(tag_folder) > 0 && len(vars) > 0 { 258 | if tag_folder == tag_value_folder { 259 | p.out0(w, level, tag_folder, "Variables") 260 | } else if tag_folder == tag_const_folder { 261 | p.out0(w, level, tag_folder, "Constants") 262 | } 263 | level++ 264 | } 265 | for _, v := range vars { 266 | if v.Decl == nil { 267 | continue 268 | } 269 | for _, s := range v.Decl.Specs { 270 | if m, ok := s.(*ast.ValueSpec); ok { 271 | for i := 0; i < len(m.Names); i++ { 272 | if p.expr && m.Type != nil { 273 | p.out2(w, level, m, m.Type, tag, m.Names[i].String()) 274 | } else { 275 | p.out1(w, level, m, tag, m.Names[i].String()) 276 | } 277 | } 278 | } 279 | } 280 | } 281 | } 282 | func (p *PackageView) PrintTypes(w io.Writer, types []*TypeDoc, level int) { 283 | for _, d := range types { 284 | if d.Decl == nil { 285 | continue 286 | } 287 | typespec := d.Decl.Specs[0].(*ast.TypeSpec) 288 | var tag = tag_type 289 | if _, ok := typespec.Type.(*ast.InterfaceType); ok { 290 | tag = tag_interface 291 | } else if _, ok := typespec.Type.(*ast.StructType); ok { 292 | tag = tag_struct 293 | } 294 | p.out1(w, level, d.Decl, tag, typeName(typespec, astViewShowTypeParams)) 295 | p.printFuncsHelper(w, d.Funcs, level+1, tag_type_factor, "") 296 | p.printFuncsHelper(w, d.Methods, level+1, tag_type_method, "") 297 | p.PrintTypeFields(w, d.Decl, level+1) 298 | //p.PrintVars(w, d.Consts, level+1, tag_const, "") 299 | //p.PrintVars(w, d.Vars, level+1, tag_value, "") 300 | } 301 | } 302 | 303 | func (p *PackageView) PrintTypeFields(w io.Writer, decl *ast.GenDecl, level int) { 304 | spec, ok := decl.Specs[0].(*ast.TypeSpec) 305 | if ok == false { 306 | return 307 | } 308 | switch d := spec.Type.(type) { 309 | case *ast.StructType: 310 | for _, field := range d.Fields.List { 311 | if field.Names == nil { 312 | p.out2(w, level, field, field.Type, tag_type, types.ExprString(field.Type)) 313 | } else { 314 | for _, m := range field.Names { 315 | if field.Type != nil { 316 | p.out2(w, level, field, field.Type, tag_type_value, m.Name) 317 | } else { 318 | p.out1(w, level, field, tag_type_value, m.Name) 319 | } 320 | } 321 | } 322 | } 323 | case *ast.InterfaceType: 324 | for _, list := range d.Methods.List { 325 | if len(list.Names) == 0 { 326 | p.out2(w, level, list, list.Type, tag_type, types.ExprString(list.Type)) 327 | } else { 328 | for _, m := range list.Names { 329 | p.out2(w, level, list.Type, m, tag_type_method, m.Name) 330 | } 331 | } 332 | } 333 | } 334 | } 335 | 336 | func (p *PackageView) PrintHeader(w io.Writer, level int) { 337 | p.out0(w, level, tag_package, p.pdoc.PackageName) 338 | } 339 | 340 | func (p *PackageView) PrintImports(w io.Writer, level int, tag, tag_folder string) { 341 | if tag_folder != "" && len(p.pdoc.Imports) > 0 { 342 | p.out0(w, level, tag_folder, "Imports") 343 | level++ 344 | } 345 | var parentPkg *pkgutil.Package 346 | if pkgutil.IsVendorExperiment() && p.pkg != nil { 347 | for filename, _ := range p.pkg.Files { 348 | if !filepath.IsAbs(filename) { 349 | name, err := filepath.Abs(filename) 350 | if err == nil { 351 | filename = name 352 | } 353 | } 354 | parentPkg = pkgutil.ImportFile(filename) 355 | break 356 | } 357 | } 358 | for _, name := range p.pdoc.Imports { 359 | vname := "\"" + name + "\"" 360 | var ps []string 361 | for _, file := range p.pkg.Files { 362 | for _, v := range file.Imports { 363 | if v.Path.Value == vname { 364 | ps = append(ps, p.posText(v)) 365 | } 366 | } 367 | } 368 | if parentPkg != nil { 369 | name, _ = pkgutil.VendoredImportPath(parentPkg, name) 370 | } 371 | p.out0(w, level, tag, name, strings.Join(ps, ";")) 372 | } 373 | } 374 | 375 | func (p *PackageView) PrintFuncs(w io.Writer, level int, tag_folder string) { 376 | hasFolder := false 377 | if len(p.pdoc.Funcs) > 0 || len(p.pdoc.Factorys) > 0 { 378 | hasFolder = true 379 | } 380 | if !hasFolder { 381 | return 382 | } 383 | if len(tag_folder) > 0 { 384 | p.out0(w, level, tag_folder, "Functions") 385 | level++ 386 | } 387 | p.printFuncsHelper(w, p.pdoc.Factorys, level, tag_type_factor, tag_func_folder) 388 | p.printFuncsHelper(w, p.pdoc.Funcs, level, tag_func, tag_func_folder) 389 | } 390 | 391 | func (p *PackageView) PrintTodos(w io.Writer, level int, tag, tag_folder string) { 392 | hasFolder := false 393 | if len(p.pdoc.Todos) > 0 { 394 | hasFolder = true 395 | } 396 | if !hasFolder { 397 | return 398 | } 399 | if len(tag_folder) > 0 { 400 | p.out0(w, level, tag_folder, "TodoList") 401 | level++ 402 | } 403 | for _, todo := range p.pdoc.Todos { 404 | c := todo.Comments.List[0] 405 | p.out2s(w, level, c, todo.Text, tag, todo.Tag) 406 | } 407 | } 408 | 409 | func (p *PackageView) PrintPackage(w io.Writer, level int) { 410 | p.PrintHeader(w, level) 411 | level++ 412 | p.PrintImports(w, level, tag_import, tag_imports_folder) 413 | p.PrintVars(w, p.pdoc.Vars, level, tag_value, tag_value_folder) 414 | p.PrintVars(w, p.pdoc.Consts, level, tag_const, tag_const_folder) 415 | p.PrintFuncs(w, level, tag_func_folder) 416 | p.PrintTypes(w, p.pdoc.Types, level) 417 | p.PrintTodos(w, level, tag_todo, tag_todo_folder) 418 | } 419 | 420 | // level,tag,pos@info 421 | func (p *PackageView) PrintTree(w io.Writer) { 422 | p.PrintPackage(w, 0) 423 | } 424 | 425 | // level,tag,pos@info 426 | func PrintFileOutline(filename string, w io.Writer, sep string, showexpr bool) error { 427 | fset := token.NewFileSet() 428 | mode := parser.AllErrors 429 | if astViewShowTodo { 430 | mode |= parser.ParseComments 431 | } 432 | f, err := parser.ParseFile(fset, filename, nil, mode) 433 | if err != nil { 434 | return err 435 | } 436 | posText := func(node ast.Node) string { 437 | pos := fset.Position(node.Pos()) 438 | if astViewShowEndPos { 439 | end := fset.Position(node.End()) 440 | return fmt.Sprintf("%d:%d:%d:%d:%d", 0, pos.Line, pos.Column, end.Line, end.Column) 441 | } 442 | return fmt.Sprintf("%d:%d:%d", 0, pos.Line, pos.Column) 443 | } 444 | 445 | out0 := func(level int, text ...string) { 446 | fmt.Fprintf(w, "%v%s%s\n", level, sep, strings.Join(text, sep)) 447 | } 448 | out1 := func(level int, pos ast.Node, text ...string) { 449 | fmt.Fprintf(w, "%v%s%s%s%s\n", level, sep, strings.Join(text, sep), sep, posText(pos)) 450 | } 451 | out2 := func(level int, pos ast.Node, expr ast.Expr, text ...string) { 452 | fmt.Fprintf(w, "%v%s%s%s%s@%v\n", level, sep, strings.Join(text, sep), sep, posText(pos), types.ExprString(expr)) 453 | } 454 | out2s := func(level int, pos ast.Node, expr string, text ...string) { 455 | fmt.Fprintf(w, "%v%s%s%s%s@%v\n", level, sep, strings.Join(text, sep), sep, posText(pos), expr) 456 | } 457 | 458 | fmt.Fprintf(w, "@%s\n", filename) 459 | level := 0 460 | out1(level, f.Name, tag_package, f.Name.Name) 461 | // level++ 462 | if len(f.Imports) > 0 { 463 | sort.Slice(f.Imports, func(i, j int) bool { 464 | return f.Imports[i].Pos() < f.Imports[j].Pos() 465 | }) 466 | out0(level, tag_imports_folder, "Imports") 467 | level++ 468 | for _, imp := range f.Imports { 469 | path, _ := strconv.Unquote(imp.Path.Value) 470 | out1(level, imp, tag_import, path) 471 | } 472 | level-- 473 | } 474 | 475 | sort.Slice(f.Decls, func(i, j int) bool { 476 | return f.Decls[i].Pos() < f.Decls[j].Pos() 477 | }) 478 | for _, decl := range f.Decls { 479 | switch d := decl.(type) { 480 | case *ast.GenDecl: 481 | switch d.Tok { 482 | case token.IMPORT: 483 | case token.TYPE: 484 | for _, spec := range d.Specs { 485 | ts := spec.(*ast.TypeSpec) 486 | switch t := ts.Type.(type) { 487 | case *ast.StructType: 488 | out2(level, ts, t, tag_struct, typeName(ts, astViewShowTypeParams)) 489 | level++ 490 | for _, f := range t.Fields.List { 491 | for _, name := range f.Names { 492 | out2(level, f, f.Type, tag_type_value, name.String()) 493 | } 494 | } 495 | level-- 496 | case *ast.InterfaceType: 497 | out2(level, ts, t, tag_interface, typeName(ts, astViewShowTypeParams)) 498 | level++ 499 | for _, f := range t.Methods.List { 500 | if len(f.Names) != 0 { 501 | for _, name := range f.Names { 502 | out2(level, f, f.Type, tag_type_method, name.String()) 503 | } 504 | } else { 505 | out2(level, f, f.Type, tag_type, types.ExprString(f.Type)) 506 | } 507 | } 508 | level-- 509 | default: 510 | out2(level, ts, t, tag_type, typeName(ts, astViewShowTypeParams)) 511 | } 512 | } 513 | case token.CONST: 514 | for _, spec := range d.Specs { 515 | vs := spec.(*ast.ValueSpec) 516 | for i, name := range vs.Names { 517 | if vs.Values == nil { 518 | out1(level, vs, tag_const, name.String()) 519 | } else { 520 | out2(level, vs, vs.Values[i], tag_const, name.String()) 521 | } 522 | } 523 | } 524 | case token.VAR: 525 | for _, spec := range d.Specs { 526 | vs := spec.(*ast.ValueSpec) 527 | for _, name := range vs.Names { 528 | if vs.Type == nil { 529 | out1(level, vs, tag_value, name.String()) 530 | } else { 531 | out2(level, vs, vs.Type, tag_value, name.String()) 532 | } 533 | } 534 | } 535 | } 536 | case *ast.FuncDecl: 537 | if d.Recv != nil { 538 | var name string 539 | if astViewShowTypeParams { 540 | name = types.ExprString(d.Recv.List[0].Type) 541 | } else { 542 | var star bool 543 | name, star = recvTypeName(d.Recv.List[0].Type, true) 544 | if star { 545 | name = "*" + name 546 | } 547 | } 548 | out2(level, d, d.Type, tag_func, "("+name+")."+d.Name.String()) 549 | } else { 550 | out2(level, d, d.Type, tag_func, funcName(d, astViewShowTypeParams)) 551 | } 552 | } 553 | } 554 | 555 | if astViewShowTodo { 556 | var todoList []*TodoDoc 557 | for _, c := range f.Comments { 558 | text := c.List[0].Text 559 | if m := todo_markers.FindStringSubmatchIndex(text); m != nil { 560 | todoList = append(todoList, &TodoDoc{text[m[2]:m[3]], text[m[2]:], c}) 561 | } 562 | } 563 | if len(todoList) > 0 { 564 | out0(level, tag_todo_folder, "TodoList") 565 | level++ 566 | for _, todo := range todoList { 567 | c := todo.Comments.List[0] 568 | out2s(level, c, todo.Text, tag_todo, todo.Tag) 569 | } 570 | level-- 571 | } 572 | } 573 | return nil 574 | } 575 | -------------------------------------------------------------------------------- /finddoc/finddoc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The rspace 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 | // Doc is a simple document printer that produces the doc comments for its 6 | // argument symbols, plus a link to the full documentation and a pointer to 7 | // the source. It has a more Go-like UI than godoc. It can also search for 8 | // symbols by looking in all packages, and case is ignored. For instance: 9 | // doc isupper 10 | // will find unicode.IsUpper. 11 | // 12 | // The -pkg flag retrieves package-level doc comments only. 13 | // 14 | // Usage: 15 | // doc pkg.name # "doc io.Writer" 16 | // doc pkg name # "doc fmt Printf" 17 | // doc name # "doc isupper" (finds unicode.IsUpper) 18 | // doc -pkg pkg # "doc fmt" 19 | // 20 | // The pkg is the last element of the package path; 21 | // no slashes (ast.Node not go/ast.Node). 22 | // 23 | // Flags 24 | // -c(onst) -f(unc) -i(nterface) -m(ethod) -s(truct) -t(ype) -v(ar) 25 | // restrict hits to declarations of the corresponding kind. 26 | // Flags 27 | // -doc -src -url 28 | // restrict printing to the documentation, source path, or godoc URL. 29 | package finddoc 30 | 31 | import ( 32 | "bytes" 33 | "fmt" 34 | "go/ast" 35 | "go/parser" 36 | "go/printer" 37 | "go/token" 38 | "go/types" 39 | "os" 40 | "path" 41 | "path/filepath" 42 | "regexp" 43 | "runtime" 44 | "strings" 45 | 46 | "github.com/visualfc/gotools/pkg/command" 47 | ) 48 | 49 | const usageDoc = `Find documentation for names. 50 | usage: 51 | doc pkg.name # "doc io.Writer" 52 | doc pkg name # "doc fmt Printf" 53 | doc name # "doc isupper" finds unicode.IsUpper 54 | doc -pkg pkg # "doc fmt" 55 | doc -r expr # "doc -r '.*exported'" 56 | pkg is the last component of any package, e.g. fmt, parser 57 | name is the name of an exported symbol; case is ignored in matches. 58 | 59 | The name may also be a regular expression to select which names 60 | to match. In regular expression searches, case is ignored and 61 | the pattern must match the entire name, so ".?print" will match 62 | Print, Fprint and Sprint but not Fprintf. 63 | 64 | Flags 65 | -c(onst) -f(unc) -i(nterface) -m(ethod) -s(truct) -t(ype) -v(ar) 66 | restrict hits to declarations of the corresponding kind. 67 | Flags 68 | -doc -src -url 69 | restrict printing to the documentation, source path, or godoc URL. 70 | Flag 71 | -r 72 | takes a single argument (no package), a name or regular expression 73 | to search for in all packages. 74 | ` 75 | 76 | var Command = &command.Command{ 77 | Run: runDoc, 78 | UsageLine: "finddoc [pkg.name|pkg name|-pkg name]", 79 | Short: "golang doc lookup", 80 | Long: usageDoc, 81 | } 82 | 83 | var ( 84 | // If none is set, all are set. 85 | docFlag bool 86 | srcFlag bool 87 | urlFlag bool 88 | regexpFlag bool 89 | matchWordFlag bool 90 | matchCaseFlag bool 91 | constantFlag bool 92 | functionFlag bool 93 | interfaceFlag bool 94 | methodFlag bool 95 | packageFlag bool 96 | structFlag bool 97 | typeFlag bool 98 | variableFlag bool 99 | urlHeadTag string 100 | ) 101 | 102 | func init() { 103 | Command.Flag.BoolVar(&docFlag, "doc", false, "restrict output to documentation only") 104 | Command.Flag.BoolVar(&srcFlag, "src", false, "restrict output to source file only") 105 | Command.Flag.BoolVar(&urlFlag, "url", false, "restrict output to godoc URL only") 106 | Command.Flag.BoolVar(®expFlag, "r", false, "single argument is a regular expression for a name") 107 | Command.Flag.BoolVar(&matchWordFlag, "word", false, "search match whole word") 108 | Command.Flag.BoolVar(&matchCaseFlag, "case", false, "search match case") 109 | 110 | Command.Flag.BoolVar(&constantFlag, "const", false, "show doc for consts only") 111 | Command.Flag.BoolVar(&functionFlag, "func", false, "show doc for funcs only") 112 | Command.Flag.BoolVar(&interfaceFlag, "interface", false, "show doc for interfaces only") 113 | Command.Flag.BoolVar(&methodFlag, "method", false, "show doc for methods only") 114 | Command.Flag.BoolVar(&packageFlag, "package", false, "show top-level package doc only") 115 | Command.Flag.BoolVar(&structFlag, "struct", false, "show doc for structs only") 116 | Command.Flag.BoolVar(&typeFlag, "type", false, "show doc for types only") 117 | Command.Flag.BoolVar(&variableFlag, "var", false, "show doc for vars only") 118 | 119 | Command.Flag.BoolVar(&constantFlag, "c", false, "alias for -const") 120 | Command.Flag.BoolVar(&functionFlag, "f", false, "alias for -func") 121 | Command.Flag.BoolVar(&interfaceFlag, "i", false, "alias for -interface") 122 | Command.Flag.BoolVar(&methodFlag, "m", false, "alias for -method") 123 | Command.Flag.BoolVar(&packageFlag, "pkg", false, "alias for -package") 124 | Command.Flag.BoolVar(&structFlag, "s", false, "alias for -struct") 125 | Command.Flag.BoolVar(&typeFlag, "t", false, "alias for -type") 126 | Command.Flag.BoolVar(&variableFlag, "v", false, "alias for -var") 127 | 128 | Command.Flag.StringVar(&urlHeadTag, "urltag", "", "url head tag, liteide provate") 129 | } 130 | 131 | func runDoc(cmd *command.Command, args []string) error { 132 | if !(constantFlag || functionFlag || interfaceFlag || methodFlag || packageFlag || structFlag || typeFlag || variableFlag) { // none set 133 | constantFlag = true 134 | functionFlag = true 135 | methodFlag = true 136 | // Not package! It's special. 137 | typeFlag = true 138 | variableFlag = true 139 | } 140 | if !(docFlag || srcFlag || urlFlag) { 141 | docFlag = true 142 | srcFlag = true 143 | urlFlag = true 144 | } 145 | var pkg, name string 146 | switch len(args) { 147 | case 1: 148 | if packageFlag { 149 | pkg = args[0] 150 | } else if regexpFlag { 151 | name = args[0] 152 | } else if strings.Contains(args[0], ".") { 153 | pkg, name = split(args[0]) 154 | } else { 155 | name = args[0] 156 | } 157 | case 2: 158 | if packageFlag { 159 | cmd.Usage() 160 | return os.ErrInvalid 161 | } 162 | pkg, name = args[0], args[1] 163 | default: 164 | cmd.Usage() 165 | return os.ErrInvalid 166 | } 167 | if strings.Contains(pkg, "/") { 168 | fmt.Fprintf(os.Stderr, "doc: package name cannot contain slash (TODO)\n") 169 | os.Exit(2) 170 | } 171 | for _, path := range Paths(pkg) { 172 | lookInDirectory(path, name) 173 | } 174 | return nil 175 | } 176 | 177 | var slash = string(filepath.Separator) 178 | var slashDot = string(filepath.Separator) + "." 179 | var goRootSrcPkg = filepath.Join(runtime.GOROOT(), "src", "pkg") 180 | var goRootSrcCmd = filepath.Join(runtime.GOROOT(), "src", "cmd") 181 | var goPaths = SplitGopath() 182 | 183 | func split(arg string) (pkg, name string) { 184 | dot := strings.IndexRune(arg, '.') // We know there's one there. 185 | return arg[0:dot], arg[dot+1:] 186 | } 187 | 188 | func Paths(pkg string) []string { 189 | pkgs := pathsFor(runtime.GOROOT(), pkg) 190 | for _, root := range goPaths { 191 | pkgs = append(pkgs, pathsFor(root, pkg)...) 192 | } 193 | return pkgs 194 | } 195 | 196 | func SplitGopath() []string { 197 | gopath := os.Getenv("GOPATH") 198 | if gopath == "" { 199 | return nil 200 | } 201 | return strings.Split(gopath, string(os.PathListSeparator)) 202 | } 203 | 204 | // pathsFor recursively walks the tree looking for possible directories for the package: 205 | // those whose basename is pkg. 206 | func pathsFor(root, pkg string) []string { 207 | root = path.Join(root, "src") 208 | pkgPaths := make([]string, 0, 10) 209 | visit := func(pathName string, f os.FileInfo, err error) error { 210 | if err != nil { 211 | return nil 212 | } 213 | // One package per directory. Ignore the files themselves. 214 | if !f.IsDir() { 215 | return nil 216 | } 217 | // No .hg or other dot nonsense please. 218 | if strings.Contains(pathName, slashDot) { 219 | return filepath.SkipDir 220 | } 221 | // Is the last element of the path correct 222 | if pkg == "" || filepath.Base(pathName) == pkg { 223 | pkgPaths = append(pkgPaths, pathName) 224 | } 225 | return nil 226 | } 227 | 228 | filepath.Walk(root, visit) 229 | return pkgPaths 230 | } 231 | 232 | // lookInDirectory looks in the package (if any) in the directory for the named exported identifier. 233 | func lookInDirectory(directory, name string) { 234 | fset := token.NewFileSet() 235 | pkgs, _ := parser.ParseDir(fset, directory, nil, parser.ParseComments) // Ignore the error. 236 | for _, pkg := range pkgs { 237 | if pkg.Name == "main" || strings.HasSuffix(pkg.Name, "_test") { 238 | continue 239 | } 240 | doPackage(pkg, fset, name) 241 | } 242 | } 243 | 244 | // prefixDirectory places the directory name on the beginning of each name in the list. 245 | func prefixDirectory(directory string, names []string) { 246 | if directory != "." { 247 | for i, name := range names { 248 | names[i] = filepath.Join(directory, name) 249 | } 250 | } 251 | } 252 | 253 | // File is a wrapper for the state of a file used in the parser. 254 | // The parse tree walkers are all methods of this type. 255 | type File struct { 256 | fset *token.FileSet 257 | name string // Name of file. 258 | ident string // Identifier we are searching for. 259 | lowerIdent string // lower ident 260 | regexp *regexp.Regexp 261 | pathPrefix string // Prefix from GOROOT/GOPATH. 262 | urlPrefix string // Start of corresponding URL for golang.org or godoc.org. 263 | file *ast.File 264 | comments ast.CommentMap 265 | defs map[*ast.Ident]types.Object 266 | doPrint bool 267 | found bool 268 | allFiles []*File // All files in the package. 269 | } 270 | 271 | // doPackage analyzes the single package constructed from the named files, looking for 272 | // the definition of ident. 273 | func doPackage(pkg *ast.Package, fset *token.FileSet, ident string) { 274 | var files []*File 275 | found := false 276 | for name, astFile := range pkg.Files { 277 | if packageFlag && astFile.Doc == nil { 278 | continue 279 | } 280 | file := &File{ 281 | fset: fset, 282 | name: name, 283 | ident: ident, 284 | lowerIdent: strings.ToLower(ident), 285 | file: astFile, 286 | comments: ast.NewCommentMap(fset, astFile, astFile.Comments), 287 | } 288 | if regexpFlag && regexp.QuoteMeta(ident) != ident { 289 | // It's a regular expression. 290 | var err error 291 | file.regexp, err = regexp.Compile("^(?i:" + ident + ")$") 292 | if err != nil { 293 | fmt.Fprintf(os.Stderr, "regular expression `%s`:", err) 294 | os.Exit(2) 295 | } 296 | } 297 | switch { 298 | case strings.HasPrefix(name, goRootSrcPkg): 299 | file.urlPrefix = "http://golang.org/pkg" 300 | file.pathPrefix = goRootSrcPkg 301 | case strings.HasPrefix(name, goRootSrcCmd): 302 | file.urlPrefix = "http://golang.org/cmd" 303 | file.pathPrefix = goRootSrcCmd 304 | default: 305 | file.urlPrefix = "http://godoc.org" 306 | for _, path := range goPaths { 307 | p := filepath.Join(path, "src") 308 | if strings.HasPrefix(name, p) { 309 | file.pathPrefix = p 310 | break 311 | } 312 | } 313 | } 314 | file.urlPrefix = urlHeadTag + file.urlPrefix 315 | files = append(files, file) 316 | if found { 317 | continue 318 | } 319 | file.doPrint = false 320 | if packageFlag { 321 | file.pkgComments() 322 | } else { 323 | ast.Walk(file, file.file) 324 | if file.found { 325 | found = true 326 | } 327 | } 328 | } 329 | 330 | if !found { 331 | return 332 | } 333 | 334 | // By providing the Context with our own error function, it will continue 335 | // past the first error. There is no need for that function to do anything. 336 | config := types.Config{ 337 | Error: func(error) {}, 338 | } 339 | info := &types.Info{ 340 | Defs: make(map[*ast.Ident]types.Object), 341 | } 342 | path := "" 343 | var astFiles []*ast.File 344 | for name, astFile := range pkg.Files { 345 | if path == "" { 346 | path = name 347 | } 348 | astFiles = append(astFiles, astFile) 349 | } 350 | config.Check(path, fset, astFiles, info) // Ignore errors. 351 | 352 | // We need to search all files for methods, so record the full list in each file. 353 | for _, file := range files { 354 | file.allFiles = files 355 | } 356 | for _, file := range files { 357 | file.doPrint = true 358 | file.defs = info.Defs 359 | if packageFlag { 360 | file.pkgComments() 361 | } else { 362 | ast.Walk(file, file.file) 363 | } 364 | } 365 | } 366 | 367 | // Visit implements the ast.Visitor interface. 368 | func (f *File) Visit(node ast.Node) ast.Visitor { 369 | switch n := node.(type) { 370 | case *ast.GenDecl: 371 | // Variables, constants, types. 372 | for _, spec := range n.Specs { 373 | switch spec := spec.(type) { 374 | case *ast.ValueSpec: 375 | if constantFlag && n.Tok == token.CONST || variableFlag && n.Tok == token.VAR { 376 | for _, ident := range spec.Names { 377 | if f.match(ident.Name) { 378 | f.printNode(n, ident, f.nameURL(ident.Name)) 379 | break 380 | } 381 | } 382 | } 383 | case *ast.TypeSpec: 384 | // If there is only one Spec, there are probably no parens and the 385 | // comment we want appears before the type keyword, bound to 386 | // the GenDecl. If the Specs are parenthesized, the comment we want 387 | // is bound to the Spec. Hence we dig into the GenDecl to the Spec, 388 | // but only if there are no parens. 389 | node := ast.Node(n) 390 | if n.Lparen.IsValid() { 391 | node = spec 392 | } 393 | if f.match(spec.Name.Name) { 394 | if typeFlag { 395 | f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) 396 | } else { 397 | switch spec.Type.(type) { 398 | case *ast.InterfaceType: 399 | if interfaceFlag { 400 | f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) 401 | } 402 | case *ast.StructType: 403 | if structFlag { 404 | f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) 405 | } 406 | } 407 | } 408 | if f.doPrint && f.defs[spec.Name] != nil && f.defs[spec.Name].Type() != nil { 409 | ms := types.NewMethodSet(f.defs[spec.Name].Type()) //.Type().MethodSet() 410 | if ms.Len() == 0 { 411 | ms = types.NewMethodSet(types.NewPointer(f.defs[spec.Name].Type())) //.MethodSet() 412 | } 413 | f.methodSet(ms) 414 | } 415 | } 416 | case *ast.ImportSpec: 417 | continue // Don't care. 418 | } 419 | } 420 | case *ast.FuncDecl: 421 | // Methods, top-level functions. 422 | if f.match(n.Name.Name) { 423 | n.Body = nil // Do not print the function body. 424 | if methodFlag && n.Recv != nil { 425 | f.printNode(n, n.Name, f.methodURL(n.Recv.List[0].Type, n.Name.Name)) 426 | } else if functionFlag && n.Recv == nil { 427 | f.printNode(n, n.Name, f.nameURL(n.Name.Name)) 428 | } 429 | } 430 | } 431 | return f 432 | } 433 | 434 | func (f *File) match(name string) bool { 435 | // name must be exported. 436 | if !ast.IsExported(name) { 437 | return false 438 | } 439 | if f.regexp == nil { 440 | if matchWordFlag { 441 | if matchCaseFlag { 442 | return name == f.ident 443 | } 444 | return strings.ToLower(name) == f.lowerIdent 445 | } else { 446 | if matchCaseFlag { 447 | return strings.Contains(name, f.ident) 448 | } 449 | return strings.Contains(strings.ToLower(name), f.lowerIdent) 450 | } 451 | } 452 | return f.regexp.MatchString(name) 453 | } 454 | 455 | func (f *File) printNode(node, ident ast.Node, url string) { 456 | if !f.doPrint { 457 | f.found = true 458 | return 459 | } 460 | fmt.Printf("%s%s%s", url, f.sourcePos(f.fset.Position(ident.Pos())), f.docs(node)) 461 | } 462 | 463 | func (f *File) docs(node ast.Node) []byte { 464 | if !docFlag { 465 | return nil 466 | } 467 | commentedNode := printer.CommentedNode{Node: node} 468 | if comments := f.comments.Filter(node).Comments(); comments != nil { 469 | commentedNode.Comments = comments 470 | } 471 | var b bytes.Buffer 472 | printer.Fprint(&b, f.fset, &commentedNode) 473 | b.Write([]byte("\n\n")) // Add a blank line between entries if we print documentation. 474 | return b.Bytes() 475 | } 476 | 477 | func (f *File) pkgComments() { 478 | doc := f.file.Doc 479 | if doc == nil { 480 | return 481 | } 482 | url := "" 483 | if urlFlag { 484 | url = f.packageURL() + "\n" 485 | } 486 | docText := "" 487 | if docFlag { 488 | docText = fmt.Sprintf("package %s\n%s\n\n", f.file.Name.Name, doc.Text()) 489 | } 490 | fmt.Printf("%s%s%s", url, f.sourcePos(f.fset.Position(doc.Pos())), docText) 491 | } 492 | 493 | func (f *File) packageURL() string { 494 | s := strings.TrimPrefix(f.name, f.pathPrefix) 495 | // Now we have a path with a final file name. Drop it. 496 | if i := strings.LastIndex(s, slash); i > 0 { 497 | s = s[:i+1] 498 | } 499 | return f.urlPrefix + s 500 | } 501 | 502 | func (f *File) packageName() string { 503 | s := strings.TrimPrefix(f.name, f.pathPrefix) 504 | // Now we have a path with a final file name. Drop it. 505 | if i := strings.LastIndex(s, slash); i > 0 { 506 | s = s[:i+1] 507 | } 508 | s = strings.Trim(s, slash) 509 | return filepath.ToSlash(s) 510 | } 511 | 512 | func (f *File) sourcePos(posn token.Position) string { 513 | if !srcFlag { 514 | return "" 515 | } 516 | return fmt.Sprintf("%s:%d:\n", posn.Filename, posn.Line) 517 | } 518 | 519 | func (f *File) nameURL(name string) string { 520 | if !urlFlag { 521 | return "" 522 | } 523 | return fmt.Sprintf("%s#%s\n", f.packageURL(), name) 524 | } 525 | 526 | func (f *File) methodURL(typ ast.Expr, name string) string { 527 | if !urlFlag { 528 | return "" 529 | } 530 | var b bytes.Buffer 531 | printer.Fprint(&b, f.fset, typ) 532 | typeName := b.Bytes() 533 | if len(typeName) > 0 && typeName[0] == '*' { 534 | typeName = typeName[1:] 535 | } 536 | return fmt.Sprintf("%s#%s.%s\n", f.packageURL(), typeName, name) 537 | } 538 | 539 | // Here follows the code to find and print a method (actually a method set, because 540 | // we want to do only one redundant tree walk, not one per method). 541 | // It should be much easier than walking the whole tree again, but that's what we must do. 542 | // TODO. 543 | 544 | type method struct { 545 | index int // Which doc to write. (Keeps the results sorted) 546 | *types.Selection 547 | } 548 | 549 | type methodVisitor struct { 550 | *File 551 | methods []method 552 | docs []string 553 | } 554 | 555 | func (f *File) methodSet(set *types.MethodSet) { 556 | // Build the set of things we're looking for. 557 | methods := make([]method, 0, set.Len()) 558 | docs := make([]string, set.Len()) 559 | for i := 0; i < set.Len(); i++ { 560 | if ast.IsExported(set.At(i).Obj().Name()) { 561 | m := method{ 562 | i, 563 | set.At(i), 564 | } 565 | methods = append(methods, m) 566 | } 567 | } 568 | if len(methods) == 0 { 569 | return 570 | } 571 | // Collect the docs. 572 | for _, file := range f.allFiles { 573 | visitor := &methodVisitor{ 574 | File: file, 575 | methods: methods, 576 | docs: docs, 577 | } 578 | ast.Walk(visitor, file.file) 579 | methods = visitor.methods 580 | } 581 | // Print them in order. The incoming method set is sorted by name. 582 | for _, doc := range docs { 583 | if doc != "" { 584 | fmt.Print(doc) 585 | } 586 | } 587 | } 588 | 589 | // Visit implements the ast.Visitor interface. 590 | func (visitor *methodVisitor) Visit(node ast.Node) ast.Visitor { 591 | switch n := node.(type) { 592 | case *ast.FuncDecl: 593 | for i, method := range visitor.methods { 594 | // If this is the right one, the position of the name of its identifier will match. 595 | if method.Obj().Pos() == n.Name.Pos() { 596 | n.Body = nil // TODO. Ugly - don't print the function body. 597 | visitor.docs[method.index] = fmt.Sprintf("%s", visitor.File.docs(n)) 598 | // If this was the last method, we're done. 599 | if len(visitor.methods) == 1 { 600 | return nil 601 | } 602 | // Drop this one from the list. 603 | visitor.methods = append(visitor.methods[:i], visitor.methods[i+1:]...) 604 | return visitor 605 | } 606 | } 607 | } 608 | return visitor 609 | } 610 | --------------------------------------------------------------------------------