= 1 && page <= lessons[$scope.lessonId].Pages.length) {
44 | $scope.curPage = page;
45 | } else {
46 | l = (page < 1) ? toc.prevLesson(l) : toc.nextLesson(l);
47 | if (l === '') { // If there's not previous or next
48 | $location.path('/list');
49 | return;
50 | }
51 | page = (page < 1) ? lessons[l].Pages.length : 1;
52 | }
53 | $location.path('/' + l + '/' + page);
54 | $scope.openFile($scope.curFile);
55 | analytics.trackView();
56 | };
57 | $scope.openFile = function(file) {
58 | $scope.curFile = file;
59 | editor.paint();
60 | };
61 |
62 | function log(mode, text) {
63 | $('.output.active').html('' + text + '
');
64 | }
65 |
66 | function clearOutput() {
67 | $('.output.active').html('');
68 | }
69 |
70 | function file() {
71 | return lessons[$scope.lessonId].Pages[$scope.curPage - 1].Files[$scope.curFile];
72 | }
73 |
74 | $scope.run = function() {
75 | log('info', i18n.l('waiting'));
76 | var f = file();
77 | run(f.Content, $('.output.active > pre')[0], {
78 | path: f.Name
79 | });
80 | };
81 |
82 | $scope.format = function() {
83 | log('info', i18n.l('waiting'));
84 | fmt(file().Content).then(
85 | function(data) {
86 | if (data.data.Error !== '') {
87 | log('stderr', data.data.Error);
88 | return;
89 | }
90 | clearOutput();
91 | file().Content = data.data.Body;
92 | },
93 | function(error) {
94 | log('stderr', error);
95 | });
96 | };
97 |
98 | $scope.reset = function() {
99 | file().Content = file().OrigContent;
100 | };
101 | }
102 | ]);
103 |
--------------------------------------------------------------------------------
/godoc/snippet.go:
--------------------------------------------------------------------------------
1 | // Copyright 2009 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 infrastructure to create a code
6 | // snippet for search results.
7 | //
8 | // Note: At the moment, this only creates HTML snippets.
9 |
10 | package godoc
11 |
12 | import (
13 | "bytes"
14 | "fmt"
15 | "go/ast"
16 | "go/token"
17 | )
18 |
19 | type Snippet struct {
20 | Line int
21 | Text string // HTML-escaped
22 | }
23 |
24 | func (p *Presentation) newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
25 | // TODO instead of pretty-printing the node, should use the original source instead
26 | var buf1 bytes.Buffer
27 | p.writeNode(&buf1, fset, decl)
28 | // wrap text with tag
29 | var buf2 bytes.Buffer
30 | buf2.WriteString("")
31 | FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil)
32 | buf2.WriteString("")
33 | return &Snippet{fset.Position(id.Pos()).Line, buf2.String()}
34 | }
35 |
36 | func findSpec(list []ast.Spec, id *ast.Ident) ast.Spec {
37 | for _, spec := range list {
38 | switch s := spec.(type) {
39 | case *ast.ImportSpec:
40 | if s.Name == id {
41 | return s
42 | }
43 | case *ast.ValueSpec:
44 | for _, n := range s.Names {
45 | if n == id {
46 | return s
47 | }
48 | }
49 | case *ast.TypeSpec:
50 | if s.Name == id {
51 | return s
52 | }
53 | }
54 | }
55 | return nil
56 | }
57 |
58 | func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet {
59 | s := findSpec(d.Specs, id)
60 | if s == nil {
61 | return nil // declaration doesn't contain id - exit gracefully
62 | }
63 |
64 | // only use the spec containing the id for the snippet
65 | dd := &ast.GenDecl{
66 | Doc: d.Doc,
67 | TokPos: d.Pos(),
68 | Tok: d.Tok,
69 | Lparen: d.Lparen,
70 | Specs: []ast.Spec{s},
71 | Rparen: d.Rparen,
72 | }
73 |
74 | return p.newSnippet(fset, dd, id)
75 | }
76 |
77 | func (p *Presentation) funcSnippet(fset *token.FileSet, d *ast.FuncDecl, id *ast.Ident) *Snippet {
78 | if d.Name != id {
79 | return nil // declaration doesn't contain id - exit gracefully
80 | }
81 |
82 | // only use the function signature for the snippet
83 | dd := &ast.FuncDecl{
84 | Doc: d.Doc,
85 | Recv: d.Recv,
86 | Name: d.Name,
87 | Type: d.Type,
88 | }
89 |
90 | return p.newSnippet(fset, dd, id)
91 | }
92 |
93 | // NewSnippet creates a text snippet from a declaration decl containing an
94 | // identifier id. Parts of the declaration not containing the identifier
95 | // may be removed for a more compact snippet.
96 | func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
97 | // TODO(bradfitz, adg): remove this function. But it's used by indexer, which
98 | // doesn't have a *Presentation, and NewSnippet needs a TabWidth.
99 | var p Presentation
100 | p.TabWidth = 4
101 | return p.NewSnippet(fset, decl, id)
102 | }
103 |
104 | // NewSnippet creates a text snippet from a declaration decl containing an
105 | // identifier id. Parts of the declaration not containing the identifier
106 | // may be removed for a more compact snippet.
107 | func (p *Presentation) NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet {
108 | var s *Snippet
109 | switch d := decl.(type) {
110 | case *ast.GenDecl:
111 | s = p.genSnippet(fset, d, id)
112 | case *ast.FuncDecl:
113 | s = p.funcSnippet(fset, d, id)
114 | }
115 |
116 | // handle failure gracefully
117 | if s == nil {
118 | var buf bytes.Buffer
119 | fmt.Fprintf(&buf, `could not generate a snippet for %s`, id.Name)
120 | s = &Snippet{fset.Position(id.Pos()).Line, buf.String()}
121 | }
122 | return s
123 | }
124 |
--------------------------------------------------------------------------------
/local/init.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . 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 local
6 |
7 | import (
8 | "archive/zip"
9 | "go/build"
10 | "log"
11 | "os"
12 | "path/filepath"
13 | "runtime"
14 | "strings"
15 |
16 | "golang.org/x/tools/godoc/static"
17 | "golang.org/x/tools/godoc/vfs"
18 | "golang.org/x/tools/godoc/vfs/mapfs"
19 | "golang.org/x/tools/godoc/vfs/zipfs"
20 | )
21 |
22 | // Default is the translations dir.
23 | const (
24 | Default = "translations" // $(RootFS)/translations
25 | )
26 |
27 | var (
28 | defaultGodocGoos = getGodocGoos()
29 | defaultGodocGoarch = getGodocGoarch()
30 | defaultRootFS vfs.NameSpace = getNameSpace(vfs.OS(runtime.GOROOT()), "/")
31 | defaultStaticFS vfs.NameSpace = getNameSpace(mapfs.New(static.Files), "/")
32 | defaultDocFS vfs.NameSpace = getNameSpace(defaultRootFS, "/doc")
33 | defaultBlogFS vfs.NameSpace = getNameSpace(defaultRootFS, "/blog")
34 | defaultLocalFS vfs.NameSpace = getLocalRootNS(defaultRootFS)
35 | defaultTranslater Translater = new(localTranslater)
36 | )
37 |
38 | func getGodocGoos() string {
39 | if v := strings.TrimSpace(os.Getenv("GOOS")); v != "" {
40 | return v
41 | }
42 | return runtime.GOOS
43 | }
44 |
45 | func getGodocGoarch() string {
46 | if v := strings.TrimSpace(os.Getenv("GOARCH")); v != "" {
47 | return v
48 | }
49 | return runtime.GOARCH
50 | }
51 |
52 | func getLocalRootNS(rootfs vfs.NameSpace) vfs.NameSpace {
53 | if s := os.Getenv("GODOC_LOCAL_ROOT"); s != "" {
54 | return getNameSpace(vfs.OS(s), "/")
55 | }
56 | return getNameSpace(defaultRootFS, "/"+Default)
57 | }
58 |
59 | // Init initialize the translations environment.
60 | func Init(goRoot, goTranslations, goZipFile, goTemplateDir, goPath string) {
61 | if goZipFile != "" {
62 | rc, err := zip.OpenReader(goZipFile)
63 | if err != nil {
64 | log.Fatalf("local: %s: %s\n", goZipFile, err)
65 | }
66 |
67 | defaultRootFS = getNameSpace(zipfs.New(rc, goZipFile), goRoot)
68 | defaultDocFS = getNameSpace(defaultRootFS, "/doc")
69 | defaultBlogFS = getNameSpace(defaultRootFS, "/blog")
70 | if goTranslations != "" && goTranslations != Default {
71 | defaultLocalFS = getNameSpace(defaultRootFS, "/"+goTranslations)
72 | } else {
73 | defaultLocalFS = getNameSpace(defaultRootFS, "/"+Default)
74 | }
75 | } else {
76 | if goRoot != "" && goRoot != runtime.GOROOT() {
77 | defaultRootFS = getNameSpace(vfs.OS(goRoot), "/")
78 | defaultDocFS = getNameSpace(defaultRootFS, "/doc")
79 | defaultBlogFS = getNameSpace(defaultRootFS, "/blog")
80 | if goTranslations == "" || goTranslations == Default {
81 | defaultLocalFS = getNameSpace(defaultRootFS, "/"+Default)
82 | }
83 | }
84 | if goTranslations != "" && goTranslations != Default {
85 | defaultLocalFS = getNameSpace(vfs.OS(goTranslations), "/")
86 | }
87 |
88 | if goTemplateDir != "" {
89 | defaultStaticFS = getNameSpace(vfs.OS(goTemplateDir), "/")
90 | }
91 |
92 | // Bind $GOPATH trees into Go root.
93 | for _, p := range filepath.SplitList(goPath) {
94 | defaultRootFS.Bind("/src", vfs.OS(p), "/src", vfs.BindAfter)
95 | }
96 |
97 | // Prefer content from go.blog repository if present.
98 | if _, err := defaultBlogFS.Lstat("/"); err != nil {
99 | const blogRepo = "golang.org/x/blog"
100 | if pkg, err := build.Import(blogRepo, "", build.FindOnly); err == nil {
101 | defaultBlogFS = getNameSpace(defaultRootFS, pkg.Dir)
102 | }
103 | }
104 | }
105 |
106 | }
107 |
108 | func getNameSpace(fs vfs.FileSystem, ns string) vfs.NameSpace {
109 | newns := make(vfs.NameSpace)
110 | if ns != "" {
111 | newns.Bind("/", fs, ns, vfs.BindReplace)
112 | } else {
113 | newns.Bind("/", fs, "/", vfs.BindReplace)
114 | }
115 | return newns
116 | }
117 |
--------------------------------------------------------------------------------
/service_manage_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !appengine
6 |
7 | package main
8 |
9 | import (
10 | "fmt"
11 | "os"
12 | "path/filepath"
13 | "strings"
14 | "time"
15 |
16 | "golang.org/x/sys/windows"
17 | "golang.org/x/sys/windows/svc"
18 | "golang.org/x/sys/windows/svc/eventlog"
19 | "golang.org/x/sys/windows/svc/mgr"
20 | )
21 |
22 | func installService(name, desc string, args ...string) error {
23 | exepath, err := exePath()
24 | if err != nil {
25 | return err
26 | }
27 | if len(args) > 0 {
28 | exepath = exepath + " " + strings.Join(args, " ")
29 | }
30 | println("exepath:", exepath)
31 |
32 | m, err := mgr.Connect()
33 | if err != nil {
34 | return err
35 | }
36 | defer m.Disconnect()
37 | s, err := m.OpenService(name)
38 | if err == nil {
39 | s.Close()
40 | return fmt.Errorf("service %s already exists", name)
41 | }
42 | s, err = m.CreateService(name, exepath, mgr.Config{
43 | DisplayName: desc,
44 | StartType: windows.SERVICE_AUTO_START,
45 | })
46 | if err != nil {
47 | return err
48 | }
49 | defer s.Close()
50 | err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
51 | if err != nil {
52 | s.Delete()
53 | return fmt.Errorf("SetupEventLogSource() failed: %s", err)
54 | }
55 | return nil
56 | }
57 |
58 | func removeService(name string) error {
59 | m, err := mgr.Connect()
60 | if err != nil {
61 | return err
62 | }
63 | defer m.Disconnect()
64 | s, err := m.OpenService(name)
65 | if err != nil {
66 | return fmt.Errorf("service %s is not installed", name)
67 | }
68 | defer s.Close()
69 | err = s.Delete()
70 | if err != nil {
71 | return err
72 | }
73 | err = eventlog.Remove(name)
74 | if err != nil {
75 | return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
76 | }
77 | return nil
78 | }
79 |
80 | func startService(name string) error {
81 | m, err := mgr.Connect()
82 | if err != nil {
83 | return err
84 | }
85 | defer m.Disconnect()
86 | s, err := m.OpenService(name)
87 | if err != nil {
88 | return fmt.Errorf("could not access service: %v", err)
89 | }
90 | defer s.Close()
91 | err = s.Start("p1", "p2", "p3")
92 | if err != nil {
93 | return fmt.Errorf("could not start service: %v", err)
94 | }
95 | return nil
96 | }
97 |
98 | func controlService(name string, c svc.Cmd, to svc.State) error {
99 | m, err := mgr.Connect()
100 | if err != nil {
101 | return err
102 | }
103 | defer m.Disconnect()
104 | s, err := m.OpenService(name)
105 | if err != nil {
106 | return fmt.Errorf("could not access service: %v", err)
107 | }
108 | defer s.Close()
109 | status, err := s.Control(c)
110 | if err != nil {
111 | return fmt.Errorf("could not send control=%d: %v", c, err)
112 | }
113 | timeout := time.Now().Add(10 * time.Second)
114 | for status.State != to {
115 | if timeout.Before(time.Now()) {
116 | return fmt.Errorf("timeout waiting for service to go to state=%d", to)
117 | }
118 | time.Sleep(300 * time.Millisecond)
119 | status, err = s.Query()
120 | if err != nil {
121 | return fmt.Errorf("could not retrieve service status: %v", err)
122 | }
123 | }
124 | return nil
125 | }
126 |
127 | func exePath() (string, error) {
128 | prog := os.Args[0]
129 | p, err := filepath.Abs(prog)
130 | if err != nil {
131 | return "", err
132 | }
133 | fi, err := os.Stat(p)
134 | if err == nil {
135 | if !fi.Mode().IsDir() {
136 | return p, nil
137 | }
138 | err = fmt.Errorf("%s is directory", p)
139 | }
140 | if filepath.Ext(p) == "" {
141 | p += ".exe"
142 | fi, err := os.Stat(p)
143 | if err == nil {
144 | if !fi.Mode().IsDir() {
145 | return p, nil
146 | }
147 | err = fmt.Errorf("%s is directory", p)
148 | }
149 | }
150 | return "", err
151 | }
152 |
--------------------------------------------------------------------------------
/local/local_translater.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . 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 local
6 |
7 | import (
8 | "fmt"
9 | "go/ast"
10 | "go/doc"
11 | "go/parser"
12 | "go/token"
13 | "log"
14 |
15 | "golang.org/x/tools/godoc/vfs"
16 | )
17 |
18 | type localTranslater struct{}
19 |
20 | func (p *localTranslater) Static(lang string) vfs.FileSystem {
21 | if lang == "" {
22 | return defaultStaticFS
23 | }
24 | return p.NameSpace("/static/" + lang)
25 | }
26 |
27 | func (p *localTranslater) Document(lang string) vfs.FileSystem {
28 | if lang == "" {
29 | return defaultDocFS
30 | }
31 | return p.NameSpace("/doc/" + lang)
32 | }
33 |
34 | func (p *localTranslater) Package(lang, importPath string, pkg ...*doc.Package) *doc.Package {
35 | if lang == "" {
36 | if len(pkg) > 0 {
37 | return pkg[0]
38 | } else {
39 | return nil
40 | }
41 | }
42 |
43 | // try parse and register new pkg doc
44 | localPkg := p.ParseDocPackage(lang, importPath)
45 | if localPkg == nil {
46 | return nil
47 | }
48 | RegisterPackage(lang, localPkg)
49 |
50 | // retry Package func
51 | return Package(lang, importPath, pkg...)
52 | }
53 |
54 | func (p *localTranslater) Blog(lang string) vfs.FileSystem {
55 | if lang == "" {
56 | return defaultBlogFS
57 | }
58 | return p.NameSpace("/blog/" + lang)
59 | }
60 |
61 | func (p *localTranslater) ParseDocPackage(lang, importPath string) *doc.Package {
62 | if lang == "" || importPath == "" || importPath[0] == '/' {
63 | return nil
64 | }
65 | docCode := p.loadDocCode(lang, importPath)
66 | if docCode == nil {
67 | return nil
68 | }
69 |
70 | // parse doc
71 | fset := token.NewFileSet()
72 | astFile, err := parser.ParseFile(fset, importPath, docCode, parser.ParseComments)
73 | if err != nil {
74 | log.Printf("local.localTranslater.ParseDocPackage: err = %v\n", err)
75 | return nil
76 | }
77 | astPkg, _ := ast.NewPackage(fset,
78 | map[string]*ast.File{importPath: astFile},
79 | nil,
80 | nil,
81 | )
82 | docPkg := doc.New(astPkg, importPath, doc.AllDecls)
83 | return docPkg
84 | }
85 |
86 | func (p *localTranslater) NameSpace(ns string) vfs.FileSystem {
87 | if ns != "" {
88 | if fi, err := defaultLocalFS.Stat(ns); err != nil || !fi.IsDir() {
89 | return nil
90 | }
91 | subfs := make(vfs.NameSpace)
92 | subfs.Bind("/", defaultLocalFS, ns, vfs.BindReplace)
93 | return subfs
94 | }
95 | return defaultLocalFS
96 | }
97 |
98 | func (p *localTranslater) loadDocCode(lang, importPath string) []byte {
99 | // {FS}:/src/importPath/doc_$(lang)_GOOS_GOARCH.go
100 | // {FS}:/src/importPath/doc_$(lang)_GOARCH.go
101 | // {FS}:/src/importPath/doc_$(lang)_GOOS.go
102 | // {FS}:/src/importPath/doc_$(lang).go
103 | filenames := []string{
104 | fmt.Sprintf("/src/%s/doc_%s_%s_%s.go", importPath, lang, defaultGodocGoos, defaultGodocGoarch),
105 | fmt.Sprintf("/src/%s/doc_%s_%s.go", importPath, lang, defaultGodocGoarch),
106 | fmt.Sprintf("/src/%s/doc_%s_%s.go", importPath, lang, defaultGodocGoos),
107 | fmt.Sprintf("/src/%s/doc_%s.go", importPath, lang),
108 | }
109 |
110 | for i := 0; i < len(filenames); i++ {
111 | // $(GOROOT)/translates/
112 | if p.fileExists(defaultLocalFS, filenames[i]) {
113 | docCode, _ := vfs.ReadFile(defaultLocalFS, filenames[i])
114 | if docCode != nil {
115 | return docCode
116 | }
117 | }
118 |
119 | // $(GOROOT)/
120 | if p.fileExists(defaultRootFS, filenames[i]) {
121 | docCode, _ := vfs.ReadFile(defaultRootFS, filenames[i])
122 | if docCode != nil {
123 | return docCode
124 | }
125 | }
126 | }
127 |
128 | return nil
129 | }
130 |
131 | func (p *localTranslater) fileExists(fs vfs.NameSpace, name string) bool {
132 | if fi, err := fs.Stat(name); err != nil || fi.IsDir() {
133 | return false
134 | }
135 | return true
136 | }
137 |
--------------------------------------------------------------------------------
/main_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !appengine
6 |
7 | // Godoc service
8 | //
9 | // # install as windows service
10 | // golangdoc -service-install -http=:6060
11 | //
12 | // # start/stop service
13 | // golangdoc -service-start
14 | // golangdoc -service-stop
15 | //
16 | // # remove service
17 | // golangdoc -service-remove
18 | //
19 |
20 | package main
21 |
22 | import (
23 | "flag"
24 | "fmt"
25 | "log"
26 | "os"
27 | "strings"
28 | "time"
29 |
30 | "golang.org/x/sys/windows/svc"
31 | )
32 |
33 | const (
34 | ServiceName = "golangdoc"
35 | ServiceDesc = "Go Documentation Server"
36 | )
37 |
38 | var (
39 | flagServiceInstall = flag.Bool("service-install", false, "Install service")
40 | flagServiceUninstall = flag.Bool("service-remove", false, "Remove service")
41 | flagServiceStart = flag.Bool("service-start", false, "Start service")
42 | flagServiceStop = flag.Bool("service-stop", false, "Stop service")
43 | )
44 |
45 | func main() {
46 | flag.Usage = usage
47 | flag.Parse()
48 |
49 | playEnabled = *flagShowPlayground
50 |
51 | if *flagServiceInstall {
52 | var args []string
53 | args = append(args, fmt.Sprintf("-goroot=%s", *flagGoroot))
54 | for i := 1; i < len(os.Args); i++ {
55 | if strings.HasPrefix(os.Args[i], "-service-install") {
56 | continue
57 | }
58 | if strings.HasPrefix(os.Args[i], "-goroot") {
59 | continue
60 | }
61 | args = append(args, os.Args[i])
62 | }
63 | if *flagHttpAddr == "" {
64 | args = append(args, "-http=:6060")
65 | }
66 | if err := installService(ServiceName, ServiceDesc, args...); err != nil {
67 | log.Fatalf("installService(%s, %s): %v", ServiceName, ServiceDesc, err)
68 | }
69 | fmt.Printf("Done\n")
70 | return
71 | }
72 | if *flagServiceUninstall {
73 | if err := removeService(ServiceName); err != nil {
74 | log.Fatalf("removeService: %v\n", err)
75 | }
76 | fmt.Printf("Done\n")
77 | return
78 | }
79 | if *flagServiceStart {
80 | if err := startService(ServiceName); err != nil {
81 | log.Fatalf("startService: %v\n", err)
82 | }
83 | fmt.Printf("Done\n")
84 | return
85 | }
86 | if *flagServiceStop {
87 | if err := controlService(ServiceName, svc.Stop, svc.Stopped); err != nil {
88 | log.Fatalf("stopService: %v\n", err)
89 | }
90 | fmt.Printf("Done\n")
91 | return
92 | }
93 |
94 | // Check usage: either server and no args, command line and args, or index creation mode
95 | if (*flagHttpAddr != "" || *flagUrlFlag != "") != (flag.NArg() == 0) && !*flagWriteIndex {
96 | usage()
97 | }
98 |
99 | // run as service
100 | if isIntSess, err := svc.IsAnInteractiveSession(); err == nil && !isIntSess {
101 | runService(ServiceName)
102 | return
103 | }
104 |
105 | runGodoc()
106 | }
107 |
108 | type GodocService struct{}
109 |
110 | func (m *GodocService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
111 | const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
112 | changes <- svc.Status{State: svc.StartPending}
113 | changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
114 |
115 | go runGodoc()
116 |
117 | loop:
118 | for {
119 | select {
120 | case c := <-r:
121 | switch c.Cmd {
122 | case svc.Interrogate:
123 | changes <- c.CurrentStatus
124 | time.Sleep(100 * time.Millisecond)
125 | changes <- c.CurrentStatus
126 | case svc.Stop, svc.Shutdown:
127 | break loop
128 | case svc.Pause:
129 | changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
130 | case svc.Continue:
131 | changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
132 | default:
133 | // warning: unexpected control request ${c}
134 | }
135 | }
136 | }
137 | changes <- svc.Status{State: svc.StopPending}
138 | return
139 | }
140 |
141 | func runService(name string) {
142 | if err := svc.Run(name, &GodocService{}); err != nil {
143 | return
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/appengine/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !appengine
6 |
7 | //
8 | // Create goroot.zip for GAE.
9 | //
10 | // Example:
11 | // go run main.go
12 | //
13 | package main
14 |
15 | import (
16 | "archive/zip"
17 | "flag"
18 | "fmt"
19 | "io/ioutil"
20 | "log"
21 | "os"
22 | "path/filepath"
23 | "runtime"
24 | "strings"
25 | )
26 |
27 | const (
28 | GODOC_LOCAL_ROOT = "GODOC_LOCAL_ROOT"
29 | )
30 |
31 | var (
32 | flagGoroot = flag.String("goroot", runtime.GOROOT(), "Go root directory")
33 | flagLocalRoot = flag.String("godoc-local-root", "", "Godoc translations root, default is $(GOROOT)/translations")
34 | )
35 |
36 | func main() {
37 | flag.Parse()
38 |
39 | if *flagLocalRoot == "" {
40 | if s := os.Getenv(GODOC_LOCAL_ROOT); s != "" {
41 | *flagLocalRoot = s
42 | }
43 | }
44 | if *flagLocalRoot == "" || *flagLocalRoot == "translations" {
45 | *flagLocalRoot = *flagGoroot + "/translations"
46 | }
47 |
48 | file, err := os.Create("goroot.zip")
49 | if err != nil {
50 | log.Fatal("os.Create: ", err)
51 | }
52 | defer file.Close()
53 |
54 | zipFile := zip.NewWriter(file)
55 | defer zipFile.Close()
56 |
57 | // create /goroot/
58 | f, err := zipFile.Create("goroot/")
59 | if err != nil {
60 | log.Fatal(err)
61 | }
62 | if _, err = f.Write([]byte("")); err != nil {
63 | log.Fatal(err)
64 | }
65 | filepath.Walk(*flagGoroot, func(path string, info os.FileInfo, err error) error {
66 | if err != nil {
67 | log.Fatal("filepath.Walk: ", err)
68 | }
69 | if info.IsDir() {
70 | return nil
71 | }
72 | relpath, err := filepath.Rel(*flagGoroot, path)
73 | if err != nil {
74 | log.Fatal("filepath.Rel: ", err)
75 | }
76 |
77 | filename := filepath.ToSlash(relpath)
78 | if isIngoreFile(filename) || isTranslationsFile(filename) {
79 | return nil
80 | }
81 |
82 | data, err := ioutil.ReadFile(path)
83 | if err != nil {
84 | log.Fatal("ioutil.ReadFile: ", err)
85 | }
86 |
87 | f, err := zipFile.Create("goroot/" + filename)
88 | if err != nil {
89 | log.Fatal(err)
90 | }
91 | if _, err = f.Write(data); err != nil {
92 | log.Fatal(err)
93 | }
94 |
95 | fmt.Printf("%s\n", filename)
96 | return nil
97 | })
98 |
99 | // create /goroot/translations/
100 | f, err = zipFile.Create("goroot/translations/")
101 | if err != nil {
102 | log.Fatal(err)
103 | }
104 | if _, err = f.Write([]byte("")); err != nil {
105 | log.Fatal(err)
106 | }
107 | filepath.Walk(*flagLocalRoot, func(path string, info os.FileInfo, err error) error {
108 | if err != nil {
109 | log.Fatal("filepath.Walk: ", err)
110 | }
111 | if info.IsDir() {
112 | return nil
113 | }
114 | relpath, err := filepath.Rel(*flagLocalRoot, path)
115 | if err != nil {
116 | log.Fatal("filepath.Rel: ", err)
117 | }
118 |
119 | filename := filepath.ToSlash(relpath)
120 | if isIngoreFile(filename) {
121 | return nil
122 | }
123 |
124 | data, err := ioutil.ReadFile(path)
125 | if err != nil {
126 | log.Fatal("ioutil.ReadFile: ", err)
127 | }
128 |
129 | f, err := zipFile.Create("goroot/translations/" + filename)
130 | if err != nil {
131 | log.Fatal(err)
132 | }
133 | if _, err = f.Write(data); err != nil {
134 | log.Fatal(err)
135 | }
136 |
137 | fmt.Printf("translations/%s\n", filename)
138 | return nil
139 | })
140 |
141 | fmt.Printf("Done\n")
142 | }
143 |
144 | func isTranslationsFile(path string) bool {
145 | if strings.HasPrefix(path, "translations") {
146 | return true
147 | }
148 | return false
149 | }
150 |
151 | func isIngoreFile(path string) bool {
152 | if strings.HasPrefix(path, "bin") {
153 | return true
154 | }
155 | if strings.HasPrefix(path, "pkg") {
156 | return true
157 | }
158 | if strings.HasPrefix(path, ".git") {
159 | return true
160 | }
161 | if strings.HasPrefix(path, "talks") {
162 | return true
163 | }
164 | if strings.HasPrefix(path, "tour") {
165 | return true
166 | }
167 | switch strings.ToLower(filepath.Ext(path)) {
168 | case ".exe", ".dll":
169 | return true
170 | }
171 | return false
172 | }
173 |
--------------------------------------------------------------------------------
/godoc/meta.go:
--------------------------------------------------------------------------------
1 | // Copyright 2009 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 godoc
6 |
7 | import (
8 | "bytes"
9 | "encoding/json"
10 | "log"
11 | pathpkg "path"
12 | "strings"
13 | "time"
14 |
15 | "golang.org/x/tools/godoc/vfs"
16 | )
17 |
18 | var (
19 | doctype = []byte("")
22 | )
23 |
24 | // ----------------------------------------------------------------------------
25 | // Documentation Metadata
26 |
27 | // TODO(adg): why are some exported and some aren't? -brad
28 | type Metadata struct {
29 | Title string
30 | Subtitle string
31 | Template bool // execute as template
32 | Path string // canonical path for this page
33 | filePath string // filesystem path relative to goroot
34 | }
35 |
36 | func (m *Metadata) FilePath() string { return m.filePath }
37 |
38 | // extractMetadata extracts the Metadata from a byte slice.
39 | // It returns the Metadata value and the remaining data.
40 | // If no metadata is present the original byte slice is returned.
41 | //
42 | func extractMetadata(b []byte) (meta Metadata, tail []byte, err error) {
43 | tail = b
44 | if !bytes.HasPrefix(b, jsonStart) {
45 | return
46 | }
47 | end := bytes.Index(b, jsonEnd)
48 | if end < 0 {
49 | return
50 | }
51 | b = b[len(jsonStart)-1 : end+1] // drop leading %s", command, buf.Bytes())
104 | return text, nil
105 | }
106 |
107 | // parseArg returns the integer or string value of the argument and tells which it is.
108 | func parseArg(arg interface{}, file string, max int) (ival int, sval string, isInt bool) {
109 | switch n := arg.(type) {
110 | case int:
111 | if n <= 0 || n > max {
112 | log.Panicf("%q:%d is out of range", file, n)
113 | }
114 | return n, "", true
115 | case string:
116 | return 0, n, false
117 | }
118 | log.Panicf("unrecognized argument %v type %T", arg, arg)
119 | return
120 | }
121 |
122 | // oneLine returns the single line generated by a two-argument code invocation.
123 | func (c *Corpus) oneLine(file, text string, arg interface{}) string {
124 | lines := strings.SplitAfter(c.contents(file), "\n")
125 | line, pattern, isInt := parseArg(arg, file, len(lines))
126 | if isInt {
127 | return lines[line-1]
128 | }
129 | return lines[match(file, 0, lines, pattern)-1]
130 | }
131 |
132 | // multipleLines returns the text generated by a three-argument code invocation.
133 | func (c *Corpus) multipleLines(file, text string, arg1, arg2 interface{}) string {
134 | lines := strings.SplitAfter(c.contents(file), "\n")
135 | line1, pattern1, isInt1 := parseArg(arg1, file, len(lines))
136 | line2, pattern2, isInt2 := parseArg(arg2, file, len(lines))
137 | if !isInt1 {
138 | line1 = match(file, 0, lines, pattern1)
139 | }
140 | if !isInt2 {
141 | line2 = match(file, line1, lines, pattern2)
142 | } else if line2 < line1 {
143 | log.Panicf("lines out of order for %q: %d %d", text, line1, line2)
144 | }
145 | for k := line1 - 1; k < line2; k++ {
146 | if strings.HasSuffix(lines[k], "OMIT\n") {
147 | lines[k] = ""
148 | }
149 | }
150 | return strings.Join(lines[line1-1:line2], "")
151 | }
152 |
153 | // match identifies the input line that matches the pattern in a code invocation.
154 | // If start>0, match lines starting there rather than at the beginning.
155 | // The return value is 1-indexed.
156 | func match(file string, start int, lines []string, pattern string) int {
157 | // $ matches the end of the file.
158 | if pattern == "$" {
159 | if len(lines) == 0 {
160 | log.Panicf("%q: empty file", file)
161 | }
162 | return len(lines)
163 | }
164 | // /regexp/ matches the line that matches the regexp.
165 | if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
166 | re, err := regexp.Compile(pattern[1 : len(pattern)-1])
167 | if err != nil {
168 | log.Panic(err)
169 | }
170 | for i := start; i < len(lines); i++ {
171 | if re.MatchString(lines[i]) {
172 | return i + 1
173 | }
174 | }
175 | log.Panicf("%s: no match for %#q", file, pattern)
176 | }
177 | log.Panicf("unrecognized pattern: %q", pattern)
178 | return 0
179 | }
180 |
--------------------------------------------------------------------------------
/godoc/cmdline.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 godoc
6 |
7 | import (
8 | "fmt"
9 | "go/ast"
10 | "go/build"
11 | "io"
12 | "log"
13 | "os"
14 | pathpkg "path"
15 | "path/filepath"
16 | "regexp"
17 | "strings"
18 |
19 | "golang.org/x/tools/godoc/vfs"
20 | )
21 |
22 | const (
23 | target = "/target"
24 | cmdPrefix = "cmd/"
25 | srcPrefix = "src/"
26 | toolsPath = "golang.org/x/tools/cmd/"
27 | )
28 |
29 | // CommandLine returns godoc results to w.
30 | // Note that it may add a /target path to fs.
31 | func CommandLine(w io.Writer, fs vfs.NameSpace, pres *Presentation, args []string, lang ...string) error {
32 | path := args[0]
33 | srcMode := pres.SrcMode
34 | cmdMode := strings.HasPrefix(path, cmdPrefix)
35 | if strings.HasPrefix(path, srcPrefix) {
36 | path = strings.TrimPrefix(path, srcPrefix)
37 | srcMode = true
38 | }
39 | var abspath, relpath string
40 | if cmdMode {
41 | path = strings.TrimPrefix(path, cmdPrefix)
42 | } else {
43 | abspath, relpath = paths(fs, pres, path)
44 | }
45 |
46 | var mode PageInfoMode
47 | if relpath == builtinPkgPath {
48 | // the fake built-in package contains unexported identifiers
49 | mode = NoFiltering | NoTypeAssoc
50 | }
51 | if srcMode {
52 | // only filter exports if we don't have explicit command-line filter arguments
53 | if len(args) > 1 {
54 | mode |= NoFiltering
55 | }
56 | mode |= ShowSource
57 | }
58 |
59 | // First, try as package unless forced as command.
60 | var info *PageInfo
61 | if !cmdMode {
62 | info = pres.GetPkgPageInfo(abspath, relpath, mode)
63 | }
64 |
65 | // Second, try as command (if the path is not absolute).
66 | var cinfo *PageInfo
67 | if !filepath.IsAbs(path) {
68 | // First try go.tools/cmd.
69 | abspath = pathpkg.Join(pres.PkgFSRoot(), toolsPath+path)
70 | cinfo = pres.GetCmdPageInfo(abspath, relpath, mode, lang...)
71 | if cinfo.IsEmpty() {
72 | // Then try $GOROOT/cmd.
73 | abspath = pathpkg.Join(pres.CmdFSRoot(), path)
74 | cinfo = pres.GetCmdPageInfo(abspath, relpath, mode, lang...)
75 | }
76 | }
77 |
78 | // determine what to use
79 | if info == nil || info.IsEmpty() {
80 | if cinfo != nil && !cinfo.IsEmpty() {
81 | // only cinfo exists - switch to cinfo
82 | info = cinfo
83 | }
84 | } else if cinfo != nil && !cinfo.IsEmpty() {
85 | // both info and cinfo exist - use cinfo if info
86 | // contains only subdirectory information
87 | if info.PAst == nil && info.PDoc == nil {
88 | info = cinfo
89 | } else if relpath != target {
90 | // The above check handles the case where an operating system path
91 | // is provided (see documentation for paths below). In that case,
92 | // relpath is set to "/target" (in anticipation of accessing packages there),
93 | // and is therefore not expected to match a command.
94 | fmt.Fprintf(w, "use 'godoc %s%s' for documentation on the %s command \n\n", cmdPrefix, relpath, relpath)
95 | }
96 | }
97 |
98 | if info == nil {
99 | return fmt.Errorf("%s: no such directory or package", args[0])
100 | }
101 | if info.Err != nil {
102 | return info.Err
103 | }
104 |
105 | if info.PDoc != nil && info.PDoc.ImportPath == target {
106 | // Replace virtual /target with actual argument from command line.
107 | info.PDoc.ImportPath = args[0]
108 | }
109 |
110 | // If we have more than one argument, use the remaining arguments for filtering.
111 | if len(args) > 1 {
112 | info.IsFiltered = true
113 | filterInfo(args[1:], info)
114 | }
115 |
116 | packageText := pres.PackageText
117 | if pres.HTMLMode {
118 | packageText = pres.PackageHTML
119 | }
120 | if err := packageText.Execute(w, info); err != nil {
121 | return err
122 | }
123 | return nil
124 | }
125 |
126 | // paths determines the paths to use.
127 | //
128 | // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc,
129 | // we need to map that path somewhere in the fs name space so that routines
130 | // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target"
131 | // for this. That is, if we get passed a directory like the above, we map that
132 | // directory so that getPageInfo sees it as /target.
133 | // Returns the absolute and relative paths.
134 | func paths(fs vfs.NameSpace, pres *Presentation, path string) (string, string) {
135 | if filepath.IsAbs(path) {
136 | fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
137 | return target, target
138 | }
139 | if build.IsLocalImport(path) {
140 | cwd, _ := os.Getwd() // ignore errors
141 | path = filepath.Join(cwd, path)
142 | fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace)
143 | return target, target
144 | }
145 | if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" {
146 | fs.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace)
147 | return target, bp.ImportPath
148 | }
149 | return pathpkg.Join(pres.PkgFSRoot(), path), path
150 | }
151 |
152 | // filterInfo updates info to include only the nodes that match the given
153 | // filter args.
154 | func filterInfo(args []string, info *PageInfo) {
155 | rx, err := makeRx(args)
156 | if err != nil {
157 | log.Fatalf("illegal regular expression from %v: %v", args, err)
158 | }
159 |
160 | filter := func(s string) bool { return rx.MatchString(s) }
161 | switch {
162 | case info.PAst != nil:
163 | newPAst := map[string]*ast.File{}
164 | for name, a := range info.PAst {
165 | cmap := ast.NewCommentMap(info.FSet, a, a.Comments)
166 | a.Comments = []*ast.CommentGroup{} // remove all comments.
167 | ast.FilterFile(a, filter)
168 | if len(a.Decls) > 0 {
169 | newPAst[name] = a
170 | }
171 | for _, d := range a.Decls {
172 | // add back the comments associated with d only
173 | comments := cmap.Filter(d).Comments()
174 | a.Comments = append(a.Comments, comments...)
175 | }
176 | }
177 | info.PAst = newPAst // add only matching files.
178 | case info.PDoc != nil:
179 | info.PDoc.Filter(filter)
180 | }
181 | }
182 |
183 | // Does s look like a regular expression?
184 | func isRegexp(s string) bool {
185 | return strings.IndexAny(s, ".(|)*+?^$[]") >= 0
186 | }
187 |
188 | // Make a regular expression of the form
189 | // names[0]|names[1]|...names[len(names)-1].
190 | // Returns an error if the regular expression is illegal.
191 | func makeRx(names []string) (*regexp.Regexp, error) {
192 | if len(names) == 0 {
193 | return nil, fmt.Errorf("no expression provided")
194 | }
195 | s := ""
196 | for i, name := range names {
197 | if i > 0 {
198 | s += "|"
199 | }
200 | if isRegexp(name) {
201 | s += name
202 | } else {
203 | s += "^" + name + "$" // must match exactly
204 | }
205 | }
206 | return regexp.Compile(s)
207 | }
208 |
--------------------------------------------------------------------------------
/tour/static/js/directives.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 | 'use strict';
6 |
7 | /* Directives */
8 |
9 | angular.module('tour.directives', []).
10 |
11 | // onpageup executes the given expression when Page Up is released.
12 | directive('onpageup', function() {
13 | return function(scope, elm, attrs) {
14 | elm.attr('tabindex', 0);
15 | elm.keyup(function(evt) {
16 | var key = evt.key || evt.keyCode;
17 | if (key == 33) {
18 | scope.$apply(attrs.onpageup);
19 | evt.preventDefault();
20 | }
21 | });
22 | };
23 | }).
24 |
25 | // onpagedown executes the given expression when Page Down is released.
26 | directive('onpagedown', function() {
27 | return function(scope, elm, attrs) {
28 | elm.attr('tabindex', 0);
29 | elm.keyup(function(evt) {
30 | var key = evt.key || evt.keyCode;
31 | if (key == 34) {
32 | scope.$apply(attrs.onpagedown);
33 | evt.preventDefault();
34 | }
35 | });
36 | };
37 | }).
38 |
39 | // autofocus sets the focus on the given element when the condition is true.
40 | directive('autofocus', function() {
41 | return function(scope, elm, attrs) {
42 | elm.attr('tabindex', 0);
43 | scope.$watch(function() {
44 | return scope.$eval(attrs.autofocus);
45 | }, function(val) {
46 | if (val === true) $(elm).focus();
47 | });
48 | };
49 | }).
50 |
51 | // syntax-checkbox activates and deactivates
52 | directive('syntaxCheckbox', ['editor',
53 | function(editor) {
54 | return function(scope, elm) {
55 | elm.click(function() {
56 | editor.toggleSyntax();
57 | scope.$digest();
58 | });
59 | scope.editor = editor;
60 | };
61 | }
62 | ]).
63 |
64 | // verticalSlide creates a sliding separator between the left and right elements.
65 | // e.g.:
66 | //
67 | //
68 | //
69 | directive('verticalSlide', ['editor',
70 | function(editor) {
71 | return function(scope, elm, attrs) {
72 | var moveTo = function(x) {
73 | if (x < 0) {
74 | x = 0;
75 | }
76 | if (x > $(window).width()) {
77 | x = $(window).width();
78 | }
79 | elm.css('left', x);
80 | $(attrs.left).width(x);
81 | $(attrs.right).offset({
82 | left: x
83 | });
84 | editor.x = x;
85 | };
86 |
87 | elm.draggable({
88 | axis: 'x',
89 | drag: function(event) {
90 | moveTo(event.clientX);
91 | return true;
92 | },
93 | containment: 'parent',
94 | });
95 |
96 | if (editor.x !== undefined) {
97 | moveTo(editor.x);
98 | }
99 | };
100 | }
101 | ]).
102 |
103 | // horizontalSlide creates a sliding separator between the top and bottom elements.
104 | //
105 | //
106 | // Some content
107 | directive('horizontalSlide', ['editor',
108 | function(editor) {
109 | return function(scope, elm, attrs) {
110 | var moveTo = function(y) {
111 | var top = $(attrs.top).offset().top;
112 | if (y < top) {
113 | y = top;
114 | }
115 | elm.css('top', y - top);
116 | $(attrs.top).height(y - top);
117 | $(attrs.bottom).offset({
118 | top: y,
119 | height: 0
120 | });
121 | editor.y = y;
122 | };
123 | elm.draggable({
124 | axis: 'y',
125 | drag: function(event) {
126 | moveTo(event.clientY);
127 | return true;
128 | },
129 | containment: 'parent',
130 | });
131 |
132 | if (editor.y !== undefined) {
133 | moveTo(editor.y);
134 | }
135 | };
136 | }
137 | ]).
138 |
139 | directive('tableOfContentsButton', function() {
140 | var speed = 250;
141 | return {
142 | restrict: 'A',
143 | templateUrl: '/static/partials/toc-button.html',
144 | link: function(scope, elm, attrs) {
145 | elm.on('click', function() {
146 | var toc = $(attrs.tableOfContentsButton);
147 | // hide all non active lessons before displaying the toc.
148 | var visible = toc.css('display') != 'none';
149 | if (!visible) {
150 | toc.find('.toc-lesson:not(.active) .toc-page').hide();
151 | toc.find('.toc-lesson.active .toc-page').show();
152 | }
153 | toc.toggle('slide', {
154 | direction: 'right'
155 | }, speed);
156 |
157 | // if fullscreen hide the rest of the content when showing the atoc.
158 | var fullScreen = toc.width() == $(window).width();
159 | if (fullScreen) $('#editor-container')[visible ? 'show' : 'hide']();
160 | });
161 | }
162 | };
163 | }).
164 |
165 | // side bar with dynamic table of contents
166 | directive('tableOfContents', ['$routeParams', 'toc',
167 | function($routeParams, toc) {
168 | var speed = 250;
169 | return {
170 | restrict: 'A',
171 | templateUrl: '/static/partials/toc.html',
172 | link: function(scope, elm) {
173 | scope.toc = toc;
174 | scope.params = $routeParams;
175 |
176 | scope.toggleLesson = function(id) {
177 | var l = $('#toc-l-' + id + ' .toc-page');
178 | l[l.css('display') == 'none' ? 'slideDown' : 'slideUp']();
179 | };
180 |
181 | scope.$watch(function() {
182 | return scope.params.lessonId + scope.params.lessonId;
183 | }, function() {
184 | $('.toc-lesson:not(#toc-l-' + scope.params.lessonId + ') .toc-page').slideUp(speed);
185 | });
186 |
187 | scope.hideTOC = function(fullScreenOnly) {
188 | var fullScreen = elm.find('.toc').width() == $(window).width();
189 | if (fullScreenOnly && !fullScreen) {
190 | return;
191 | }
192 | $('.toc').toggle('slide', {
193 | direction: 'right'
194 | }, speed);
195 | };
196 | }
197 | };
198 | }
199 | ]);
--------------------------------------------------------------------------------
/tour/static/lib/codemirror/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | }
8 | .CodeMirror-scroll {
9 | /* Set scrolling behaviour here */
10 | overflow: auto;
11 | }
12 |
13 | /* PADDING */
14 |
15 | .CodeMirror-lines {
16 | padding: 4px 0; /* Vertical padding around content */
17 | }
18 | .CodeMirror pre {
19 | padding: 0 4px; /* Horizontal padding of content */
20 | }
21 |
22 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
23 | background-color: white; /* The little square between H and V scrollbars */
24 | }
25 |
26 | /* GUTTER */
27 |
28 | .CodeMirror-gutters {
29 | border-right: 1px solid #ddd;
30 | background-color: #f7f7f7;
31 | white-space: nowrap;
32 | }
33 | .CodeMirror-linenumbers {}
34 | .CodeMirror-linenumber {
35 | padding: 0 3px 0 5px;
36 | min-width: 20px;
37 | text-align: right;
38 | color: #999;
39 | }
40 |
41 | /* CURSOR */
42 |
43 | .CodeMirror div.CodeMirror-cursor {
44 | border-left: 1px solid black;
45 | z-index: 3;
46 | }
47 | /* Shown when moving in bi-directional text */
48 | .CodeMirror div.CodeMirror-secondarycursor {
49 | border-left: 1px solid silver;
50 | }
51 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
52 | width: auto;
53 | border: 0;
54 | background: #7e7;
55 | z-index: 1;
56 | }
57 | /* Can style cursor different in overwrite (non-insert) mode */
58 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
59 |
60 | .cm-tab { display: inline-block; }
61 |
62 | /* DEFAULT THEME */
63 |
64 | .cm-s-default .cm-keyword {color: #708;}
65 | .cm-s-default .cm-atom {color: #219;}
66 | .cm-s-default .cm-number {color: #164;}
67 | .cm-s-default .cm-def {color: #00f;}
68 | .cm-s-default .cm-variable {color: black;}
69 | .cm-s-default .cm-variable-2 {color: #05a;}
70 | .cm-s-default .cm-variable-3 {color: #085;}
71 | .cm-s-default .cm-property {color: black;}
72 | .cm-s-default .cm-operator {color: black;}
73 | .cm-s-default .cm-comment {color: #a50;}
74 | .cm-s-default .cm-string {color: #a11;}
75 | .cm-s-default .cm-string-2 {color: #f50;}
76 | .cm-s-default .cm-meta {color: #555;}
77 | .cm-s-default .cm-error {color: #f00;}
78 | .cm-s-default .cm-qualifier {color: #555;}
79 | .cm-s-default .cm-builtin {color: #30a;}
80 | .cm-s-default .cm-bracket {color: #997;}
81 | .cm-s-default .cm-tag {color: #170;}
82 | .cm-s-default .cm-attribute {color: #00c;}
83 | .cm-s-default .cm-header {color: blue;}
84 | .cm-s-default .cm-quote {color: #090;}
85 | .cm-s-default .cm-hr {color: #999;}
86 | .cm-s-default .cm-link {color: #00c;}
87 |
88 | .cm-negative {color: #d44;}
89 | .cm-positive {color: #292;}
90 | .cm-header, .cm-strong {font-weight: bold;}
91 | .cm-em {font-style: italic;}
92 | .cm-link {text-decoration: underline;}
93 |
94 | .cm-invalidchar {color: #f00;}
95 |
96 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
97 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
98 | .CodeMirror-activeline-background {background: #e8f2ff;}
99 |
100 | /* STOP */
101 |
102 | /* The rest of this file contains styles related to the mechanics of
103 | the editor. You probably shouldn't touch them. */
104 |
105 | .CodeMirror {
106 | line-height: 1;
107 | position: relative;
108 | overflow: hidden;
109 | background: white;
110 | color: black;
111 | }
112 |
113 | .CodeMirror-scroll {
114 | /* 30px is the magic margin used to hide the element's real scrollbars */
115 | /* See overflow: hidden in .CodeMirror */
116 | margin-bottom: -30px; margin-right: -30px;
117 | padding-bottom: 30px; padding-right: 30px;
118 | height: 100%;
119 | outline: none; /* Prevent dragging from highlighting the element */
120 | position: relative;
121 | }
122 | .CodeMirror-sizer {
123 | position: relative;
124 | }
125 |
126 | /* The fake, visible scrollbars. Used to force redraw during scrolling
127 | before actuall scrolling happens, thus preventing shaking and
128 | flickering artifacts. */
129 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
130 | position: absolute;
131 | z-index: 6;
132 | display: none;
133 | }
134 | .CodeMirror-vscrollbar {
135 | right: 0; top: 0;
136 | overflow-x: hidden;
137 | overflow-y: scroll;
138 | }
139 | .CodeMirror-hscrollbar {
140 | bottom: 0; left: 0;
141 | overflow-y: hidden;
142 | overflow-x: scroll;
143 | }
144 | .CodeMirror-scrollbar-filler {
145 | right: 0; bottom: 0;
146 | }
147 | .CodeMirror-gutter-filler {
148 | left: 0; bottom: 0;
149 | }
150 |
151 | .CodeMirror-gutters {
152 | position: absolute; left: 0; top: 0;
153 | padding-bottom: 30px;
154 | z-index: 3;
155 | }
156 | .CodeMirror-gutter {
157 | white-space: normal;
158 | height: 100%;
159 | padding-bottom: 30px;
160 | margin-bottom: -32px;
161 | display: inline-block;
162 | /* Hack to make IE7 behave */
163 | *zoom:1;
164 | *display:inline;
165 | }
166 | .CodeMirror-gutter-elt {
167 | position: absolute;
168 | cursor: default;
169 | z-index: 4;
170 | }
171 |
172 | .CodeMirror-lines {
173 | cursor: text;
174 | }
175 | .CodeMirror pre {
176 | /* Reset some styles that the rest of the page might have set */
177 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
178 | border-width: 0;
179 | background: transparent;
180 | font-family: inherit;
181 | font-size: inherit;
182 | margin: 0;
183 | white-space: pre;
184 | word-wrap: normal;
185 | line-height: inherit;
186 | color: inherit;
187 | z-index: 2;
188 | position: relative;
189 | overflow: visible;
190 | }
191 | .CodeMirror-wrap pre {
192 | word-wrap: break-word;
193 | white-space: pre-wrap;
194 | word-break: normal;
195 | }
196 | .CodeMirror-code pre {
197 | border-right: 30px solid transparent;
198 | width: -webkit-fit-content;
199 | width: -moz-fit-content;
200 | width: fit-content;
201 | }
202 | .CodeMirror-wrap .CodeMirror-code pre {
203 | border-right: none;
204 | width: auto;
205 | }
206 | .CodeMirror-linebackground {
207 | position: absolute;
208 | left: 0; right: 0; top: 0; bottom: 0;
209 | z-index: 0;
210 | }
211 |
212 | .CodeMirror-linewidget {
213 | position: relative;
214 | z-index: 2;
215 | overflow: auto;
216 | }
217 |
218 | .CodeMirror-widget {
219 | }
220 |
221 | .CodeMirror-wrap .CodeMirror-scroll {
222 | overflow-x: hidden;
223 | }
224 |
225 | .CodeMirror-measure {
226 | position: absolute;
227 | width: 100%; height: 0px;
228 | overflow: hidden;
229 | visibility: hidden;
230 | }
231 | .CodeMirror-measure pre { position: static; }
232 |
233 | .CodeMirror div.CodeMirror-cursor {
234 | position: absolute;
235 | visibility: hidden;
236 | border-right: none;
237 | width: 0;
238 | }
239 | .CodeMirror-focused div.CodeMirror-cursor {
240 | visibility: visible;
241 | }
242 |
243 | .CodeMirror-selected { background: #d9d9d9; }
244 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
245 |
246 | .cm-searching {
247 | background: #ffa;
248 | background: rgba(255, 255, 0, .4);
249 | }
250 |
251 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
252 | .CodeMirror span { *vertical-align: text-bottom; }
253 |
254 | @media print {
255 | /* Hide the cursor when printing */
256 | .CodeMirror div.CodeMirror-cursor {
257 | visibility: hidden;
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/godoc/linkify.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 | // This file implements LinkifyText which introduces
6 | // links for identifiers pointing to their declarations.
7 | // The approach does not cover all cases because godoc
8 | // doesn't have complete type information, but it's
9 | // reasonably good for browsing.
10 |
11 | package godoc
12 |
13 | import (
14 | "fmt"
15 | "go/ast"
16 | "go/token"
17 | "io"
18 | "strconv"
19 | )
20 |
21 | // LinkifyText HTML-escapes source text and writes it to w.
22 | // Identifiers that are in a "use" position (i.e., that are
23 | // not being declared), are wrapped with HTML links pointing
24 | // to the respective declaration, if possible. Comments are
25 | // formatted the same way as with FormatText.
26 | //
27 | func LinkifyText(w io.Writer, text []byte, n ast.Node) {
28 | links := linksFor(n)
29 |
30 | i := 0 // links index
31 | prev := "" // prev HTML tag
32 | linkWriter := func(w io.Writer, _ int, start bool) {
33 | // end tag
34 | if !start {
35 | if prev != "" {
36 | fmt.Fprintf(w, `%s>`, prev)
37 | prev = ""
38 | }
39 | return
40 | }
41 |
42 | // start tag
43 | prev = ""
44 | if i < len(links) {
45 | switch info := links[i]; {
46 | case info.path != "" && info.name == "":
47 | // package path
48 | fmt.Fprintf(w, ``, info.path)
49 | prev = "a"
50 | case info.path != "" && info.name != "":
51 | // qualified identifier
52 | fmt.Fprintf(w, ``, info.path, info.name)
53 | prev = "a"
54 | case info.path == "" && info.name != "":
55 | // local identifier
56 | if info.mode == identVal {
57 | fmt.Fprintf(w, ``, info.name)
58 | prev = "span"
59 | } else if ast.IsExported(info.name) {
60 | fmt.Fprintf(w, ``, info.name)
61 | prev = "a"
62 | }
63 | }
64 | i++
65 | }
66 | }
67 |
68 | idents := tokenSelection(text, token.IDENT)
69 | comments := tokenSelection(text, token.COMMENT)
70 | FormatSelections(w, text, linkWriter, idents, selectionTag, comments)
71 | }
72 |
73 | // A link describes the (HTML) link information for an identifier.
74 | // The zero value of a link represents "no link".
75 | //
76 | type link struct {
77 | mode identMode
78 | path, name string // package path, identifier name
79 | }
80 |
81 | // linksFor returns the list of links for the identifiers used
82 | // by node in the same order as they appear in the source.
83 | //
84 | func linksFor(node ast.Node) (list []link) {
85 | modes := identModesFor(node)
86 |
87 | // NOTE: We are expecting ast.Inspect to call the
88 | // callback function in source text order.
89 | ast.Inspect(node, func(node ast.Node) bool {
90 | switch n := node.(type) {
91 | case *ast.Ident:
92 | m := modes[n]
93 | info := link{mode: m}
94 | switch m {
95 | case identUse:
96 | if n.Obj == nil && predeclared[n.Name] {
97 | info.path = builtinPkgPath
98 | }
99 | info.name = n.Name
100 | case identDef:
101 | // any declaration expect const or var - empty link
102 | case identVal:
103 | // const or var declaration
104 | info.name = n.Name
105 | }
106 | list = append(list, info)
107 | return false
108 | case *ast.SelectorExpr:
109 | // Detect qualified identifiers of the form pkg.ident.
110 | // If anything fails we return true and collect individual
111 | // identifiers instead.
112 | if x, _ := n.X.(*ast.Ident); x != nil {
113 | // x must be a package for a qualified identifier
114 | if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
115 | if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
116 | // spec.Path.Value is the import path
117 | if path, err := strconv.Unquote(spec.Path.Value); err == nil {
118 | // Register two links, one for the package
119 | // and one for the qualified identifier.
120 | info := link{path: path}
121 | list = append(list, info)
122 | info.name = n.Sel.Name
123 | list = append(list, info)
124 | return false
125 | }
126 | }
127 | }
128 | }
129 | }
130 | return true
131 | })
132 |
133 | return
134 | }
135 |
136 | // The identMode describes how an identifier is "used" at its source location.
137 | type identMode int
138 |
139 | const (
140 | identUse identMode = iota // identifier is used (must be zero value for identMode)
141 | identDef // identifier is defined
142 | identVal // identifier is defined in a const or var declaration
143 | )
144 |
145 | // identModesFor returns a map providing the identMode for each identifier used by node.
146 | func identModesFor(node ast.Node) map[*ast.Ident]identMode {
147 | m := make(map[*ast.Ident]identMode)
148 |
149 | ast.Inspect(node, func(node ast.Node) bool {
150 | switch n := node.(type) {
151 | case *ast.Field:
152 | for _, n := range n.Names {
153 | m[n] = identDef
154 | }
155 | case *ast.ImportSpec:
156 | if name := n.Name; name != nil {
157 | m[name] = identDef
158 | }
159 | case *ast.ValueSpec:
160 | for _, n := range n.Names {
161 | m[n] = identVal
162 | }
163 | case *ast.TypeSpec:
164 | m[n.Name] = identDef
165 | case *ast.FuncDecl:
166 | m[n.Name] = identDef
167 | case *ast.AssignStmt:
168 | // Short variable declarations only show up if we apply
169 | // this code to all source code (as opposed to exported
170 | // declarations only).
171 | if n.Tok == token.DEFINE {
172 | // Some of the lhs variables may be re-declared,
173 | // so technically they are not defs. We don't
174 | // care for now.
175 | for _, x := range n.Lhs {
176 | // Each lhs expression should be an
177 | // ident, but we are conservative and check.
178 | if n, _ := x.(*ast.Ident); n != nil {
179 | m[n] = identVal
180 | }
181 | }
182 | }
183 | }
184 | return true
185 | })
186 |
187 | return m
188 | }
189 |
190 | // The predeclared map represents the set of all predeclared identifiers.
191 | // TODO(gri) This information is also encoded in similar maps in go/doc,
192 | // but not exported. Consider exporting an accessor and using
193 | // it instead.
194 | var predeclared = map[string]bool{
195 | "bool": true,
196 | "byte": true,
197 | "complex64": true,
198 | "complex128": true,
199 | "error": true,
200 | "float32": true,
201 | "float64": true,
202 | "int": true,
203 | "int8": true,
204 | "int16": true,
205 | "int32": true,
206 | "int64": true,
207 | "rune": true,
208 | "string": true,
209 | "uint": true,
210 | "uint8": true,
211 | "uint16": true,
212 | "uint32": true,
213 | "uint64": true,
214 | "uintptr": true,
215 | "true": true,
216 | "false": true,
217 | "iota": true,
218 | "nil": true,
219 | "append": true,
220 | "cap": true,
221 | "close": true,
222 | "complex": true,
223 | "copy": true,
224 | "delete": true,
225 | "imag": true,
226 | "len": true,
227 | "make": true,
228 | "new": true,
229 | "panic": true,
230 | "print": true,
231 | "println": true,
232 | "real": true,
233 | "recover": true,
234 | }
235 |
--------------------------------------------------------------------------------
/godoc/cmdline_test.go:
--------------------------------------------------------------------------------
1 | package godoc
2 |
3 | import (
4 | "bytes"
5 | "go/build"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 | "reflect"
10 | "regexp"
11 | "runtime"
12 | "testing"
13 | "text/template"
14 |
15 | "golang.org/x/tools/godoc/vfs"
16 | "golang.org/x/tools/godoc/vfs/mapfs"
17 | )
18 |
19 | // setupGoroot creates temporary directory to act as GOROOT when running tests
20 | // that depend upon the build package. It updates build.Default to point to the
21 | // new GOROOT.
22 | // It returns a function that can be called to reset build.Default and remove
23 | // the temporary directory.
24 | func setupGoroot(t *testing.T) (cleanup func()) {
25 | var stdLib = map[string]string{
26 | "src/fmt/fmt.go": `// Package fmt implements formatted I/O.
27 | package fmt
28 |
29 | type Stringer interface {
30 | String() string
31 | }
32 | `,
33 | }
34 | goroot, err := ioutil.TempDir("", "cmdline_test")
35 | if err != nil {
36 | t.Fatal(err)
37 | }
38 | origContext := build.Default
39 | build.Default = build.Context{
40 | GOROOT: goroot,
41 | Compiler: "gc",
42 | }
43 | for relname, contents := range stdLib {
44 | name := filepath.Join(goroot, relname)
45 | if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil {
46 | t.Fatal(err)
47 | }
48 | if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil {
49 | t.Fatal(err)
50 | }
51 | }
52 |
53 | return func() {
54 | if err := os.RemoveAll(goroot); err != nil {
55 | t.Log(err)
56 | }
57 | build.Default = origContext
58 | }
59 | }
60 |
61 | func TestPaths(t *testing.T) {
62 | cleanup := setupGoroot(t)
63 | defer cleanup()
64 |
65 | pres := &Presentation{
66 | pkgHandler: handlerServer{
67 | fsRoot: "/fsroot",
68 | },
69 | }
70 | fs := make(vfs.NameSpace)
71 |
72 | absPath := "/foo/fmt"
73 | if runtime.GOOS == "windows" {
74 | absPath = `c:\foo\fmt`
75 | }
76 |
77 | for _, tc := range []struct {
78 | desc string
79 | path string
80 | expAbs string
81 | expRel string
82 | }{
83 | {
84 | "Absolute path",
85 | absPath,
86 | "/target",
87 | "/target",
88 | },
89 | {
90 | "Local import",
91 | "../foo/fmt",
92 | "/target",
93 | "/target",
94 | },
95 | {
96 | "Import",
97 | "fmt",
98 | "/target",
99 | "fmt",
100 | },
101 | {
102 | "Default",
103 | "unknownpkg",
104 | "/fsroot/unknownpkg",
105 | "unknownpkg",
106 | },
107 | } {
108 | abs, rel := paths(fs, pres, tc.path)
109 | if abs != tc.expAbs || rel != tc.expRel {
110 | t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel)
111 | }
112 | }
113 | }
114 |
115 | func TestMakeRx(t *testing.T) {
116 | for _, tc := range []struct {
117 | desc string
118 | names []string
119 | exp string
120 | }{
121 | {
122 | desc: "empty string",
123 | names: []string{""},
124 | exp: `^$`,
125 | },
126 | {
127 | desc: "simple text",
128 | names: []string{"a"},
129 | exp: `^a$`,
130 | },
131 | {
132 | desc: "two words",
133 | names: []string{"foo", "bar"},
134 | exp: `^foo$|^bar$`,
135 | },
136 | {
137 | desc: "word & non-trivial",
138 | names: []string{"foo", `ab?c`},
139 | exp: `^foo$|ab?c`,
140 | },
141 | {
142 | desc: "bad regexp",
143 | names: []string{`(."`},
144 | exp: `(."`,
145 | },
146 | } {
147 | expRE, expErr := regexp.Compile(tc.exp)
148 | if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) {
149 | t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr)
150 | }
151 | }
152 | }
153 |
154 | func TestCommandLine(t *testing.T) {
155 | cleanup := setupGoroot(t)
156 | defer cleanup()
157 | mfs := mapfs.New(map[string]string{
158 | "src/bar/bar.go": `// Package bar is an example.
159 | package bar
160 | `,
161 | "src/foo/foo.go": `// Package foo.
162 | package foo
163 |
164 | // First function is first.
165 | func First() {
166 | }
167 |
168 | // Second function is second.
169 | func Second() {
170 | }
171 | `,
172 | "src/gen/gen.go": `// Package gen
173 | package gen
174 |
175 | //line notgen.go:3
176 | // F doc //line 1 should appear
177 | // line 2 should appear
178 | func F()
179 | //line foo.go:100`, // no newline on end to check corner cases!
180 | "src/vet/vet.go": `// Package vet
181 | package vet
182 | `,
183 | "src/cmd/go/doc.go": `// The go command
184 | package main
185 | `,
186 | "src/cmd/gofmt/doc.go": `// The gofmt command
187 | package main
188 | `,
189 | "src/cmd/vet/vet.go": `// The vet command
190 | package main
191 | `,
192 | })
193 | fs := make(vfs.NameSpace)
194 | fs.Bind("/", mfs, "/", vfs.BindReplace)
195 | c := NewCorpus(fs)
196 | p := &Presentation{Corpus: c}
197 | p.cmdHandler = handlerServer{
198 | p: p,
199 | c: c,
200 | pattern: "/cmd/",
201 | fsRoot: "/src/cmd",
202 | }
203 | p.pkgHandler = handlerServer{
204 | p: p,
205 | c: c,
206 | pattern: "/pkg/",
207 | fsRoot: "/src",
208 | exclude: []string{"/src/cmd"},
209 | }
210 | p.initFuncMap()
211 | p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{$info := .}}{{$filtered := .IsFiltered}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}:
212 | {{node $ $ast}}{{end}}{{end}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}}
213 | {{range .}}{{node $ .Decl}}
214 | {{comment_text .Doc " " "\t"}}{{end}}{{end}}{{end}}`))
215 |
216 | for _, tc := range []struct {
217 | desc string
218 | args []string
219 | exp string
220 | err bool
221 | }{
222 | {
223 | desc: "standard package",
224 | args: []string{"fmt"},
225 | exp: "PACKAGE Package fmt implements formatted I/O.\n",
226 | },
227 | {
228 | desc: "package",
229 | args: []string{"bar"},
230 | exp: "PACKAGE Package bar is an example.\n",
231 | },
232 | {
233 | desc: "package w. filter",
234 | args: []string{"foo", "First"},
235 | exp: "PACKAGE \nfunc First()\n First function is first.\n",
236 | },
237 | {
238 | desc: "package w. bad filter",
239 | args: []string{"foo", "DNE"},
240 | exp: "PACKAGE ",
241 | },
242 | {
243 | desc: "source mode",
244 | args: []string{"src/bar"},
245 | exp: "bar/bar.go:\n// Package bar is an example.\npackage bar\n",
246 | },
247 | {
248 | desc: "source mode w. filter",
249 | args: []string{"src/foo", "Second"},
250 | exp: "// Second function is second.\nfunc Second() {\n}",
251 | },
252 | {
253 | desc: "package w. //line comments",
254 | args: []string{"gen", "F"},
255 | exp: "PACKAGE \nfunc F()\n F doc //line 1 should appear line 2 should appear\n",
256 | },
257 | {
258 | desc: "command",
259 | args: []string{"go"},
260 | exp: "COMMAND The go command\n",
261 | },
262 | {
263 | desc: "forced command",
264 | args: []string{"cmd/gofmt"},
265 | exp: "COMMAND The gofmt command\n",
266 | },
267 | {
268 | desc: "bad arg",
269 | args: []string{"doesnotexist"},
270 | err: true,
271 | },
272 | {
273 | desc: "both command and package",
274 | args: []string{"vet"},
275 | exp: "use 'godoc cmd/vet' for documentation on the vet command \n\nPACKAGE Package vet\n",
276 | },
277 | {
278 | desc: "root directory",
279 | args: []string{"/"},
280 | exp: "",
281 | },
282 | } {
283 | w := new(bytes.Buffer)
284 | err := CommandLine(w, fs, p, tc.args)
285 | if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) {
286 | t.Errorf("%s: CommandLine(%v) = %q (%v); want %q (%v)",
287 | tc.desc, tc.args, got, err, want, tc.err)
288 | }
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/tour/tour.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 main
6 |
7 | import (
8 | "bytes"
9 | "encoding/json"
10 | "fmt"
11 | "html/template"
12 | "io"
13 | "io/ioutil"
14 | "net/http"
15 | "os"
16 | "path/filepath"
17 | "strings"
18 | "time"
19 |
20 | "golang.org/x/tools/godoc/static"
21 | "golang.org/x/tools/present"
22 | )
23 |
24 | var (
25 | uiContent []byte
26 | lessons = make(map[string][]byte)
27 | lessonNotFound = fmt.Errorf("lesson not found")
28 | )
29 |
30 | // initTour loads tour.article and the relevant HTML templates from the given
31 | // tour root, and renders the template to the tourContent global variable.
32 | func initTour(root, transport string) error {
33 | // Make sure playground is enabled before rendering.
34 | present.PlayEnabled = true
35 |
36 | // Set up templates.
37 | action := filepath.Join(root, "template", "action.tmpl")
38 | tmpl, err := present.Template().ParseFiles(action)
39 | if err != nil {
40 | return fmt.Errorf("parse templates: %v", err)
41 | }
42 |
43 | // Init lessons.
44 | contentPath := filepath.Join(root, "content")
45 | if err := initLessons(tmpl, contentPath); err != nil {
46 | return fmt.Errorf("init lessons: %v", err)
47 | }
48 |
49 | // Init UI
50 | index := filepath.Join(root, "template", "index.tmpl")
51 | ui, err := template.ParseFiles(index)
52 | if err != nil {
53 | return fmt.Errorf("parse index.tmpl: %v", err)
54 | }
55 | buf := new(bytes.Buffer)
56 |
57 | data := struct {
58 | SocketAddr string
59 | Transport template.JS
60 | }{socketAddr(), template.JS(transport)}
61 |
62 | if err := ui.Execute(buf, data); err != nil {
63 | return fmt.Errorf("render UI: %v", err)
64 | }
65 | uiContent = buf.Bytes()
66 |
67 | return initScript(root)
68 | }
69 |
70 | // initLessonss finds all the lessons in the passed directory, renders them,
71 | // using the given template and saves the content in the lessons map.
72 | func initLessons(tmpl *template.Template, content string) error {
73 | dir, err := os.Open(content)
74 | if err != nil {
75 | return err
76 | }
77 | files, err := dir.Readdirnames(0)
78 | if err != nil {
79 | return err
80 | }
81 | for _, f := range files {
82 | if filepath.Ext(f) != ".article" {
83 | continue
84 | }
85 | content, err := parseLesson(tmpl, filepath.Join(content, f))
86 | if err != nil {
87 | return fmt.Errorf("parsing %v: %v", f, err)
88 | }
89 | name := strings.TrimSuffix(f, ".article")
90 | lessons[name] = content
91 | }
92 | return nil
93 | }
94 |
95 | // File defines the JSON form of a code file in a page.
96 | type File struct {
97 | Name string
98 | Content string
99 | }
100 |
101 | // Page defines the JSON form of a tour lesson page.
102 | type Page struct {
103 | Title string
104 | Content string
105 | Files []File
106 | }
107 |
108 | // Lesson defines the JSON form of a tour lesson.
109 | type Lesson struct {
110 | Title string
111 | Description string
112 | Pages []Page
113 | }
114 |
115 | // parseLesson parses and returns a lesson content given its name and
116 | // the template to render it.
117 | func parseLesson(tmpl *template.Template, path string) ([]byte, error) {
118 | f, err := os.Open(path)
119 | if err != nil {
120 | return nil, err
121 | }
122 | defer f.Close()
123 | doc, err := present.Parse(prepContent(f), path, 0)
124 | if err != nil {
125 | return nil, err
126 | }
127 |
128 | lesson := Lesson{
129 | doc.Title,
130 | doc.Subtitle,
131 | make([]Page, len(doc.Sections)),
132 | }
133 |
134 | for i, sec := range doc.Sections {
135 | p := &lesson.Pages[i]
136 | w := new(bytes.Buffer)
137 | if err := sec.Render(w, tmpl); err != nil {
138 | return nil, fmt.Errorf("render section: %v", err)
139 | }
140 | p.Title = sec.Title
141 | p.Content = w.String()
142 | codes := findPlayCode(sec)
143 | p.Files = make([]File, len(codes))
144 | for i, c := range codes {
145 | f := &p.Files[i]
146 | f.Name = c.FileName
147 | f.Content = string(c.Raw)
148 | }
149 | }
150 |
151 | w := new(bytes.Buffer)
152 | if err := json.NewEncoder(w).Encode(lesson); err != nil {
153 | return nil, fmt.Errorf("encode lesson: %v", err)
154 | }
155 | return w.Bytes(), nil
156 | }
157 |
158 | // findPlayCode returns a slide with all the Code elements in the given
159 | // Elem with Play set to true.
160 | func findPlayCode(e present.Elem) []*present.Code {
161 | var r []*present.Code
162 | switch v := e.(type) {
163 | case present.Code:
164 | if v.Play {
165 | r = append(r, &v)
166 | }
167 | case present.Section:
168 | for _, s := range v.Elem {
169 | r = append(r, findPlayCode(s)...)
170 | }
171 | }
172 | return r
173 | }
174 |
175 | // writeLesson writes the tour content to the provided Writer.
176 | func writeLesson(name string, w io.Writer) error {
177 | if uiContent == nil {
178 | panic("writeLesson called before successful initTour")
179 | }
180 | if len(name) == 0 {
181 | return writeAllLessons(w)
182 | }
183 | l, ok := lessons[name]
184 | if !ok {
185 | return lessonNotFound
186 | }
187 | _, err := w.Write(l)
188 | return err
189 | }
190 |
191 | func writeAllLessons(w io.Writer) error {
192 | if _, err := fmt.Fprint(w, "{"); err != nil {
193 | return err
194 | }
195 | nLessons := len(lessons)
196 | for k, v := range lessons {
197 | if _, err := fmt.Fprintf(w, "%q:%s", k, v); err != nil {
198 | return err
199 | }
200 | nLessons--
201 | if nLessons != 0 {
202 | if _, err := fmt.Fprint(w, ","); err != nil {
203 | return err
204 | }
205 | }
206 | }
207 | _, err := fmt.Fprint(w, "}")
208 | return err
209 | }
210 |
211 | // renderUI writes the tour UI to the provided Writer.
212 | func renderUI(w io.Writer) error {
213 | if uiContent == nil {
214 | panic("renderUI called before successful initTour")
215 | }
216 | _, err := w.Write(uiContent)
217 | return err
218 | }
219 |
220 | // nocode returns true if the provided Section contains
221 | // no Code elements with Play enabled.
222 | func nocode(s present.Section) bool {
223 | for _, e := range s.Elem {
224 | if c, ok := e.(present.Code); ok && c.Play {
225 | return false
226 | }
227 | }
228 | return true
229 | }
230 |
231 | // initScript concatenates all the javascript files needed to render
232 | // the tour UI and serves the result on /script.js.
233 | func initScript(root string) error {
234 | modTime := time.Now()
235 | b := new(bytes.Buffer)
236 |
237 | content, ok := static.Files["playground.js"]
238 | if !ok {
239 | return fmt.Errorf("playground.js not found in static files")
240 | }
241 | b.WriteString(content)
242 |
243 | // Keep this list in dependency order
244 | files := []string{
245 | "static/lib/jquery.min.js",
246 | "static/lib/jquery-ui.min.js",
247 | "static/lib/angular.min.js",
248 | "static/lib/codemirror/lib/codemirror.js",
249 | "static/lib/codemirror/mode/go/go.js",
250 | "static/lib/angular-ui.min.js",
251 | "static/js/app.js",
252 | "static/js/controllers.js",
253 | "static/js/directives.js",
254 | "static/js/services.js",
255 | "static/js/values.js",
256 | }
257 |
258 | for _, file := range files {
259 | f, err := ioutil.ReadFile(filepath.Join(root, file))
260 | if err != nil {
261 | return fmt.Errorf("couldn't open %v: %v", file, err)
262 | }
263 | _, err = b.Write(f)
264 | if err != nil {
265 | return fmt.Errorf("error concatenating %v: %v", file, err)
266 | }
267 | }
268 |
269 | http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) {
270 | w.Header().Set("Content-type", "application/javascript")
271 | // Set expiration time in one week.
272 | w.Header().Set("Cache-control", "max-age=604800")
273 | http.ServeContent(w, r, "", modTime, bytes.NewReader(b.Bytes()))
274 | })
275 |
276 | return nil
277 | }
278 |
--------------------------------------------------------------------------------
/tour/static/lib/codemirror/mode/go/go.js:
--------------------------------------------------------------------------------
1 | function goMode(commentsOnly) {
2 | return function(config) {
3 | var indentUnit = config.indentUnit;
4 |
5 | var keywords = {
6 | "break":true, "case":true, "chan":true, "const":true, "continue":true,
7 | "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
8 | "func":true, "go":true, "goto":true, "if":true, "import":true,
9 | "interface":true, "map":true, "package":true, "range":true, "return":true,
10 | "select":true, "struct":true, "switch":true, "type":true, "var":true,
11 | "bool":true, "byte":true, "complex64":true, "complex128":true, "error":true,
12 | "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
13 | "int64":true, "rune":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
14 | "uint64":true, "int":true, "uint":true, "uintptr":true
15 | };
16 |
17 | var atoms = {
18 | "true":true, "false":true, "iota":true, "nil":true, "append":true,
19 | "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
20 | "len":true, "make":true, "new":true, "panic":true, "print":true,
21 | "println":true, "real":true, "recover":true
22 | };
23 |
24 | var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
25 |
26 | var curPunc;
27 |
28 | function tokenBase(stream, state) {
29 | var ch = stream.next();
30 | if (ch == '"' || ch == "'" || ch == "`") {
31 | state.tokenize = tokenString(ch);
32 | return state.tokenize(stream, state);
33 | }
34 | if (/[\d\.]/.test(ch)) {
35 | if (ch == ".") {
36 | stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
37 | } else if (ch == "0") {
38 | stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
39 | } else {
40 | stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
41 | }
42 | return "number";
43 | }
44 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
45 | curPunc = ch;
46 | return null;
47 | }
48 | if (ch == "/") {
49 | if (stream.eat("*")) {
50 | state.tokenize = tokenComment;
51 | return tokenComment(stream, state);
52 | }
53 | if (stream.eat("/")) {
54 | stream.skipToEnd();
55 | return "comment";
56 | }
57 | }
58 | if (isOperatorChar.test(ch)) {
59 | stream.eatWhile(isOperatorChar);
60 | return "operator";
61 | }
62 | stream.eatWhile(/[\w\$_]/);
63 | var cur = stream.current();
64 | if (keywords.propertyIsEnumerable(cur)) {
65 | if (cur == "case" || cur == "default") curPunc = "case";
66 | return "keyword";
67 | }
68 | if (atoms.propertyIsEnumerable(cur)) return "atom";
69 | return "variable";
70 | }
71 |
72 | function tokenString(quote) {
73 | return function(stream, state) {
74 | var escaped = false,
75 | next, end = false;
76 | while ((next = stream.next()) != null) {
77 | if (next == quote && !escaped) {
78 | end = true;
79 | break;
80 | }
81 | escaped = !escaped && next == "\\";
82 | }
83 | if (end || !(escaped || quote == "`"))
84 | state.tokenize = tokenBase;
85 | return "string";
86 | };
87 | }
88 |
89 | function tokenComment(stream, state) {
90 | var maybeEnd = false,
91 | ch;
92 | while (ch = stream.next()) {
93 | if (ch == "/" && maybeEnd) {
94 | state.tokenize = tokenBase;
95 | break;
96 | }
97 | maybeEnd = (ch == "*");
98 | }
99 | return "comment";
100 | }
101 |
102 | function Context(indented, column, type, align, prev) {
103 | this.indented = indented;
104 | this.column = column;
105 | this.type = type;
106 | this.align = align;
107 | this.prev = prev;
108 | }
109 |
110 | function pushContext(state, col, type) {
111 | return state.context = new Context(state.indented, col, type, null, state.context);
112 | }
113 |
114 | function popContext(state) {
115 | var t = state.context.type;
116 | if (t == ")" || t == "]" || t == "}")
117 | state.indented = state.context.indented;
118 | return state.context = state.context.prev;
119 | }
120 |
121 | // Interface
122 |
123 | return {
124 | startState: function(basecolumn) {
125 | return {
126 | tokenize: null,
127 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
128 | indented: 0,
129 | startOfLine: true
130 | };
131 | },
132 |
133 | token: function(stream, state) {
134 | var ctx = state.context;
135 | if (stream.sol()) {
136 | if (ctx.align == null) ctx.align = false;
137 | state.indented = stream.indentation();
138 | state.startOfLine = true;
139 | if (ctx.type == "case") ctx.type = "}";
140 | }
141 | if (stream.eatSpace()) return null;
142 | curPunc = null;
143 | var style = (state.tokenize || tokenBase)(stream, state);
144 | if (style == "comment") return style;
145 | if (ctx.align == null) ctx.align = true;
146 |
147 | if (curPunc == "{") pushContext(state, stream.column(), "}");
148 | else if (curPunc == "[") pushContext(state, stream.column(), "]");
149 | else if (curPunc == "(") pushContext(state, stream.column(), ")");
150 | else if (curPunc == "case") ctx.type = "case";
151 | else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
152 | else if (curPunc == ctx.type) popContext(state);
153 | state.startOfLine = false;
154 | if (commentsOnly) return "";
155 | return style;
156 | },
157 |
158 | indent: function(state, textAfter) {
159 | if (state.tokenize != tokenBase && state.tokenize != null) return 0;
160 | var ctx = state.context,
161 | firstChar = textAfter && textAfter.charAt(0);
162 | if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
163 | state.context.type = "}";
164 | return ctx.indented;
165 | }
166 | var closing = firstChar == ctx.type;
167 | if (ctx.align) return ctx.column + (closing ? 0 : 1);
168 | else return ctx.indented + (closing ? 0 : indentUnit);
169 | },
170 |
171 | electricChars: "{}:",
172 | blockCommentStart: "/*",
173 | blockCommentEnd: "*/",
174 | lineComment: "//"
175 | };
176 | }
177 | }
178 |
179 | CodeMirror.defineMode("go", goMode(false));
180 | CodeMirror.defineMIME("text/x-go", "go");
181 |
182 | CodeMirror.defineMode("goComments", goMode(true));
183 | CodeMirror.defineMIME("text/x-go-comments", "goComments");
184 |
--------------------------------------------------------------------------------
/local/local.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 ChaiShushan . 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 local
6 |
7 | import (
8 | "fmt"
9 | "go/doc"
10 |
11 | "golang.org/x/tools/godoc/vfs"
12 | )
13 |
14 | const (
15 | __pkg__ = "__pkg__"
16 | __name__ = "__name__"
17 | __doc__ = "__doc__"
18 | )
19 |
20 | // Translater interface.
21 | type Translater interface {
22 | Static(lang string) vfs.FileSystem
23 | Document(lang string) vfs.FileSystem
24 | Package(lang, importPath string, pkg ...*doc.Package) *doc.Package
25 | Blog(lang string) vfs.FileSystem
26 | }
27 |
28 | var (
29 | staticFSTable = make(map[string]vfs.FileSystem) // map[lang]...
30 | docFSTable = make(map[string]vfs.FileSystem) // map[lang]...
31 | blogFSTable = make(map[string]vfs.FileSystem) // map[lang]...
32 | pkgDocTable = make(map[string]*doc.Package) // map[mapKey(...)]...
33 | pkgDocIndexTable = make(map[string]string) // map[mapKey(...)]...
34 | trList = make([]Translater, 0)
35 | )
36 |
37 | func mapKey(lang, importPath, id string) string {
38 | return fmt.Sprintf("%s.%s@%s", importPath, id, lang)
39 | }
40 |
41 | func methodId(typeName, methodName string) string {
42 | return typeName + "." + methodName
43 | }
44 |
45 | // RegisterStaticFS Register StaticFS.
46 | func RegisterStaticFS(lang string, staticFiles vfs.FileSystem) {
47 | staticFSTable[lang] = staticFiles
48 | }
49 |
50 | // RegisterDocumentFS Register DocumentFS.
51 | func RegisterDocumentFS(lang string, docFiles vfs.FileSystem) {
52 | docFSTable[lang] = docFiles
53 | }
54 |
55 | // RegisterBlogFS Register BlogFS.
56 | func RegisterBlogFS(lang string, blogFiles vfs.FileSystem) {
57 | blogFSTable[lang] = blogFiles
58 | }
59 |
60 | // RegisterPackage Register Package.
61 | func RegisterPackage(lang string, pkg *doc.Package) {
62 | pkgDocTable[mapKey(lang, pkg.ImportPath, __pkg__)] = pkg
63 | initDocTable(lang, pkg)
64 | }
65 |
66 | // RegisterTranslater Register Translater.
67 | func RegisterTranslater(tr Translater) {
68 | trList = append(trList, tr)
69 | }
70 |
71 | // RootFS return root filesystem.
72 | func RootFS() vfs.FileSystem {
73 | return defaultRootFS
74 | }
75 |
76 | // StaticFS return Static filesystem.
77 | func StaticFS(lang string) vfs.FileSystem {
78 | if lang == "" {
79 | return defaultStaticFS
80 | }
81 | if fs, _ := staticFSTable[lang]; fs != nil {
82 | return fs
83 | }
84 | for _, tr := range trList {
85 | if fs := tr.Static(lang); fs != nil {
86 | return fs
87 | }
88 | }
89 | if fs := defaultTranslater.Static(lang); fs != nil {
90 | return fs
91 | }
92 | return defaultStaticFS
93 | }
94 |
95 | // DocumentFS return Document filesystem.
96 | func DocumentFS(lang string) vfs.FileSystem {
97 | if lang == "" {
98 | return defaultDocFS
99 | }
100 | if fs, _ := docFSTable[lang]; fs != nil {
101 | return fs
102 | }
103 | for _, tr := range trList {
104 | if fs := tr.Document(lang); fs != nil {
105 | return fs
106 | }
107 | }
108 | if fs := defaultTranslater.Document(lang); fs != nil {
109 | return fs
110 | }
111 | return defaultDocFS
112 | }
113 |
114 | // Package translate Package doc.
115 | func Package(lang, importPath string, pkg ...*doc.Package) *doc.Package {
116 | if lang == "" {
117 | if len(pkg) > 0 {
118 | return pkg[0]
119 | } else {
120 | return nil
121 | }
122 | }
123 | if len(pkg) > 0 && pkg[0] != nil {
124 | if p := trPackage(lang, pkg[0].ImportPath, pkg[0]); p != nil {
125 | return p
126 | }
127 | } else {
128 | if p, _ := pkgDocTable[mapKey(lang, importPath, __pkg__)]; p != nil {
129 | return p
130 | }
131 | }
132 | for _, tr := range trList {
133 | if p := tr.Package(lang, importPath, pkg...); p != nil {
134 | return p
135 | }
136 | }
137 | if fs := defaultTranslater.Package(lang, importPath, pkg...); fs != nil {
138 | return fs
139 | }
140 | if len(pkg) > 0 {
141 | return pkg[0]
142 | }
143 | return nil
144 | }
145 |
146 | // BlogFS return Blog filesystem.
147 | func BlogFS(lang string) vfs.FileSystem {
148 | if lang == "" {
149 | return defaultBlogFS
150 | }
151 | if fs, _ := blogFSTable[lang]; fs != nil {
152 | return fs
153 | }
154 | for _, tr := range trList {
155 | if fs := tr.Blog(lang); fs != nil {
156 | return fs
157 | }
158 | }
159 | if fs := defaultTranslater.Blog(lang); fs != nil {
160 | return fs
161 | }
162 | return defaultBlogFS
163 | }
164 |
165 | func initDocTable(lang string, pkg *doc.Package) {
166 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, __name__)] = pkg.Name
167 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, __doc__)] = pkg.Doc
168 |
169 | for _, v := range pkg.Consts {
170 | for _, id := range v.Names {
171 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, id)] = v.Doc
172 | }
173 | }
174 | for _, v := range pkg.Types {
175 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, v.Name)] = v.Doc
176 |
177 | for _, x := range v.Consts {
178 | for _, id := range x.Names {
179 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, id)] = x.Doc
180 | }
181 | }
182 | for _, x := range v.Vars {
183 | for _, id := range x.Names {
184 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, id)] = x.Doc
185 | }
186 | }
187 | for _, x := range v.Funcs {
188 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, x.Name)] = x.Doc
189 | }
190 | for _, x := range v.Methods {
191 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, methodId(v.Name, x.Name))] = x.Doc
192 | }
193 | }
194 | for _, v := range pkg.Vars {
195 | for _, id := range v.Names {
196 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, id)] = v.Doc
197 | }
198 | }
199 | for _, v := range pkg.Funcs {
200 | pkgDocIndexTable[mapKey(lang, pkg.ImportPath, v.Name)] = v.Doc
201 | }
202 | }
203 |
204 | func trPackage(lang, importPath string, pkg *doc.Package) *doc.Package {
205 | key := mapKey(lang, pkg.ImportPath, __pkg__)
206 | localPkg, _ := pkgDocTable[key]
207 | if localPkg == nil {
208 | return nil
209 | }
210 |
211 | pkg.Name = localPkg.Name
212 | pkg.Doc = localPkg.Doc
213 |
214 | for k, _ := range pkg.Notes {
215 | if notes, _ := localPkg.Notes[k]; notes != nil {
216 | pkg.Notes[k] = notes
217 | }
218 | }
219 |
220 | for i := 0; i < len(pkg.Consts); i++ {
221 | key := mapKey(lang, pkg.ImportPath, pkg.Consts[i].Names[0])
222 | if s, _ := pkgDocIndexTable[key]; s != "" {
223 | pkg.Consts[i].Doc = s
224 | }
225 | }
226 | for i := 0; i < len(pkg.Types); i++ {
227 | key := mapKey(lang, pkg.ImportPath, pkg.Types[i].Name)
228 | if s, _ := pkgDocIndexTable[key]; s != "" {
229 | pkg.Types[i].Doc = s
230 | }
231 |
232 | for j := 0; j < len(pkg.Types[i].Consts); j++ {
233 | key := mapKey(lang, pkg.ImportPath, pkg.Types[i].Consts[j].Names[0])
234 | if s, _ := pkgDocIndexTable[key]; s != "" {
235 | pkg.Types[i].Consts[j].Doc = s
236 | }
237 | }
238 | for j := 0; j < len(pkg.Types[i].Vars); j++ {
239 | key := mapKey(lang, pkg.ImportPath, pkg.Types[i].Vars[j].Names[0])
240 | if s, _ := pkgDocIndexTable[key]; s != "" {
241 | pkg.Types[i].Vars[j].Doc = s
242 | }
243 | }
244 | for j := 0; j < len(pkg.Types[i].Funcs); j++ {
245 | key := mapKey(lang, pkg.ImportPath, pkg.Types[i].Funcs[j].Name)
246 | if s, _ := pkgDocIndexTable[key]; s != "" {
247 | pkg.Types[i].Funcs[j].Doc = s
248 | }
249 | }
250 | for j := 0; j < len(pkg.Types[i].Methods); j++ {
251 | id := methodId(pkg.Types[i].Name, pkg.Types[i].Methods[j].Name)
252 | key := mapKey(lang, pkg.ImportPath, id)
253 | if s, _ := pkgDocIndexTable[key]; s != "" {
254 | pkg.Types[i].Methods[j].Doc = s
255 | }
256 | }
257 | }
258 | for i := 0; i < len(pkg.Vars); i++ {
259 | key := mapKey(lang, pkg.ImportPath, pkg.Vars[i].Names[0])
260 | if s, _ := pkgDocIndexTable[key]; s != "" {
261 | pkg.Vars[i].Doc = s
262 | }
263 | }
264 | for i := 0; i < len(pkg.Funcs); i++ {
265 | key := mapKey(lang, pkg.ImportPath, pkg.Funcs[i].Name)
266 | if s, _ := pkgDocIndexTable[key]; s != "" {
267 | pkg.Funcs[i].Doc = s
268 | }
269 | }
270 | return pkg
271 | }
272 |
--------------------------------------------------------------------------------