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