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