From 1104bc201991debc4fcd11ba6b5225b9b60b40ce Mon Sep 17 00:00:00 2001 From: Castlebin Date: Wed, 31 Jan 2024 16:07:57 +0800 Subject: [PATCH] add example Logging and Embed Directive --- PROGRESS.md | 2 + examples.txt | 2 + examples/embed-directive/embed-directive.go | 41 +++++ examples/embed-directive/embed-directive.hash | 2 + examples/embed-directive/embed-directive.sh | 13 ++ examples/embed-directive/folder/file1.hash | 1 + examples/embed-directive/folder/file2.hash | 1 + .../embed-directive/folder/single_file.txt | 1 + examples/logging/logging.go | 60 +++++++ examples/logging/logging.hash | 2 + examples/logging/logging.sh | 17 ++ examples/xml/xml.go | 8 +- go.mod | 2 + go.sum | 5 + templates/404.tmpl | 15 ++ templates/example.tmpl | 9 +- templates/footer.tmpl | 2 +- templates/index.tmpl | 17 +- templates/site.css | 23 +-- tools/build | 2 +- tools/build-loop | 2 +- tools/format | 2 +- tools/generate | 2 +- tools/generate.go | 161 +++++++++--------- tools/measure | 2 +- tools/measure.go | 25 ++- tools/serve | 2 +- tools/serve.go | 19 +-- tools/test | 2 +- tools/upload | 2 +- 30 files changed, 297 insertions(+), 147 deletions(-) create mode 100644 examples/embed-directive/embed-directive.go create mode 100644 examples/embed-directive/embed-directive.hash create mode 100644 examples/embed-directive/embed-directive.sh create mode 100644 examples/embed-directive/folder/file1.hash create mode 100644 examples/embed-directive/folder/file2.hash create mode 100644 examples/embed-directive/folder/single_file.txt create mode 100644 examples/logging/logging.go create mode 100644 examples/logging/logging.hash create mode 100644 examples/logging/logging.sh create mode 100644 templates/404.tmpl diff --git a/PROGRESS.md b/PROGRESS.md index fc139ab4..154c9905 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -73,11 +73,13 @@ - [x] File Paths->文件路径 - [x] Directories->目录 - [x] Temporary Files and Directories->临时文件和目录 +- [x] Embed Directive->嵌入的指令 - [x] Testing and Benchmarking->单元测试和基准测试 - [x] Command-Line Arguments->命令行参数 - [x] Command-Line Flags->命令行标志 - [x] Command-Line Subcommands->命令行子命令 - [x] Environment Variables->环境变量 +- [x] Logging->日志 - [x] HTTP Clients->HTTP 客户端 - [x] HTTP Servers->HTTP 服务端 - [x] Context diff --git a/examples.txt b/examples.txt index 2ef0ce00..b051fcee 100644 --- a/examples.txt +++ b/examples.txt @@ -65,11 +65,13 @@ Line Filters->行过滤器 File Paths->文件路径 Directories->目录 Temporary Files and Directories->临时文件和目录 +Embed Directive->嵌入的指令 Testing and Benchmarking->单元测试和基准测试 Command-Line Arguments->命令行参数 Command-Line Flags->命令行标志 Command-Line Subcommands->命令行子命令 Environment Variables->环境变量 +Logging->日志 HTTP Clients->HTTP 客户端 HTTP Servers->HTTP 服务端 Context diff --git a/examples/embed-directive/embed-directive.go b/examples/embed-directive/embed-directive.go new file mode 100644 index 00000000..958500c4 --- /dev/null +++ b/examples/embed-directive/embed-directive.go @@ -0,0 +1,41 @@ +// `//go:embed` 是一个 [编译器指令](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives), +// 它允许程序在构建时将任意文件和文件夹包含在 Go 二进制文件中。在这里阅读有关嵌入指令的更多信息:[这里](https://pkg.go.dev/embed)。 +package main + +// 导入 `embed` 包;如果您不使用该包中的任何导出标识符,可以使用 `_ "embed"` 进行空白导入。 +import ( + "embed" +) + +// `embed` 指令接受相对于包含 Go 源文件的目录的路径。 +// 该指令将文件的内容嵌入到紧随其后的 `string` 变量中。 +// +//go:embed folder/single_file.txt +var fileString string + +// 将文件的内容嵌入到一个 `[]byte` 中。 +// +//go:embed folder/single_file.txt +var fileByte []byte + +// 我们还可以使用通配符嵌入多个文件甚至文件夹。 +// 这将使用 [embed.FS 类型](https://pkg.go.dev/embed#FS)的变量, +// 该类型实现了一个简单的虚拟文件系统。 +// +//go:embed folder/single_file.txt +//go:embed folder/*.hash +var folder embed.FS + +func main() { + + // 打印出 `single_file.txt` 的内容。 + print(fileString) + print(string(fileByte)) + + // 从嵌入的文件夹中检索一些文件。 + content1, _ := folder.ReadFile("folder/file1.hash") + print(string(content1)) + + content2, _ := folder.ReadFile("folder/file2.hash") + print(string(content2)) +} diff --git a/examples/embed-directive/embed-directive.hash b/examples/embed-directive/embed-directive.hash new file mode 100644 index 00000000..f81e3174 --- /dev/null +++ b/examples/embed-directive/embed-directive.hash @@ -0,0 +1,2 @@ +69526bd78ac861c85bb12b96e9f1273e8aecc5a6 +6m2ll-D52BB diff --git a/examples/embed-directive/embed-directive.sh b/examples/embed-directive/embed-directive.sh new file mode 100644 index 00000000..eb6998b9 --- /dev/null +++ b/examples/embed-directive/embed-directive.sh @@ -0,0 +1,13 @@ +# 使用这些命令来运行示例。 +#(注意:由于 Go Playground 的限制,这个示例只能在您的本地机器上运行。) +$ mkdir -p folder +$ echo "hello go" > folder/single_file.txt +$ echo "123" > folder/file1.hash +$ echo "456" > folder/file2.hash + +$ go run embed-directive.go +hello go +hello go +123 +456 + diff --git a/examples/embed-directive/folder/file1.hash b/examples/embed-directive/folder/file1.hash new file mode 100644 index 00000000..190a1803 --- /dev/null +++ b/examples/embed-directive/folder/file1.hash @@ -0,0 +1 @@ +123 diff --git a/examples/embed-directive/folder/file2.hash b/examples/embed-directive/folder/file2.hash new file mode 100644 index 00000000..8d38505c --- /dev/null +++ b/examples/embed-directive/folder/file2.hash @@ -0,0 +1 @@ +456 diff --git a/examples/embed-directive/folder/single_file.txt b/examples/embed-directive/folder/single_file.txt new file mode 100644 index 00000000..606d5970 --- /dev/null +++ b/examples/embed-directive/folder/single_file.txt @@ -0,0 +1 @@ +hello go diff --git a/examples/logging/logging.go b/examples/logging/logging.go new file mode 100644 index 00000000..65782c1b --- /dev/null +++ b/examples/logging/logging.go @@ -0,0 +1,60 @@ +// Go标准库提供了直观的工具用于从Go程序输出日志 +// 使用 [log](https://pkg.go.dev/log) 包进行自由格式输出 +// 使用 [log/slog](https://pkg.go.dev/log/slog) 包进行结构化输出。 + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + + "log/slog" +) + +func main() { + + // 只需调用 `log` 包中的 `Println` 等函数即可使用 _标准_ logger。 + // 它已经预先配置为将日志输出到 `os.Stderr`。 + // 像 `Fatal*` 或 `Panic*` 这样的附加方法将在记录日志后退出程序。 + log.Println("standard logger") + + // 日志记录器可以使用 _flags_ 进行配置,以设置它们的输出格式。 + // 默认情况下,标准记录器已设置了 `log.Ldate` 和 `log.Ltime` 标志, + // 并将它们收集在 `log.LstdFlags` 中。 + // 我们可以更改其标志以发出微秒精度的时间,例如: + log.SetFlags(log.LstdFlags | log.Lmicroseconds) + log.Println("with micro") + + // 它还支持发出调用 log` 函数的文件名和行号。 + log.SetFlags(log.LstdFlags | log.Lshortfile) + log.Println("with file/line") + + // 可能会有用创建一个自定义记录器并在各处传递它。 + // 创建新记录器时,我们可以设置一个 _前缀_ 来区分其输出和其他日志记录器。 + mylog := log.New(os.Stdout, "my:", log.LstdFlags) + mylog.Println("from mylog") + + // 我们可以使用 `SetPrefix` 方法在现有的记录器(包括标准记录器)上设置前缀。 + mylog.SetPrefix("ohmy:") + mylog.Println("from mylog") + + // 日志记录器可以具有自定义的输出目标;任何 `io.Writer` 都可以使用。 + var buf bytes.Buffer + buflog := log.New(&buf, "buf:", log.LstdFlags) + + // 这个调用将日志输出写入到 `buf` 中. + buflog.Println("hello") + + // 这将实际上显示在标准输出上。 + fmt.Print("from buflog:", buf.String()) + + // `slog 包提供了 _结构化_ 的日志输出。例如,以 JSON 格式记录日志非常直接。 + jsonHandler := slog.NewJSONHandler(os.Stderr, nil) + myslog := slog.New(jsonHandler) + myslog.Info("hi there") + + // 除了 `msg` 之外,`slog` 输出还可以包含任意数量的键值对。 + myslog.Info("hello again", "key", "val", "age", 25) +} diff --git a/examples/logging/logging.hash b/examples/logging/logging.hash new file mode 100644 index 00000000..4d1c7a70 --- /dev/null +++ b/examples/logging/logging.hash @@ -0,0 +1,2 @@ +38a7ef451859bb4c163df938b3a9d0e5ac293bef +Qd0uCqBlYUn diff --git a/examples/logging/logging.sh b/examples/logging/logging.sh new file mode 100644 index 00000000..a28dd8d9 --- /dev/null +++ b/examples/logging/logging.sh @@ -0,0 +1,17 @@ +# 示例输出; +# 发出的日期和时间将取决于示例运行的时间。 +$ go run logging.go +2023/08/22 10:45:16 standard logger +2023/08/22 10:45:16.904141 with micro +2023/08/22 10:45:16 logging.go:40: with file/line +my:2023/08/22 10:45:16 from mylog +ohmy:2023/08/22 10:45:16 from mylog +from buflog:buf:2023/08/22 10:45:16 hello + +# 这些被换行以便在网站上更清晰地呈现; +# 实际上它们是在单行上发出的。 +{"time":"2023-08-22T10:45:16.904166391-07:00", + "level":"INFO","msg":"hi there"} +{"time":"2023-08-22T10:45:16.904178985-07:00", + "level":"INFO","msg":"hello again", + "key":"val","age":25} diff --git a/examples/xml/xml.go b/examples/xml/xml.go index 8f8085a3..368e81a1 100644 --- a/examples/xml/xml.go +++ b/examples/xml/xml.go @@ -7,11 +7,13 @@ import ( "fmt" ) -// `Plant` 结构将被映射到 `XML` 。 +// `Plant` 结构将被映射到 `XML` 。 +// // 与 `JSON` 示例类似,字段标签包含用于编码器和解码器的指令。 // 这里我们使用了 `XML` 包的一些特性: -// `XMLName` 字段名称规定了该结构的 `XML` 元素名称; -// `id,attrr` 表示 `Id` 字段是一个 `XML` 属性,而不是嵌套元素。 +// +// `XMLName` 字段名称规定了该结构的 `XML` 元素名称; +// `id,attrr` 表示 `Id` 字段是一个 `XML` 属性,而不是嵌套元素。 type Plant struct { XMLName xml.Name `xml:"plant"` Id int `xml:"id,attr"` diff --git a/go.mod b/go.mod index 5b3a4b0c..11c75dc1 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + github.com/alecthomas/chroma/v2 v2.12.0 // indirect github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect github.com/aws/aws-sdk-go-v2/config v1.7.0 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect @@ -20,4 +21,5 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect github.com/aws/smithy-go v1.8.0 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect ) diff --git a/go.sum b/go.sum index a395d871..a95560ee 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,14 @@ github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/chroma v0.8.2 h1:x3zkuE2lUk/RIekyAJ3XRqSCP4zwWDfcw/YJCuCAACg= github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= +github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= +github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/aws/aws-sdk-go-v2 v1.9.0 h1:+S+dSqQCN3MSU5vJRu1HqHrq00cJn6heIMU7X9hcsoo= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.7.0 h1:J2cZ7qe+3IpqBEXnHUrFrOjoB9BlsXg7j53vxcl5IVg= @@ -37,6 +40,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= diff --git a/templates/404.tmpl b/templates/404.tmpl new file mode 100644 index 00000000..10aae100 --- /dev/null +++ b/templates/404.tmpl @@ -0,0 +1,15 @@ + + + + + Go by Example: Not Found + + + +
+

