From 097700a846570cb6cb6a41c235fc4a1437f0c632 Mon Sep 17 00:00:00 2001 From: Henry Wong Date: Mon, 26 Aug 2019 17:14:37 +0800 Subject: [PATCH] [elastic] Move the deps download into 'intialize' request handle The original implementation relies on 'go list' to download the deps. And 'go list' will be called by the request handler indirectly. This approach is unpredictable and 'go list' is black box to us. So it's better download the deps manually in 'ManageDeps', i.e. inside 'initialize' request handler. If the client sends the initialize option `installGoDependency: true`, download the deps in `initialize` handler. If the client doesn't send the initialize option `installGoDependency: true`, disable the network access by `GOPROXY=off` and find the deps from vendor folder. --- go/internal/packagesdriver/sizes.go | 2 ++ go/packages/golist.go | 3 ++- internal/lsp/cache/session.go | 5 +++++ internal/lsp/elasticserver.go | 6 ++---- internal/lsp/general.go | 3 +++ internal/lsp/protocol/elasticserver.go | 16 ++++++++++++++++ internal/lsp/server.go | 1 + internal/lsp/workspace.go | 12 ++++++++++++ 8 files changed, 43 insertions(+), 5 deletions(-) diff --git a/go/internal/packagesdriver/sizes.go b/go/internal/packagesdriver/sizes.go index ea15d57be1a..ecce7405c69 100644 --- a/go/internal/packagesdriver/sizes.go +++ b/go/internal/packagesdriver/sizes.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "go/types" + "golang.org/x/tools/internal/lsp/protocol" "log" "os" "os/exec" @@ -125,6 +126,7 @@ func InvokeGo(ctx context.Context, env []string, dir string, usesExportData bool cmd.Dir = dir cmd.Stdout = stdout cmd.Stderr = stderr + protocol.AdjustGoListForVendorMode(&(cmd.Env), &(cmd.Args)) if err := cmd.Run(); err != nil { exitErr, ok := err.(*exec.ExitError) if !ok { diff --git a/go/packages/golist.go b/go/packages/golist.go index 8c3594e3796..2a495ec6ef1 100644 --- a/go/packages/golist.go +++ b/go/packages/golist.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "go/types" + "golang.org/x/tools/internal/lsp/protocol" "hash/fnv" "io/ioutil" "log" @@ -860,7 +861,7 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { defer func(start time.Time) { cfg.Logf("%s for %v, stderr: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr) }(time.Now()) - + protocol.AdjustGoListForVendorMode(&(cmd.Env), &(cmd.Args)) if err := cmd.Run(); err != nil { // Check for 'go' executable not being found. if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go index 3511bb3fec0..8baa7bef95c 100644 --- a/internal/lsp/cache/session.go +++ b/internal/lsp/cache/session.go @@ -93,6 +93,11 @@ func (s *session) NewView(ctx context.Context, name string, folder span.URI) sou }, ignoredURIs: make(map[span.URI]struct{}), } + if enableVendor, ok := ctx.Value("ENABLEVENDOR").(bool); ok && enableVendor { + // Set 'GOPROXY=off' to disable the network access + v.env = append(v.env, "GOPROXY=off") + v.env = append(v.env, "ENABLEVENDOR=on") + } // Preemptively build the builtin package, // so we immediately add builtin.go to the list of ignored files. v.buildBuiltinPkg(ctx) diff --git a/internal/lsp/elasticserver.go b/internal/lsp/elasticserver.go index b3bffe108de..2ebeedab661 100644 --- a/internal/lsp/elasticserver.go +++ b/internal/lsp/elasticserver.go @@ -189,10 +189,8 @@ type WorkspaceFolderMeta struct { // manageDeps will try its best to convert the folders to modules. The core functions, like deps downloading and deps // management, will be implemented in the package 'cache'. func (s ElasticServer) ManageDeps(folders *[]protocol.WorkspaceFolder) error { - // Note: For the upstream go langserver, granularity of the workspace folders is repository. But for the elastic go - // language server, there are repositories contain multiple modules. In order to handle the modules separately, we - // consider different modules as different workspace folders, so we can manage the dependency of different modules - // separately. + // In order to handle the modules separately, we consider different modules as different workspace folders, so we + // can manage the dependency of different modules separately. for _, folder := range *folders { metadata := &WorkspaceFolderMeta{} if folder.URI != "" { diff --git a/internal/lsp/general.go b/internal/lsp/general.go index 207aa8e6682..b2d5faa9268 100644 --- a/internal/lsp/general.go +++ b/internal/lsp/general.go @@ -38,6 +38,9 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara if opt, ok := opts["noIncrementalSync"].(bool); ok && opt { s.textDocumentSyncKind = protocol.Full } + if opt, ok := opts["installGoDependency"].(bool); ok && opt { + s.installGoDependency = true + } } // Default to using synopsis as a default for hover information. diff --git a/internal/lsp/protocol/elasticserver.go b/internal/lsp/protocol/elasticserver.go index cdcaab39260..f9425249feb 100644 --- a/internal/lsp/protocol/elasticserver.go +++ b/internal/lsp/protocol/elasticserver.go @@ -14,6 +14,22 @@ type ElasticServer interface { ManageDeps(folders *[]WorkspaceFolder) error } +func AdjustGoListForVendorMode(env *[]string, args *[]string) { + l := len(*env) + for i := range *env { + if (*env)[l-i-1] == "ENABLEVENDOR=on" { + // If 'ENABLEVENDOR' is on, append '-mod=vendor' to go list command + for j := range *args { + if (j+2 < len(*args)) && (*args)[j] == "go" && (*args)[j+1] == "list" { + *args = append((*args)[:j+2], append([]string{"-mod=vendor"}, (*args)[j+2:]...)...) + break + } + } + return + } + } +} + type elasticServerHandler struct { canceller server ElasticServer diff --git a/internal/lsp/server.go b/internal/lsp/server.go index 6ab758b6c05..3a738093eab 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -88,6 +88,7 @@ type Server struct { preferredContentFormat protocol.MarkupKind disabledAnalyses map[string]struct{} wantSuggestedFixes bool + installGoDependency bool supportedCodeActions map[source.FileKind]map[protocol.CodeActionKind]bool diff --git a/internal/lsp/workspace.go b/internal/lsp/workspace.go index 58127569001..214d69662f3 100644 --- a/internal/lsp/workspace.go +++ b/internal/lsp/workspace.go @@ -8,8 +8,10 @@ import ( "context" "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/lsp/telemetry/log" "golang.org/x/tools/internal/span" errors "golang.org/x/xerrors" + "os/exec" ) func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFoldersChangeEvent) error { @@ -31,6 +33,16 @@ func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFold } func (s *Server) addView(ctx context.Context, name string, uri span.URI) error { + if s.installGoDependency { + cmd := exec.Command("go", "mod", "download") + cmd.Dir = uri.Filename() + if err := cmd.Run(); err != nil { + log.Error(ctx, "failed to download the dependencies", err) + } + } else { + // If we disable the go dependency download, trying to find the deps from the vendor folder. + ctx = context.WithValue(ctx, "ENABLEVENDOR", true) + } view := s.session.NewView(ctx, name, uri) s.stateMu.Lock() state := s.state