diff --git a/build/Dockerfile b/build/Dockerfile
index 7bda7fe8..10eed988 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -24,7 +24,8 @@ WORKDIR /opt/playground
ENV GOROOT /usr/local/go
ENV APP_CLEAN_INTERVAL=10m
ENV APP_DEBUG=false
-ENV APP_PLAYGROUND_URL=https://play.golang.org
+ENV APP_PLAYGROUND_URL='https://play.golang.org'
+ENV APP_GOTIP_URL='https://gotipplay.golang.org'
COPY data ./data
COPY --from=ui-build /tmp/web/build ./public
COPY --from=build /tmp/playground/server .
@@ -32,4 +33,4 @@ COPY --from=build /tmp/playground/worker.wasm ./public
COPY --from=build /tmp/playground/wasm_exec.js ./public
EXPOSE 8000
ENTRYPOINT /opt/playground/server -f=/opt/playground/data/packages.json -addr=:8000 \
- -clean-interval=${APP_CLEAN_INTERVAL} -debug=${APP_DEBUG} -playground-url=${APP_PLAYGROUND_URL}
\ No newline at end of file
+ -clean-interval=${APP_CLEAN_INTERVAL} -debug=${APP_DEBUG} -playground-url=${APP_PLAYGROUND_URL} -gotip-url=${APP_GOTIP_URL}
\ No newline at end of file
diff --git a/cmd/playground/main.go b/cmd/playground/main.go
index 006c41b2..ea825d81 100644
--- a/cmd/playground/main.go
+++ b/cmd/playground/main.go
@@ -24,13 +24,15 @@ import (
var Version = "testing"
type appArgs struct {
- packagesFile string
- playgroundUrl string
- addr string
- debug bool
- buildDir string
- cleanupInterval string
- assetsDirectory string
+ packagesFile string
+ playgroundURL string
+ goTipPlaygroundURL string
+ addr string
+ debug bool
+ buildDir string
+ cleanupInterval string
+ assetsDirectory string
+ connectTimeout time.Duration
}
func (a appArgs) getCleanDuration() (time.Duration, error) {
@@ -49,9 +51,11 @@ func main() {
flag.StringVar(&args.addr, "addr", ":8080", "TCP Listen address")
flag.StringVar(&args.buildDir, "wasm-build-dir", os.TempDir(), "Directory for WASM builds")
flag.StringVar(&args.cleanupInterval, "clean-interval", "10m", "Build directory cleanup interval")
- flag.StringVar(&args.playgroundUrl, "playground-url", goplay.DefaultPlaygroundURL, "Go Playground URL")
+ flag.StringVar(&args.playgroundURL, "playground-url", goplay.DefaultPlaygroundURL, "Go Playground URL")
+ flag.StringVar(&args.goTipPlaygroundURL, "gotip-url", goplay.DefaultGoTipPlaygroundURL, "GoTip Playground URL")
flag.BoolVar(&args.debug, "debug", false, "Enable debug mode")
flag.StringVar(&args.assetsDirectory, "static-dir", filepath.Join(wd, "public"), "Path to web page assets (HTML, JS, etc)")
+ flag.DurationVar(&args.connectTimeout, "timeout", 15*time.Second, "Go Playground server connect timeout")
l := getLogger(args.debug)
defer l.Sync() //nolint:errcheck
@@ -92,7 +96,7 @@ func start(goRoot string, args appArgs) error {
zap.S().Info("Server version: ", Version)
zap.S().Infof("GOROOT is %q", goRoot)
- zap.S().Infof("Playground url: %q", args.playgroundUrl)
+ zap.S().Infof("Playground url: %q", args.playgroundURL)
zap.S().Infof("Packages file is %q", args.packagesFile)
zap.S().Infof("Cleanup interval is %s", cleanInterval.String())
zap.S().Infof("Serving web page from %q", args.assetsDirectory)
@@ -112,10 +116,14 @@ func start(goRoot string, args appArgs) error {
go store.StartCleaner(ctx, cleanInterval, nil)
r := mux.NewRouter()
- pg := goplay.NewClient(args.playgroundUrl, goplay.DefaultUserAgent, 15*time.Second)
-
+ pgClient := goplay.NewClient(args.playgroundURL, goplay.DefaultUserAgent, args.connectTimeout)
+ goTipClient := goplay.NewClient(args.goTipPlaygroundURL, goplay.DefaultUserAgent, args.connectTimeout)
+ clients := &langserver.PlaygroundServices{
+ Default: pgClient,
+ GoTip: goTipClient,
+ }
// API routes
- langserver.New(Version, pg, packages, compiler.NewBuildService(zap.S(), store)).
+ langserver.New(Version, clients, packages, compiler.NewBuildService(zap.S(), store)).
Mount(r.PathPrefix("/api").Subrouter())
// Web UI routes
diff --git a/pkg/goplay/client.go b/pkg/goplay/client.go
index 0ee5c00a..f65b4d7c 100644
--- a/pkg/goplay/client.go
+++ b/pkg/goplay/client.go
@@ -13,8 +13,9 @@ import (
)
const (
- DefaultUserAgent = "goplay.tools/1.0 (http://goplay.tools/)"
- DefaultPlaygroundURL = "https://play.golang.org"
+ DefaultUserAgent = "goplay.tools/1.0 (http://goplay.tools/)"
+ DefaultPlaygroundURL = "https://play.golang.org"
+ DefaultGoTipPlaygroundURL = "https://gotipplay.golang.org"
// maxSnippetSize value taken from
// https://github.com/golang/playground/blob/master/app/goplay/share.go
diff --git a/pkg/langserver/server.go b/pkg/langserver/server.go
index efa62dff..e4a0a4c2 100644
--- a/pkg/langserver/server.go
+++ b/pkg/langserver/server.go
@@ -6,6 +6,7 @@ import (
"io"
"net/http"
"strconv"
+ "strings"
"time"
"github.com/gorilla/mux"
@@ -23,30 +24,37 @@ const (
frameTime = time.Second
maxBuildTimeDuration = time.Second * 30
- wasmMimeType = "application/wasm"
- formatQueryParam = "format"
- artifactParamVal = "artifactId"
+ wasmMimeType = "application/wasm"
+ formatQueryParam = "format"
+ artifactParamVal = "artifactId"
+ playgroundBackendParam = "backend"
+ playgroundGoTip = "gotip"
)
+type PlaygroundServices struct {
+ Default *goplay.Client
+ GoTip *goplay.Client
+}
+
// Service is language server service
type Service struct {
- version string
- log *zap.SugaredLogger
- index analyzer.PackageIndex
- compiler compiler.BuildService
- playground *goplay.Client
- limiter *rate.Limiter
+ version string
+ log *zap.SugaredLogger
+ index analyzer.PackageIndex
+ compiler compiler.BuildService
+ playgrounds *PlaygroundServices
+ limiter *rate.Limiter
}
// New is Service constructor
-func New(version string, playground *goplay.Client, packages []*analyzer.Package, builder compiler.BuildService) *Service {
+func New(version string, playgrounds *PlaygroundServices, packages []*analyzer.Package, builder compiler.BuildService) *Service {
return &Service{
- compiler: builder,
- version: version,
- playground: playground,
- log: zap.S().Named("langserver"),
- index: analyzer.BuildPackageIndex(packages),
- limiter: rate.NewLimiter(rate.Every(frameTime), compileRequestsPerFrame),
+ compiler: builder,
+ version: version,
+ playgrounds: playgrounds,
+ log: zap.S().Named("langserver"),
+ index: analyzer.BuildPackageIndex(packages),
+ limiter: rate.NewLimiter(rate.Every(frameTime), compileRequestsPerFrame),
}
}
@@ -122,7 +130,7 @@ func (s *Service) provideSuggestion(req SuggestionRequest) (*SuggestionsResponse
}
// HandleGetVersion handles /api/version
-func (s *Service) HandleGetVersion(w http.ResponseWriter, r *http.Request) error {
+func (s *Service) HandleGetVersion(w http.ResponseWriter, _ *http.Request) error {
WriteJSON(w, VersionResponse{Version: s.version})
return nil
}
@@ -149,7 +157,7 @@ func (s *Service) HandleFormatCode(w http.ResponseWriter, r *http.Request) error
return err
}
- formatted, _, err := s.goImportsCode(r.Context(), src)
+ formatted, _, err := s.goImportsCode(r, src)
if err != nil {
if goplay.IsCompileError(err) {
return NewHTTPError(http.StatusBadRequest, err)
@@ -165,7 +173,8 @@ func (s *Service) HandleFormatCode(w http.ResponseWriter, r *http.Request) error
// HandleShare handles snippet share
func (s *Service) HandleShare(w http.ResponseWriter, r *http.Request) error {
- shareID, err := s.playground.Share(r.Context(), r.Body)
+ client := s.getPlaygroundClientFromRequest(r)
+ shareID, err := client.Share(r.Context(), r.Body)
defer r.Body.Close()
if err != nil {
if err == goplay.ErrSnippetTooLarge {
@@ -184,7 +193,8 @@ func (s *Service) HandleShare(w http.ResponseWriter, r *http.Request) error {
func (s *Service) HandleGetSnippet(w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
snippetID := vars["id"]
- snippet, err := s.playground.GetSnippet(r.Context(), snippetID)
+ client := s.getPlaygroundClientFromRequest(r)
+ snippet, err := client.GetSnippet(r.Context(), snippetID)
if err != nil {
if err == goplay.ErrSnippetNotFound {
return Errorf(http.StatusNotFound, "snippet %q not found", snippetID)
@@ -218,7 +228,7 @@ func (s *Service) HandleRunCode(w http.ResponseWriter, r *http.Request) error {
var changed bool
if shouldFormat {
- src, changed, err = s.goImportsCode(r.Context(), src)
+ src, changed, err = s.goImportsCode(r, src)
if err != nil {
if goplay.IsCompileError(err) {
return NewHTTPError(http.StatusBadRequest, err)
@@ -228,7 +238,8 @@ func (s *Service) HandleRunCode(w http.ResponseWriter, r *http.Request) error {
}
}
- res, err := s.playground.Compile(r.Context(), src)
+ client := s.getPlaygroundClientFromRequest(r)
+ res, err := client.Compile(r.Context(), src)
if err != nil {
return err
}
@@ -276,6 +287,16 @@ func (s *Service) HandleArtifactRequest(w http.ResponseWriter, r *http.Request)
return nil
}
+func (s *Service) getPlaygroundClientFromRequest(r *http.Request) *goplay.Client {
+ playgroundBackend := strings.TrimSpace(r.URL.Query().Get(playgroundBackendParam))
+ if playgroundBackend == playgroundGoTip {
+ s.log.Debugw("Using goTip backend for request", zap.String("url", r.RequestURI))
+ return s.playgrounds.GoTip
+ }
+
+ return s.playgrounds.Default
+}
+
// HandleCompile handles WASM build request
func (s *Service) HandleCompile(w http.ResponseWriter, r *http.Request) error {
// Limit for request timeout
@@ -299,7 +320,7 @@ func (s *Service) HandleCompile(w http.ResponseWriter, r *http.Request) error {
var changed bool
if shouldFormat {
- src, changed, err = s.goImportsCode(r.Context(), src)
+ src, changed, err = s.goImportsCode(r, src)
if err != nil {
if goplay.IsCompileError(err) {
return NewHTTPError(http.StatusBadRequest, err)
@@ -333,8 +354,9 @@ func (s *Service) HandleCompile(w http.ResponseWriter, r *http.Request) error {
// if any error occurs, it sends error response to client and closes connection
//
// if "format" url query param is undefined or set to "false", just returns code as is
-func (s *Service) goImportsCode(ctx context.Context, src []byte) ([]byte, bool, error) {
- resp, err := s.playground.GoImports(ctx, src)
+func (s *Service) goImportsCode(r *http.Request, src []byte) ([]byte, bool, error) {
+ client := s.getPlaygroundClientFromRequest(r)
+ resp, err := client.GoImports(r.Context(), src)
if err != nil {
if err == goplay.ErrSnippetTooLarge {
return nil, false, NewHTTPError(http.StatusRequestEntityTooLarge, err)
diff --git a/web/src/components/settings/SettingsModal.tsx b/web/src/components/settings/SettingsModal.tsx
index b73efe1d..cee3825e 100644
--- a/web/src/components/settings/SettingsModal.tsx
+++ b/web/src/components/settings/SettingsModal.tsx
@@ -1,19 +1,33 @@
import React from 'react';
-import { Checkbox, Dropdown, getTheme, IconButton, IDropdownOption, Modal } from '@fluentui/react';
-import { Pivot, PivotItem } from '@fluentui/react/lib/Pivot';
-import { MessageBar, MessageBarType } from '@fluentui/react/lib/MessageBar';
-import { Link } from '@fluentui/react/lib/Link';
+import {
+ Checkbox,
+ Dropdown,
+ getTheme,
+ IconButton,
+ IDropdownOption,
+ Modal
+} from '@fluentui/react';
+import {Pivot, PivotItem} from '@fluentui/react/lib/Pivot';
+import {MessageBar, MessageBarType} from '@fluentui/react/lib/MessageBar';
+import {Link} from '@fluentui/react/lib/Link';
-import { getContentStyles, getIconButtonStyles } from '~/styles/modal';
+import {getContentStyles, getIconButtonStyles} from '~/styles/modal';
import SettingsProperty from './SettingsProperty';
-import { MonacoSettings, RuntimeType } from '~/services/config';
-import { DEFAULT_FONT, getAvailableFonts } from '~/services/fonts';
-import { BuildParamsArgs, Connect, MonacoParamsChanges, SettingsState } from '~/store';
+import {MonacoSettings, RuntimeType} from '~/services/config';
+import {DEFAULT_FONT, getAvailableFonts} from '~/services/fonts';
+import {
+ BuildParamsArgs,
+ Connect,
+ MonacoParamsChanges,
+ SettingsState
+} from '~/store';
const WASM_SUPPORTED = 'WebAssembly' in window;
const COMPILER_OPTIONS: IDropdownOption[] = [
{ key: RuntimeType.GoPlayground, text: 'Go Playground' },
+ {
+ key: RuntimeType.GoTipPlayground, text: 'Go Playground (Go Tip)' },
{
key: RuntimeType.WebAssembly,
text: `WebAssembly (${WASM_SUPPORTED ? 'Experimental' : 'Unsupported'})`,
@@ -62,6 +76,7 @@ export interface SettingsProps {
interface SettingsModalState {
isOpen?: boolean,
showWarning?: boolean
+ showGoTipMessage?: boolean
}
@Connect(state => ({
@@ -77,7 +92,8 @@ export default class SettingsModal extends React.Component
- Seedocumentation for more details. -
-+ Seedocumentation for more details. +
++ Seegotip help for more details. +
+