github.com/v2fly/tools@v0.100.0/internal/lsp/cmd/serve.go (about) 1 // Copyright 2018 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 cmd 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "io" 12 "log" 13 "os" 14 "time" 15 16 "github.com/v2fly/tools/internal/fakenet" 17 "github.com/v2fly/tools/internal/jsonrpc2" 18 "github.com/v2fly/tools/internal/lsp/cache" 19 "github.com/v2fly/tools/internal/lsp/debug" 20 "github.com/v2fly/tools/internal/lsp/lsprpc" 21 "github.com/v2fly/tools/internal/lsp/protocol" 22 "github.com/v2fly/tools/internal/tool" 23 errors "golang.org/x/xerrors" 24 ) 25 26 // Serve is a struct that exposes the configurable parts of the LSP server as 27 // flags, in the right form for tool.Main to consume. 28 type Serve struct { 29 Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"` 30 Mode string `flag:"mode" help:"no effect"` 31 Port int `flag:"port" help:"port on which to run gopls for debugging purposes"` 32 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."` 33 IdleTimeout time.Duration `flag:"listen.timeout" help:"when used with -listen, shut down the server when there are no connected clients for this duration"` 34 Trace bool `flag:"rpc.trace" help:"print the full rpc trace in lsp inspector format"` 35 Debug string `flag:"debug" help:"serve debug information on the supplied address"` 36 37 RemoteListenTimeout time.Duration `flag:"remote.listen.timeout" help:"when used with -remote=auto, the -listen.timeout value used to start the daemon"` 38 RemoteDebug string `flag:"remote.debug" help:"when used with -remote=auto, the -debug value used to start the daemon"` 39 RemoteLogfile string `flag:"remote.logfile" help:"when used with -remote=auto, the -logfile value used to start the daemon"` 40 41 app *Application 42 } 43 44 func (s *Serve) Name() string { return "serve" } 45 func (s *Serve) Usage() string { return "" } 46 func (s *Serve) ShortHelp() string { 47 return "run a server for Go code using the Language Server Protocol" 48 } 49 func (s *Serve) DetailedHelp(f *flag.FlagSet) { 50 fmt.Fprint(f.Output(), ` 51 The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as 52 a child of an editor process. 53 54 gopls server flags are: 55 `) 56 f.PrintDefaults() 57 } 58 59 // Run configures a server based on the flags, and then runs it. 60 // It blocks until the server shuts down. 61 func (s *Serve) Run(ctx context.Context, args ...string) error { 62 if len(args) > 0 { 63 return tool.CommandLineErrorf("server does not take arguments, got %v", args) 64 } 65 66 di := debug.GetInstance(ctx) 67 isDaemon := s.Address != "" || s.Port != 0 68 if di != nil { 69 closeLog, err := di.SetLogFile(s.Logfile, isDaemon) 70 if err != nil { 71 return err 72 } 73 defer closeLog() 74 di.ServerAddress = s.Address 75 di.MonitorMemory(ctx) 76 di.Serve(ctx, s.Debug) 77 } 78 var ss jsonrpc2.StreamServer 79 if s.app.Remote != "" { 80 network, addr := lsprpc.ParseAddr(s.app.Remote) 81 ss = lsprpc.NewForwarder(network, addr, 82 lsprpc.RemoteDebugAddress(s.RemoteDebug), 83 lsprpc.RemoteListenTimeout(s.RemoteListenTimeout), 84 lsprpc.RemoteLogfile(s.RemoteLogfile), 85 ) 86 } else { 87 ss = lsprpc.NewStreamServer(cache.New(s.app.options), isDaemon) 88 } 89 90 var network, addr string 91 if s.Address != "" { 92 network, addr = lsprpc.ParseAddr(s.Address) 93 } 94 if s.Port != 0 { 95 network = "tcp" 96 addr = fmt.Sprintf(":%v", s.Port) 97 } 98 if addr != "" { 99 log.Printf("Gopls daemon: listening on %s network, address %s...", network, addr) 100 defer log.Printf("Gopls daemon: exiting") 101 return jsonrpc2.ListenAndServe(ctx, network, addr, ss, s.IdleTimeout) 102 } 103 stream := jsonrpc2.NewHeaderStream(fakenet.NewConn("stdio", os.Stdin, os.Stdout)) 104 if s.Trace && di != nil { 105 stream = protocol.LoggingStream(stream, di.LogWriter) 106 } 107 conn := jsonrpc2.NewConn(stream) 108 err := ss.ServeStream(ctx, conn) 109 if errors.Is(err, io.EOF) { 110 return nil 111 } 112 return err 113 }