├── .gitignore ├── tour ├── static │ ├── partials │ │ ├── toc-button.html │ │ ├── lesson.html │ │ ├── list.html │ │ ├── toc.html │ │ └── editor.html │ ├── img │ │ ├── burger.png │ │ ├── favicon.ico │ │ └── gopher.png │ ├── lib │ │ └── codemirror │ │ │ ├── README.md │ │ │ ├── LICENSE │ │ │ ├── AUTHORS │ │ │ ├── lib │ │ │ └── codemirror.css │ │ │ └── mode │ │ │ └── go │ │ │ └── go.js │ └── js │ │ ├── app.js │ │ ├── values.js │ │ ├── controllers.js │ │ └── directives.js ├── template │ ├── action.tmpl │ └── index.tmpl ├── fmt.go ├── appengine.go ├── local.go └── tour.go ├── talks ├── static │ ├── favicon.ico │ ├── print.css │ ├── dir.js │ ├── article.css │ └── dir.css ├── appengine.go ├── play.go ├── doc.go ├── templates │ ├── action.tmpl │ ├── article.tmpl │ ├── slides.tmpl │ └── dir.tmpl ├── local.go └── dir.go ├── appengine ├── app.yaml ├── init.go ├── README.md └── main.go ├── local ├── doc.go ├── doc_zh_CN.go ├── init.go ├── local_translater.go └── local.go ├── run.bat ├── run_zh_CN.bat ├── docgen └── main_test.go ├── run_zh_CN_posix.bat ├── dl.go ├── appconfig.go ├── main_posix.go ├── doc.go ├── godoc ├── page.go ├── server_test.go ├── tab.go ├── spot.go ├── parser.go ├── godoc_test.go ├── snippet.go ├── meta.go ├── spec.go ├── search.go ├── pres.go ├── corpus.go ├── template.go ├── cmdline.go ├── linkify.go └── cmdline_test.go ├── play.go ├── README.godoc-app ├── blog ├── local.go └── walk.go ├── README.md ├── remotesearch.go ├── blog.go ├── appinit.go ├── handlers.go ├── x.go ├── setup-godoc-app.bash ├── service_manage_windows.go └── main_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | appengine/goroot.zip 2 | golangdoc.exe 3 | golangdoc 4 | -------------------------------------------------------------------------------- /tour/static/partials/toc-button.html: -------------------------------------------------------------------------------- 1 | menu -------------------------------------------------------------------------------- /talks/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golang-china/golangdoc/HEAD/talks/static/favicon.ico -------------------------------------------------------------------------------- /tour/static/img/burger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golang-china/golangdoc/HEAD/tour/static/img/burger.png -------------------------------------------------------------------------------- /tour/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golang-china/golangdoc/HEAD/tour/static/img/favicon.ico -------------------------------------------------------------------------------- /tour/static/img/gopher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golang-china/golangdoc/HEAD/tour/static/img/gopher.png -------------------------------------------------------------------------------- /appengine/app.yaml: -------------------------------------------------------------------------------- 1 | application: golang-china 2 | version: 1 3 | runtime: go 4 | api_version: go1 5 | 6 | handlers: 7 | - url: /.* 8 | script: _go_app 9 | -------------------------------------------------------------------------------- /tour/static/partials/lesson.html: -------------------------------------------------------------------------------- 1 |
2 | {{title}} 3 |

{{description}}

