Skip to content

Commit ca86fb0

Browse files
committed
gopls/lsprpc: Adjust gopls to cuelsp and simplify
More replacing of gopls with cuelsp, but this time for rpc logging and protocol. Simplify the "fowarding" implementation: the logfile and debug options are no longer supported so the forwarding handshake can be simplified. The logfile is no longer supported because the lsp protocol itself allows trace logging back to the editor which is perfectly sufficient. Signed-off-by: Matthew Sackman <[email protected]> Change-Id: I80302d363ea5e95c3b40ce7321cdbc092465e545 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1218088 Reviewed-by: Daniel Martí <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent 4752c04 commit ca86fb0

File tree

6 files changed

+54
-118
lines changed

6 files changed

+54
-118
lines changed

internal/golangorgx/gopls/cmd/serve.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,12 @@ import (
2424
// Serve is a struct that exposes the configurable parts of the LSP server as
2525
// flags, in the right form for tool.Main to consume.
2626
type Serve struct {
27-
Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
2827
Mode string `flag:"mode" help:"no effect"`
2928
Port int `flag:"port" help:"port on which to run cuelsp for debugging purposes"`
3029
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."`
3130
IdleTimeout time.Duration `flag:"listen.timeout" help:"when used with -listen, shut down the server when there are no connected clients for this duration"`
3231

3332
RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the -listen.timeout value used to start the daemon"`
34-
RemoteDebug string `flag:"remote.debug" help:"when used with -remote=auto, the -debug value used to start the daemon"`
35-
RemoteLogfile string `flag:"remote.logfile" help:"when used with -remote=auto, the -logfile value used to start the daemon"`
3633

3734
app *Application
3835
}
@@ -58,15 +55,9 @@ func (s *Serve) remoteArgs(network, address string) []string {
5855
args := []string{"serve",
5956
"-listen", fmt.Sprintf(`%s;%s`, network, address),
6057
}
61-
if s.RemoteDebug != "" {
62-
args = append(args, "-debug", s.RemoteDebug)
63-
}
6458
if s.RemoteListenTimeout != 0 {
6559
args = append(args, "-listen.timeout", s.RemoteListenTimeout.String())
6660
}
67-
if s.RemoteLogfile != "" {
68-
args = append(args, "-logfile", s.RemoteLogfile)
69-
}
7061
return args
7162
}
7263

internal/golangorgx/gopls/lsprpc/autostart_default.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ var (
1818
func runRemote(cmd *exec.Cmd) error {
1919
daemonize(cmd)
2020
if err := cmd.Start(); err != nil {
21-
return fmt.Errorf("starting remote gopls: %w", err)
21+
return fmt.Errorf("starting remote cuelsp: %w", err)
2222
}
2323
return nil
2424
}
2525

2626
// autoNetworkAddressDefault returns the default network and address for the
27-
// automatically-started gopls remote. See autostart_posix.go for more
27+
// automatically-started cuelsp remote. See autostart_posix.go for more
2828
// information.
29-
func autoNetworkAddressDefault(goplsPath, id string) (network string, address string) {
29+
func autoNetworkAddressDefault(cuePath, id string) (network string, address string) {
3030
if id != "" {
3131
panic("identified remotes are not supported on windows")
3232
}
33-
return "tcp", "localhost:37374"
33+
return "tcp", "localhost:37375"
3434
}
3535

3636
func verifyRemoteOwnershipDefault(network, address string) (bool, error) {

internal/golangorgx/gopls/lsprpc/autostart_posix.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,30 +34,30 @@ func daemonizePosix(cmd *exec.Cmd) {
3434

3535
// autoNetworkAddressPosix resolves an id on the 'auto' pseduo-network to a
3636
// real network and address. On unix, this uses unix domain sockets.
37-
func autoNetworkAddressPosix(goplsPath, id string) (network string, address string) {
37+
func autoNetworkAddressPosix(cuePath, id string) (network string, address string) {
3838
// Especially when doing local development or testing, it's important that
39-
// the remote gopls instance we connect to is running the same binary as our
39+
// the remote cuelsp instance we connect to is running the same binary as our
4040
// forwarder. So we encode a short hash of the binary path into the daemon
4141
// socket name. If possible, we also include the buildid in this hash, to
4242
// account for long-running processes where the binary has been subsequently
4343
// rebuilt.
4444
h := sha256.New()
45-
cmd := exec.Command("go", "tool", "buildid", goplsPath)
45+
cmd := exec.Command("go", "tool", "buildid", cuePath)
4646
cmd.Stdout = h
4747
var pathHash []byte
4848
if err := cmd.Run(); err == nil {
4949
pathHash = h.Sum(nil)
5050
} else {
5151
log.Printf("error getting current buildid: %v", err)
52-
sum := sha256.Sum256([]byte(goplsPath))
52+
sum := sha256.Sum256([]byte(cuePath))
5353
pathHash = sum[:]
5454
}
5555
shortHash := fmt.Sprintf("%x", pathHash)[:6]
5656
user := os.Getenv("USER")
5757
if user == "" {
5858
user = "shared"
5959
}
60-
basename := filepath.Base(goplsPath)
60+
basename := filepath.Base(cuePath)
6161
idComponent := ""
6262
if id != "" {
6363
idComponent = "-" + id

internal/golangorgx/gopls/lsprpc/dialer.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"cuelang.org/go/internal/golangorgx/tools/event"
1717
)
1818

19-
// autoNetwork is the pseudo network type used to signal that gopls should use
19+
// autoNetwork is the pseudo network type used to signal that cuelsp should use
2020
// automatic discovery to resolve a remote address.
2121
const autoNetwork = "auto"
2222

@@ -36,12 +36,12 @@ func newAutoDialer(rawAddr string, argFunc func(network, addr string) []string)
3636
d.network, d.addr = ParseAddr(rawAddr)
3737
if d.network == autoNetwork {
3838
d.isAuto = true
39-
bin, err := os.Executable()
39+
cuePath, err := os.Executable()
4040
if err != nil {
4141
return nil, fmt.Errorf("getting executable: %w", err)
4242
}
43-
d.executable = bin
44-
d.network, d.addr = autoNetworkAddress(bin, d.addr)
43+
d.executable = cuePath
44+
d.network, d.addr = autoNetworkAddress(cuePath, d.addr)
4545
}
4646
return &d, nil
4747
}

internal/golangorgx/gopls/lsprpc/lsprpc.go

Lines changed: 40 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ type streamServer struct {
4646
serverForTest protocol.Server
4747
}
4848

49-
// NewStreamServer creates a StreamServer using the shared cache. If
50-
// withTelemetry is true, each session is instrumented with telemetry that
51-
// records RPC statistics.
49+
// NewStreamServer creates a StreamServer using the shared cache.
5250
func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*settings.Options)) jsonrpc2.StreamServer {
5351
return &streamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc}
5452
}
@@ -70,17 +68,11 @@ func (s *streamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) erro
7068
event.Error(ctx, "error shutting down", err)
7169
}
7270
}()
73-
executable, err := os.Executable()
74-
if err != nil {
75-
log.Printf("error getting gopls path: %v", err)
76-
executable = ""
77-
}
7871
ctx = protocol.WithClient(ctx, client)
7972
conn.Go(ctx,
8073
protocol.Handlers(
81-
handshaker("unknown", executable, s.daemon,
82-
protocol.ServerHandler(svr,
83-
jsonrpc2.MethodNotFound))))
74+
handshaker("unknown", s.daemon,
75+
protocol.ServerHandler(svr, jsonrpc2.MethodNotFound))))
8476
if s.daemon {
8577
log.Printf("Session %s: connected", "unknown")
8678
defer log.Printf("Session %s: exited", "unknown")
@@ -89,12 +81,12 @@ func (s *streamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) erro
8981
return conn.Err()
9082
}
9183