Go by Example

+

Sorry, we couldn't find that! Check out the home page?

+{{ template "footer" }} +
+ + diff --git a/templates/example.tmpl b/templates/example.tmpl index 49d2f040..29b185c3 100644 --- a/templates/example.tmpl +++ b/templates/example.tmpl @@ -2,7 +2,7 @@ - Go by Example 中文版: {{.Name}} + Go by Example: {{.Name}} -
-

Go by Example 中文版: {{.Name}}

+

Go by Example: {{.Name}}

{{range .Segs}} {{range .}} @@ -31,7 +30,7 @@ {{.DocsRendered}} @@ -40,7 +39,7 @@ {{end}} {{if .NextExample}}

- 下一个例子: {{.NextExample.Name}} + Next example: {{.NextExample.Name}}.

{{end}} {{ template "footer" }} diff --git a/templates/footer.tmpl b/templates/footer.tmpl index f659096f..916132f5 100644 --- a/templates/footer.tmpl +++ b/templates/footer.tmpl @@ -1,5 +1,5 @@ {{define "footer"}} + by Mark McGranaghan and Eli Bendersky | source | license

{{end}} diff --git a/templates/index.tmpl b/templates/index.tmpl index ba1b5053..520f68bc 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -2,18 +2,27 @@ - Go by Example 中文版 + Go by Example
-

