Skip to content

Commit 30fd94b

Browse files
committed
internal/lsp/lsprpc: expose configuration for auto-started daemon
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent 7975679 commit 30fd94b

File tree

7 files changed

+114
-35
lines changed

7 files changed

+114
-35
lines changed

gopls/test/gopls_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func testCommandLine(t *testing.T, exporter packagestest.Exporter) {
4444
ctx := tests.Context(t)
4545
ctx = debug.WithInstance(ctx, "", "")
4646
cache := cache.New(ctx, commandLineOptions)
47-
ss := lsprpc.NewStreamServer(cache, false)
47+
ss := lsprpc.NewStreamServer(cache)
4848
ts := servertest.NewTCPServer(ctx, ss)
4949
for _, data := range data {
5050
defer data.Exported.Cleanup()

internal/lsp/cmd/cmd.go

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"os"
1919
"strings"
2020
"sync"
21+
"time"
2122

2223
"golang.org/x/tools/internal/jsonrpc2"
2324
"golang.org/x/tools/internal/lsp"
@@ -81,6 +82,11 @@ func New(name, wd string, env []string, options func(*source.Options)) *Applicat
8182
wd: wd,
8283
env: env,
8384
OCAgent: "off", //TODO: Remove this line to default the exporter to on
85+
86+
Serve: Serve{
87+
RemoteListenTimeout: 1 * time.Minute,
88+
RemoteLogfile: "auto",
89+
},
8490
}
8591
return app
8692
}

