diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go index a95913fa6d..7e91753dac 100644 --- a/internal/lsp/cache/load.go +++ b/internal/lsp/cache/load.go @@ -93,7 +93,7 @@ func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error { return ctx.Err() } - event.Print(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs))) + event.Print(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs))) if len(pkgs) == 0 { return err } diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go index 2feb4c0bc2..2d2a290739 100644 --- a/internal/lsp/cache/view.go +++ b/internal/lsp/cache/view.go @@ -262,6 +262,24 @@ func (v *view) buildBuiltinPackage(ctx context.Context, goFiles []string) error return nil } +func (v *view) WriteEnv(ctx context.Context, w io.Writer) error { + env, buildFlags := v.env() + // TODO(rstambler): We could probably avoid running this by saving the + // output on original create, but I'm not sure if it's worth it. + inv := gocommand.Invocation{ + Verb: "env", + Env: env, + WorkingDir: v.Folder().Filename(), + } + stdout, err := inv.Run(ctx) + if err != nil { + return err + } + fmt.Fprintf(w, "go env for %v\n(valid build configuration = %v)\n(build flags: %v)\n", v.folder.Filename(), v.hasValidBuildConfiguration, buildFlags) + fmt.Fprint(w, stdout) + return nil +} + func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error { v.importsMu.Lock() defer v.importsMu.Unlock() diff --git a/internal/lsp/cmd/info.go b/internal/lsp/cmd/info.go index b376b7945f..1f3ab843c2 100644 --- a/internal/lsp/cmd/info.go +++ b/internal/lsp/cmd/info.go @@ -32,7 +32,7 @@ func (v *version) DetailedHelp(f *flag.FlagSet) { // Run prints version information to stdout. func (v *version) Run(ctx context.Context, args ...string) error { - debug.PrintVersionInfo(os.Stdout, v.app.Verbose, debug.PlainText) + debug.PrintVersionInfo(ctx, os.Stdout, v.app.Verbose, debug.PlainText) return nil } @@ -69,7 +69,7 @@ A failing unit test is the best. func (b *bug) Run(ctx context.Context, args ...string) error { buf := &bytes.Buffer{} fmt.Fprint(buf, goplsBugHeader) - debug.PrintVersionInfo(buf, true, debug.Markdown) + debug.PrintVersionInfo(ctx, buf, true, debug.Markdown) body := buf.String() title := strings.Join(args, " ") if !strings.HasPrefix(title, goplsBugPrefix) { diff --git a/internal/lsp/debug/info.go b/internal/lsp/debug/info.go index 5890396c92..8baf1d932c 100644 --- a/internal/lsp/debug/info.go +++ b/internal/lsp/debug/info.go @@ -6,10 +6,12 @@ package debug import ( + "context" "fmt" "io" - "os/exec" "strings" + + "golang.org/x/tools/internal/gocommand" ) type PrintMode int @@ -24,7 +26,7 @@ const ( var Version = "master" // PrintServerInfo writes HTML debug info to w for the Instance. -func (i *Instance) PrintServerInfo(w io.Writer) { +func (i *Instance) PrintServerInfo(ctx context.Context, w io.Writer) { section(w, HTML, "Server Instance", func() { fmt.Fprintf(w, "Start time: %v\n", i.StartTime) fmt.Fprintf(w, "LogFile: %s\n", i.Logfile) @@ -32,13 +34,13 @@ func (i *Instance) PrintServerInfo(w io.Writer) { fmt.Fprintf(w, "Address: %s\n", i.ServerAddress) fmt.Fprintf(w, "Debug address: %s\n", i.DebugAddress) }) - PrintVersionInfo(w, true, HTML) + PrintVersionInfo(ctx, w, true, HTML) } -// PrintVersionInfo writes version and environment information to w, using the -// output format specified by mode. verbose controls whether additional -// information is written, including section headers. -func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) { +// PrintVersionInfo writes version information to w, using the output format +// specified by mode. verbose controls whether additional information is +// written, including section headers. +func PrintVersionInfo(ctx context.Context, w io.Writer, verbose bool, mode PrintMode) { if !verbose { printBuildInfo(w, false, mode) return @@ -48,13 +50,14 @@ func PrintVersionInfo(w io.Writer, verbose bool, mode PrintMode) { }) fmt.Fprint(w, "\n") section(w, mode, "Go info", func() { - cmd := exec.Command("go", "version") - cmd.Stdout = w - cmd.Run() - fmt.Fprint(w, "\n") - cmd = exec.Command("go", "env") - cmd.Stdout = w - cmd.Run() + i := &gocommand.Invocation{ + Verb: "version", + } + version, err := i.Run(ctx) + if err != nil { + panic(err) + } + fmt.Fprintln(w, version.String()) }) } diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go index ab08e14792..7835740467 100644 --- a/internal/lsp/debug/serve.go +++ b/internal/lsp/debug/serve.go @@ -376,7 +376,7 @@ func (i *Instance) getFile(r *http.Request) interface{} { func (i *Instance) getInfo(r *http.Request) interface{} { buf := &bytes.Buffer{} - i.PrintServerInfo(buf) + i.PrintServerInfo(r.Context(), buf) return template.HTML(buf.String()) } diff --git a/internal/lsp/general.go b/internal/lsp/general.go index 887254ce9d..c9d2efb19c 100644 --- a/internal/lsp/general.go +++ b/internal/lsp/general.go @@ -13,6 +13,7 @@ import ( "golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/lsp/debug" + "golang.org/x/tools/internal/lsp/debug/tag" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/span" @@ -156,7 +157,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa } buf := &bytes.Buffer{} - debug.PrintVersionInfo(buf, true, debug.PlainText) + debug.PrintVersionInfo(ctx, buf, true, debug.PlainText) event.Print(ctx, buf.String()) s.addFolders(ctx, s.pendingFolders) @@ -171,11 +172,20 @@ func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFol for _, folder := range folders { uri := span.URIFromURI(folder.URI) - _, snapshot, err := s.addView(ctx, folder.Name, uri) + view, snapshot, err := s.addView(ctx, folder.Name, uri) if err != nil { viewErrors[uri] = err continue } + // Print each view's environment. + buf := &bytes.Buffer{} + if err := view.WriteEnv(ctx, buf); err != nil { + event.Error(ctx, "failed to write environment", err, tag.Directory.Of(view.Folder())) + continue + } + event.Print(ctx, buf.String()) + + // Diagnose the newly created view. go s.diagnoseDetached(snapshot) } if len(viewErrors) > 0 { diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go index 6da78605da..ae14c3fa1d 100644 --- a/internal/lsp/source/view.go +++ b/internal/lsp/source/view.go @@ -10,6 +10,7 @@ import ( "go/ast" "go/token" "go/types" + "io" "golang.org/x/mod/modfile" "golang.org/x/tools/go/analysis" @@ -125,6 +126,9 @@ type View interface { // Ignore returns true if this file should be ignored by this view. Ignore(span.URI) bool + // WriteEnv writes the view-specific environment to the io.Writer. + WriteEnv(ctx context.Context, w io.Writer) error + // RunProcessEnvFunc runs fn with the process env for this snapshot's view. // Note: the process env contains cached module and filesystem state. RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error