Skip to content
This repository was archived by the owner on Aug 24, 2022. It is now read-only.

Commit 2d8ed87

Browse files
committed
添加 tour 资源
1 parent 946040c commit 2d8ed87

30 files changed

+8705
-0
lines changed

tour/appengine.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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 "" }

tour/fmt.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
}

tour/local.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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+
"flag"
11+
"fmt"
12+
"go/build"
13+
"io"
14+
"log"
15+
"net"
16+
"net/http"
17+
"net/url"
18+
"os"
19+
"os/exec"
20+
"path/filepath"
21+
"runtime"
22+
"strings"
23+
"time"
24+
25+
"golang.org/x/tools/playground/socket"
26+
27+
// Imports so that go build/install automatically installs them.
28+
_ "golang.org/x/tour/pic"
29+
_ "golang.org/x/tour/tree"
30+
_ "golang.org/x/tour/wc"
31+
)
32+
33+
const (
34+
basePkg = "golang.org/x/tour/"
35+
socketPath = "/socket"
36+
)
37+
38+
var (
39+
httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
40+
openBrowser = flag.Bool("openbrowser", true, "open browser automatically")
41+
)
42+
43+
var (
44+
// GOPATH containing the tour packages
45+
gopath = os.Getenv("GOPATH")
46+
47+
httpAddr string
48+
)
49+
50+
// isRoot reports whether path is the root directory of the tour tree.
51+
// To be the root, it must have content and template subdirectories.
52+
func isRoot(path string) bool {
53+
_, err := os.Stat(filepath.Join(path, "content", "welcome.article"))
54+
if err == nil {
55+
_, err = os.Stat(filepath.Join(path, "template", "index.tmpl"))
56+
}
57+
return err == nil
58+
}
59+
60+
func findRoot() (string, error) {
61+
ctx := build.Default
62+
p, err := ctx.Import(basePkg, "", build.FindOnly)
63+
if err == nil && isRoot(p.Dir) {
64+
return p.Dir, nil
65+
}
66+
tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour")
67+
ctx.GOPATH = tourRoot
68+
p, err = ctx.Import(basePkg, "", build.FindOnly)
69+
if err == nil && isRoot(tourRoot) {
70+
gopath = tourRoot
71+
return tourRoot, nil
72+
}
73+
return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH")
74+
}
75+
76+
func main() {
77+
flag.Parse()
78+
79+
// find and serve the go tour files
80+
root, err := findRoot()
81+
if err != nil {
82+
log.Fatalf("Couldn't find tour files: %v", err)
83+
}
84+
85+
log.Println("Serving content from", root)
86+
87+
host, port, err := net.SplitHostPort(*httpListen)
88+
if err != nil {
89+
log.Fatal(err)
90+
}
91+
if host == "" {
92+
host = "localhost"
93+
}
94+
if host != "127.0.0.1" && host != "localhost" {
95+
log.Print(localhostWarning)
96+
}
97+
httpAddr = host + ":" + port
98+
99+
if err := initTour(root, "SocketTransport"); err != nil {
100+
log.Fatal(err)
101+
}
102+
103+
http.HandleFunc("/", rootHandler)
104+
http.HandleFunc("/lesson/", lessonHandler)
105+
106+
origin := &url.URL{Scheme: "http", Host: host + ":" + port}
107+
http.Handle(socketPath, socket.NewHandler(origin))
108+
109+
// Keep these static file handlers in sync with ../app.yaml.
110+
static := http.FileServer(http.Dir(root))
111+
http.Handle("/content/img/", static)
112+
http.Handle("/static/", static)
113+
imgDir := filepath.Join(root, "static", "img")
114+
http.Handle("/favicon.ico", http.FileServer(http.Dir(imgDir)))
115+
116+
go func() {
117+
url := "http://" + httpAddr
118+
if waitServer(url) && *openBrowser && startBrowser(url) {
119+
log.Printf("A browser window should open. If not, please visit %s", url)
120+
} else {
121+
log.Printf("Please open your web browser and visit %s", url)
122+
}
123+
}()
124+
log.Fatal(http.ListenAndServe(httpAddr, nil))
125+
}
126+
127+
// rootHandler returns a handler for all the requests except the ones for lessons.
128+
func rootHandler(w http.ResponseWriter, r *http.Request) {
129+
if err := renderUI(w); err != nil {
130+
log.Println(err)
131+
}
132+
}
133+
134+
// lessonHandler handler the HTTP requests for lessons.
135+
func lessonHandler(w http.ResponseWriter, r *http.Request) {
136+
lesson := strings.TrimPrefix(r.URL.Path, "/lesson/")
137+
if err := writeLesson(lesson, w); err != nil {
138+
if err == lessonNotFound {
139+
http.NotFound(w, r)
140+
} else {
141+
log.Println(err)
142+
}
143+
}
144+
}
145+
146+
const localhostWarning = `
147+
WARNING! WARNING! WARNING!
148+
149+
I appear to be listening on an address that is not localhost.
150+
Anyone with access to this address and port will have access
151+
to this machine as the user running gotour.
152+
153+
If you don't understand this message, hit Control-C to terminate this process.
154+
155+
WARNING! WARNING! WARNING!
156+
`
157+
158+
type response struct {
159+
Output string `json:"output"`
160+
Errors string `json:"compile_errors"`
161+
}
162+
163+
func init() {
164+
socket.Environ = environ
165+
}
166+
167+
// environ returns the original execution environment with GOPATH
168+
// replaced (or added) with the value of the global var gopath.
169+
func environ() (env []string) {
170+
for _, v := range os.Environ() {
171+
if !strings.HasPrefix(v, "GOPATH=") {
172+
env = append(env, v)
173+
}
174+
}
175+
env = append(env, "GOPATH="+gopath)
176+
return
177+
}
178+
179+
// waitServer waits some time for the http Server to start
180+
// serving url. The return value reports whether it starts.
181+
func waitServer(url string) bool {
182+
tries := 20
183+
for tries > 0 {
184+
resp, err := http.Get(url)
185+
if err == nil {
186+
resp.Body.Close()
187+
return true
188+
}
189+
time.Sleep(100 * time.Millisecond)
190+
tries--
191+
}
192+
return false
193+
}
194+
195+
// startBrowser tries to open the URL in a browser, and returns
196+
// whether it succeed.
197+
func startBrowser(url string) bool {
198+
// try to start the browser
199+
var args []string
200+
switch runtime.GOOS {
201+
case "darwin":
202+
args = []string{"open"}
203+
case "windows":
204+
args = []string{"cmd", "/c", "start"}
205+
default:
206+
args = []string{"xdg-open"}
207+
}
208+
cmd := exec.Command(args[0], append(args[1:], url)...)
209+
return cmd.Start() == nil
210+
}
211+
212+
// prepContent for the local tour simply returns the content as-is.
213+
func prepContent(r io.Reader) io.Reader { return r }
214+
215+
// socketAddr returns the WebSocket handler address.
216+
func socketAddr() string { return "ws://" + httpAddr + socketPath }

0 commit comments

Comments
 (0)