mirror of
https://github.com/golang/go
synced 2024-11-18 20:14:43 -07:00
30fd94b347
Three new flags are added to the serve command, and threaded through to the LSP forwarder: -remote.listen.timeout: -listen.timeout for the auto-started daemon -remote.debug: -debug for the auto-started daemon -remote.logfile: -logfile for the auto-started daemon As part of this change, no longer enable debugging the daemon by default. Notably none of this configuration affects serving, so modifying this configuration has been chosen not to change the path to the automatic daemon. In other words, this configuration has effect only for the forwarder process that starts the daemon: all others will connect to the daemon and inherit whatever configuration it had at startup. This should be OK, because in the common case this configuration should be static across all clients (e.g., many Vim sessions all sharing the same .vimrc). Exposing this configuration made the signature of lsprpc.NewForwarder a bit hard to understand, so I decided to go ahead and switch to a variadic options pattern for initializing both the Forwarder and StreamServer, the latter just for consistency with the Forwarder. Updates golang/go#34111 Change-Id: Iefb71e337befe08b23e451477d19fd57e69f36c6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/222670 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
115 lines
4.1 KiB
Go
115 lines
4.1 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
"golang.org/x/tools/internal/lsp/cache"
|
|
"golang.org/x/tools/internal/lsp/debug"
|
|
"golang.org/x/tools/internal/lsp/lsprpc"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/tool"
|
|
)
|
|
|
|
// Serve is a struct that exposes the configurable parts of the LSP server as
|
|
// flags, in the right form for tool.Main to consume.
|
|
type Serve struct {
|
|
Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
|
|
Mode string `flag:"mode" help:"no effect"`
|
|
Port int `flag:"port" help:"port on which to run gopls for debugging purposes"`
|
|
Address string `flag:"listen" help:"address on which to listen for remote connections. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. Otherwise, TCP is used."`
|
|
IdleTimeout time.Duration `flag:"listen.timeout" help:"when used with -listen, shut down the server when there are no connected clients for this duration"`
|
|
Trace bool `flag:"rpc.trace" help:"print the full rpc trace in lsp inspector format"`
|
|
Debug string `flag:"debug" help:"serve debug information on the supplied address"`
|
|
|
|
RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the listen.timeout used when auto-starting the remote"`
|
|
RemoteDebug string `flag:"remote.debug" help:"when used with -remote=auto, the debug address used when auto-starting the remote"`
|
|
RemoteLogfile string `flag:"remote.logfile" help:"when used with -remote=auto, the filename for the remote daemon to log to"`
|
|
|
|
app *Application
|
|
}
|
|
|
|
func (s *Serve) Name() string { return "serve" }
|
|
func (s *Serve) Usage() string { return "" }
|
|
func (s *Serve) ShortHelp() string {
|
|
return "run a server for Go code using the Language Server Protocol"
|
|
}
|
|
func (s *Serve) DetailedHelp(f *flag.FlagSet) {
|
|
fmt.Fprint(f.Output(), `
|
|
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
|
|
a child of an editor process.
|
|
|
|
gopls server flags are:
|
|
`)
|
|
f.PrintDefaults()
|
|
}
|
|
|
|
// Run configures a server based on the flags, and then runs it.
|
|
// It blocks until the server shuts down.
|
|
func (s *Serve) Run(ctx context.Context, args ...string) error {
|
|
if len(args) > 0 {
|
|
return tool.CommandLineErrorf("server does not take arguments, got %v", args)
|
|
}
|
|
|
|
di := debug.GetInstance(ctx)
|
|
if di != nil {
|
|
closeLog, err := di.SetLogFile(s.Logfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer closeLog()
|
|
di.ServerAddress = s.Address
|
|
di.DebugAddress = s.Debug
|
|
di.Serve(ctx)
|
|
di.MonitorMemory(ctx)
|
|
}
|
|
var ss jsonrpc2.StreamServer
|
|
if s.app.Remote != "" {
|
|
network, addr := parseAddr(s.app.Remote)
|
|
ss = lsprpc.NewForwarder(network, addr,
|
|
lsprpc.WithTelemetry(true),
|
|
lsprpc.RemoteDebugAddress(s.RemoteDebug),
|
|
lsprpc.RemoteListenTimeout(s.RemoteListenTimeout),
|
|
lsprpc.RemoteLogfile(s.RemoteLogfile),
|
|
)
|
|
} else {
|
|
ss = lsprpc.NewStreamServer(cache.New(ctx, s.app.options), lsprpc.WithTelemetry(true))
|
|
}
|
|
|
|
if s.Address != "" {
|
|
network, addr := parseAddr(s.Address)
|
|
return jsonrpc2.ListenAndServe(ctx, network, addr, ss, s.IdleTimeout)
|
|
}
|
|
if s.Port != 0 {
|
|
addr := fmt.Sprintf(":%v", s.Port)
|
|
return jsonrpc2.ListenAndServe(ctx, "tcp", addr, ss, s.IdleTimeout)
|
|
}
|
|
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
|
|
if s.Trace && di != nil {
|
|
stream = protocol.LoggingStream(stream, di.LogWriter)
|
|
}
|
|
return ss.ServeStream(ctx, stream)
|
|
}
|
|
|
|
// parseAddr parses the -listen flag in to a network, and address.
|
|
func parseAddr(listen string) (network string, address string) {
|
|
// Allow passing just -remote=auto, as a shorthand for using automatic remote
|
|
// resolution.
|
|
if listen == lsprpc.AutoNetwork {
|
|
return lsprpc.AutoNetwork, ""
|
|
}
|
|
if parts := strings.SplitN(listen, ";", 2); len(parts) == 2 {
|
|
return parts[0], parts[1]
|
|
}
|
|
return "tcp", listen
|
|
}
|