1
0
mirror of https://github.com/golang/go synced 2024-11-18 11:34:45 -07:00

internal/lsp: changing server noun to serve verb

Also adding in --remote support and using it to implement the equivalent
functionality of the external forward command
Also adding in --listen as a replacement for --port as it is more flexible,
specifically it allows localhost:port which
is helpful in environments where opening remotely accesible ports is
problematic.

Change-Id: I5de1cea7dd6f1ee46e7423f3be2a4caca6f040b2
Reviewed-on: https://go-review.googlesource.com/c/161658
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2019-02-07 17:05:20 -05:00
parent 657755b003
commit 44bee7e801
4 changed files with 55 additions and 72 deletions

View File

@ -1,59 +0,0 @@
// The forward command writes and reads to a gopls server on a network socket.
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"golang.org/x/tools/internal/lsp/cmd"
"golang.org/x/tools/internal/tool"
)
func main() {
tool.Main(context.Background(), &app{&cmd.Server{}}, os.Args[1:])
}
type app struct {
*cmd.Server
}
func (*app) Name() string { return "forward" }
func (*app) Usage() string { return "[-port=<value>]" }
func (*app) ShortHelp() string { return "An intermediary between an editor and gopls." }
func (*app) DetailedHelp(*flag.FlagSet) {}
func (a *app) Run(ctx context.Context, args ...string) error {
if a.Server.Port == 0 {
a.ShortHelp()
os.Exit(0)
}
conn, err := net.Dial("tcp", fmt.Sprintf(":%v", a.Server.Port))
if err != nil {
log.Print(err)
os.Exit(0)
}
go func(conn net.Conn) {
_, err := io.Copy(conn, os.Stdin)
if err != nil {
log.Print(err)
os.Exit(0)
}
}(conn)
go func(conn net.Conn) {
_, err := io.Copy(os.Stdout, conn)
if err != nil {
log.Print(err)
os.Exit(0)
}
}(conn)
for {
}
}

View File