4 |
5 | -------------------------------------------------------------------------------- /local/doc.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 support translate for godoc. 6 | package local 7 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 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 | setlocal 6 | 7 | cd %~dp0 8 | 9 | set GOPATH="" && golangdoc.exe -http=:6060 -play 10 | -------------------------------------------------------------------------------- /run_zh_CN.bat: -------------------------------------------------------------------------------- 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 | setlocal 6 | 7 | cd %~dp0 8 | 9 | set GOPATH="" && golangdoc.exe -http=:6060 -lang=zh_CN -play 10 | pause -------------------------------------------------------------------------------- /docgen/main_test.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 main 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestDocgen(t *testing.T) { 12 | // 13 | } 14 | -------------------------------------------------------------------------------- /run_zh_CN_posix.bat: -------------------------------------------------------------------------------- 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 | setlocal 6 | 7 | cd %~dp0 8 | 9 | set GOOS="linux" 10 | set GOPATH="" 11 | golangdoc.exe -http=:6060 -lang=zh_CN -play 12 | pause 13 | -------------------------------------------------------------------------------- /appengine/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 | // +build appengine 6 | 7 | package main 8 | 9 | import ( 10 | "log" 11 | 12 | _ "github.com/golang-china/golangdoc" 13 | ) 14 | 15 | func init() { 16 | log.Println("initializing age ...") 17 | } 18 | -------------------------------------------------------------------------------- /dl.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 main 6 | 7 | import "net/http" 8 | 9 | // Register a redirect handler for /dl/ to the golang.org download page. 10 | // This file will not be included when deploying godoc to golang.org. 11 | 12 | func init() { 13 | http.Handle("/dl/", http.RedirectHandler("http://golang.org/dl/", http.StatusFound)) 14 | } 15 | -------------------------------------------------------------------------------- /appengine/README.md: -------------------------------------------------------------------------------- 1 | # GAE 版本 golangdoc 2 | 3 | 构建步骤: 4 | 5 | 1. 安装 [go_appengine](https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go), 并添加到 `$PATH` 环境变量 6 | 2. 进入当前目录, 运行命令: `go run main.go`, 生成 `goroot.zip` 文件 7 | 3. 本地启动AGE程序: `goapp serve .`, 打开网页 http://127.0.0.1:8080 8 | 4. OK 9 | 10 | 部署到GAE: 11 | 12 | 1. 打开 `app.yaml` 文件, 设置 `application:` 字段为对应的 `app-id` (改成自己的 `APPID`) 13 | 2. 打开VPN, 运行 `goapp deploy` 上传应用 14 | 3. 打开网页 http://app-id.appspot.com/ 15 | 4. OK 16 | 17 | 补充: 18 | 目前GAE版本没有启动搜索功能, 可以自己手工添加索引文件. 19 | -------------------------------------------------------------------------------- /appconfig.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 | const ( 10 | // .zip filename 11 | flagZipFilename = "goroot.zip" 12 | 13 | // goroot directory in .zip file 14 | flagZipGoroot = "goroot" 15 | 16 | // glob pattern describing search index files 17 | // (if empty, the index is built at run-time) 18 | flagIndexFilenames = "" 19 | ) 20 | 21 | var flagLang = func() *string { 22 | v := "zh_CN" 23 | return &v 24 | }() 25 | -------------------------------------------------------------------------------- /main_posix.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 !windows 6 | // +build !appengine 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | ) 13 | 14 | func main() { 15 | flag.Usage = usage 16 | flag.Parse() 17 | 18 | playEnabled = *flagShowPlayground 19 | 20 | // Check usage: either server and no args, command line and args, or index creation mode 21 | if (*flagHttpAddr != "" || *flagUrlFlag != "") != (flag.NArg() == 0) && !*flagWriteIndex { 22 | usage() 23 | } 24 | 25 | runGodoc() 26 | } 27 | -------------------------------------------------------------------------------- /tour/static/partials/list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 7 | 8 |
9 |

{{m.title}}

10 |
11 | 12 |
13 | {{m.lesson[l].Title}} 14 |

{{m.lesson[l].Description}}

15 |
16 |
17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /tour/static/lib/codemirror/README.md: -------------------------------------------------------------------------------- 1 | # CodeMirror 2 | [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) 3 | [![NPM version](https://badge.fury.io/js/codemirror.png)](http://badge.fury.io/js/codemirror) 4 | 5 | CodeMirror is a JavaScript component that provides a code editor in 6 | the browser. When a mode is available for the language you are coding 7 | in, it will color your code, and optionally help with indentation. 8 | 9 | The project page is http://codemirror.net 10 | The manual is at http://codemirror.net/doc/manual.html 11 | The contributing guidelines are in [CONTRIBUTING.md](https://github.com/marijnh/CodeMirror/blob/master/CONTRIBUTING.md) 12 | -------------------------------------------------------------------------------- /talks/appengine.go: -------------------------------------------------------------------------------- 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 | // +build appengine 6 | 7 | package main 8 | 9 | import ( 10 | "mime" 11 | 12 | "golang.org/x/tools/present" 13 | 14 | _ "golang.org/x/tools/playground" 15 | ) 16 | 17 | var basePath = "./present/" 18 | 19 | func init() { 20 | initTemplates(basePath) 21 | playScript(basePath, "HTTPTransport") 22 | present.PlayEnabled = true 23 | 24 | // App Engine has no /etc/mime.types 25 | mime.AddExtensionType(".svg", "image/svg+xml") 26 | } 27 | 28 | func playable(c present.Code) bool { 29 | return present.PlayEnabled && c.Play && c.Ext == ".go" 30 | } 31 | -------------------------------------------------------------------------------- /tour/static/partials/toc.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 | {{m.title}} 5 |
      6 |
    • 7 | {{m.lesson[l].Title}} 8 | 13 |
    • 14 |
    15 |
  • 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /tour/static/js/app.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 | angular.module('tour', ['ui', 'tour.services', 'tour.controllers', 'tour.directives', 'tour.values', 'ng']). 8 | 9 | config(['$routeProvider', '$locationProvider', 10 | function($routeProvider, $locationProvider) { 11 | $routeProvider. 12 | when('/', { 13 | redirectTo: '/welcome/1' 14 | }). 15 | when('/list', { 16 | templateUrl: '/static/partials/list.html', 17 | }). 18 | when('/:lessonId/:pageNumber', { 19 | templateUrl: '/static/partials/editor.html', 20 | controller: 'EditorCtrl' 21 | }). 22 | when('/:lessonId', { 23 | redirectTo: '/:lessonId/1' 24 | }). 25 | otherwise({ 26 | redirectTo: '/' 27 | }); 28 | 29 | $locationProvider.html5Mode(true); 30 | } 31 | ]); 32 | -------------------------------------------------------------------------------- /talks/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 | -------------------------------------------------------------------------------- /doc.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 | /* 6 | Godoc for Golang, support translate. 7 | 8 | Download Chinese Translate: 9 | 10 | git clone https://github.com/golang-china/golangdoc.translations.git $(GOROOT)/translations 11 | 12 | Start Chinese Godoc Server: 13 | 14 | go get github.com/golang-china/golangdoc 15 | golangdoc -http=:6060 -lang=zh_CN 16 | 17 | See: 18 | 19 | http://github.com/golang-china/golangdoc.translations 20 | http://godoc.org/github.com/golang-china/golangdoc/docgen 21 | http://godoc.org/github.com/golang-china/golangdoc/local 22 | 23 | 24 | Run as windows service: 25 | 26 | # install as windows service 27 | golangdoc -service-install -http=:6060 28 | 29 | # start/stop service 30 | golangdoc -service-start 31 | golangdoc -service-stop 32 | 33 | # remove service 34 | golangdoc -service-remove 35 | 36 | 37 | # BUGS 38 | 39 | Report bugs to chaishushan@gmail.com. 40 | 41 | Thanks! 42 | */ 43 | package main 44 | -------------------------------------------------------------------------------- /talks/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 | -------------------------------------------------------------------------------- /tour/template/action.tmpl: -------------------------------------------------------------------------------- 1 | {{/*{ 2 | This is the action template. 3 | It determines how the formatting actions are rendered. 4 | */}} 5 | 6 | {{define "section"}} 7 |

{{.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 | {{if .Play}} 32 | {{/* playable code is not displayed in the slides */}} 33 | {{else}} 34 |
{{.Text}}
35 | {{end}} 36 | {{end}} 37 | 38 | {{define "image"}} 39 | 40 | {{end}} 41 | 42 | {{define "link"}} 43 | 44 | {{end}} 45 | 46 | {{define "html"}}{{.HTML}}{{end}} 47 | 48 | {{define "newline"}} 49 | {{/* No automatic line break. Paragraphs are free-form. */}} 50 | {{end}} 51 | -------------------------------------------------------------------------------- /godoc/page.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 | "net/http" 9 | "runtime" 10 | ) 11 | 12 | // Page describes the contents of the top-level godoc webpage. 13 | type Page struct { 14 | Title string 15 | Tabtitle string 16 | Subtitle string 17 | Query string 18 | Body []byte 19 | 20 | // filled in by servePage 21 | SearchBox bool 22 | Playground bool 23 | Version string 24 | } 25 | 26 | func (p *Presentation) ServePage(w http.ResponseWriter, page Page) { 27 | if page.Tabtitle == "" { 28 | page.Tabtitle = page.Title 29 | } 30 | page.SearchBox = p.Corpus.IndexEnabled 31 | page.Playground = p.ShowPlayground 32 | page.Version = runtime.Version() 33 | applyTemplateToResponseWriter(w, p.GodocHTML, page) 34 | } 35 | 36 | func (p *Presentation) ServeError(w http.ResponseWriter, r *http.Request, relpath string, err error) { 37 | w.WriteHeader(http.StatusNotFound) 38 | p.ServePage(w, Page{ 39 | Title: "File " + relpath, 40 | Subtitle: relpath, 41 | Body: applyTemplate(p.ErrorHTML, "errorHTML", err), // err may contain an absolute path! 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /tour/fmt.go: -------------------------------------------------------------------------------- 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 | package main 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "go/ast" 11 | "go/parser" 12 | "go/printer" 13 | "go/token" 14 | "net/http" 15 | ) 16 | 17 | func init() { 18 | http.HandleFunc("/fmt", fmtHandler) 19 | } 20 | 21 | type fmtResponse struct { 22 | Body string 23 | Error string 24 | } 25 | 26 | func fmtHandler(w http.ResponseWriter, r *http.Request) { 27 | resp := new(fmtResponse) 28 | body, err := gofmt(r.FormValue("body")) 29 | if err != nil { 30 | resp.Error = err.Error() 31 | } else { 32 | resp.Body = body 33 | } 34 | json.NewEncoder(w).Encode(resp) 35 | } 36 | 37 | func gofmt(body string) (string, error) { 38 | fset := token.NewFileSet() 39 | f, err := parser.ParseFile(fset, "prog.go", body, parser.ParseComments) 40 | if err != nil { 41 | return "", err 42 | } 43 | ast.SortImports(fset, f) 44 | var buf bytes.Buffer 45 | config := &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} 46 | err = config.Fprint(&buf, fset, f) 47 | if err != nil { 48 | return "", err 49 | } 50 | return buf.String(), nil 51 | } 52 | -------------------------------------------------------------------------------- /play.go: -------------------------------------------------------------------------------- 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 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "go/format" 11 | "net/http" 12 | 13 | // This package registers "/compile" and "/share" handlers 14 | // that redirect to the golang.org playground. 15 | _ "golang.org/x/tools/playground" 16 | ) 17 | 18 | func init() { 19 | http.HandleFunc("/fmt", fmtHandler) 20 | } 21 | 22 | type fmtResponse struct { 23 | Body string 24 | Error string 25 | } 26 | 27 | // fmtHandler takes a Go program in its "body" form value, formats it with 28 | // standard gofmt formatting, and writes a fmtResponse as a JSON object. 29 | func fmtHandler(w http.ResponseWriter, r *http.Request) { 30 | resp := new(fmtResponse) 31 | body, err := format.Source([]byte(r.FormValue("body"))) 32 | if err != nil { 33 | resp.Error = err.Error() 34 | } else { 35 | resp.Body = string(body) 36 | } 37 | json.NewEncoder(w).Encode(resp) 38 | } 39 | 40 | // disabledHandler serves a 501 "Not Implemented" response. 41 | func disabledHandler(w http.ResponseWriter, r *http.Request) { 42 | w.WriteHeader(http.StatusNotImplemented) 43 | fmt.Fprint(w, "This functionality is not available via local godoc.") 44 | } 45 | -------------------------------------------------------------------------------- /talks/play.go: -------------------------------------------------------------------------------- 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 | package main 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io/ioutil" 11 | "net/http" 12 | "path/filepath" 13 | "time" 14 | 15 | "golang.org/x/tools/godoc/static" 16 | ) 17 | 18 | var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"} 19 | 20 | // playScript registers an HTTP handler at /play.js that serves all the 21 | // scripts specified by the variable above, and appends a line that 22 | // initializes the playground with the specified transport. 23 | func playScript(root, transport string) { 24 | modTime := time.Now() 25 | var buf bytes.Buffer 26 | for _, p := range scripts { 27 | if s, ok := static.Files[p]; ok { 28 | buf.WriteString(s) 29 | continue 30 | } 31 | b, err := ioutil.ReadFile(filepath.Join(root, "static", p)) 32 | if err != nil { 33 | panic(err) 34 | } 35 | buf.Write(b) 36 | } 37 | fmt.Fprintf(&buf, "\ninitPlayground(new %v());\n", transport) 38 | b := buf.Bytes() 39 | http.HandleFunc("/play.js", func(w http.ResponseWriter, r *http.Request) { 40 | w.Header().Set("Content-type", "application/javascript") 41 | http.ServeContent(w, r, "", modTime, bytes.NewReader(b)) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /tour/static/lib/codemirror/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 by Marijn Haverbeke and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | Please note that some subdirectories of the CodeMirror distribution 22 | include their own LICENSE files, and are released under different 23 | licences. 24 | -------------------------------------------------------------------------------- /README.godoc-app: -------------------------------------------------------------------------------- 1 | godoc on appengine 2 | ------------------ 3 | 4 | Prerequisites 5 | ------------- 6 | 7 | * Go appengine SDK 8 | https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go 9 | 10 | * Go sources at tip under $GOROOT 11 | 12 | * Godoc sources at tip inside $GOPATH 13 | (go get -d golang.org/x/tools/cmd/godoc) 14 | 15 | 16 | Directory structure 17 | ------------------- 18 | 19 | * Let $APPDIR be the directory containing the app engine files. 20 | (e.g., $APPDIR=$HOME/godoc-app) 21 | 22 | * $APPDIR contains the following entries (this may change depending on 23 | app-engine release and version of godoc): 24 | 25 | app.yaml 26 | golang.org/x/tools/cmd/godoc 27 | godoc.zip 28 | index.split.* 29 | 30 | * The app.yaml file is set up per app engine documentation. 31 | For instance: 32 | 33 | application: godoc-app 34 | version: 1 35 | runtime: go 36 | api_version: go1 37 | 38 | handlers: 39 | - url: /.* 40 | script: _go_app 41 | 42 | 43 | Configuring and running godoc 44 | ----------------------------- 45 | 46 | To configure godoc, run 47 | 48 | bash setup-godoc-app.bash 49 | 50 | to prepare an $APPDIR as described above. See the script for details on usage. 51 | 52 | To run godoc locally, using the App Engine development server, run 53 | 54 | /dev_appserver.py $APPDIR 55 | 56 | godoc should come up at http://localhost:8080 . 57 | -------------------------------------------------------------------------------- /blog/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 | // +build ingore 6 | 7 | package main 8 | 9 | import ( 10 | "log" 11 | "net/http" 12 | "path" 13 | "runtime" 14 | 15 | "golang.org/x/tools/godoc/static" 16 | "golang.org/x/tools/godoc/vfs" 17 | "golang.org/x/tools/godoc/vfs/httpfs" 18 | "golang.org/x/tools/godoc/vfs/mapfs" 19 | 20 | "github.com/chai2010/golangdoc/blog" 21 | ) 22 | 23 | const ( 24 | hostname = "blog.golang.org" 25 | ) 26 | 27 | var cfg = blog.Config{ 28 | Hostname: hostname, 29 | RootFS: vfs.OS(path.Join(runtime.GOROOT(), `/translations/blog/zh_CN`)), 30 | ContentPath: "content", 31 | TemplatePath: "template", 32 | BaseURL: "//" + hostname, 33 | GodocURL: "//golang.org", 34 | HomeArticles: 5, // articles to display on the home page 35 | FeedArticles: 10, // articles to include in Atom and JSON feeds 36 | PlayEnabled: true, 37 | FeedTitle: "The Go Programming Language Blog", 38 | } 39 | 40 | func main() { 41 | server, err := blog.NewServer(cfg) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | http.Handle("/", server) 47 | http.Handle("/lib/godoc/", http.StripPrefix("/lib/godoc/", 48 | http.FileServer(httpfs.New(mapfs.New(static.Files))), 49 | )) 50 | 51 | log.Fatal(http.ListenAndServe(":3999", nil)) 52 | } 53 | -------------------------------------------------------------------------------- /talks/doc.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 | /* 6 | Present displays slide presentations and articles. It runs a web server that 7 | presents slide and article files from the current directory. 8 | 9 | It may be run as a stand-alone command or an App Engine app. 10 | Instructions for deployment to App Engine are in the README of the 11 | golang.org/x/tools repository. 12 | 13 | Usage of present: 14 | -base="": base path for slide template and static resources 15 | -http="127.0.0.1:3999": HTTP service address (e.g., '127.0.0.1:3999') 16 | -nacl=false: use Native Client environment playground (prevents non-Go code execution) 17 | -orighost="": host component of web origin URL (e.g., 'localhost') 18 | -play=true: enable playground (permit execution of arbitrary user code) 19 | 20 | The setup of the Go version of NaCl is documented at: 21 | https://golang.org/wiki/NativeClient 22 | 23 | Input files are named foo.extension, where "extension" defines the format of 24 | the generated output. The supported formats are: 25 | .slide // HTML5 slide presentation 26 | .article // article format, such as a blog post 27 | 28 | The present file format is documented by the present package: 29 | http://godoc.org/golang.org/x/tools/present 30 | */ 31 | package main // import "golang.org/x/tools/cmd/present" 32 | -------------------------------------------------------------------------------- /talks/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 | 48 | {{define "caption"}}
{{style .Text}}
{{end}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godoc 改进版本, 支持翻译文档的动态加载 2 | 3 | GAE预览 https://golang-china.appspot.com/ 4 | 5 | # 安装 golangdoc 6 | 7 | 安装 golangdoc : 8 | 9 | go get github.com/golang-china/golangdoc 10 | 11 | 下载翻译文件 到 `$(GOROOT)/translations` 目录: 12 | 13 | https://github.com/golang-china/golangdoc.translations 14 | 15 | 启用简体中文版文档服务: 16 | 17 | golangdoc -http=:6060 -lang=zh_CN 18 | 19 | 动态切换包文档: 20 | 21 | - https://golang-china.appspot.com/pkg/builtin/ 22 | - https://golang-china.appspot.com/pkg/builtin/?lang=en 23 | - https://golang-china.appspot.com/pkg/builtin/?lang=raw 24 | - https://golang-china.appspot.com/pkg/builtin/?lang=zh_CN 25 | 26 | 其中 URL 的 `lang` 参数为 `en`/`raw` 或 无对应语言时 表示使用原始的文档, 27 | 缺少或为空时用 golangdoc 服务器启动时命令行指定的 `lang` 参数. 28 | 29 | ## 部署到 AGE 环境 30 | 31 | golangdoc 支持 GAE 环境. 具体请参考: [appengine/README.md](appengine/README.md) 32 | 33 | 34 | # 系统服务模式运行(Windows平台) 35 | 36 | # 安装 Windows 服务 37 | golangdoc -service-install -http=:6060 38 | 39 | # 启动/停止 Windows 服务 40 | golangdoc -service-start 41 | golangdoc -service-stop 42 | 43 | # 卸载 Windows 服务 44 | golangdoc -service-remove 45 | 46 | 47 | # 其他 48 | 49 | - GAE环境支持: https://github.com/golang-china/golangdoc/tree/master/appengine 50 | - 文档翻译项目: http://github.com/golang-china/golangdoc.translations 51 | - 文档提取工具: http://godoc.org/github.com/golang-china/golangdoc/docgen 52 | - 本地化支持包: http://godoc.org/github.com/golang-china/golangdoc/local 53 | -------------------------------------------------------------------------------- /local/doc_zh_CN.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 ingore 6 | 7 | // local 包用于提供 godoc 的翻译支持. 8 | package local 9 | 10 | // 翻译文件所在的默认目录. 11 | const ( 12 | Default = "translations" // $(RootFS)/translations 13 | ) 14 | 15 | // DocumentFS 函数返回 doc 目录对应的文件系统. 16 | func DocumentFS(lang string) vfs.FileSystem 17 | 18 | // DocumentFS 函数初始化本地翻译资源的环境. 19 | func Init(goRoot, goZipFile, goTemplateDir string) 20 | 21 | // Package 函数返回翻译后的包结构体. 22 | func Package(lang, importPath string, pkg ...*doc.Package) *doc.Package 23 | 24 | // RegisterDocumentFS 函数注册 doc 目录的翻译版本. 25 | func RegisterDocumentFS(lang string, docFiles vfs.FileSystem) 26 | 27 | // RegisterPackage 函数注册包对应的翻译信息. 28 | func RegisterPackage(lang string, pkg *doc.Package) 29 | 30 | // RegisterStaticFS 函数注册静态文件的翻译版本. 31 | func RegisterStaticFS(lang string, staticFiles vfs.FileSystem) 32 | 33 | // RegisterTranslater 函数注册翻译器. 34 | func RegisterTranslater(tr Translater) 35 | 36 | // RootFS 函数返回根目录文件系统. 37 | func RootFS() vfs.FileSystem 38 | 39 | // StaticFS 函数返回静态文件的文件系统. 40 | func StaticFS(lang string) vfs.FileSystem 41 | 42 | // Translater 为翻译器接口类型. 43 | type Translater interface { 44 | Static(lang string) vfs.FileSystem 45 | Document(lang string) vfs.FileSystem 46 | Package(lang, importPath string, pkg ...*doc.Package) *doc.Package 47 | } 48 | -------------------------------------------------------------------------------- /talks/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 | {{if .Authors}} 33 |

Authors

34 | {{range .Authors}} 35 |
36 | {{range .Elem}}{{elem $.Template .}}{{end}} 37 |
38 | {{end}} 39 | {{end}} 40 |
41 |
42 | 43 | 44 | 45 | {{end}} 46 | 47 | {{define "TOC"}} 48 |
    49 | {{range .}} 50 |
  • {{.Title}}
  • 51 | {{with .Sections}}{{template "TOC" .}}{{end}} 52 | {{end}} 53 |
54 | {{end}} 55 | 56 | {{define "newline"}} 57 | {{/* No automatic line break. Paragraphs are free-form. */}} 58 | {{end}} 59 | -------------------------------------------------------------------------------- /talks/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 | -------------------------------------------------------------------------------- /godoc/server_test.go: -------------------------------------------------------------------------------- 1 | package godoc 2 | 3 | import ( 4 | "errors" 5 | "expvar" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | "text/template" 10 | ) 11 | 12 | var ( 13 | // NOTE: with no plain-text in the template, template.Execute will not 14 | // return an error when http.ResponseWriter.Write does return an error. 15 | tmpl = template.Must(template.New("test").Parse("{{.Foo}}")) 16 | ) 17 | 18 | type withFoo struct { 19 | Foo int 20 | } 21 | 22 | type withoutFoo struct { 23 | } 24 | 25 | type errResponseWriter struct { 26 | } 27 | 28 | func (*errResponseWriter) Header() http.Header { 29 | return http.Header{} 30 | } 31 | 32 | func (*errResponseWriter) WriteHeader(int) { 33 | } 34 | 35 | func (*errResponseWriter) Write(p []byte) (int, error) { 36 | return 0, errors.New("error") 37 | } 38 | 39 | func TestApplyTemplateToResponseWriter(t *testing.T) { 40 | for _, tc := range []struct { 41 | desc string 42 | rw http.ResponseWriter 43 | data interface{} 44 | expVars int 45 | }{ 46 | { 47 | desc: "no error", 48 | rw: &httptest.ResponseRecorder{}, 49 | data: &withFoo{}, 50 | expVars: 0, 51 | }, 52 | { 53 | desc: "template error", 54 | rw: &httptest.ResponseRecorder{}, 55 | data: &withoutFoo{}, 56 | expVars: 0, 57 | }, 58 | { 59 | desc: "ResponseWriter error", 60 | rw: &errResponseWriter{}, 61 | data: &withFoo{}, 62 | expVars: 1, 63 | }, 64 | } { 65 | httpErrors.Init() 66 | applyTemplateToResponseWriter(tc.rw, tmpl, tc.data) 67 | gotVars := 0 68 | httpErrors.Do(func(expvar.KeyValue) { 69 | gotVars++ 70 | }) 71 | if gotVars != tc.expVars { 72 | t.Errorf("applyTemplateToResponseWriter(%q): got %d vars, want %d", tc.desc, gotVars, tc.expVars) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /godoc/tab.go: -------------------------------------------------------------------------------- 1 | // TODO(bradfitz,adg): move to util 2 | 3 | package godoc 4 | 5 | import "io" 6 | 7 | var spaces = []byte(" ") // 32 spaces seems like a good number 8 | 9 | const ( 10 | indenting = iota 11 | collecting 12 | ) 13 | 14 | // A tconv is an io.Writer filter for converting leading tabs into spaces. 15 | type tconv struct { 16 | output io.Writer 17 | state int // indenting or collecting 18 | indent int // valid if state == indenting 19 | p *Presentation 20 | } 21 | 22 | func (p *tconv) writeIndent() (err error) { 23 | i := p.indent 24 | for i >= len(spaces) { 25 | i -= len(spaces) 26 | if _, err = p.output.Write(spaces); err != nil { 27 | return 28 | } 29 | } 30 | // i < len(spaces) 31 | if i > 0 { 32 | _, err = p.output.Write(spaces[0:i]) 33 | } 34 | return 35 | } 36 | 37 | func (p *tconv) Write(data []byte) (n int, err error) { 38 | if len(data) == 0 { 39 | return 40 | } 41 | pos := 0 // valid if p.state == collecting 42 | var b byte 43 | for n, b = range data { 44 | switch p.state { 45 | case indenting: 46 | switch b { 47 | case '\t': 48 | p.indent += p.p.TabWidth 49 | case '\n': 50 | p.indent = 0 51 | if _, err = p.output.Write(data[n : n+1]); err != nil { 52 | return 53 | } 54 | case ' ': 55 | p.indent++ 56 | default: 57 | p.state = collecting 58 | pos = n 59 | if err = p.writeIndent(); err != nil { 60 | return 61 | } 62 | } 63 | case collecting: 64 | if b == '\n' { 65 | p.state = indenting 66 | p.indent = 0 67 | if _, err = p.output.Write(data[pos : n+1]); err != nil { 68 | return 69 | } 70 | } 71 | } 72 | } 73 | n = len(data) 74 | if pos < n && p.state == collecting { 75 | _, err = p.output.Write(data[pos:]) 76 | } 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /tour/template/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | A Tour of Go 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 |
25 | 26 | 27 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /remotesearch.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 | // +build !appengine 6 | 7 | package main 8 | 9 | import ( 10 | "errors" 11 | "flag" 12 | "io" 13 | "log" 14 | "net/http" 15 | "net/url" 16 | "os" 17 | ) 18 | 19 | func handleRemoteSearch() { 20 | // Command-line queries. 21 | for i := 0; i < flag.NArg(); i++ { 22 | res, err := remoteSearch(flag.Arg(i)) 23 | if err != nil { 24 | log.Fatalf("remoteSearch: %s", err) 25 | } 26 | io.Copy(os.Stdout, res.Body) 27 | } 28 | return 29 | } 30 | 31 | // remoteSearchURL returns the search URL for a given query as needed by 32 | // remoteSearch. If html is set, an html result is requested; otherwise 33 | // the result is in textual form. 34 | // Adjust this function as necessary if modeNames or FormValue parameters 35 | // change. 36 | func remoteSearchURL(query string, html bool) string { 37 | s := "/search?m=text&q=" 38 | if html { 39 | s = "/search?q=" 40 | } 41 | return s + url.QueryEscape(query) 42 | } 43 | 44 | func remoteSearch(query string) (res *http.Response, err error) { 45 | // list of addresses to try 46 | var addrs []string 47 | if *flagServerAddr != "" { 48 | // explicit server address - only try this one 49 | addrs = []string{*flagServerAddr} 50 | } else { 51 | addrs = []string{ 52 | defaultAddr, 53 | "golang.org", 54 | } 55 | } 56 | 57 | // remote search 58 | search := remoteSearchURL(query, *flagHtml) 59 | for _, addr := range addrs { 60 | url := "http://" + addr + search 61 | res, err = http.Get(url) 62 | if err == nil && res.StatusCode == http.StatusOK { 63 | break 64 | } 65 | } 66 | 67 | if err == nil && res.StatusCode != http.StatusOK { 68 | err = errors.New(res.Status) 69 | } 70 | 71 | return 72 | } 73 | -------------------------------------------------------------------------------- /blog.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 | "fmt" 9 | "log" 10 | "net/http" 11 | "os" 12 | "strings" 13 | "sync" 14 | 15 | "golang.org/x/tools/godoc/redirect" 16 | 17 | "github.com/golang-china/golangdoc/blog" 18 | "github.com/golang-china/golangdoc/local" 19 | ) 20 | 21 | const ( 22 | blogRepo = "golang.org/x/blog" 23 | blogURL = "http://blog.golang.org/" 24 | blogPath = "/blog/" 25 | ) 26 | 27 | var ( 28 | blogServer http.Handler // set by blogInit 29 | blogInitOnce sync.Once 30 | playEnabled bool 31 | ) 32 | 33 | func init() { 34 | // Initialize blog only when first accessed. 35 | http.HandleFunc(blogPath, func(w http.ResponseWriter, r *http.Request) { 36 | blogInitOnce.Do(blogInit) 37 | blogServer.ServeHTTP(w, r) 38 | }) 39 | } 40 | 41 | func blogInit() { 42 | blogFS := local.BlogFS(*flagLang) 43 | 44 | // If content is not available fall back to redirect. 45 | if fi, err := blogFS.Lstat("/"); err != nil || !fi.IsDir() { 46 | fmt.Fprintf(os.Stderr, "Blog content not available locally. "+ 47 | "To install, run \n\tgo get %v\n", blogRepo) 48 | blogServer = http.HandlerFunc(blogRedirectHandler) 49 | return 50 | } 51 | 52 | s, err := blog.NewServer(blog.Config{ 53 | RootFS: blogFS, 54 | BaseURL: blogPath, 55 | BasePath: strings.TrimSuffix(blogPath, "/"), 56 | ContentPath: "content", 57 | TemplatePath: "template", 58 | HomeArticles: 5, 59 | PlayEnabled: playEnabled, 60 | }) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | blogServer = s 65 | } 66 | 67 | func blogRedirectHandler(w http.ResponseWriter, r *http.Request) { 68 | if r.URL.Path == blogPath { 69 | http.Redirect(w, r, blogURL, http.StatusFound) 70 | return 71 | } 72 | blogPrefixHandler.ServeHTTP(w, r) 73 | } 74 | 75 | var blogPrefixHandler = redirect.PrefixHandler(blogPath, blogURL) 76 | -------------------------------------------------------------------------------- /talks/templates/dir.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Talks - The Go Programming Language 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 22 | 23 |
24 | 25 |
26 | 27 |
28 | 29 |

Go talks

30 | 31 | {{with .Path}}

{{.}}

{{end}} 32 | 33 | {{with .Articles}} 34 |

Articles:

35 |
36 | {{range .}} 37 |
{{.Name}}: {{.Title}}
38 | {{end}} 39 |
40 | {{end}} 41 | 42 | {{with .Slides}} 43 |

Slide decks:

44 |
45 | {{range .}} 46 |
{{.Name}}: {{.Title}}
47 | {{end}} 48 |
49 | {{end}} 50 | 51 | {{with .Other}} 52 |

Files:

53 |
54 | {{range .}} 55 |
{{.Name}}
56 | {{end}} 57 |
58 | {{end}} 59 | 60 | {{with .Dirs}} 61 |

Sub-directories:

62 |
63 | {{range .}} 64 |
{{.Name}}
65 | {{end}} 66 |
67 | {{end}} 68 | 69 |
70 | 71 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /godoc/spot.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 | // ---------------------------------------------------------------------------- 8 | // SpotInfo 9 | 10 | // A SpotInfo value describes a particular identifier spot in a given file; 11 | // It encodes three values: the SpotKind (declaration or use), a line or 12 | // snippet index "lori", and whether it's a line or index. 13 | // 14 | // The following encoding is used: 15 | // 16 | // bits 32 4 1 0 17 | // value [lori|kind|isIndex] 18 | // 19 | type SpotInfo uint32 20 | 21 | // SpotKind describes whether an identifier is declared (and what kind of 22 | // declaration) or used. 23 | type SpotKind uint32 24 | 25 | const ( 26 | PackageClause SpotKind = iota 27 | ImportDecl 28 | ConstDecl 29 | TypeDecl 30 | VarDecl 31 | FuncDecl 32 | MethodDecl 33 | Use 34 | nKinds 35 | ) 36 | 37 | var ( 38 | // These must match the SpotKind values above. 39 | name = []string{ 40 | "Packages", 41 | "Imports", 42 | "Constants", 43 | "Types", 44 | "Variables", 45 | "Functions", 46 | "Methods", 47 | "Uses", 48 | "Unknown", 49 | } 50 | ) 51 | 52 | func (x SpotKind) Name() string { return name[x] } 53 | 54 | func init() { 55 | // sanity check: if nKinds is too large, the SpotInfo 56 | // accessor functions may need to be updated 57 | if nKinds > 8 { 58 | panic("internal error: nKinds > 8") 59 | } 60 | } 61 | 62 | // makeSpotInfo makes a SpotInfo. 63 | func makeSpotInfo(kind SpotKind, lori int, isIndex bool) SpotInfo { 64 | // encode lori: bits [4..32) 65 | x := SpotInfo(lori) << 4 66 | if int(x>>4) != lori { 67 | // lori value doesn't fit - since snippet indices are 68 | // most certainly always smaller then 1<<28, this can 69 | // only happen for line numbers; give it no line number (= 0) 70 | x = 0 71 | } 72 | // encode kind: bits [1..4) 73 | x |= SpotInfo(kind) << 1 74 | // encode isIndex: bit 0 75 | if isIndex { 76 | x |= 1 77 | } 78 | return x 79 | } 80 | 81 | func (x SpotInfo) Kind() SpotKind { return SpotKind(x >> 1 & 7) } 82 | func (x SpotInfo) Lori() int { return int(x >> 4) } 83 | func (x SpotInfo) IsIndex() bool { return x&1 != 0 } 84 | -------------------------------------------------------------------------------- /tour/appengine.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 | // +build appengine 6 | 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "bytes" 12 | "io" 13 | "net/http" 14 | "strings" 15 | 16 | "appengine" 17 | 18 | _ "golang.org/x/tools/playground" 19 | ) 20 | 21 | const runUrl = "http://golang.org/compile" 22 | 23 | func init() { 24 | http.HandleFunc("/lesson/", lessonHandler) 25 | http.HandleFunc("/", rootHandler) 26 | 27 | if err := initTour(".", "HTTPTransport"); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | func rootHandler(w http.ResponseWriter, r *http.Request) { 33 | c := appengine.NewContext(r) 34 | if err := renderUI(w); err != nil { 35 | c.Criticalf("UI render: %v", err) 36 | } 37 | } 38 | 39 | func lessonHandler(w http.ResponseWriter, r *http.Request) { 40 | c := appengine.NewContext(r) 41 | lesson := strings.TrimPrefix(r.URL.Path, "/lesson/") 42 | if err := writeLesson(lesson, w); err != nil { 43 | if err == lessonNotFound { 44 | http.NotFound(w, r) 45 | } else { 46 | c.Criticalf("tour render: %v", err) 47 | } 48 | } 49 | } 50 | 51 | // prepContent returns a Reader that produces the content from the given 52 | // Reader, but strips the prefix "#appengine: " from each line. It also drops 53 | // any non-blank like that follows a series of 1 or more lines with the prefix. 54 | func prepContent(in io.Reader) io.Reader { 55 | var prefix = []byte("#appengine: ") 56 | out, w := io.Pipe() 57 | go func() { 58 | r := bufio.NewReader(in) 59 | drop := false 60 | for { 61 | b, err := r.ReadBytes('\n') 62 | if err != nil && err != io.EOF { 63 | w.CloseWithError(err) 64 | return 65 | } 66 | if bytes.HasPrefix(b, prefix) { 67 | b = b[len(prefix):] 68 | drop = true 69 | } else if drop { 70 | if len(b) > 1 { 71 | b = nil 72 | } 73 | drop = false 74 | } 75 | if len(b) > 0 { 76 | w.Write(b) 77 | } 78 | if err == io.EOF { 79 | w.Close() 80 | return 81 | } 82 | } 83 | }() 84 | return out 85 | } 86 | 87 | // socketAddr returns the WebSocket handler address. 88 | // The App Engine version does not provide a WebSocket handler. 89 | func socketAddr() string { return "" } 90 | -------------------------------------------------------------------------------- /godoc/parser.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 contains support functions for parsing .go files 6 | // accessed via godoc's file system fs. 7 | 8 | package godoc 9 | 10 | import ( 11 | "bytes" 12 | "go/ast" 13 | "go/parser" 14 | "go/token" 15 | pathpkg "path" 16 | 17 | "golang.org/x/tools/godoc/vfs" 18 | ) 19 | 20 | var linePrefix = []byte("//line ") 21 | 22 | // This function replaces source lines starting with "//line " with a blank line. 23 | // It does this irrespective of whether the line is truly a line comment or not; 24 | // e.g., the line may be inside a string, or a /*-style comment; however that is 25 | // rather unlikely (proper testing would require a full Go scan which we want to 26 | // avoid for performance). 27 | func replaceLinePrefixCommentsWithBlankLine(src []byte) { 28 | for { 29 | i := bytes.Index(src, linePrefix) 30 | if i < 0 { 31 | break // we're done 32 | } 33 | // 0 <= i && i+len(linePrefix) <= len(src) 34 | if i == 0 || src[i-1] == '\n' { 35 | // at beginning of line: blank out line 36 | for i < len(src) && src[i] != '\n' { 37 | src[i] = ' ' 38 | i++ 39 | } 40 | } else { 41 | // not at beginning of line: skip over prefix 42 | i += len(linePrefix) 43 | } 44 | // i <= len(src) 45 | src = src[i:] 46 | } 47 | } 48 | 49 | func (c *Corpus) parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.File, error) { 50 | src, err := vfs.ReadFile(c.fs, filename) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | // Temporary ad-hoc fix for issue 5247. 56 | // TODO(gri) Remove this in favor of a better fix, eventually (see issue 7702). 57 | replaceLinePrefixCommentsWithBlankLine(src) 58 | 59 | return parser.ParseFile(fset, filename, src, mode) 60 | } 61 | 62 | func (c *Corpus) parseFiles(fset *token.FileSet, relpath string, abspath string, localnames []string) (map[string]*ast.File, error) { 63 | files := make(map[string]*ast.File) 64 | for _, f := range localnames { 65 | absname := pathpkg.Join(abspath, f) 66 | file, err := c.parseFile(fset, absname, parser.ParseComments) 67 | if err != nil { 68 | return nil, err 69 | } 70 | files[pathpkg.Join(relpath, f)] = file 71 | } 72 | 73 | return files, nil 74 | } 75 | -------------------------------------------------------------------------------- /appinit.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 | // +build appengine 6 | 7 | package main 8 | 9 | // This file replaces main.go when running godoc under app-engine. 10 | // See README.godoc-app for details. 11 | 12 | import ( 13 | "go/doc" 14 | "log" 15 | "regexp" 16 | 17 | "golang.org/x/tools/godoc/vfs" 18 | 19 | "github.com/golang-china/golangdoc/godoc" 20 | "github.com/golang-china/golangdoc/local" 21 | ) 22 | 23 | func init() { 24 | playEnabled = true 25 | 26 | log.Println("initializing godoc ...") 27 | log.Printf(".zip file = %s", flagZipFilename) 28 | log.Printf(".zip GOROOT = %s", flagZipGoroot) 29 | log.Printf("index files = %s", flagIndexFilenames) 30 | 31 | // Determine file system to use. 32 | local.Init(flagZipGoroot, local.Default, flagZipFilename, "", "") 33 | fs.Bind("/", local.RootFS(), "/", vfs.BindReplace) 34 | fs.Bind("/lib/godoc", local.StaticFS(*flagLang), "/", vfs.BindReplace) 35 | fs.Bind("/doc", local.DocumentFS(*flagLang), "/", vfs.BindReplace) 36 | 37 | corpus := godoc.NewCorpus(fs) 38 | corpus.Verbose = false 39 | corpus.MaxResults = 10000 // matches flag default in main.go 40 | corpus.IndexEnabled = true 41 | corpus.IndexFiles = flagIndexFilenames 42 | 43 | // translate hook 44 | corpus.SummarizePackage = func(importPath string, langs ...string) (summary string, showList, ok bool) { 45 | lang := *flagLang 46 | if len(langs) > 0 && langs[0] != "" { 47 | lang = langs[0] 48 | } 49 | if lang == "en" || lang == "raw" || lang == "EN" { 50 | lang = "" 51 | } 52 | if pkg := local.Package(lang, importPath); pkg != nil { 53 | summary = doc.Synopsis(pkg.Doc) 54 | } 55 | ok = (summary != "") 56 | return 57 | } 58 | corpus.TranslateDocPackage = func(pkg *doc.Package, langs ...string) *doc.Package { 59 | lang := *flagLang 60 | if len(langs) > 0 && langs[0] != "" { 61 | lang = langs[0] 62 | } 63 | if lang == "en" || lang == "raw" || lang == "EN" { 64 | lang = "" 65 | } 66 | return local.Package(lang, pkg.ImportPath, pkg) 67 | } 68 | 69 | if err := corpus.Init(); err != nil { 70 | log.Fatal(err) 71 | } 72 | if corpus.IndexEnabled && corpus.IndexFiles != "" { 73 | go corpus.RunIndexer() 74 | } 75 | 76 | pres = godoc.NewPresentation(corpus) 77 | pres.TabWidth = 8 78 | pres.ShowPlayground = true 79 | pres.ShowExamples = true 80 | pres.DeclLinks = true 81 | pres.NotesRx = regexp.MustCompile("BUG") 82 | 83 | readTemplates(pres, true) 84 | registerHandlers(pres) 85 | 86 | log.Println("godoc initialization complete") 87 | } 88 | -------------------------------------------------------------------------------- /talks/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 | -------------------------------------------------------------------------------- /tour/static/partials/editor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 |
9 | < 10 | {{curPage}}/{{toc.lessons[lessonId].Pages.length}} 11 | > 12 |
13 |
14 |
15 | 16 |
17 |
18 | {{f.Name}} 19 | Syntax 20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 |
36 | 37 |
38 | Run 39 | Format 40 | Reset 41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 | -------------------------------------------------------------------------------- /handlers.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 | // The /doc/codewalk/ tree is synthesized from codewalk descriptions, 6 | // files named $GOROOT/doc/codewalk/*.xml. 7 | // For an example and a description of the format, see 8 | // http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060 9 | // and see http://localhost:6060/doc/codewalk/codewalk . 10 | // That page is itself a codewalk; the source code for it is 11 | // $GOROOT/doc/codewalk/codewalk.xml. 12 | 13 | package main 14 | 15 | import ( 16 | "log" 17 | "net/http" 18 | "text/template" 19 | 20 | "golang.org/x/tools/godoc/redirect" 21 | "golang.org/x/tools/godoc/vfs" 22 | 23 | "github.com/golang-china/golangdoc/godoc" 24 | ) 25 | 26 | var ( 27 | pres *godoc.Presentation 28 | fs = vfs.NameSpace{} 29 | ) 30 | 31 | func registerHandlers(pres *godoc.Presentation) { 32 | if pres == nil { 33 | panic("nil Presentation") 34 | } 35 | http.HandleFunc("/doc/codewalk/", codewalk) 36 | http.Handle("/doc/play/", pres.FileServer()) 37 | http.Handle("/robots.txt", pres.FileServer()) 38 | http.Handle("/", pres) 39 | http.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/")) 40 | redirect.Register(nil) 41 | } 42 | 43 | func readTemplate(name string) *template.Template { 44 | if pres == nil { 45 | panic("no global Presentation set yet") 46 | } 47 | path := "lib/godoc/" + name 48 | 49 | // use underlying file system fs to read the template file 50 | // (cannot use template ParseFile functions directly) 51 | data, err := vfs.ReadFile(fs, path) 52 | if err != nil { 53 | log.Fatal("readTemplate: ", err) 54 | } 55 | // be explicit with errors (for app engine use) 56 | t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data)) 57 | if err != nil { 58 | log.Fatal("readTemplate: ", err) 59 | } 60 | return t 61 | } 62 | 63 | func readTemplates(p *godoc.Presentation, html bool) { 64 | p.PackageText = readTemplate("package.txt") 65 | p.SearchText = readTemplate("search.txt") 66 | 67 | if html || p.HTMLMode { 68 | codewalkHTML = readTemplate("codewalk.html") 69 | codewalkdirHTML = readTemplate("codewalkdir.html") 70 | p.CallGraphHTML = readTemplate("callgraph.html") 71 | p.DirlistHTML = readTemplate("dirlist.html") 72 | p.ErrorHTML = readTemplate("error.html") 73 | p.ExampleHTML = readTemplate("example.html") 74 | p.GodocHTML = readTemplate("godoc.html") 75 | p.ImplementsHTML = readTemplate("implements.html") 76 | p.MethodSetHTML = readTemplate("methodset.html") 77 | p.PackageHTML = readTemplate("package.html") 78 | p.SearchHTML = readTemplate("search.html") 79 | p.SearchDocHTML = readTemplate("searchdoc.html") 80 | p.SearchCodeHTML = readTemplate("searchcode.html") 81 | p.SearchTxtHTML = readTemplate("searchtxt.html") 82 | p.SearchDescXML = readTemplate("opensearch.xml") 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /x.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 contains the handlers that serve go-import redirects for Go 6 | // sub-repositories. It specifies the mapping from import paths like 7 | // "golang.org/x/tools" to the actual repository locations. 8 | 9 | package main 10 | 11 | import ( 12 | "html/template" 13 | "log" 14 | "net/http" 15 | "strings" 16 | ) 17 | 18 | const xPrefix = "/x/" 19 | 20 | type xRepo struct { 21 | URL, VCS string 22 | } 23 | 24 | var xMap = map[string]xRepo{ 25 | "codereview": {"https://code.google.com/p/go.codereview", "hg"}, 26 | 27 | "benchmarks": {"https://go.googlesource.com/benchmarks", "git"}, 28 | "blog": {"https://go.googlesource.com/blog", "git"}, 29 | "crypto": {"https://go.googlesource.com/crypto", "git"}, 30 | "debug": {"https://go.googlesource.com/debug", "git"}, 31 | "exp": {"https://go.googlesource.com/exp", "git"}, 32 | "image": {"https://go.googlesource.com/image", "git"}, 33 | "mobile": {"https://go.googlesource.com/mobile", "git"}, 34 | "net": {"https://go.googlesource.com/net", "git"}, 35 | "oauth2": {"https://go.googlesource.com/oauth2", "git"}, 36 | "playground": {"https://go.googlesource.com/playground", "git"}, 37 | "review": {"https://go.googlesource.com/review", "git"}, 38 | "sys": {"https://go.googlesource.com/sys", "git"}, 39 | "talks": {"https://go.googlesource.com/talks", "git"}, 40 | "text": {"https://go.googlesource.com/text", "git"}, 41 | "tools": {"https://go.googlesource.com/tools", "git"}, 42 | } 43 | 44 | func init() { 45 | http.HandleFunc(xPrefix, xHandler) 46 | } 47 | 48 | func xHandler(w http.ResponseWriter, r *http.Request) { 49 | head, tail := strings.TrimPrefix(r.URL.Path, xPrefix), "" 50 | if i := strings.Index(head, "/"); i != -1 { 51 | head, tail = head[:i], head[i:] 52 | } 53 | repo, ok := xMap[head] 54 | if !ok { 55 | http.NotFound(w, r) 56 | return 57 | } 58 | data := struct { 59 | Prefix, Head, Tail string 60 | Repo xRepo 61 | }{xPrefix, head, tail, repo} 62 | if err := xTemplate.Execute(w, data); err != nil { 63 | log.Println("xHandler:", err) 64 | } 65 | } 66 | 67 | var xTemplate = template.Must(template.New("x").Parse(` 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Nothing to see here; move along. 77 | 78 | 79 | `)) 80 | -------------------------------------------------------------------------------- /tour/static/js/values.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 | angular.module('tour.values', []). 8 | 9 | // List of modules with description and lessons in it. 10 | value('tableOfContents', [{ 11 | 'id': 'mechanics', 12 | 'title': 'Using the tour', 13 | 'description': '

Welcome to a tour of the Go programming language. The tour covers the most important features of the language, mainly:

', 14 | 'lessons': ['welcome'] 15 | }, { 16 | 'id': 'basics', 17 | 'title': 'Basics', 18 | 'description': '

The starting point, learn all the basics of the language.

Declaring variables, calling functions, and all the things you need to know before moving to the next lessons.

', 19 | 'lessons': ['basics', 'flowcontrol', 'moretypes'] 20 | }, { 21 | 'id': 'methods', 22 | 'title': 'Methods and interfaces', 23 | 'description': '

Learn how to define methods on types, how to declare interfaces, and how to put everything together.

', 24 | 'lessons': ['methods'] 25 | }, { 26 | 'id': 'concurrency', 27 | 'title': 'Concurrency', 28 | 'description': '

Go provides concurrency features as part of the core language.

This module goes over goroutines and channels, and how they are used to implement different concurrency patterns.

', 29 | 'lessons': ['concurrency'] 30 | }]). 31 | 32 | // translation 33 | value('translation', { 34 | 'off': 'off', 35 | 'on': 'on', 36 | 'syntax': 'Syntax-Highlighting', 37 | 'lineno': 'Line-Numbers', 38 | 'reset': 'Reset Slide', 39 | 'format': 'Format Source Code', 40 | 'kill': 'Kill Program', 41 | 'run': 'Run', 42 | 'compile': 'Compile and Run', 43 | 'more': 'Options', 44 | 'toc': 'Table of Contents', 45 | 'prev': 'Previous', 46 | 'next': 'Next', 47 | 'waiting': 'Waiting for remote server...', 48 | 'errcomm': 'Error communicating with remote server.', 49 | }). 50 | 51 | // Config for codemirror plugin 52 | value('ui.config', { 53 | codemirror: { 54 | mode: 'text/x-go', 55 | matchBrackets: true, 56 | lineNumbers: true, 57 | autofocus: true, 58 | indentWithTabs: true, 59 | indentUnit: 4, 60 | tabSize: 4, 61 | lineWrapping: true, 62 | extraKeys: { 63 | 'Shift-Enter': function() { 64 | $('#run').click(); 65 | }, 66 | 'Ctrl-Enter': function() { 67 | $('#format').click(); 68 | }, 69 | 'PageDown': function() { 70 | return false; 71 | }, 72 | 'PageUp': function() { 73 | return false; 74 | }, 75 | }, 76 | // TODO: is there a better way to do this? 77 | // AngularJS values can't depend on factories. 78 | onChange: function() { 79 | if (window.codeChanged !== null) window.codeChanged(); 80 | } 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /talks/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 | -------------------------------------------------------------------------------- /blog/walk.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 blog 6 | 7 | import ( 8 | "errors" 9 | "os" 10 | "path/filepath" 11 | "sort" 12 | 13 | "golang.org/x/tools/godoc/vfs" 14 | ) 15 | 16 | // SkipDir is used as a return value from WalkFuncs to indicate that 17 | // the directory named in the call is to be skipped. It is not returned 18 | // as an error by any function. 19 | var SkipDir = errors.New("skip this directory") 20 | 21 | // WalkFunc is the type of the function called for each file or directory 22 | // visited by Walk. The path argument contains the argument to Walk as a 23 | // prefix; that is, if Walk is called with "dir", which is a directory 24 | // containing the file "a", the walk function will be called with argument 25 | // "dir/a". The info argument is the os.FileInfo for the named path. 26 | // 27 | // If there was a problem walking to the file or directory named by path, the 28 | // incoming error will describe the problem and the function can decide how 29 | // to handle that error (and Walk will not descend into that directory). If 30 | // an error is returned, processing stops. The sole exception is that if path 31 | // is a directory and the function returns the special value SkipDir, the 32 | // contents of the directory are skipped and processing continues as usual on 33 | // the next file. 34 | type WalkFunc func(fs vfs.FileSystem, path string, info os.FileInfo, err error) error 35 | 36 | // walk recursively descends path, calling w. 37 | func walk(fs vfs.FileSystem, path string, info os.FileInfo, walkFn WalkFunc) error { 38 | err := walkFn(fs, path, info, nil) 39 | if err != nil { 40 | if info.IsDir() && err == SkipDir { 41 | return nil 42 | } 43 | return err 44 | } 45 | 46 | if !info.IsDir() { 47 | return nil 48 | } 49 | 50 | names, err := readDirNames(fs, path) 51 | if err != nil { 52 | return walkFn(fs, path, info, err) 53 | } 54 | 55 | for _, name := range names { 56 | filename := filepath.ToSlash(filepath.Join(path, name)) 57 | fileInfo, err := fs.Lstat(filename) 58 | if err != nil { 59 | if err := walkFn(fs, filename, fileInfo, err); err != nil && err != SkipDir { 60 | return err 61 | } 62 | } else { 63 | err = walk(fs, filename, fileInfo, walkFn) 64 | if err != nil { 65 | if !fileInfo.IsDir() || err != SkipDir { 66 | return err 67 | } 68 | } 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | // Walk walks the file tree rooted at root, calling walkFn for each file or 75 | // directory in the tree, including root. All errors that arise visiting files 76 | // and directories are filtered by walkFn. The files are walked in lexical 77 | // order, which makes the output deterministic but means that for very 78 | // large directories Walk can be inefficient. 79 | // Walk does not follow symbolic links. 80 | func Walk(fs vfs.FileSystem, root string, walkFn WalkFunc) error { 81 | root = filepath.ToSlash(root) 82 | info, err := fs.Lstat(root) 83 | if err != nil { 84 | return walkFn(fs, root, nil, err) 85 | } 86 | return walk(fs, root, info, walkFn) 87 | } 88 | 89 | // readDirNames reads the directory named by dirname and returns 90 | // a sorted list of directory entries. 91 | func readDirNames(fs vfs.FileSystem, dirname string) ([]string, error) { 92 | dirname = filepath.ToSlash(dirname) 93 | fileInfoList, err := fs.ReadDir(dirname) 94 | if err != nil { 95 | return nil, err 96 | } 97 | var names []string 98 | for _, fi := range fileInfoList { 99 | names = append(names, fi.Name()) 100 | } 101 | sort.Strings(names) 102 | return names, nil 103 | } 104 | -------------------------------------------------------------------------------- /setup-godoc-app.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2011 The Go Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | # This script creates a complete godoc app in $APPDIR. 8 | # It copies the cmd/godoc and src/go/... sources from GOROOT, 9 | # synthesizes an app.yaml file, and creates the .zip, index, and 10 | # configuration files. 11 | # 12 | # If an argument is provided it is assumed to be the app-engine godoc directory. 13 | # Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env" 14 | # is consulted to find the $GOROOT. 15 | # 16 | # The script creates a .zip file representing the $GOROOT file system 17 | # and computes the correspondig search index files. These files are then 18 | # copied to $APPDIR. A corresponding godoc configuration file is created 19 | # in $APPDIR/appconfig.go. 20 | 21 | ZIPFILE=godoc.zip 22 | INDEXFILE=godoc.index 23 | SPLITFILES=index.split. 24 | GODOC=golang.org/x/tools/cmd/godoc 25 | CONFIGFILE=$GODOC/appconfig.go 26 | 27 | error() { 28 | echo "error: $1" 29 | exit 2 30 | } 31 | 32 | getArgs() { 33 | if [ -z $APPENGINE_SDK ]; then 34 | error "APPENGINE_SDK environment variable not set" 35 | fi 36 | if [ ! -x $APPENGINE_SDK/goapp ]; then 37 | error "couldn't find goapp command in $APPENGINE_SDK" 38 | fi 39 | if [ -z $GOROOT ]; then 40 | GOROOT=$(go env GOROOT) 41 | echo "GOROOT not set explicitly, using go env value instead" 42 | fi 43 | if [ -z $APPDIR ]; then 44 | if [ $# == 0 ]; then 45 | error "APPDIR not set, and no argument provided" 46 | fi 47 | APPDIR=$1 48 | echo "APPDIR not set, using argument instead" 49 | fi 50 | 51 | # safety checks 52 | if [ ! -d $GOROOT ]; then 53 | error "$GOROOT is not a directory" 54 | fi 55 | if [ -e $APPDIR ]; then 56 | error "$APPDIR exists; check and remove it before trying again" 57 | fi 58 | 59 | # reporting 60 | echo "GOROOT = $GOROOT" 61 | echo "APPDIR = $APPDIR" 62 | } 63 | 64 | fetchGodoc() { 65 | echo "*** Fetching godoc (if not already in GOPATH)" 66 | unset GOBIN 67 | go=$APPENGINE_SDK/goapp 68 | $go get -d -tags appengine $GODOC 69 | mkdir -p $APPDIR/$GODOC 70 | cp $(find $($go list -f '{{.Dir}}' $GODOC) -type f -depth 1) $APPDIR/$GODOC/ 71 | } 72 | 73 | makeAppYaml() { 74 | echo "*** make $APPDIR/app.yaml" 75 | cat > $APPDIR/app.yaml < $APPDIR/$CONFIGFILE <= 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, ``, 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 | --------------------------------------------------------------------------------