├── public ├── img │ ├── favicon.png │ ├── digitalocean.png │ ├── badge.svg │ ├── sourcegraph.svg │ └── sourcegraph-logo.svg ├── fonts │ ├── octicons.eot │ ├── octicons.ttf │ └── octicons.woff ├── less │ ├── _link.less │ └── main.less ├── css │ └── gowalker.min.css └── js │ └── gowalker.js ├── internal ├── base │ ├── README │ ├── template.go │ ├── path.go │ ├── tool.go │ └── gen.go ├── route │ ├── apiv1 │ │ └── api.go │ ├── home.go │ ├── search.go │ └── docs.go ├── doc │ ├── http.go │ ├── golang.go │ ├── struct.go │ ├── code.go │ ├── crawl.go │ ├── github.go │ ├── vcs.go │ └── walker.go ├── spaces │ └── spaces.go ├── prometheus │ ├── js_file.go │ └── package.go ├── db │ ├── models.go │ ├── js_file.go │ ├── routine.go │ └── package.go ├── httplib │ ├── README.md │ ├── httplib_test.go │ └── httplib.go ├── setting │ └── setting.go └── context │ └── context.go ├── Makefile ├── .gitignore ├── README.md ├── .github └── workflows │ └── lsif.yml ├── conf ├── app.ini └── locale │ ├── locale_zh-CN.ini │ └── locale_en-US.ini ├── templates ├── docs │ ├── header.html │ ├── imports.html │ ├── docs.html │ └── tpl.html ├── search.html ├── home.html └── base │ └── base.html ├── go.mod ├── gowalker.go └── LICENSE /public/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/gowalker/HEAD/public/img/favicon.png -------------------------------------------------------------------------------- /public/fonts/octicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/gowalker/HEAD/public/fonts/octicons.eot -------------------------------------------------------------------------------- /public/fonts/octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/gowalker/HEAD/public/fonts/octicons.ttf -------------------------------------------------------------------------------- /public/fonts/octicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/gowalker/HEAD/public/fonts/octicons.woff -------------------------------------------------------------------------------- /public/img/digitalocean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknwon/gowalker/HEAD/public/img/digitalocean.png -------------------------------------------------------------------------------- /internal/base/README: -------------------------------------------------------------------------------- 1 | Run following code when Go version upgrades: 2 | 3 | go run gen.go -output data.go 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go build -v -o gowalker 3 | 4 | web: build 5 | ./gowalker 6 | 7 | release: 8 | env GOOS=linux GOARCH=amd64 go build -o gowalker 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | raw 2 | custom 3 | public/.idea 4 | gowalker 5 | 6 | gowalker.sublime-project 7 | gowalker.sublime-workspace 8 | public/sass/.sass-cache 9 | .idea 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /public/img/badge.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Go Walker 2 | ========= 3 | 4 | Go Walker is a server that generates Go projects API documentation on the fly for the projects on **GitHub**. 5 | 6 | ## Credits 7 | 8 | - [github.com/golang/gddo](https://github.com/golang/gddo) 9 | - Contributors: [chenwenli](http://www.lavachen.cn), [slene](https://github.com/slene), [atotto](https://github.com/atotto). 10 | 11 | ## License 12 | 13 | This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. 14 | -------------------------------------------------------------------------------- /.github/workflows/lsif.yml: -------------------------------------------------------------------------------- 1 | name: LSIF 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - name: Generate LSIF data 9 | uses: sourcegraph/lsif-go-action@master 10 | with: 11 | verbose: 'true' 12 | - name: Upload LSIF data 13 | uses: sourcegraph/lsif-upload-action@master 14 | continue-on-error: true 15 | with: 16 | endpoint: https://sourcegraph.com 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /conf/app.ini: -------------------------------------------------------------------------------- 1 | RUN_MODE = dev 2 | DISABLE_ROUTER_LOG = false 3 | 4 | [server] 5 | HTTP_PORT = 8080 6 | FETCH_TIMEOUT = 60 7 | DOCS_JS_PATH = raw/docs/ 8 | DOCS_GOB_PATH = raw/gob/ 9 | 10 | [database] 11 | USER = root 12 | PASSWD = 13 | HOST = 127.0.0.1:3306 14 | NAME = gowalker 15 | 16 | [i18n] 17 | LANGS = en-US,zh-CN 18 | NAMES = English,简体中文 19 | REDIRECT = true 20 | 21 | [github] 22 | CLIENT_ID = 23 | CLIENT_SECRET = 24 | 25 | [digitalocean.spaces] 26 | ENABLED = false 27 | ENDPOINT = 28 | ACCESS_KEY = 29 | SECRET_KEY = 30 | BUCKET = 31 | BUCKET_URL = 32 | 33 | [maintenance] 34 | JS_RECYCLE_DAYS = 14 35 | 36 | [log.discord] 37 | ENABLED = false 38 | URL = 39 | -------------------------------------------------------------------------------- /public/img/sourcegraph.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/docs/header.html: -------------------------------------------------------------------------------- 1 |
| {{Tr(Lang, "docs.path")}} | 19 |{{Tr(Lang, "docs.synopsis")}} | 20 |
|---|---|
| {{pkg.ImportPath}} | 26 |{{pkg.Synopsis}} | 27 |
{{Tr(Lang, "docs.imports.go_back", Link) | safe}}
34 || {{Tr(Lang, "import_path")}} | 19 | 20 |21 | |
|---|---|
| {{p.ImportPath}} | 27 | 28 |{{p.Stars}} | 29 |
{{Tr(Lang, "search.not_found")}}
35 | {% endif %} 36 |10 | {% if Lang == "zh-CN" %} 11 | Go Walker 是一个可以在线生成并浏览 Go 项目 API 文档的 Web 服务器,目前已支持包括 GitHub 等代码托管平台。 12 | {% else %} 13 | Go Walker is a server that generates Go projects API documentation on the fly for the projects on GitHub. 14 | {% endif %} 15 |
16 | 17 | 30 || = 40 %}class="popup" data-content="{{p.ImportPath}}"{% endif %}> 43 | {{RearSubStr(p.ImportPath, 40)}} 44 | {% if p.IsGoRepo %} 45 | {{Tr(Lang, "home.standard")}} 46 | {% endif %} 47 | | 48 | 49 |
| {{Tr(Lang, "docs.path")}} | 35 |{{Tr(Lang, "docs.synopsis")}} | 36 |
|---|---|
| {{dir.Name}} | 42 |{{dir.Synopsis}} | 43 |
52 | {{Tr(Lang, "docs.note.package")}} {{ProjectName}} {% if RefNum == int64(0) %}{{Tr(Lang, "docs.note.import", Link, ImportNum) | safe}}{% else %}{{Tr(Lang, "docs.note.import_ref", Link, ImportNum, RefNum) | safe}}{% endif %} {{Tr(Lang, "docs.note.generated", TimeDuration)}} 53 | 54 | {% if CanRefresh %} 55 | 56 | {{Tr(Lang, "docs.refresh")}} 57 | 58 | {% endif %} 59 |
60 |{{ex.Code | safe}}
58 | {% if ex.Output %}
59 | Output:
60 | {{ex.Output}}
61 | {% endif %}
62 | {{c.FmtDecl | safe}}
88 | {{c.Doc | safe}}
89 | {% endfor %}
90 | {% endif %}
91 | {# END: Constants #}
92 |
93 | {# START: Variables #}
94 | {% if IsHasVar %}
95 | {{v.FmtDecl | safe}}
98 | {{v.Doc | safe}}
99 | {% endfor %}
100 | {% endif %}
101 |
102 | {# END: Variables #}
103 |
104 | {# START: Functions #}
105 | {% for fn in Funcs %}
106 | {{fn.FmtDecl | safe}}
116 | {{fn.Code | safe}}
119 | {{tp.FmtDecl | safe}}
139 |
140 | {{tp.Doc | safe}}
141 |
142 | {% for ex in tp.Examples %}
143 | {{example_detail(ex)}}
144 | {% endfor %}
145 |
146 | {# START: Types.Constants #}
147 | {% for c in tp.Consts %}
148 | {{c.FmtDecl | safe}}
149 | {{c.Doc | safe}}
150 | {% endfor %}
151 | {# END: Types.Constants #}
152 |
153 | {# START: Types.Variables #}
154 | {% for v in tp.Vars %}
155 | {{v.FmtDecl | safe}}
156 | {{v.Doc | safe}}
157 | {% endfor %}
158 |
159 | {# END: Types.Variables #}
160 |
161 | {# START: Types.Functions #}
162 | {% for fn in tp.Funcs %}
163 | {{fn.FmtDecl | safe}}
173 | {{fn.Code | safe}}
176 | {{fn.FmtDecl | safe}}
201 | {{fn.Code | safe}}
204 | 223 | {% for f in Files %} 224 | {{f.SrcName}} 225 | {% endfor %} 226 |
227 | {% endif %} 228 | 229 | {{ExportDataSrc|safe}} -------------------------------------------------------------------------------- /internal/db/routine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Unknwon 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package db 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "sync/atomic" 21 | "time" 22 | 23 | log "gopkg.in/clog.v1" 24 | 25 | "github.com/unknwon/gowalker/internal/setting" 26 | "github.com/unknwon/gowalker/internal/spaces" 27 | ) 28 | 29 | func RefreshNumTotalPackages() { 30 | count, _ := x.Count(new(PkgInfo)) 31 | atomic.StoreInt64(&numTotalPackages, count) 32 | } 33 | 34 | func ComposeSpacesObjectNames(importPath, etag string, numExtraFiles int) []string { 35 | names := make([]string, numExtraFiles+1) 36 | for i := range names { 37 | if i == 0 { 38 | names[i] = fmt.Sprintf("%s-%s.js", importPath, etag) 39 | } else { 40 | names[i] = fmt.Sprintf("%s-%s-%d.js", importPath, etag, i) 41 | } 42 | } 43 | return names 44 | } 45 | 46 | var distributeJSFilesStatus int32 = 0 47 | 48 | // DistributeJSFiles uploads local JS files to DigitalOcean Spaces. 49 | func DistributeJSFiles() { 50 | if !setting.DigitalOcean.Spaces.Enabled { 51 | return 52 | } 53 | 54 | if !atomic.CompareAndSwapInt32(&distributeJSFilesStatus, 0, 1) { 55 | return 56 | } 57 | defer atomic.StoreInt32(&distributeJSFilesStatus, 0) 58 | 59 | log.Trace("Routine started: DistributeJSFiles") 60 | defer log.Trace("Routine ended: DistributeJSFiles") 61 | 62 | if err := x.Where("status = ?", JSFileStatusGenerated).Iterate(new(JSFile), func(idx int, bean interface{}) error { 63 | jsFile := bean.(*JSFile) 64 | 65 | // Gather package information 66 | pinfo, err := GetPkgInfoByID(jsFile.PkgID) 67 | if err != nil { 68 | if err == ErrPackageVersionTooOld { 69 | return nil 70 | } 71 | log.Error(2, "Failed to get package info by ID[%d]: %v", jsFile.PkgID, err) 72 | return nil 73 | } 74 | log.Trace("DistributeJSFiles[%d]: Distributing %q", jsFile.ID, pinfo.ImportPath) 75 | 76 | // Compose object names 77 | localJSPaths := pinfo.LocalJSPaths() 78 | objectNames := ComposeSpacesObjectNames(pinfo.ImportPath, jsFile.Etag, jsFile.NumExtraFiles) 79 | if len(objectNames) != len(localJSPaths) { 80 | log.Warn("DistributeJSFiles[%d]: Number of object names does not match local JS files: %d != %d", 81 | jsFile.ID, len(objectNames), len(localJSPaths)) 82 | return nil 83 | } 84 | for i, localPath := range localJSPaths { 85 | if err = spaces.PutObject(localPath, objectNames[i]); err != nil { 86 | log.Error(2, "Failed to put object[%s]: %v", objectNames[i], err) 87 | return nil 88 | } 89 | } 90 | 91 | // Update database records and clean up local disk 92 | jsFile.Status = JSFileStatusDistributed 93 | if err = SaveJSFile(jsFile); err != nil { 94 | log.Error(2, "Failed to save JS file[%d]: %v", jsFile.ID, err) 95 | return nil 96 | } 97 | 98 | for i := range localJSPaths { 99 | os.Remove(localJSPaths[i]) 100 | } 101 | 102 | log.Trace("DistributeJSFiles[%d]: Distributed %d files", jsFile.ID, len(objectNames)) 103 | return nil 104 | }); err != nil { 105 | log.Error(2, "Failed to distribute JS files: %v", err) 106 | } 107 | } 108 | 109 | var recycleJSFilesStatus int32 = 0 110 | 111 | // RecycleJSFiles deletes local or distributed JS files due to inactive status. 112 | func RecycleJSFiles() { 113 | if !atomic.CompareAndSwapInt32(&recycleJSFilesStatus, 0, 1) { 114 | return 115 | } 116 | defer atomic.StoreInt32(&recycleJSFilesStatus, 0) 117 | 118 | log.Trace("Routine started: RecycleJSFiles") 119 | defer log.Trace("Routine ended: RecycleJSFiles") 120 | 121 | outdated := time.Now().Add(-1 * time.Duration(setting.Maintenance.JSRecycleDays) * 24 * time.Hour).Unix() 122 | if err := x.Join("INNER", "pkg_info", "pkg_info.id = js_file.pkg_id"). 123 | Where("js_file.status < ? AND pkg_info.last_viewed < ?", JSFileStatusRecycled, outdated). 124 | Iterate(new(JSFile), func(idx int, bean interface{}) error { 125 | jsFile := bean.(*JSFile) 126 | 127 | // Gather package information 128 | pinfo, err := GetPkgInfoByID(jsFile.PkgID) 129 | if err != nil { 130 | if err == ErrPackageVersionTooOld { 131 | return nil 132 | } 133 | log.Error(2, "Failed to get package info by ID[%d]: %v", jsFile.PkgID, err) 134 | return nil 135 | } 136 | 137 | var numFiles int 138 | switch jsFile.Status { 139 | case JSFileStatusGenerated: 140 | localJSPaths := pinfo.LocalJSPaths() 141 | for i := range localJSPaths { 142 | os.Remove(localJSPaths[i]) 143 | } 144 | numFiles = len(localJSPaths) 145 | 146 | case JSFileStatusDistributed: 147 | if !setting.DigitalOcean.Spaces.Enabled { 148 | log.Warn("RecycleJSFiles[%d]: DigitalOcean Spaces is not enabled", jsFile.ID) 149 | return nil 150 | } 151 | 152 | objectNames := ComposeSpacesObjectNames(pinfo.ImportPath, jsFile.Etag, jsFile.NumExtraFiles) 153 | for i := range objectNames { 154 | if err = spaces.RemoveObject(objectNames[i]); err != nil { 155 | log.Error(2, "Failed to remove object[%s]: %v", objectNames[i], err) 156 | return nil 157 | } 158 | } 159 | numFiles = len(objectNames) 160 | 161 | default: 162 | log.Warn("RecycleJSFiles[%d]: Unexpected status %v", jsFile.ID, jsFile.Status) 163 | return nil 164 | } 165 | 166 | // FIXME: Database could be outdated if this operation fails, human must take action! 167 | jsFile.Status = JSFileStatusRecycled 168 | if err = SaveJSFile(jsFile); err != nil { 169 | log.Error(2, "Failed to save JS file[%d]: %v", jsFile.ID, err) 170 | return nil 171 | } 172 | 173 | log.Trace("RecycleJSFiles[%d]: Recycled %d files", jsFile.ID, numFiles) 174 | return nil 175 | }); err != nil { 176 | log.Error(2, "Failed to recycle JS files: %v", err) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /internal/route/docs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Unknwon 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package route 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "path" 21 | "strings" 22 | "time" 23 | 24 | "github.com/unknwon/com" 25 | 26 | "github.com/unknwon/gowalker/internal/base" 27 | "github.com/unknwon/gowalker/internal/context" 28 | "github.com/unknwon/gowalker/internal/db" 29 | "github.com/unknwon/gowalker/internal/doc" 30 | "github.com/unknwon/gowalker/internal/setting" 31 | ) 32 | 33 | const ( 34 | DOCS = "docs/docs" 35 | DOCS_IMPORTS = "docs/imports" 36 | ) 37 | 38 | // updateHistory updates browser history. 39 | func updateHistory(ctx *context.Context, id int64) { 40 | pairs := make([]string, 1, 10) 41 | pairs[0] = com.ToStr(id) + ":" + com.ToStr(time.Now().UTC().Unix()) 42 | 43 | count := 0 44 | for _, pair := range strings.Split(ctx.GetCookie("user_history"), "|") { 45 | infos := strings.Split(pair, ":") 46 | if len(infos) != 2 { 47 | continue 48 | } 49 | 50 | pid := com.StrTo(infos[0]).MustInt64() 51 | if pid == 0 || pid == id { 52 | continue 53 | } 54 | 55 | pairs = append(pairs, pair) 56 | 57 | count++ 58 | if count == 9 { 59 | break 60 | } 61 | } 62 | ctx.SetCookie("user_history", strings.Join(pairs, "|"), 9999999) 63 | } 64 | 65 | func handleError(ctx *context.Context, err error) { 66 | importPath := ctx.Params("*") 67 | if err == doc.ErrInvalidRemotePath { 68 | ctx.Redirect("/search?q=" + importPath) 69 | return 70 | } 71 | 72 | if strings.Contains(err.Error(), " not found") || 73 | strings.Contains(err.Error(), "resource not found") { 74 | db.DeletePackageByPath(importPath) 75 | } 76 | 77 | ctx.Flash.Error(importPath+": "+err.Error(), true) 78 | ctx.Flash.Info(ctx.Tr("form.click_to_search", importPath), true) 79 | Home(ctx) 80 | } 81 | 82 | func specialHandles(ctx *context.Context, pinfo *db.PkgInfo) bool { 83 | // Only show imports. 84 | if strings.HasSuffix(ctx.Req.RequestURI, "?imports") { 85 | ctx.Data["PageIsImports"] = true 86 | ctx.Data["Packages"] = db.GetPkgInfosByPaths(strings.Split(pinfo.ImportPaths, "|")) 87 | ctx.HTML(200, DOCS_IMPORTS) 88 | return true 89 | } 90 | 91 | // Only show references. 92 | if strings.HasSuffix(ctx.Req.RequestURI, "?refs") { 93 | ctx.Data["PageIsRefs"] = true 94 | ctx.Data["Packages"] = pinfo.GetRefs() 95 | ctx.HTML(200, DOCS_IMPORTS) 96 | return true 97 | } 98 | 99 | // Refresh documentation. 100 | if strings.HasSuffix(ctx.Req.RequestURI, "?refresh") { 101 | if !pinfo.CanRefresh() { 102 | ctx.Flash.Info(ctx.Tr("docs.refresh.too_often")) 103 | } else { 104 | importPath := ctx.Params("*") 105 | _, err := doc.CheckPackage(importPath, ctx.Render, doc.RequestTypeRefresh) 106 | if err != nil { 107 | handleError(ctx, err) 108 | return true 109 | } 110 | } 111 | ctx.Redirect(ctx.Data["Link"].(string)) 112 | return true 113 | } 114 | 115 | return false 116 | } 117 | 118 | func Docs(c *context.Context) { 119 | importPath := c.Params("*") 120 | 121 | // Check if import path looks like a vendor directory 122 | if strings.Contains(importPath, "/vendor/") { 123 | handleError(c, errors.New("import path looks like is a vendor directory, don't try to fool me! :D")) 124 | return 125 | } 126 | 127 | if base.IsGAERepoPath(importPath) { 128 | c.Redirect("/google.golang.org/" + importPath) 129 | return 130 | } 131 | 132 | pinfo, err := doc.CheckPackage(importPath, c.Render, doc.RequestTypeHuman) 133 | if err != nil { 134 | handleError(c, err) 135 | return 136 | } 137 | 138 | c.PageIs("Docs") 139 | c.Title(pinfo.ImportPath) 140 | c.Data["ParentPath"] = path.Dir(pinfo.ImportPath) 141 | c.Data["ProjectName"] = path.Base(pinfo.ImportPath) 142 | c.Data["ProjectPath"] = pinfo.ProjectPath 143 | c.Data["NumStars"] = pinfo.Stars 144 | 145 | if specialHandles(c, pinfo) { 146 | return 147 | } 148 | 149 | if pinfo.IsGoRepo { 150 | c.Flash.Info(c.Tr("docs.turn_into_search", importPath), true) 151 | } 152 | 153 | c.Data["PkgDesc"] = pinfo.Synopsis 154 | 155 | // README 156 | lang := c.Data["Lang"].(string)[:2] 157 | readmePath := setting.DocsJSPath + pinfo.ImportPath + "_RM_" + lang + ".js" 158 | if com.IsFile(readmePath) { 159 | c.Data["IsHasReadme"] = true 160 | c.Data["ReadmePath"] = readmePath 161 | } else { 162 | readmePath := setting.DocsJSPath + pinfo.ImportPath + "_RM_en.js" 163 | if com.IsFile(readmePath) { 164 | c.Data["IsHasReadme"] = true 165 | c.Data["ReadmePath"] = readmePath 166 | } 167 | } 168 | 169 | // Documentation 170 | if pinfo.JSFile.Status == db.JSFileStatusDistributed { 171 | docJS := db.ComposeSpacesObjectNames(pinfo.ImportPath, pinfo.JSFile.Etag, pinfo.JSFile.NumExtraFiles) 172 | for i := range docJS { 173 | docJS[i] = setting.DigitalOcean.Spaces.BucketURL + docJS[i] 174 | } 175 | c.Data["DocJS"] = docJS 176 | 177 | } else { 178 | docJS := make([]string, 0, pinfo.JSFile.NumExtraFiles+1) 179 | docJS = append(docJS, "/"+setting.DocsJSPath+importPath+".js") 180 | for i := 1; i <= pinfo.JSFile.NumExtraFiles; i++ { 181 | docJS = append(docJS, fmt.Sprintf("/%s%s-%d.js", setting.DocsJSPath, importPath, i)) 182 | } 183 | c.Data["DocJS"] = docJS 184 | } 185 | c.Data["Timestamp"] = pinfo.Created 186 | if time.Now().UTC().Add(-5*time.Second).Unix() < pinfo.Created { 187 | c.Flash.Success(c.Tr("docs.generate_success"), true) 188 | } 189 | 190 | // Subdirs 191 | if len(pinfo.Subdirs) > 0 { 192 | c.Data["IsHasSubdirs"] = true 193 | c.Data["ViewDirPath"] = pinfo.ViewDirPath 194 | c.Data["Subdirs"] = db.GetSubPkgs(pinfo.ImportPath, strings.Split(pinfo.Subdirs, "|")) 195 | } 196 | 197 | // Imports and references 198 | c.Data["ImportNum"] = pinfo.ImportNum 199 | c.Data["RefNum"] = pinfo.RefNum 200 | 201 | // Tools 202 | c.Data["TimeDuration"] = base.TimeSince(time.Unix(pinfo.Created, 0), c.Locale.Language()) 203 | c.Data["CanRefresh"] = pinfo.CanRefresh() 204 | 205 | updateHistory(c, pinfo.ID) 206 | 207 | c.Success(DOCS) 208 | } 209 | -------------------------------------------------------------------------------- /public/js/gowalker.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | $('.ui.accordion .title').click(function () { 3 | var $icon = $(this).find('.fas'); 4 | var $content = $('.ui.accordion .content'); 5 | if ($content.hasClass('d-hide')) { 6 | // Resize images if too large. 7 | if ($('#readme').length) { 8 | $(this).find("img").each(function () { 9 | var w = $(this).width(); 10 | $(this).width(w > 600 ? 600 : w); 11 | }); 12 | } 13 | 14 | $content.removeClass('d-hide'); 15 | $icon.removeClass('fa-caret-right'); 16 | $icon.addClass('fa-caret-down'); 17 | return; 18 | } 19 | 20 | $content.addClass('d-hide'); 21 | $icon.removeClass('fa-caret-down'); 22 | $icon.addClass('fa-caret-right'); 23 | }); 24 | 25 | var delay = (function(){ 26 | var timer = 0; 27 | return function(callback, ms){ 28 | clearTimeout(timer); 29 | timer = setTimeout(callback, ms); 30 | }; 31 | })(); 32 | 33 | $('#import-search').keyup(function() { 34 | delay(function () { 35 | var $this = $('#import-search'); 36 | if ($this.val().length < 3) { 37 | $('#search-results').html(""); 38 | return; 39 | } 40 | 41 | $('.form-autocomplete i.fa-search').addClass('d-hide'); 42 | $('.form-autocomplete .loading').removeClass('d-hide'); 43 | $.get('/search/json?q=' + $this.val(), function (data) { 44 | $('#search-results').html(""); 45 | for (var i = 0; i < data.results.length; i++) { 46 | $('#search-results').append(` 47 |` + data.results[i].title + `
50 |` + data.results[i].description + `
51 |{{Flash.ErrorMsg|safe}}
49 |{{Tr(Lang, "help.desc")}}
107 || {{Tr(Lang, "help.shortcut")}} | 110 |{{Tr(Lang, "help.usage")}} | 111 | 112 | {% if Lang=="zh-CN" %} 113 | 114 |
|---|---|
| 116 | ? 117 | | 118 |119 | 显示控制面板 120 | | 121 |
| 124 | s 125 | | 126 |127 | 搜索导出对象(文档页面) 128 | | 129 |
| 132 | g + g 133 | | 134 |135 | 跳转至顶部 136 | | 137 |
| 140 | g + b 141 | | 142 |143 | 跳转至底部 144 | | 145 |
| 151 | ? 152 | | 153 |154 | Show control panel 155 | | 156 |
| 159 | s 160 | | 161 |162 | Show search exports panel(Documentation page) 163 | | 164 |
| 167 | g + g 168 | | 169 |170 | Go to top of page 171 | | 172 |
| 175 | g + b 176 | | 177 |178 | Go to bottom of page 179 | | 180 |
", "
", 1) 580 | w.Pdoc.Doc = strings.Replace(w.Pdoc.Doc, "
", "", 1) 581 | 582 | if wr.WalkMode&WM_NoExample == 0 { 583 | w.getExamples() 584 | } 585 | 586 | w.SrcLines = make(map[string][]string) 587 | w.Pdoc.Consts = w.values(pdoc.Consts) 588 | w.Pdoc.Funcs, w.Pdoc.Ifuncs = w.funcs(pdoc.Funcs) 589 | w.Pdoc.Types, w.Pdoc.Itypes = w.types(pdoc.Types) 590 | w.Pdoc.Vars = w.values(pdoc.Vars) 591 | w.Pdoc.ImportPaths = strings.Join(pdoc.Imports, "|") 592 | w.Pdoc.ImportNum = int64(len(pdoc.Imports)) 593 | //w.Pdoc.Notes = w.notes(pdoc.Notes) 594 | 595 | return w.Pdoc, nil 596 | } 597 | --------------------------------------------------------------------------------