func NewSession(options *Options) *Session { if options.GOROOT == "" { options.GOROOT = build.Default.GOROOT } if options.GOPATH == "" { options.GOPATH = build.Default.GOPATH } options.Verbose = options.Verbose || options.Watch s := &Session{ options: options, Packages: make(map[string]*PackageData), } s.ImportContext = compiler.NewImportContext(s.ImportPackage) if options.Watch { if out, err := exec.Command("ulimit", "-n").Output(); err == nil { if n, err := strconv.Atoi(strings.TrimSpace(string(out))); err == nil && n < 1024 { fmt.Printf("Warning: The maximum number of open file descriptors is very low (%d). Change it with 'ulimit -n 8192'.\n", n) } } var err error s.Watcher, err = fsnotify.NewWatcher() if err != nil { panic(err) } } return s }
func NewSession(options *Options) *Session { if options.GOROOT == "" { options.GOROOT = build.Default.GOROOT } if options.GOPATH == "" { options.GOPATH = build.Default.GOPATH } options.Verbose = options.Verbose || options.Watch s := &Session{ options: options, Packages: make(map[string]*PackageData), } s.ImportContext = compiler.NewImportContext(s.ImportPackage) if options.Watch { var err error s.Watcher, err = fsnotify.NewWatcher() if err != nil { panic(err) } } return s }
func main() { var location = dom.GetWindow().Top().Location() // We might be inside an iframe, but want to use the location of topmost window. codeReady := make(chan struct{}) // Used to synchronize when "code" value is ready. app := angularjs.NewModule("playground", nil, nil) app.NewController("PlaygroundCtrl", func(scope *angularjs.Scope) { if strings.HasPrefix(location.Hash, "#/") { id := location.Hash[2:] req := xhr.NewRequest("GET", "http://"+snippetStoreHost+"/p/"+id) req.ResponseType = xhr.ArrayBuffer go func() { err := req.Send(nil) if err != nil || req.Status != 200 { scope.Apply(func() { scope.Set("output", []Line{Line{"type": "err", "content": `failed to load snippet "` + id + `"`}}) }) return } data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte) scope.Apply(func() { scope.Set("code", string(data)) close(codeReady) }) }() } else { scope.Set("code", "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gopherjs/gopherjs/js\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n\tjs.Global.Call(\"alert\", \"Hello, JavaScript\")\n\tprintln(\"Hello, JS console\")\n}\n") close(codeReady) } scope.Set("shareUrl", "") scope.Set("showShareUrl", false) packages := make(map[string]*compiler.Archive) var pkgsToLoad map[string]struct{} importContext := compiler.NewImportContext(func(path string) (*compiler.Archive, error) { if pkg, found := packages[path]; found { return pkg, nil } pkgsToLoad[path] = struct{}{} return &compiler.Archive{}, nil }) fileSet := token.NewFileSet() pkgsReceived := 0 setupEnvironment(scope) codeArea := angularjs.ElementById("code") codeArea.On("input", func(e *angularjs.Event) { scope.Set("showShareUrl", false) location.Hash = "" }) codeArea.On("keydown", func(e *angularjs.Event) { toInsert := "" switch e.KeyCode { case '\t': toInsert = "\t" case '\r': toInsert = "\n" start := codeArea.Prop("selectionStart").Int() code := scope.Get("code").String() i := strings.LastIndex(code[:start], "\n") + 1 for i < start { c := code[i] if c != ' ' && c != '\t' { break } toInsert += string(c) i++ } } if toInsert != "" { scope.Set("showShareUrl", false) location.Hash = "" start := codeArea.Prop("selectionStart").Int() end := codeArea.Prop("selectionEnd").Int() code := scope.Get("code").String() scope.Apply(func() { scope.Set("code", code[:start]+toInsert+code[end:]) }) codeArea.SetProp("selectionStart", start+len(toInsert)) codeArea.SetProp("selectionEnd", start+len(toInsert)) e.PreventDefault() } }) var run func(bool) run = func(loadOnly bool) { output = nil scope.Set("output", output) pkgsToLoad = make(map[string]struct{}) file, err := parser.ParseFile(fileSet, "prog.go", []byte(scope.Get("code").String()), parser.ParseComments) if err != nil { if list, ok := err.(scanner.ErrorList); ok { for _, entry := range list { output = append(output, Line{"type": "err", "content": entry.Error()}) } scope.Set("output", output) return } scope.Set("output", []Line{Line{"type": "err", "content": err.Error()}}) return } mainPkg, err := compiler.Compile("main", []*ast.File{file}, fileSet, importContext, false) packages["main"] = mainPkg if err != nil && len(pkgsToLoad) == 0 { if list, ok := err.(compiler.ErrorList); ok { var output []Line for _, entry := range list { output = append(output, Line{"type": "err", "content": entry.Error()}) } scope.Set("output", output) return } scope.Set("output", []Line{Line{"type": "err", "content": err.Error()}}) return } var allPkgs []*compiler.Archive if len(pkgsToLoad) == 0 { allPkgs, _ = compiler.ImportDependencies(mainPkg, importContext.Import) } if len(pkgsToLoad) != 0 { pkgsReceived = 0 for path := range pkgsToLoad { req := xhr.NewRequest("GET", "pkg/"+path+".a.js") req.ResponseType = xhr.ArrayBuffer go func(path string) { err := req.Send(nil) if err != nil || req.Status != 200 { scope.Apply(func() { scope.Set("output", []Line{Line{"type": "err", "content": `failed to load package "` + path + `"`}}) }) return } data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte) packages[path], err = compiler.ReadArchive(path+".a", path, bytes.NewReader(data), importContext.Packages) if err != nil { scope.Apply(func() { scope.Set("output", []Line{Line{"type": "err", "content": err.Error()}}) }) return } pkgsReceived++ if pkgsReceived == len(pkgsToLoad) { run(loadOnly) } }(path) } return } if loadOnly { return } jsCode := bytes.NewBuffer(nil) jsCode.WriteString("try{\n") compiler.WriteProgramCode(allPkgs, &compiler.SourceMapFilter{Writer: jsCode}) jsCode.WriteString("} catch (err) {\ngoPanicHandler(err.message);\n}\n") js.Global.Set("$checkForDeadlock", true) js.Global.Call("eval", js.InternalObject(jsCode.String())) } scope.Set("run", run) go func() { <-codeReady // Wait for "code" value to be ready. run(true) }() scope.Set("format", func() { out, err := format.Source([]byte(scope.Get("code").String())) if err != nil { scope.Set("output", []Line{Line{"type": "err", "content": err.Error()}}) return } scope.Set("code", string(out)) scope.Set("output", []Line{}) }) scope.Set("share", func() { req := xhr.NewRequest("POST", "http://"+snippetStoreHost+"/share") req.ResponseType = xhr.ArrayBuffer go func() { err := req.Send([]byte(scope.Get("code").String())) // Send as binary. if err != nil || req.Status != 200 { scope.Apply(func() { scope.Set("output", []Line{Line{"type": "err", "content": `failed to share snippet`}}) }) return } data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte) scope.Apply(func() { id := string(data) location.Hash = "#/" + id scope.Set("shareUrl", location.String()) scope.Set("showShareUrl", true) // TODO: Do this better using AngularJS. // Perhaps using http://stackoverflow.com/questions/14833326/how-to-set-focus-on-input-field/18295416. go func() { time.Sleep(time.Millisecond) dom.GetWindow().Document().GetElementByID("share-url").(*dom.HTMLInputElement).Select() }() }) }() }) // Start watching for hashchange events, and reload snippet if it happens. dom.GetWindow().Top().AddEventListener("hashchange", false, func(event dom.Event) { event.PreventDefault() if strings.HasPrefix(location.Hash, "#/") { id := location.Hash[2:] req := xhr.NewRequest("GET", "http://"+snippetStoreHost+"/p/"+id) req.ResponseType = xhr.ArrayBuffer go func() { err := req.Send(nil) if err != nil || req.Status != 200 { scope.Apply(func() { scope.Set("output", []Line{Line{"type": "err", "content": `failed to load snippet "` + id + `"`}}) }) return } data := js.Global.Get("Uint8Array").New(req.Response).Interface().([]byte) scope.Apply(func() { scope.Set("code", string(data)) }) }() } }) }) }
func control(scope *angularjs.Scope) { scope.Set("code", initCode) // scope.Set("showGenerated", false) // scope.Set("generated", `(generated code will be shown here after clicking "Run")`) packages := make(map[string]*compiler.Archive) var pkgsToLoad []string importContext := compiler.NewImportContext( func(path string) (*compiler.Archive, error) { if pkg, found := packages[path]; found { return pkg, nil } pkgsToLoad = append(pkgsToLoad, path) return &compiler.Archive{}, nil }, ) fileSet := token.NewFileSet() pkgsReceived := 0 setupEnvironment(scope) var run func(bool) run = func(loadOnly bool) { output = nil scope.Set("output", output) pkgsToLoad = nil file, err := parser.ParseFile(fileSet, "prog.go", getCode(), parser.ParseComments, ) if err != nil { if list, ok := err.(scanner.ErrorList); ok { for _, entry := range list { output = append(output, errErrLine(entry)) } scope.Set("output", output) return } scope.Set("output", []Line{errErrLine(err)}) return } mainPkg, err := compiler.Compile("main", []*ast.File{file}, fileSet, importContext, false, ) packages["main"] = mainPkg if err != nil && len(pkgsToLoad) == 0 { if list, ok := err.(compiler.ErrorList); ok { output := make([]Line, 0) for _, entry := range list { output = append(output, errErrLine(entry)) } scope.Set("output", output) return } scope.Set("output", []Line{errErrLine(err)}) return } var allPkgs []*compiler.Archive if len(pkgsToLoad) == 0 { for _, depPath := range mainPkg.Dependencies { dep, _ := importContext.Import(string(depPath)) allPkgs = append(allPkgs, dep) } allPkgs = append(allPkgs, mainPkg) } if len(pkgsToLoad) != 0 { pkgsReceived = 0 for _, p := range pkgsToLoad { path := p req := js.Global.Get("XMLHttpRequest").New() req.Call("open", "GET", "pkg/"+path+".a", true) req.Set("responseType", "arraybuffer") req.Set("onload", func() { if req.Get("status").Int() != 200 { f := func() { emsg := fmt.Sprintf("cannot load package \"%s\"", path) scope.Set("output", []Line{errLine(emsg)}) } scope.Apply(f) return } data := js.Global.Get("Uint8Array").New(req.Get("response")).Interface().([]byte) packages[path], err = compiler.UnmarshalArchive( path+".a", path, []byte(data), importContext, ) if err != nil { scope.Apply(func() { scope.Set("output", []Line{errErrLine(err)}) }) return } pkgsReceived++ if pkgsReceived == len(pkgsToLoad) { run(loadOnly) } }) req.Call("send") } return } if loadOnly { return } mainPkgCode := bytes.NewBuffer(nil) compiler.WritePkgCode(packages["main"], false, &compiler.SourceMapFilter{Writer: mainPkgCode}, ) // scope.Set("generated", mainPkgCode.String()) jsCode := bytes.NewBuffer(nil) jsCode.WriteString("try{\n") compiler.WriteProgramCode(allPkgs, importContext, &compiler.SourceMapFilter{Writer: jsCode}, ) jsCode.WriteString("} catch (err) {\ngoPanicHandler(err.message);\n}\n") js.Global.Call("eval", js.InternalObject(jsCode.String())) } scope.Set("run", run) run(true) scope.Set("format", func() { out, err := format.Source(getCode()) if err != nil { scope.Set("output", []Line{errErrLine(err)}) return } setCode(string(out)) scope.Set("output", []Line{}) }) }