92-
// A forwarder is a jsonrpc2.StreamServer that handles an LSP stream by
93-
// forwarding it to a remote. This is used when the gopls process started by
94-
// the editor is in the `-remote` mode, which means it finds and connects to a
95-
// separate gopls daemon. In these cases, we still want the forwarder gopls to
96-
// be instrumented with telemetry, and want to be able to in some cases hijack
97-
// the jsonrpc2 connection with the daemon.
84+
// A forwarder is a jsonrpc2.StreamServer that handles an LSP stream
85+
// by forwarding it to a remote. This is used when the cuelsp process
86+
// started by the editor is in the `-remote` mode, which means it
87+
// finds and connects to a separate cuelsp daemon. In these cases, we
88+
// still want the forwarder cuelsp to in some cases hijack the
89+
// jsonrpc2 connection with the daemon.
9890
type forwarder struct {
9991
dialer *autoDialer
10092

@@ -128,23 +120,23 @@ func QueryServerState(ctx context.Context, addr string) (any, error) {
128120
return nil, err
129121
}
130122
var state serverState
131-
if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil {
123+
if err := protocol.Call(ctx, serverConn, serversMethod, nil, &state); err != nil {
132124
return nil, fmt.Errorf("querying server state: %w", err)
133125
}
134126
return &state, nil
135127
}
136128

137-
// dialRemote is used for making calls into the gopls daemon. addr should be a
129+
// dialRemote is used for making calls into the cuelsp daemon. addr should be a
138130
// URL, possibly on the synthetic 'auto' network (e.g. tcp://..., unix://...,
139131
// or auto://...).
140132
func dialRemote(ctx context.Context, addr string) (jsonrpc2.Conn, error) {
141133
network, address := ParseAddr(addr)
142134
if network == autoNetwork {
143-
gp, err := os.Executable()
135+
cuePath, err := os.Executable()
144136
if err != nil {
145-
return nil, fmt.Errorf("getting gopls path: %w", err)
137+
return nil, fmt.Errorf("getting cue path: %w", err)
146138
}
147-
network, address = autoNetworkAddress(gp, address)
139+
network, address = autoNetworkAddress(cuePath, address)
148140
}
149141
netConn, err := net.DialTimeout(network, address, 5*time.Second)
150142
if err != nil {
@@ -227,32 +219,16 @@ func (f *forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) e
227219
func (f *forwarder) handshake(ctx context.Context) {
228220
// This call to os.Executable is redundant, and will be eliminated by the
229221
// transition to the V2 API.
230-
goplsPath, err := os.Executable()
231-
if err != nil {
232-
event.Error(ctx, "getting executable for handshake", err)
233-
goplsPath = ""
234-
}
235-
var (
236-
hreq = handshakeRequest{
237-
ServerID: f.serverID,
238-
GoplsPath: goplsPath,
239-
}
240-
hresp handshakeResponse
241-
)
222+
hreq := handshakeRequest{ServerID: f.serverID}
223+
var hresp handshakeResponse
242224
if err := protocol.Call(ctx, f.serverConn, handshakeMethod, hreq, &hresp); err != nil {
243225
// TODO(rfindley): at some point in the future we should return an error
244226
// here. Handshakes have become functional in nature.
245-
event.Error(ctx, "forwarder: gopls handshake failed", err)
246-
}
247-
if hresp.GoplsPath != goplsPath {
248-
event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", goplsPath, hresp.GoplsPath))
227+
event.Error(ctx, "forwarder: cuelsp handshake failed", err)
249228
}
250229
event.Log(ctx, "New server",
251230
tag.NewServer.Of(f.serverID),
252-
tag.Logfile.Of(hresp.Logfile),
253-
tag.DebugAddress.Of(hresp.DebugAddr),
254-
tag.GoplsPath.Of(hresp.GoplsPath),
255-
tag.ClientID.Of(hresp.SessionID),
231+
tag.ServerID.Of(hresp.ServerID),
256232
)
257233
}
258234

@@ -266,57 +242,35 @@ func ConnectToRemote(ctx context.Context, addr string) (net.Conn, error) {
266242

267243
// A handshakeRequest identifies a client to the LSP server.
268244
type handshakeRequest struct {
269-
// ServerID is the ID of the server on the client. This should usually be 0.
245+
// ServerID is the ID of the server on the client. This should
246+
// usually be 0.
270247
ServerID string `json:"serverID"`
271-
// Logfile is the location of the clients log file.
272-
Logfile string `json:"logfile"`
273-
// DebugAddr is the client debug address.
274-
DebugAddr string `json:"debugAddr"`
275-
// GoplsPath is the path to the Gopls binary running the current client
276-
// process.
277-
GoplsPath string `json:"goplsPath"`
278248
}
279249

280-
// A handshakeResponse is returned by the LSP server to tell the LSP client
281-
// information about its session.
250+
// A handshakeResponse is returned by the LSP server to tell the LSP
251+
// client information about its server.
282252
type handshakeResponse struct {
283-
// SessionID is the server session associated with the client.
284-
SessionID string `json:"sessionID"`
285-
// Logfile is the location of the server logs.
286-
Logfile string `json:"logfile"`
287-
// DebugAddr is the server debug address.
288-
DebugAddr string `json:"debugAddr"`
289-
// GoplsPath is the path to the Gopls binary running the current server
290-
// process.
291-
GoplsPath string `json:"goplsPath"`
253+
// ServerID is the server server associated with the client.
254+
ServerID string `json:"serverID"`
292255
}
293256

294-
// clientSession identifies a current client LSP session on the server. Note
295-
// that it looks similar to handshakeResposne, but in fact 'Logfile' and
296-
// 'DebugAddr' now refer to the client.
297-
type clientSession struct {
298-
SessionID string `json:"sessionID"`
299-
Logfile string `json:"logfile"`
300-
DebugAddr string `json:"debugAddr"`
257+
// clientServer identifies a current client LSP server on the
258+
// server.
259+
type clientServer struct {
260+
ServerID string `json:"serverID"`
301261
}
302262

303-
// serverState holds information about the gopls daemon process, including its
304-
// debug information and debug information of all of its current connected
305-
// clients.
263+
// serverState holds information about the cuelsp daemon process.
306264
type serverState struct {
307-
Logfile string `json:"logfile"`
308-
DebugAddr string `json:"debugAddr"`
309-
GoplsPath string `json:"goplsPath"`
310-
CurrentClientID string `json:"currentClientID"`
311-
Clients []clientSession `json:"clients"`
265+
CurrentServerID string `json:"currentServerID"`
312266
}
313267

314268
const (
315-
handshakeMethod = "gopls/handshake"
316-
sessionsMethod = "gopls/sessions"
269+
handshakeMethod = "cuelsp/handshake"
270+
serversMethod = "cuelsp/servers"
317271
)
318272

319-
func handshaker(svrID string, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler {
273+
func handshaker(svrID string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler {
320274
return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
321275
switch r.Method() {
322276
case handshakeMethod:
@@ -326,30 +280,25 @@ func handshaker(svrID string, goplsPath string, logHandshakes bool, handler json
326280
var req handshakeRequest
327281
if err := json.Unmarshal(r.Params(), &req); err != nil {
328282
if logHandshakes {
329-
log.Printf("Error processing handshake for session %s: %v", svrID, err)
283+
log.Printf("Error processing handshake for server %s: %v", svrID, err)
330284
}
331285
sendError(ctx, reply, err)
332286
return nil
333287
}
334288
if logHandshakes {
335-
log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", svrID, req.Logfile, req.DebugAddr)
289+
log.Printf("Server %s: got handshake.", svrID)
336290
}
337-
event.Log(ctx, "Handshake session update",
338-
tag.DebugAddress.Of(req.DebugAddr),
339-
tag.Logfile.Of(req.Logfile),
291+
event.Log(ctx, "Handshake server update",
340292
tag.ServerID.Of(req.ServerID),
341-
tag.GoplsPath.Of(req.GoplsPath),
342293
)
343294
resp := handshakeResponse{
344-
SessionID: svrID,
345-
GoplsPath: goplsPath,
295+
ServerID: svrID,
346296
}
347297
return reply(ctx, resp, nil)
348298

349-
case sessionsMethod:
299+
case serversMethod:
350300
resp := serverState{
351-
GoplsPath: goplsPath,
352-
CurrentClientID: svrID,
301+
CurrentServerID: svrID,
353302
}
354303
return reply(ctx, resp, nil)
355304
}
@@ -364,7 +313,7 @@ func sendError(ctx context.Context, reply jsonrpc2.Replier, err error) {
364313
}
365314
}
366315

367-
// ParseAddr parses the address of a gopls remote.
316+
// ParseAddr parses the address of a cuelsp remote.
368317
// TODO(rFindley): further document this syntax, and allow URI-style remote
369318
// addresses such as "auto://...".
370319
func ParseAddr(listen string) (network string, address string) {

internal/golangorgx/tools/event/tag/tag.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ var (
3636
NewServer = keys.NewString("new_server", "A new server was added")
3737
EndServer = keys.NewString("end_server", "A server was shut down")
3838

39-
ServerID = keys.NewString("server", "The server ID an event is related to")
40-
Logfile = keys.NewString("logfile", "")
41-
DebugAddress = keys.NewString("debug_address", "")
42-
GoplsPath = keys.NewString("gopls_path", "")
43-
ClientID = keys.NewString("client_id", "")
39+
ServerID = keys.NewString("server", "The server ID an event is related to")
4440

4541
Level = keys.NewInt("level", "The logging level")
4642
)

0 commit comments

Comments
 (0)