weave: fix %include parsing, add highlighting

Recognize that

  %include file -

means "no tag, no caption" instead of "the tag is -, add caption."

Write "go" after backticks, which GitHub will render with syntax
highlighting.

Change-Id: I9e0b3618b5fba8834d7f06b8fc7adf0781574a2d
Reviewed-on: https://go-review.googlesource.com/c/example/+/673875
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gotypes/README.md b/gotypes/README.md
index 350fbc6..9886a11 100644
--- a/gotypes/README.md
+++ b/gotypes/README.md
@@ -143,7 +143,7 @@
 
 	// go get golang.org/x/example/gotypes/pkginfo
 
-```
+```go
 package main
 
 import (
@@ -247,7 +247,7 @@
 (The hexadecimal number may vary from one run to the next.)
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/pkginfo
 $ ./pkginfo
 Package  "cmd/hello"
@@ -505,7 +505,7 @@
 
 	// go get golang.org/x/example/gotypes/defsuses
 
-```
+```go
 func PrintDefsUses(fset *token.FileSet, files ...*ast.File) error {
 	conf := types.Config{Importer: importer.Default()}
 	info := &types.Info{
@@ -535,7 +535,7 @@
 
 	// go get golang.org/x/example/gotypes/hello
 
-```
+```go
 package main
 
 import "fmt"
@@ -549,7 +549,7 @@
 This is what it prints:
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/defsuses
 $ ./defsuses
 hello.go:1:9: "main" defines <nil>
@@ -796,7 +796,7 @@
 
 	// go get golang.org/x/example/gotypes/lookup
 
-```
+```go
 func main() {
 	fset := token.NewFileSet()
 	f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
@@ -839,7 +839,7 @@
 and so on.
 
 
-```
+```go
 const hello = `
 package main
 
@@ -863,7 +863,7 @@
 Here's the output:
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/lookup
 $ ./lookup
 At hello.go:6:1,        "append" = builtin append
@@ -1566,7 +1566,7 @@
 
 	// go get golang.org/x/example/gotypes/typeandvalue
 
-```
+```go
 // f is a parsed, type-checked *ast.File.
 ast.Inspect(f, func(n ast.Node) bool {
 	if expr, ok := n.(ast.Expr); ok {
@@ -1596,7 +1596,7 @@
 Given this input:
 
 
-```
+```go
 const input = `
 package main
 
@@ -1613,7 +1613,7 @@
 the program prints:
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/typeandvalue
 $ ./typeandvalue
 make(map[string]int)            mode:  value
@@ -1672,7 +1672,7 @@
 
 	// go get golang.org/x/example/gotypes/nilfunc
 
-```
+```go
 // CheckNilFuncComparison reports unintended comparisons
 // of functions against nil, e.g., "if x.Method == nil {".
 func CheckNilFuncComparison(info *types.Info, n ast.Node) {
@@ -1718,7 +1718,7 @@
 Given this input,
 
 
-```
+```go
 const input = `package main
 
 import "bytes"
@@ -1736,7 +1736,7 @@
 the program reports these errors:
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/nilfunc
 $ ./nilfunc
 input.go:7:5: comparison of function Bytes == nil is always false
@@ -1969,7 +1969,7 @@
 Here's an example:
 
 
-```
+```go
 $ ./skeleton io ReadWriteCloser buffer
 // *buffer implements io.ReadWriteCloser.
 type buffer struct{}
@@ -1993,7 +1993,7 @@
 
 	// go get golang.org/x/example/gotypes/skeleton
 
-```
+```go
 func PrintSkeleton(pkg *types.Package, ifacename, concname string) error {
 	obj := pkg.Scope().Lookup(ifacename)
 	if obj == nil {
@@ -2051,7 +2051,7 @@
 Here's another example that illustrates it:
 
 
-```
+```go
 $ ./skeleton net/http Handler myHandler
 // *myHandler implements net/http.Handler.
 type myHandler struct{}
@@ -2067,7 +2067,7 @@
 
 	// go get golang.org/x/example/gotypes/implements
 
-```
+```go
 // Find all named types at package level.
 var allNamed []*types.Named
 for _, name := range pkg.Scope().Names() {
@@ -2099,7 +2099,7 @@
 
 	// go get golang.org/x/example/gotypes/implements
 
-```
+```go
 const input = `package main
 
 type A struct{}
@@ -2118,7 +2118,7 @@
 the program prints:
 
 
-```
+```go
 $ go build golang.org/x/example/gotypes/implements
 $ ./implements
 *hello.A satisfies hello.I
@@ -2276,7 +2276,7 @@
 
 	// go get golang.org/x/example/gotypes/hugeparam
 
-```
+```go
 var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes")
 
 func PrintHugeParams(fset *token.FileSet, info *types.Info, sizes types.Sizes, files []*ast.File) {
@@ -2324,7 +2324,7 @@
 is copied.
 
 
-```
+```go
 % ./hugeparam encoding/xml
 /go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes
 /go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes
@@ -2408,7 +2408,7 @@
 Here's an example:
 
 
-```
+```go
 $ ./doc net/http File
 type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader}
 $GOROOT/src/io/io.go:92:2: method (net/http.File) Close() error
@@ -2435,7 +2435,7 @@
 
 	// go get golang.org/x/example/gotypes/doc
 
-```
+```go
 pkgpath, name := os.Args[1], os.Args[2]
 
 // Load complete type information for the specified packages,
@@ -2465,7 +2465,7 @@
 
 	// go get golang.org/x/example/gotypes/doc
 
-```
+```go
 // Print the object and its methods (incl. location of definition).
 fmt.Println(obj)
 for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
diff --git a/internal/cmd/weave/weave.go b/internal/cmd/weave/weave.go
index d833653..880b415 100644
--- a/internal/cmd/weave/weave.go
+++ b/internal/cmd/weave/weave.go
@@ -27,6 +27,7 @@
 import (
 	"bufio"
 	"bytes"
+	"flag"
 	"fmt"
 	"io"
 	"log"
@@ -37,13 +38,14 @@
 )
 
 func main() {
+	flag.Parse()
 	log.SetFlags(0)
 	log.SetPrefix("weave: ")
-	if len(os.Args) != 2 {
+	if flag.NArg() != 1 {
 		log.Fatal("usage: weave input.md\n")
 	}
 
-	f, err := os.Open(os.Args[1])
+	f, err := os.Open(flag.Arg(0))
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -100,26 +102,38 @@
 			}
 		case strings.HasPrefix(line, "%include"):
 			words := strings.Fields(line)
-			if len(words) < 2 {
-				log.Fatal(line)
+			var section string
+			caption := true
+			switch len(words) {
+			case 2: // %include filename
+			// Nothing to do.
+			case 3: // %include filename section OR %include filename -
+				if words[2] == "-" {
+					caption = false
+				} else {
+					section = words[2]
+				}
+			case 4: // %include filename section -
+				section = words[2]
+				if words[3] != "-" {
+					log.Fatalf("last word is not '-': %s", line)
+				}
+				caption = false
+			default:
+				log.Fatalf("wrong # words (want 2-4): %s", line)
 			}
 			filename := words[1]
 
-			// Show caption unless '-' follows.
-			if len(words) < 4 || words[3] != "-" {
+			if caption {
 				fmt.Printf("	// go get golang.org/x/example/%s/%s\n\n",
 					curDir, filepath.Dir(filename))
 			}
 
-			section := ""
-			if len(words) > 2 {
-				section = words[2]
-			}
 			s, err := include(filename, section)
 			if err != nil {
 				log.Fatal(err)
 			}
-			fmt.Println("```")
+			fmt.Println("```go")
 			fmt.Println(cleanListing(s)) // TODO(adonovan): escape /^```/ in s
 			fmt.Println("```")
 		default:
diff --git a/slog-handler-guide/README.md b/slog-handler-guide/README.md
index 01027cc..5342c06 100644
--- a/slog-handler-guide/README.md
+++ b/slog-handler-guide/README.md
@@ -115,7 +115,7 @@
 We begin with the `IndentHandler` type
 and the `New` function that constructs it from an `io.Writer` and options:
 
-```
+```go
 type IndentHandler struct {
 	opts Options
 	// TODO: state for WithGroup and WithAttrs
@@ -186,7 +186,7 @@
 Our `IndentHandler` doesn't use the context. It just compares the argument level
 with its configured minimum level:
 
-```
+```go
 func (h *IndentHandler) Enabled(ctx context.Context, level slog.Level) bool {
 	return level >= h.opts.Level.Level()
 }
@@ -232,7 +232,7 @@
 
 That is how `IndentHandler.Handle` is structured:
 
-```
+```go
 func (h *IndentHandler) Handle(ctx context.Context, r slog.Record) error {
 	buf := make([]byte, 0, 1024)
 	if !r.Time.IsZero() {
@@ -287,7 +287,7 @@
 At the heart of the handler is the `appendAttr` method, responsible for
 formatting a single attribute:
 
-```
+```go
 func (h *IndentHandler) appendAttr(buf []byte, a slog.Attr, indentLevel int) []byte {
 	// Resolve the Attr's value before doing anything else.
 	a.Value = a.Value.Resolve()
@@ -405,7 +405,7 @@
 and loop over that slice in `Handle`. We start with a struct that can hold
 either a group name or some attributes:
 
-```
+```go
 // groupOrAttrs holds either a group name or a list of slog.Attrs.
 type groupOrAttrs struct {
 	group string      // group name if non-empty
@@ -415,7 +415,7 @@
 
 Then we add a slice of `groupOrAttrs` to our handler:
 
-```
+```go
 type IndentHandler struct {
 	opts Options
 	goas []groupOrAttrs
@@ -429,7 +429,7 @@
 To that end, we define a method that will copy our handler struct
 and append one `groupOrAttrs` to the copy:
 
-```
+```go
 func (h *IndentHandler) withGroupOrAttrs(goa groupOrAttrs) *IndentHandler {
 	h2 := *h
 	h2.goas = make([]groupOrAttrs, len(h.goas)+1)
@@ -446,7 +446,7 @@
 
 The `With` methods are easy to write using `withGroupOrAttrs`:
 
-```
+```go
 func (h *IndentHandler) WithGroup(name string) slog.Handler {
 	if name == "" {
 		return h
@@ -465,7 +465,7 @@
 The `Handle` method can now process the groupOrAttrs slice after
 the built-in attributes and before the ones in the record:
 
-```
+```go
 func (h *IndentHandler) Handle(ctx context.Context, r slog.Record) error {
 	buf := make([]byte, 0, 1024)
 	if !r.Time.IsZero() {
@@ -587,7 +587,7 @@
 To pre-format the arguments to `WithAttrs`, we need to keep track of some
 additional state in the `IndentHandler` struct.
 
-```
+```go
 type IndentHandler struct {
 	opts           Options
 	preformatted   []byte   // data from WithGroup and WithAttrs
@@ -608,7 +608,7 @@
 This `WithGroup` is a lot like the previous one: it just remembers the
 new group, which is unopened initially.
 
-```
+```go
 func (h *IndentHandler) WithGroup(name string) slog.Handler {
 	if name == "" {
 		return h
@@ -624,7 +624,7 @@
 
 `WithAttrs` does all the pre-formatting:
 
-```
+```go
 func (h *IndentHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
 	if len(attrs) == 0 {
 		return h
@@ -668,7 +668,7 @@
 It's the `Handle` method's job to insert the pre-formatted material in the right
 place, which is after the built-in attributes and before the ones in the record:
 
-```
+```go
 func (h *IndentHandler) Handle(ctx context.Context, r slog.Record) error {
 	buf := make([]byte, 0, 1024)
 	if !r.Time.IsZero() {
@@ -728,7 +728,7 @@
 a function that returns its output formatted as a slice of maps. Here is the test function
 for our example handler:
 
-```
+```go
 func TestSlogtest(t *testing.T) {
 	var buf bytes.Buffer
 	err := slogtest.TestHandler(New(&buf, nil), func() []map[string]any {
@@ -754,7 +754,7 @@
 Our example output is enough like YAML so that we can use the `gopkg.in/yaml.v3`
 package to parse it:
 
-```
+```go
 func parseLogEntries(t *testing.T, data []byte) []map[string]any {
 	entries := bytes.Split(data, []byte("---\n"))
 	entries = entries[:len(entries)-1] // last one is empty
@@ -985,7 +985,7 @@
 
 Here is our pool and its associated functions:
 
-```
+```go
 var bufPool = sync.Pool{
 	New: func() any {
 		b := make([]byte, 0, 1024)