Go by Example 中文版

+

Go by Example

- Go 是一门被设计用来构建简单、高效、可信赖软件的开源程序设计语言。 + Go is an + open source programming language designed for + building simple, fast, and reliable software. + Please read the official + documentation + to learn a bit about Go code, tools packages, + and modules.

- Go by Example 是对 Go 基于实践的介绍,包含一系列带有注释说明的示例程序。查看第一个例子或者浏览下面的完整列表吧。 + Go by Example is a hands-on introduction + to Go using annotated example programs. Check out + the first example or + browse the full list below.

    diff --git a/templates/site.css b/templates/site.css index 3457fa0a..f74c733c 100644 --- a/templates/site.css +++ b/templates/site.css @@ -1,4 +1,4 @@ -/* CSS reset: http://meyerweb.com/eric/tools/css/reset/ */ +/* CSS reset: https://meyerweb.com/eric/tools/css/reset/ */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -19,7 +19,7 @@ time, mark, audio, video { font: inherit; vertical-align: baseline; } -article, aside, details, figcaption, feigure, +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } @@ -29,16 +29,6 @@ body { ol, ul { list-style: none; } -li { - padding: 2px; -} -a { - text-decoration : none; - border-bottom: #777 1px solid; -} -a:hover{ - border-bottom: #000 2px solid; -} blockquote, q { quotes: none; } @@ -60,7 +50,7 @@ body { color: #252519; } em { - font-weight: bold; + font-style: italic; } a, a:visited { color: #261a3b; @@ -90,14 +80,15 @@ p.next { } p.footer { color: grey; + font-size: 75%; } p.footer a, p.footer a:visited { color: grey; } div#intro { - width: 450px; - min-width: 450px; - max-width: 450px; + width: 420px; + min-width: 420px; + max-width: 420px; margin-left: auto; margin-right: auto; margin-bottom: 120px; diff --git a/tools/build b/tools/build index 4f669021..1aca25b1 100755 --- a/tools/build +++ b/tools/build @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e diff --git a/tools/build-loop b/tools/build-loop index 53847018..02d70d57 100755 --- a/tools/build-loop +++ b/tools/build-loop @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash TRAPPING=0 trap "{ echo finishing; TRAPPING=1; }" SIGINT diff --git a/tools/format b/tools/format index 457b13fe..c1045257 100755 --- a/tools/format +++ b/tools/format @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail diff --git a/tools/generate b/tools/generate index af897e5b..be41375a 100755 --- a/tools/generate +++ b/tools/generate @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exec go run tools/generate.go $@ diff --git a/tools/generate.go b/tools/generate.go index 8d8b9441..82e20476 100644 --- a/tools/generate.go +++ b/tools/generate.go @@ -7,16 +7,15 @@ import ( "io" "net/http" "os" - "os/exec" "path/filepath" "regexp" "strings" "text/template" - "github.com/alecthomas/chroma" - "github.com/alecthomas/chroma/formatters/html" - "github.com/alecthomas/chroma/lexers" - "github.com/alecthomas/chroma/styles" + "github.com/alecthomas/chroma/v2" + "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" "github.com/russross/blackfriday/v2" ) @@ -36,6 +35,11 @@ func check(err error) { } } +func isDir(path string) bool { + fileStat, _ := os.Stat(path) + return fileStat.IsDir() +} + func ensureDir(dir string) { err := os.MkdirAll(dir, 0755) check(err) @@ -48,25 +52,6 @@ func copyFile(src, dst string) { check(err) } -func pipe(bin string, arg []string, src string) []byte { - cmd := exec.Command(bin, arg...) - in, err := cmd.StdinPipe() - check(err) - out, err := cmd.StdoutPipe() - check(err) - err = cmd.Start() - check(err) - _, err = in.Write([]byte(src)) - check(err) - err = in.Close() - check(err) - bytes, err := io.ReadAll(out) - check(err) - err = cmd.Wait() - check(err) - return bytes -} - func sha1Sum(s string) string { h := sha1.New() h.Write([]byte(s)) @@ -110,8 +95,8 @@ func debug(msg string) { } } -var docsPat = regexp.MustCompile("^\\s*(\\/\\/|#)\\s") -var dashPat = regexp.MustCompile("\\-+") +var docsPat = regexp.MustCompile(`^(\s*(\/\/|#)\s|\s*\/\/$)`) +var dashPat = regexp.MustCompile(`\-+`) // Seg is a segment of an example type Seg struct { @@ -160,7 +145,6 @@ func parseSegs(sourcePath string) ([]*Seg, string) { lines = append(lines, strings.Replace(line, "\t", " ", -1)) source = append(source, line) } - filecontent := strings.Join(source, "\n") segs := []*Seg{} lastSeen := "" for _, line := range lines { @@ -190,7 +174,12 @@ func parseSegs(sourcePath string) ([]*Seg, string) { newSeg := Seg{Docs: "", Code: line} segs = append(segs, &newSeg) } else { - segs[len(segs)-1].Code = segs[len(segs)-1].Code + "\n" + line + lastSeg := segs[len(segs)-1] + if len(lastSeg.Code) == 0 { + lastSeg.Code = line + } else { + lastSeg.Code = lastSeg.Code + "\n" + line + } } debug("CODE: " + line) lastSeen = "code" @@ -201,11 +190,10 @@ func parseSegs(sourcePath string) ([]*Seg, string) { seg.CodeLeading = (i < (len(segs) - 1)) seg.CodeRun = strings.Contains(seg.Code, "package main") } - return segs, filecontent + return segs, strings.Join(source, "\n") } func chromaFormat(code, filePath string) string { - lexer := lexers.Get(filePath) if lexer == nil { lexer = lexers.Fallback @@ -265,14 +253,7 @@ func parseExamples() []*Example { if verbose() { fmt.Printf("Processing %s [%d/%d]\n", exampleName, i+1, len(exampleNames)) } - exampleNameDisplay := exampleName - names := strings.Split(exampleName, "->") - exampleName = names[0] - exampleNameDisplay = names[0] - if len(names) > 1 && strings.Trim(names[1], " ") != "" { - exampleNameDisplay = names[1] - } - example := Example{Name: exampleNameDisplay} + example := Example{Name: exampleName} exampleID := strings.ToLower(exampleName) exampleID = strings.Replace(exampleID, " ", "-", -1) exampleID = strings.Replace(exampleID, "/", "-", -1) @@ -282,14 +263,16 @@ func parseExamples() []*Example { example.Segs = make([][]*Seg, 0) sourcePaths := mustGlob("examples/" + exampleID + "/*") for _, sourcePath := range sourcePaths { - if strings.HasSuffix(sourcePath, ".hash") { - example.GoCodeHash, example.URLHash = parseHashFile(sourcePath) - } else { - sourceSegs, filecontents := parseAndRenderSegs(sourcePath) - if filecontents != "" { - example.GoCode = filecontents + if !isDir(sourcePath) { + if strings.HasSuffix(sourcePath, ".hash") { + example.GoCodeHash, example.URLHash = parseHashFile(sourcePath) + } else { + sourceSegs, filecontents := parseAndRenderSegs(sourcePath) + if filecontents != "" { + example.GoCode = filecontents + } + example.Segs = append(example.Segs, sourceSegs) } - example.Segs = append(example.Segs, sourceSegs) } } newCodeHash := sha1Sum(example.GoCode) @@ -314,14 +297,12 @@ func renderIndex(examples []*Example) { fmt.Println("Rendering index") } indexTmpl := template.New("index") - _, err := indexTmpl.Parse(mustReadFile("templates/footer.tmpl")) - check(err) - _, err = indexTmpl.Parse(mustReadFile("templates/index.tmpl")) - check(err) + template.Must(indexTmpl.Parse(mustReadFile("templates/footer.tmpl"))) + template.Must(indexTmpl.Parse(mustReadFile("templates/index.tmpl"))) indexF, err := os.Create(siteDir + "/index.html") check(err) - err = indexTmpl.Execute(indexF, examples) - check(err) + defer indexF.Close() + check(indexTmpl.Execute(indexF, examples)) } func renderExamples(examples []*Example) { @@ -329,17 +310,29 @@ func renderExamples(examples []*Example) { fmt.Println("Rendering examples") } exampleTmpl := template.New("example") - _, err := exampleTmpl.Parse(mustReadFile("templates/footer.tmpl")) - check(err) - _, err = exampleTmpl.Parse(mustReadFile("templates/example.tmpl")) - check(err) + template.Must(exampleTmpl.Parse(mustReadFile("templates/footer.tmpl"))) + template.Must(exampleTmpl.Parse(mustReadFile("templates/example.tmpl"))) for _, example := range examples { - exampleF, err := os.Create(siteDir + "/" + example.ID + ".html") + exampleF, err := os.Create(siteDir + "/" + example.ID) check(err) - exampleTmpl.Execute(exampleF, example) + defer exampleF.Close() + check(exampleTmpl.Execute(exampleF, example)) } } +func render404() { + if verbose() { + fmt.Println("Rendering 404") + } + tmpl := template.New("404") + template.Must(tmpl.Parse(mustReadFile("templates/footer.tmpl"))) + template.Must(tmpl.Parse(mustReadFile("templates/404.tmpl"))) + file, err := os.Create(siteDir + "/404.html") + check(err) + defer file.Close() + check(tmpl.Execute(file, "")) +} + func main() { if len(os.Args) > 1 { siteDir = os.Args[1] @@ -349,12 +342,12 @@ func main() { copyFile("templates/site.css", siteDir+"/site.css") copyFile("templates/site.js", siteDir+"/site.js") copyFile("templates/favicon.ico", siteDir+"/favicon.ico") - copyFile("templates/404.html", siteDir+"/404.html") copyFile("templates/play.png", siteDir+"/play.png") copyFile("templates/clipboard.png", siteDir+"/clipboard.png") examples := parseExamples() renderIndex(examples) renderExamples(examples) + render404() } var SimpleShellOutputLexer = chroma.MustNewLexer( @@ -364,30 +357,32 @@ var SimpleShellOutputLexer = chroma.MustNewLexer( Filenames: []string{"*.sh"}, MimeTypes: []string{}, }, - chroma.Rules{ - "root": { - // $ or > triggers the start of prompt formatting - {`^\$`, chroma.GenericPrompt, chroma.Push("prompt")}, - {`^>`, chroma.GenericPrompt, chroma.Push("prompt")}, - - // empty lines are just text - {`^$\n`, chroma.Text, nil}, - - // otherwise its all output - {`[^\n]+$\n?`, chroma.GenericOutput, nil}, - }, - "prompt": { - // when we find newline, do output formatting rules - {`\n`, chroma.Text, chroma.Push("output")}, - // otherwise its all text - {`[^\n]+$`, chroma.Text, nil}, - }, - "output": { - // sometimes there isn't output so we go right back to prompt - {`^\$`, chroma.GenericPrompt, chroma.Pop(1)}, - {`^>`, chroma.GenericPrompt, chroma.Pop(1)}, - // otherwise its all output - {`[^\n]+$\n?`, chroma.GenericOutput, nil}, - }, + func() chroma.Rules { + return chroma.Rules{ + "root": { + // $ or > triggers the start of prompt formatting + {`^\$`, chroma.GenericPrompt, chroma.Push("prompt")}, + {`^>`, chroma.GenericPrompt, chroma.Push("prompt")}, + + // empty lines are just text + {`^$\n`, chroma.Text, nil}, + + // otherwise its all output + {`[^\n]+$\n?`, chroma.GenericOutput, nil}, + }, + "prompt": { + // when we find newline, do output formatting rules + {`\n`, chroma.Text, chroma.Push("output")}, + // otherwise its all text + {`[^\n]+$`, chroma.Text, nil}, + }, + "output": { + // sometimes there isn't output so we go right back to prompt + {`^\$`, chroma.GenericPrompt, chroma.Pop(1)}, + {`^>`, chroma.GenericPrompt, chroma.Pop(1)}, + // otherwise its all output + {`[^\n]+$\n?`, chroma.GenericOutput, nil}, + }, + } }, ) diff --git a/tools/measure b/tools/measure index f62c5783..39d583b5 100755 --- a/tools/measure +++ b/tools/measure @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exec go run tools/measure.go diff --git a/tools/measure.go b/tools/measure.go index 7c9d6beb..04c1a30b 100644 --- a/tools/measure.go +++ b/tools/measure.go @@ -21,6 +21,11 @@ func readLines(path string) []string { return strings.Split(string(srcBytes), "\n") } +func isDir(path string) bool { + fileStat, _ := os.Stat(path) + return fileStat.IsDir() +} + var commentPat = regexp.MustCompile("\\s*\\/\\/") func main() { @@ -29,15 +34,17 @@ func main() { foundLongFile := false for _, sourcePath := range sourcePaths { foundLongLine := false - lines := readLines(sourcePath) - for i, line := range lines { - // Convert tabs to spaces before measuring, so we get an accurate measure - // of how long the output will end up being. - line := strings.Replace(line, "\t", " ", -1) - if !foundLongLine && !commentPat.MatchString(line) && (utf8.RuneCountInString(line) > 58) { - fmt.Printf("measure: %s:%d\n", sourcePath, i+1) - foundLongLine = true - foundLongFile = true + if !isDir(sourcePath) { + lines := readLines(sourcePath) + for i, line := range lines { + // Convert tabs to spaces before measuring, so we get an accurate measure + // of how long the output will end up being. + line := strings.Replace(line, "\t", " ", -1) + if !foundLongLine && !commentPat.MatchString(line) && (utf8.RuneCountInString(line) > 58) { + fmt.Printf("measure: %s:%d\n", sourcePath, i+1) + foundLongLine = true + foundLongFile = true + } } } } diff --git a/tools/serve b/tools/serve index baf68d12..1d4b594b 100755 --- a/tools/serve +++ b/tools/serve @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exec go run tools/serve.go diff --git a/tools/serve.go b/tools/serve.go index 57ec3fef..b31a6b0f 100644 --- a/tools/serve.go +++ b/tools/serve.go @@ -3,28 +3,11 @@ package main import ( "fmt" "net/http" - "os" ) -type HTMLDir struct { - d http.Dir -} - -func (d HTMLDir) Open(name string) (http.File, error) { - f, err := d.d.Open(name) - if os.IsNotExist(err) { - if f, err := d.d.Open(name + ".html"); err == nil { - return f, nil - } - } - return f, err -} - func main() { port := "8000" publicDir := "public" fmt.Printf("Serving Go by Example at http://127.0.0.1:%s\n", port) - fs := http.FileServer(HTMLDir{http.Dir(publicDir)}) - http.Handle("/", http.StripPrefix("/", fs)) - http.ListenAndServe(":"+port, nil) + http.ListenAndServe(":"+port, http.FileServer(http.Dir(publicDir))) } diff --git a/tools/test b/tools/test index c0c9c551..c3805156 100755 --- a/tools/test +++ b/tools/test @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Sanity testing of the examples. diff --git a/tools/upload b/tools/upload index 5ca34efb..c00a2ab4 100755 --- a/tools/upload +++ b/tools/upload @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exec go run tools/upload.go -region us-east-1 -bucket gobyexample.com
- {{if .CodeRun}}{{end}} + {{if .CodeRun}}{{end}} {{.CodeRendered}}