mirror of
https://github.com/golang/go
synced 2024-11-18 18:24:48 -07:00
internal/lsp: wire up configuration
This connects up the configuration message, and uses it to allow the client to set the environment in the config passed to packages.Load Change-Id: I75e03c01c74e9b11c8b4c47b9cbdd0574cddf778 Reviewed-on: https://go-review.googlesource.com/c/tools/+/169704 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
8f05a32dce
commit
8889bfc21e
14
internal/lsp/cache/view.go
vendored
14
internal/lsp/cache/view.go
vendored
@ -27,6 +27,12 @@ type View struct {
|
|||||||
// should be stopped.
|
// should be stopped.
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// Name is the user visible name of this view.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Folder is the root of this view.
|
||||||
|
Folder span.URI
|
||||||
|
|
||||||
// Config is the configuration used for the view's interaction with the
|
// Config is the configuration used for the view's interaction with the
|
||||||
// go/packages API. It is shared across all views.
|
// go/packages API. It is shared across all views.
|
||||||
Config packages.Config
|
Config packages.Config
|
||||||
@ -72,13 +78,14 @@ type entry struct {
|
|||||||
ready chan struct{} // closed to broadcast ready condition
|
ready chan struct{} // closed to broadcast ready condition
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewView(config *packages.Config) *View {
|
func NewView(name string, folder span.URI, config *packages.Config) *View {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
v := &View{
|
||||||
return &View{
|
|
||||||
backgroundCtx: ctx,
|
backgroundCtx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
Config: *config,
|
Config: *config,
|
||||||
|
Name: name,
|
||||||
|
Folder: folder,
|
||||||
filesByURI: make(map[span.URI]*File),
|
filesByURI: make(map[span.URI]*File),
|
||||||
filesByBase: make(map[string][]*File),
|
filesByBase: make(map[string][]*File),
|
||||||
contentChanges: make(map[span.URI]func()),
|
contentChanges: make(map[span.URI]func()),
|
||||||
@ -89,6 +96,7 @@ func NewView(config *packages.Config) *View {
|
|||||||
packages: make(map[string]*entry),
|
packages: make(map[string]*entry),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *View) BackgroundContext() context.Context {
|
func (v *View) BackgroundContext() context.Context {
|
||||||
|
@ -61,7 +61,7 @@ func (d *definition) Run(ctx context.Context, args ...string) error {
|
|||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return tool.CommandLineErrorf("definition expects 1 argument")
|
return tool.CommandLineErrorf("definition expects 1 argument")
|
||||||
}
|
}
|
||||||
view := cache.NewView(&d.query.app.Config)
|
view := cache.NewView("definition_test", span.FileURI(d.query.app.Config.Dir), &d.query.app.Config)
|
||||||
from := span.Parse(args[0])
|
from := span.Parse(args[0])
|
||||||
f, err := view.GetFile(ctx, from.URI())
|
f, err := view.GetFile(ctx, from.URI())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,6 +18,9 @@ func (s *server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
//TODO: this is an ugly hack to make the diagnostics call happen after the
|
||||||
|
// configuration is collected, we need to rewrite all the concurrency
|
||||||
|
<-s.configured
|
||||||
ctx := s.view.BackgroundContext()
|
ctx := s.view.BackgroundContext()
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return
|
return
|
||||||
|
@ -71,7 +71,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := &server{
|
s := &server{
|
||||||
view: cache.NewView(&cfg),
|
view: cache.NewView("lsp_test", span.FileURI(cfg.Dir), &cfg),
|
||||||
}
|
}
|
||||||
// Do a first pass to collect special markers for completion.
|
// Do a first pass to collect special markers for completion.
|
||||||
if err := exported.Expect(map[string]interface{}{
|
if err := exported.Expect(map[string]interface{}{
|
||||||
|
@ -13,6 +13,8 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
@ -26,7 +28,9 @@ import (
|
|||||||
// RunServer starts an LSP server on the supplied stream, and waits until the
|
// RunServer starts an LSP server on the supplied stream, and waits until the
|
||||||
// stream is closed.
|
// stream is closed.
|
||||||
func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{}) error {
|
func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{}) error {
|
||||||
s := &server{}
|
s := &server{
|
||||||
|
configured: make(chan struct{}),
|
||||||
|
}
|
||||||
conn, client := protocol.RunServer(ctx, stream, s, opts...)
|
conn, client := protocol.RunServer(ctx, stream, s, opts...)
|
||||||
s.client = client
|
s.client = client
|
||||||
return conn.Wait(ctx)
|
return conn.Wait(ctx)
|
||||||
@ -41,7 +45,6 @@ func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error {
|
|||||||
// RunServerOnPort starts an LSP server on the given port and does not exit.
|
// RunServerOnPort starts an LSP server on the given port and does not exit.
|
||||||
// This function exists for debugging purposes.
|
// This function exists for debugging purposes.
|
||||||
func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
|
func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
|
||||||
s := &server{}
|
|
||||||
ln, err := net.Listen("tcp", addr)
|
ln, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -52,11 +55,7 @@ func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
||||||
go func() {
|
go RunServer(ctx, stream, opts...)
|
||||||
conn, client := protocol.RunServer(ctx, stream, s, opts...)
|
|
||||||
s.client = client
|
|
||||||
conn.Wait(ctx)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +70,9 @@ type server struct {
|
|||||||
|
|
||||||
textDocumentSyncKind protocol.TextDocumentSyncKind
|
textDocumentSyncKind protocol.TextDocumentSyncKind
|
||||||
|
|
||||||
viewMu sync.Mutex
|
view *cache.View
|
||||||
view *cache.View
|
|
||||||
|
configured chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
|
func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
|
||||||
@ -103,9 +103,11 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
|
|||||||
// flag). Disabled for now to simplify debugging.
|
// flag). Disabled for now to simplify debugging.
|
||||||
s.textDocumentSyncKind = protocol.Full
|
s.textDocumentSyncKind = protocol.Full
|
||||||
|
|
||||||
s.view = cache.NewView(&packages.Config{
|
//TODO:use workspace folders
|
||||||
|
s.view = cache.NewView(path.Base(string(rootURI)), rootURI, &packages.Config{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Dir: rootPath,
|
Dir: rootPath,
|
||||||
|
Env: os.Environ(),
|
||||||
Mode: packages.LoadImports,
|
Mode: packages.LoadImports,
|
||||||
Fset: token.NewFileSet(),
|
Fset: token.NewFileSet(),
|
||||||
Overlay: make(map[string][]byte),
|
Overlay: make(map[string][]byte),
|
||||||
@ -143,8 +145,32 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Initialized(context.Context, *protocol.InitializedParams) error {
|
func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
||||||
return nil // ignore
|
go func() {
|
||||||
|
// we hae to do this in a go routine to unblock the jsonrpc processor
|
||||||
|
// but we also have to block all calls to packages.Load until this is done
|
||||||
|
// TODO: we need to rewrite all the concurrency handling hin the server
|
||||||
|
defer func() { close(s.configured) }()
|
||||||
|
s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
|
||||||
|
Registrations: []protocol.Registration{{
|
||||||
|
ID: "workspace/didChangeConfiguration",
|
||||||
|
Method: "workspace/didChangeConfiguration",
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
config, err := s.client.Configuration(ctx, &protocol.ConfigurationParams{
|
||||||
|
Items: []protocol.ConfigurationItem{{
|
||||||
|
ScopeURI: protocol.NewURI(s.view.Folder),
|
||||||
|
Section: "gopls",
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.Error(err)
|
||||||
|
}
|
||||||
|
if err := s.processConfig(config[0]); err != nil {
|
||||||
|
s.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Shutdown(context.Context) error {
|
func (s *server) Shutdown(context.Context) error {
|
||||||
@ -532,6 +558,45 @@ func (s *server) FoldingRanges(context.Context, *protocol.FoldingRangeParams) ([
|
|||||||
return nil, notImplemented("FoldingRanges")
|
return nil, notImplemented("FoldingRanges")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *server) Error(err error) {
|
||||||
|
s.client.LogMessage(context.Background(), &protocol.LogMessageParams{
|
||||||
|
Type: protocol.Error,
|
||||||
|
Message: fmt.Sprint(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) processConfig(config interface{}) error {
|
||||||
|
//TODO: we should probably store and process more of the config
|
||||||
|
c, ok := config.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Invalid config gopls type %T", config)
|
||||||
|
}
|
||||||
|
env := c["env"]
|
||||||
|
if env == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
menv, ok := env.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Invalid config gopls.env type %T", env)
|
||||||
|
}
|
||||||
|
for k, v := range menv {
|
||||||
|
s.view.Config.Env = applyEnv(s.view.Config.Env, k, v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyEnv(env []string, k string, v interface{}) []string {
|
||||||
|
prefix := k + "="
|
||||||
|
value := prefix + fmt.Sprint(v)
|
||||||
|
for i, s := range env {
|
||||||
|
if strings.HasPrefix(s, prefix) {
|
||||||
|
env[i] = value
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(env, value)
|
||||||
|
}
|
||||||
|
|
||||||
func notImplemented(method string) *jsonrpc2.Error {
|
func notImplemented(method string) *jsonrpc2.Error {
|
||||||
return jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not yet implemented", method)
|
return jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not yet implemented", method)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user