github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/lsprpc/goenv.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lsprpc 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "os" 12 13 "github.com/powerman/golang-tools/internal/event" 14 "github.com/powerman/golang-tools/internal/gocommand" 15 jsonrpc2_v2 "github.com/powerman/golang-tools/internal/jsonrpc2_v2" 16 "github.com/powerman/golang-tools/internal/lsp/protocol" 17 ) 18 19 func GoEnvMiddleware() (Middleware, error) { 20 return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler { 21 return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { 22 if req.Method == "initialize" { 23 if err := addGoEnvToInitializeRequestV2(ctx, req); err != nil { 24 event.Error(ctx, "adding go env to initialize", err) 25 } 26 } 27 return delegate.Handle(ctx, req) 28 }) 29 }), nil 30 } 31 32 func addGoEnvToInitializeRequestV2(ctx context.Context, req *jsonrpc2_v2.Request) error { 33 var params protocol.ParamInitialize 34 if err := json.Unmarshal(req.Params, ¶ms); err != nil { 35 return err 36 } 37 var opts map[string]interface{} 38 switch v := params.InitializationOptions.(type) { 39 case nil: 40 opts = make(map[string]interface{}) 41 case map[string]interface{}: 42 opts = v 43 default: 44 return fmt.Errorf("unexpected type for InitializationOptions: %T", v) 45 } 46 envOpt, ok := opts["env"] 47 if !ok { 48 envOpt = make(map[string]interface{}) 49 } 50 env, ok := envOpt.(map[string]interface{}) 51 if !ok { 52 return fmt.Errorf("env option is %T, expected a map", envOpt) 53 } 54 goenv, err := getGoEnv(ctx, env) 55 if err != nil { 56 return err 57 } 58 // We don't want to propagate GOWORK unless explicitly set since that could mess with 59 // path inference during cmd/go invocations, see golang/go#51825. 60 _, goworkSet := os.LookupEnv("GOWORK") 61 for govar, value := range goenv { 62 if govar == "GOWORK" && !goworkSet { 63 continue 64 } 65 env[govar] = value 66 } 67 opts["env"] = env 68 params.InitializationOptions = opts 69 raw, err := json.Marshal(params) 70 if err != nil { 71 return fmt.Errorf("marshaling updated options: %v", err) 72 } 73 req.Params = json.RawMessage(raw) 74 return nil 75 } 76 77 func getGoEnv(ctx context.Context, env map[string]interface{}) (map[string]string, error) { 78 var runEnv []string 79 for k, v := range env { 80 runEnv = append(runEnv, fmt.Sprintf("%s=%s", k, v)) 81 } 82 runner := gocommand.Runner{} 83 output, err := runner.Run(ctx, gocommand.Invocation{ 84 Verb: "env", 85 Args: []string{"-json"}, 86 Env: runEnv, 87 }) 88 if err != nil { 89 return nil, err 90 } 91 envmap := make(map[string]string) 92 if err := json.Unmarshal(output.Bytes(), &envmap); err != nil { 93 return nil, err 94 } 95 return envmap, nil 96 }