internal/lsp/cmd/cmd_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func testCommandLine(t *testing.T, exporter packagestest.Exporter) {
4949
func testServer(ctx context.Context) *servertest.TCPServer {
5050
ctx = debug.WithInstance(ctx, "", "")
5151
cache := cache.New(ctx, nil)
52-
ss := lsprpc.NewStreamServer(cache, false)
52+
ss := lsprpc.NewStreamServer(cache)
5353
return servertest.NewTCPServer(ctx, ss)
5454
}
5555

internal/lsp/cmd/serve.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ type Serve struct {
3131
Trace bool `flag:"rpc.trace" help:"print the full rpc trace in lsp inspector format"`
3232
Debug string `flag:"debug" help:"serve debug information on the supplied address"`
3333

34+
RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the listen.timeout used when auto-starting the remote"`
35+
RemoteDebug string `flag:"remote.debug" help:"when used with -remote=auto, the debug address used when auto-starting the remote"`
36+
RemoteLogfile string `flag:"remote.logfile" help:"when used with -remote=auto, the filename for the remote daemon to log to"`
37+
3438
app *Application
3539
}
3640

@@ -71,9 +75,14 @@ func (s *Serve) Run(ctx context.Context, args ...string) error {
7175
var ss jsonrpc2.StreamServer
7276
if s.app.Remote != "" {
7377
network, addr := parseAddr(s.app.Remote)
74-
ss = lsprpc.NewForwarder(network, addr, true)
78+
ss = lsprpc.NewForwarder(network, addr,
79+
lsprpc.WithTelemetry(true),
80+
lsprpc.RemoteDebugAddress(s.RemoteDebug),
81+
lsprpc.RemoteListenTimeout(s.RemoteListenTimeout),
82+
lsprpc.RemoteLogfile(s.RemoteLogfile),
83+
)
7584
} else {
76-
ss = lsprpc.NewStreamServer(cache.New(ctx, s.app.options), true)
85+
ss = lsprpc.NewStreamServer(cache.New(ctx, s.app.options), lsprpc.WithTelemetry(true))
7786
}
7887

7988
if s.Address != "" {

internal/lsp/lsprpc/lsprpc.go

+86-22
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import (
3030
// automatic discovery to resolve a remote address.
3131
const AutoNetwork = "auto"
3232

33+
// Unique identifiers for client/server.
34+
var clientIndex, serverIndex int64
35+
3336
// The StreamServer type is a jsonrpc2.StreamServer that handles incoming
3437
// streams as a new LSP session, using a shared cache.
3538
type StreamServer struct {
@@ -40,15 +43,28 @@ type StreamServer struct {
4043
serverForTest protocol.Server
4144
}
4245

43-
var clientIndex, serverIndex int64
46+
// A ServerOption configures the behavior of the LSP server.
47+
type ServerOption interface {
48+
setServer(*StreamServer)
49+
}
50+
51+
// WithTelemetry configures either a Server or Forwarder to instrument RPCs
52+
// with additional telemetry.
53+
type WithTelemetry bool
54+
55+
func (t WithTelemetry) setServer(s *StreamServer) {
56+
s.withTelemetry = bool(t)
57+
}
4458

4559
// NewStreamServer creates a StreamServer using the shared cache. If
4660
// withTelemetry is true, each session is instrumented with telemetry that
4761
// records RPC statistics.
48-
func NewStreamServer(cache *cache.Cache, withTelemetry bool) *StreamServer {
62+
func NewStreamServer(cache *cache.Cache, opts ...ServerOption) *StreamServer {
4963
s := &StreamServer{
50-
withTelemetry: withTelemetry,
51-
cache: cache,
64+
cache: cache,
65+
}
66+
for _, opt := range opts {
67+
opt.setServer(s)
5268
}
5369
return s
5470
}
@@ -133,7 +149,11 @@ func (s *StreamServer) ServeStream(ctx context.Context, stream jsonrpc2.Stream)
133149
// Clients may or may not send a shutdown message. Make sure the server is
134150
// shut down.
135151
// TODO(rFindley): this shutdown should perhaps be on a disconnected context.
136-
defer server.Shutdown(ctx)
152+
defer func() {
153+
if err := server.Shutdown(ctx); err != nil {
154+
event.Error(ctx, "error shutting down", err)
155+
}
156+
}()
137157
conn.AddHandler(protocol.ServerHandler(server))
138158
conn.AddHandler(protocol.Canceller{})
139159
if s.withTelemetry {
@@ -160,31 +180,73 @@ func (s *StreamServer) ServeStream(ctx context.Context, stream jsonrpc2.Stream)
160180
type Forwarder struct {
161181
network, addr string
162182

163-
// Configuration. Right now, not all of this may be customizable, but in the
164-
// future it probably will be.
165-
withTelemetry bool
166-
dialTimeout time.Duration
167-
retries int
168-
goplsPath string
183+
// goplsPath is the path to the current executing gopls binary.
184+
goplsPath string
185+
186+
// configuration
187+
withTelemetry bool
188+
dialTimeout time.Duration
189+
retries int
190+
remoteDebug string
191+
remoteListenTimeout time.Duration
192+
remoteLogfile string
193+
}
194+
195+
// A ForwarderOption configures the behavior of the LSP forwarder.
196+
type ForwarderOption interface {
197+
setForwarder(*Forwarder)
198+
}
199+
200+
func (t WithTelemetry) setForwarder(fwd *Forwarder) {
201+
fwd.withTelemetry = bool(t)
202+
}
203+
204+
// RemoteDebugAddress configures the address used by the auto-started Gopls daemon
205+
// for serving debug information.
206+
type RemoteDebugAddress string
207+
208+
func (d RemoteDebugAddress) setForwarder(fwd *Forwarder) {
209+
fwd.remoteDebug = string(d)
210+
}
211+
212+
// RemoteListenTimeout configures the amount of time the auto-started gopls
213+
// daemon will wait with no client connections before shutting down.
214+
type RemoteListenTimeout time.Duration
215+
216+
func (d RemoteListenTimeout) setForwarder(fwd *Forwarder) {
217+
fwd.remoteListenTimeout = time.Duration(d)
218+
}
219+
220+
// RemoteLogfile configures the logfile location for the auto-started gopls
221+
// daemon.
222+
type RemoteLogfile string
223+
224+
func (l RemoteLogfile) setForwarder(fwd *Forwarder) {
225+
fwd.remoteLogfile = string(l)
169226
}
170227

171228
// NewForwarder creates a new Forwarder, ready to forward connections to the
172229
// remote server specified by network and addr.
173-
func NewForwarder(network, addr string, withTelemetry bool) *Forwarder {
230+
func NewForwarder(network, addr string, opts ...ForwarderOption) *Forwarder {
174231
gp, err := os.Executable()
175232
if err != nil {
176233
log.Printf("error getting gopls path for forwarder: %v", err)
177234
gp = ""
178235
}
179236

180-
return &Forwarder{
181-
network: network,
182-
addr: addr,
183-
withTelemetry: withTelemetry,
184-
dialTimeout: 1 * time.Second,
185-
retries: 5,
186-
goplsPath: gp,
237+
fwd := &Forwarder{
238+
network: network,
239+
addr: addr,
240+
goplsPath: gp,
241+
dialTimeout: 1 * time.Second,
242+
retries: 5,
243+
remoteLogfile: "auto",
244+
remoteListenTimeout: 1 * time.Minute,
245+
}
246+
for _, opt := range opts {
247+
opt.setForwarder(fwd)
187248
}
249+
return fwd
188250
}
189251

190252
// QueryServerState queries the server state of the current server.
@@ -327,9 +389,11 @@ func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
327389
}
328390
args := []string{"serve",
329391
"-listen", fmt.Sprintf(`%s;%s`, network, address),
330-
"-listen.timeout", "1m",
331-
"-debug", "localhost:0",
332-
"-logfile", "auto",
392+
"-listen.timeout", f.remoteListenTimeout.String(),
393+
"-logfile", f.remoteLogfile,
394+
}
395+
if f.remoteDebug != "" {
396+
args = append(args, "-debug", f.remoteDebug)
333397
}
334398
if err := startRemote(f.goplsPath, args...); err != nil {
335399
return nil, fmt.Errorf("startRemote(%q, %v): %v", f.goplsPath, args, err)

internal/lsp/lsprpc/lsprpc_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestClientLogging(t *testing.T) {
4949
client := fakeClient{logs: make(chan string, 10)}
5050

5151
ctx = debug.WithInstance(ctx, "", "")
52-
ss := NewStreamServer(cache.New(ctx, nil), false)
52+
ss := NewStreamServer(cache.New(ctx, nil))
5353
ss.serverForTest = server
5454
ts := servertest.NewPipeServer(ctx, ss)
5555
defer ts.Close()
@@ -106,13 +106,13 @@ func TestRequestCancellation(t *testing.T) {
106106
}
107107
baseCtx := context.Background()
108108
serveCtx := debug.WithInstance(baseCtx, "", "")
109-
ss := NewStreamServer(cache.New(serveCtx, nil), false)
109+
ss := NewStreamServer(cache.New(serveCtx, nil))
110110
ss.serverForTest = server
111111
tsDirect := servertest.NewTCPServer(serveCtx, ss)
112112
defer tsDirect.Close()
113113

114114
forwarderCtx := debug.WithInstance(baseCtx, "", "")
115-
forwarder := NewForwarder("tcp", tsDirect.Addr, false)
115+
forwarder := NewForwarder("tcp", tsDirect.Addr)
116116
tsForwarded := servertest.NewPipeServer(forwarderCtx, forwarder)
117117
defer tsForwarded.Close()
118118

@@ -183,10 +183,10 @@ func TestDebugInfoLifecycle(t *testing.T) {
183183
serverCtx := debug.WithInstance(baseCtx, "", "")
184184

185185
cache := cache.New(serverCtx, nil)
186-
ss := NewStreamServer(cache, false)
186+
ss := NewStreamServer(cache)
187187
tsBackend := servertest.NewTCPServer(serverCtx, ss)
188188

189-
forwarder := NewForwarder("tcp", tsBackend.Addr, false)
189+
forwarder := NewForwarder("tcp", tsBackend.Addr)
190190
tsForwarder := servertest.NewPipeServer(clientCtx, forwarder)
191191

192192
ws, err := fake.NewWorkspace("gopls-lsprpc-test", []byte(exampleProgram))

internal/lsp/regtest/env.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (r *Runner) getTestServer() *servertest.TCPServer {
8383
if r.ts == nil {
8484
ctx := context.Background()
8585
ctx = debug.WithInstance(ctx, "", "")
86-
ss := lsprpc.NewStreamServer(cache.New(ctx, nil), false)
86+
ss := lsprpc.NewStreamServer(cache.New(ctx, nil))
8787
r.ts = servertest.NewTCPServer(context.Background(), ss)
8888
}
8989
return r.ts
@@ -189,7 +189,7 @@ func (r *Runner) RunInMode(modes EnvMode, t *testing.T, filedata string, test fu
189189

190190
func (r *Runner) singletonEnv(ctx context.Context, t *testing.T) (servertest.Connector, func()) {
191191
ctx = debug.WithInstance(ctx, "", "")
192-
ss := lsprpc.NewStreamServer(cache.New(ctx, nil), false)
192+
ss := lsprpc.NewStreamServer(cache.New(ctx, nil))
193193
ts := servertest.NewPipeServer(ctx, ss)
194194
cleanup := func() {
195195
ts.Close()
@@ -204,7 +204,7 @@ func (r *Runner) sharedEnv(ctx context.Context, t *testing.T) (servertest.Connec
204204
func (r *Runner) forwardedEnv(ctx context.Context, t *testing.T) (servertest.Connector, func()) {
205205
ctx = debug.WithInstance(ctx, "", "")
206206
ts := r.getTestServer()
207-
forwarder := lsprpc.NewForwarder("tcp", ts.Addr, false)
207+
forwarder := lsprpc.NewForwarder("tcp", ts.Addr)
208208
ts2 := servertest.NewPipeServer(ctx, forwarder)
209209
cleanup := func() {
210210
ts2.Close()
@@ -217,7 +217,7 @@ func (r *Runner) separateProcessEnv(ctx context.Context, t *testing.T) (serverte
217217
socket := r.getRemoteSocket(t)
218218
// TODO(rfindley): can we use the autostart behavior here, instead of
219219
// pre-starting the remote?
220-
forwarder := lsprpc.NewForwarder("unix", socket, false)
220+
forwarder := lsprpc.NewForwarder("unix", socket)
221221
ts2 := servertest.NewPipeServer(ctx, forwarder)
222222
cleanup := func() {
223223
ts2.Close()

0 commit comments

Comments
 (0)