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