@ -25,12 +25,16 @@ type Application struct {
// Embed the basic profiling flags supported by the tool package // Embed the basic profiling flags supported by the tool package
tool.Profile tool.Profile
// We include the server directly for now, so the flags work even without the verb. // We include the server configuration directly for now, so the flags work
// TODO: Remove this when we stop allowing the server verb by default. // even without the verb.
Server Server // TODO: Remove this when we stop allowing the serve verb by default.
Serve Serve
// An initial, common go/packages configuration // An initial, common go/packages configuration
Config packages.Config Config packages.Config
// Support for remote lsp server
Remote string `flag:"remote" help:"*EXPERIMENTAL* - forward all commands to a remote lsp"`
} }
// Name implements tool.Application returning the binary name. // Name implements tool.Application returning the binary name.
@ -65,7 +69,7 @@ gopls flags are:
// temporary measure for compatibility. // temporary measure for compatibility.
func (app *Application) Run(ctx context.Context, args ...string) error { func (app *Application) Run(ctx context.Context, args ...string) error {
if len(args) == 0 { if len(args) == 0 {
tool.Main(ctx, &app.Server, args) tool.Main(ctx, &app.Serve, args)
return nil return nil
} }
app.Config.Mode = packages.LoadSyntax app.Config.Mode = packages.LoadSyntax
@ -87,8 +91,9 @@ func (app *Application) Run(ctx context.Context, args ...string) error {
// command line. // command line.
// The command is specified by the first non flag argument. // The command is specified by the first non flag argument.
func (app *Application) commands() []tool.Application { func (app *Application) commands() []tool.Application {
app.Serve.app = app
return []tool.Application{ return []tool.Application{
&app.Server, &app.Serve,
&query{app: app}, &query{app: app},
} }
} }

View File

@ -15,26 +15,30 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"net"
"golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp" "golang.org/x/tools/internal/lsp"
"golang.org/x/tools/internal/tool" "golang.org/x/tools/internal/tool"
) )
// Server is a struct that exposes the configurable parts of the LSP server as // Serve is a struct that exposes the configurable parts of the LSP server as
// flags, in the right form for tool.Main to consume. // flags, in the right form for tool.Main to consume.
type Server struct { type Serve struct {
Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"` Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"`
Mode string `flag:"mode" help:"no effect"` Mode string `flag:"mode" help:"no effect"`
Port int `flag:"port" help:"port on which to run gopls for debugging purposes"` Port int `flag:"port" help:"port on which to run gopls for debugging purposes"`
Address string `flag:"listen" help:"address on which to listen for remote connections"`
app *Application
} }
func (s *Server) Name() string { return "server" } func (s *Serve) Name() string { return "serve" }
func (s *Server) Usage() string { return "" } func (s *Serve) Usage() string { return "" }
func (s *Server) ShortHelp() string { func (s *Serve) ShortHelp() string {
return "run a server for Go code using the Language Server Protocol" return "run a server for Go code using the Language Server Protocol"
} }
func (s *Server) DetailedHelp(f *flag.FlagSet) { func (s *Serve) DetailedHelp(f *flag.FlagSet) {
fmt.Fprint(f.Output(), ` fmt.Fprint(f.Output(), `
The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as
a child of an editor process. a child of an editor process.
@ -46,7 +50,7 @@ gopls server flags are:
// Run configures a server based on the flags, and then runs it. // Run configures a server based on the flags, and then runs it.
// It blocks until the server shuts down. // It blocks until the server shuts down.
func (s *Server) Run(ctx context.Context, args ...string) error { func (s *Serve) Run(ctx context.Context, args ...string) error {
if len(args) > 0 { if len(args) > 0 {
return tool.CommandLineErrorf("server does not take arguments, got %v", args) return tool.CommandLineErrorf("server does not take arguments, got %v", args)
} }
@ -64,6 +68,9 @@ func (s *Server) Run(ctx context.Context, args ...string) error {
log.SetOutput(io.MultiWriter(os.Stderr, f)) log.SetOutput(io.MultiWriter(os.Stderr, f))
out = f out = f
} }
if s.app.Remote != "" {
return s.forward()
}
logger := func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) { logger := func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) {
const eol = "\r\n\r\n\r\n" const eol = "\r\n\r\n\r\n"
if err != nil { if err != nil {
@ -112,9 +119,33 @@ func (s *Server) Run(ctx context.Context, args ...string) error {
fmt.Fprintf(out, "%s", outx.String()) fmt.Fprintf(out, "%s", outx.String())
} }
// For debugging purposes only. // For debugging purposes only.
if s.Address != "" {
return lsp.RunServerOnAddress(ctx, s.Address, logger)
}
if s.Port != 0 { if s.Port != 0 {
return lsp.RunServerOnPort(ctx, s.Port, logger) return lsp.RunServerOnPort(ctx, s.Port, logger)
} }
stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout) stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
return lsp.RunServer(ctx, stream, logger) return lsp.RunServer(ctx, stream, logger)
} }
func (s *Serve) forward() error {
conn, err := net.Dial("tcp", s.app.Remote)
if err != nil {
return err
}
errc := make(chan error)
go func(conn net.Conn) {
_, err := io.Copy(conn, os.Stdin)
errc <- err
}(conn)
go func(conn net.Conn) {
_, err := io.Copy(os.Stdout, conn)
errc <- err
}(conn)
return <-errc
}

View File

@ -31,8 +31,14 @@ func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{})
// RunServerOnPort starts an LSP server on the given port and does not exit. // RunServerOnPort starts an LSP server on the given port and does not exit.
// This function exists for debugging purposes. // This function exists for debugging purposes.
func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error { func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error {
return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port))
}
// RunServerOnPort starts an LSP server on the given port and does not exit.
// This function exists for debugging purposes.
func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
s := &server{} s := &server{}
ln, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) ln, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
return err return err
} }