2018-12-17 11:52:58 -07:00
|
|
|
// Copyright 2018 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2019-01-16 15:01:06 -07:00
|
|
|
// Package cmd handles the gopls command line.
|
2018-12-17 11:52:58 -07:00
|
|
|
// It contains a handler for each of the modes, along with all the flag handling
|
|
|
|
// and the command line output format.
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2018-12-14 13:46:12 -07:00
|
|
|
"go/token"
|
2019-04-01 18:08:14 -06:00
|
|
|
"io/ioutil"
|
2019-03-29 17:04:29 -06:00
|
|
|
"log"
|
2019-02-08 14:16:57 -07:00
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2019-05-07 11:54:07 -06:00
|
|
|
"sync"
|
2019-02-05 15:27:35 -07:00
|
|
|
|
2019-02-08 14:16:57 -07:00
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
|
|
"golang.org/x/tools/internal/lsp"
|
2019-05-15 10:24:49 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/cache"
|
2019-02-08 14:16:57 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2019-05-15 10:24:49 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2019-02-08 14:16:57 -07:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-08-14 10:51:42 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry/export"
|
|
|
|
"golang.org/x/tools/internal/telemetry/export/ocagent"
|
2018-12-17 11:52:58 -07:00
|
|
|
"golang.org/x/tools/internal/tool"
|
2019-07-10 19:11:23 -06:00
|
|
|
"golang.org/x/tools/internal/xcontext"
|
2019-08-06 13:13:11 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2018-12-17 11:52:58 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// Application is the main application as passed to tool.Main
|
|
|
|
// It handles the main command line parsing and dispatch to the sub commands.
|
|
|
|
type Application struct {
|
2018-12-14 13:46:12 -07:00
|
|
|
// Core application flags
|
|
|
|
|
2018-12-17 11:52:58 -07:00
|
|
|
// Embed the basic profiling flags supported by the tool package
|
|
|
|
tool.Profile
|
|
|
|
|
2019-02-07 15:05:20 -07:00
|
|
|
// We include the server configuration directly for now, so the flags work
|
|
|
|
// even without the verb.
|
|
|
|
// TODO: Remove this when we stop allowing the serve verb by default.
|
|
|
|
Serve Serve
|
2018-12-14 13:46:12 -07:00
|
|
|
|
2019-11-19 14:45:03 -07:00
|
|
|
// the options configuring function to invoke when building a server
|
|
|
|
options func(*source.Options)
|
2019-05-17 08:51:19 -06:00
|
|
|
|
2019-07-17 18:30:54 -06:00
|
|
|
// The name of the binary, used in help and telemetry.
|
|
|
|
name string
|
|
|
|
|
2019-05-17 08:51:19 -06:00
|
|
|
// The working directory to run commands in.
|
|
|
|
wd string
|
|
|
|
|
|
|
|
// The environment variables to use.
|
|
|
|
env []string
|
2019-05-15 10:24:49 -06:00
|
|
|
|
2019-02-07 15:05:20 -07:00
|
|
|
// Support for remote lsp server
|
|
|
|
Remote string `flag:"remote" help:"*EXPERIMENTAL* - forward all commands to a remote lsp"`
|
2019-05-10 16:50:29 -06:00
|
|
|
|
|
|
|
// Enable verbose logging
|
2019-12-07 06:10:13 -07:00
|
|
|
Verbose bool `flag:"v" help:"verbose output"`
|
2019-07-17 17:17:43 -06:00
|
|
|
|
|
|
|
// Control ocagent export of telemetry
|
2019-12-07 06:10:13 -07:00
|
|
|
OCAgent string `flag:"ocagent" help:"the address of the ocagent, or off"`
|
2019-10-10 18:48:16 -06:00
|
|
|
|
|
|
|
// PrepareOptions is called to update the options when a new view is built.
|
|
|
|
// It is primarily to allow the behavior of gopls to be modified by hooks.
|
|
|
|
PrepareOptions func(*source.Options)
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
|
|
|
|
2020-01-01 05:14:58 -07:00
|
|
|
// New returns a new Application ready to run.
|
2019-10-10 18:48:16 -06:00
|
|
|
func New(name, wd string, env []string, options func(*source.Options)) *Application {
|
2019-05-17 08:51:19 -06:00
|
|
|
if wd == "" {
|
|
|
|
wd, _ = os.Getwd()
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
2019-05-17 08:51:19 -06:00
|
|
|
app := &Application{
|
2019-11-19 14:45:03 -07:00
|
|
|
options: options,
|
2019-07-17 17:17:43 -06:00
|
|
|
name: name,
|
|
|
|
wd: wd,
|
|
|
|
env: env,
|
|
|
|
OCAgent: "off", //TODO: Remove this line to default the exporter to on
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
|
|
|
return app
|
|
|
|
}
|
|
|
|
|
2018-12-17 11:52:58 -07:00
|
|
|
// Name implements tool.Application returning the binary name.
|
2019-07-17 18:30:54 -06:00
|
|
|
func (app *Application) Name() string { return app.name }
|
2018-12-17 11:52:58 -07:00
|
|
|
|
|
|
|
// Usage implements tool.Application returning empty extra argument usage.
|
2018-12-14 13:46:12 -07:00
|
|
|
func (app *Application) Usage() string { return "<command> [command-flags] [command-args]" }
|
2018-12-17 11:52:58 -07:00
|
|
|
|
|
|
|
// ShortHelp implements tool.Application returning the main binary help.
|
|
|
|
func (app *Application) ShortHelp() string {
|
2018-12-14 13:46:12 -07:00
|
|
|
return "The Go Language source tools."
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// DetailedHelp implements tool.Application returning the main binary help.
|
|
|
|
// This includes the short help for all the sub commands.
|
|
|
|
func (app *Application) DetailedHelp(f *flag.FlagSet) {
|
|
|
|
fmt.Fprint(f.Output(), `
|
2019-12-07 06:10:13 -07:00
|
|
|
gopls is a Go language server. It is typically used with an editor to provide
|
|
|
|
language features. When no command is specified, gopls will default to the 'serve'
|
|
|
|
command. The language features can also be accessed via the gopls command-line interface.
|
|
|
|
|
2018-12-14 13:46:12 -07:00
|
|
|
Available commands are:
|
2019-12-25 12:51:36 -07:00
|
|
|
`)
|
2019-12-07 06:10:13 -07:00
|
|
|
fmt.Fprint(f.Output(), `
|
|
|
|
main:
|
2018-12-17 11:52:58 -07:00
|
|
|
`)
|
2019-12-07 06:10:13 -07:00
|
|
|
for _, c := range app.mainCommands() {
|
|
|
|
fmt.Fprintf(f.Output(), " %s : %v\n", c.Name(), c.ShortHelp())
|
|
|
|
}
|
|
|
|
fmt.Fprint(f.Output(), `
|
|
|
|
features:
|
|
|
|
`)
|
|
|
|
for _, c := range app.featureCommands() {
|
2018-12-17 11:52:58 -07:00
|
|
|
fmt.Fprintf(f.Output(), " %s : %v\n", c.Name(), c.ShortHelp())
|
|
|
|
}
|
|
|
|
fmt.Fprint(f.Output(), `
|
2019-01-16 15:01:06 -07:00
|
|
|
gopls flags are:
|
2018-12-17 11:52:58 -07:00
|
|
|
`)
|
|
|
|
f.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run takes the args after top level flag processing, and invokes the correct
|
|
|
|
// sub command as specified by the first argument.
|
|
|
|
// If no arguments are passed it will invoke the server sub command, as a
|
2019-01-05 12:20:03 -07:00
|
|
|
// temporary measure for compatibility.
|
2018-12-17 11:52:58 -07:00
|
|
|
func (app *Application) Run(ctx context.Context, args ...string) error {
|
2019-08-15 20:43:07 -06:00
|
|
|
ocConfig := ocagent.Discover()
|
|
|
|
//TODO: we should not need to adjust the discovered configuration
|
|
|
|
ocConfig.Address = app.OCAgent
|
|
|
|
export.AddExporters(ocagent.Connect(ocConfig))
|
2019-02-12 12:23:47 -07:00
|
|
|
app.Serve.app = app
|
2018-12-17 11:52:58 -07:00
|
|
|
if len(args) == 0 {
|
2019-09-19 15:50:14 -06:00
|
|
|
return tool.Run(ctx, &app.Serve, args)
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
2018-12-14 13:46:12 -07:00
|
|
|
command, args := args[0], args[1:]
|
|
|
|
for _, c := range app.commands() {
|
|
|
|
if c.Name() == command {
|
2019-09-19 15:50:14 -06:00
|
|
|
return tool.Run(ctx, c, args)
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
|
|
|
}
|
2018-12-14 13:46:12 -07:00
|
|
|
return tool.CommandLineErrorf("Unknown command %v", command)
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
|
|
|
|
2018-12-14 13:46:12 -07:00
|
|
|
// commands returns the set of commands supported by the gopls tool on the
|
2018-12-17 11:52:58 -07:00
|
|
|
// command line.
|
2018-12-14 13:46:12 -07:00
|
|
|
// The command is specified by the first non flag argument.
|
|
|
|
func (app *Application) commands() []tool.Application {
|
2019-12-07 06:10:13 -07:00
|
|
|
var commands []tool.Application
|
|
|
|
commands = append(commands, app.mainCommands()...)
|
|
|
|
commands = append(commands, app.featureCommands()...)
|
|
|
|
return commands
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *Application) mainCommands() []tool.Application {
|
2018-12-17 11:52:58 -07:00
|
|
|
return []tool.Application{
|
2019-02-07 15:05:20 -07:00
|
|
|
&app.Serve,
|
2019-12-07 06:10:13 -07:00
|
|
|
&version{app: app},
|
2019-05-03 12:31:03 -06:00
|
|
|
&bug{},
|
2019-12-07 06:10:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *Application) featureCommands() []tool.Application {
|
|
|
|
return []tool.Application{
|
2019-02-08 14:16:57 -07:00
|
|
|
&check{app: app},
|
2019-11-10 07:43:06 -07:00
|
|
|
&foldingRanges{app: app},
|
2019-04-07 21:08:47 -06:00
|
|
|
&format{app: app},
|
2019-11-18 00:14:06 -07:00
|
|
|
&highlight{app: app},
|
2019-11-21 14:28:59 -07:00
|
|
|
&implementation{app: app},
|
2019-10-29 02:41:12 -06:00
|
|
|
&imports{app: app},
|
2019-11-18 00:14:06 -07:00
|
|
|
&links{app: app},
|
2019-04-07 21:08:47 -06:00
|
|
|
&query{app: app},
|
2019-10-19 15:26:56 -06:00
|
|
|
&references{app: app},
|
2019-09-20 16:38:42 -06:00
|
|
|
&rename{app: app},
|
2019-10-29 02:55:42 -06:00
|
|
|
&signature{app: app},
|
2019-10-29 02:38:47 -06:00
|
|
|
&suggestedfix{app: app},
|
2019-10-27 11:41:48 -06:00
|
|
|
&symbols{app: app},
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
var (
|
|
|
|
internalMu sync.Mutex
|
|
|
|
internalConnections = make(map[string]*connection)
|
|
|
|
)
|
2019-04-01 18:08:14 -06:00
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
func (app *Application) connect(ctx context.Context) (*connection, error) {
|
2019-03-28 15:17:40 -06:00
|
|
|
switch app.Remote {
|
|
|
|
case "":
|
2019-05-07 11:54:07 -06:00
|
|
|
connection := newConnection(app)
|
2020-01-21 14:01:59 -07:00
|
|
|
ctx, connection.Server = lsp.NewClientServer(ctx, cache.New(app.options).NewSession(), connection.Client)
|
2019-12-06 00:44:16 -07:00
|
|
|
return connection, connection.initialize(ctx, app.options)
|
2019-03-28 15:17:40 -06:00
|
|
|
case "internal":
|
2019-05-07 11:54:07 -06:00
|
|
|
internalMu.Lock()
|
|
|
|
defer internalMu.Unlock()
|
2019-05-17 08:51:19 -06:00
|
|
|
if c := internalConnections[app.wd]; c != nil {
|
2019-05-07 11:54:07 -06:00
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
connection := newConnection(app)
|
2019-07-10 19:11:23 -06:00
|
|
|
ctx := xcontext.Detach(ctx) //TODO:a way of shutting down the internal server
|
2019-03-28 15:17:40 -06:00
|
|
|
cr, sw, _ := os.Pipe()
|
|
|
|
sr, cw, _ := os.Pipe()
|
2019-03-28 19:06:01 -06:00
|
|
|
var jc *jsonrpc2.Conn
|
2019-07-10 19:01:12 -06:00
|
|
|
ctx, jc, connection.Server = protocol.NewClient(ctx, jsonrpc2.NewHeaderStream(cr, cw), connection.Client)
|
2019-03-28 19:06:01 -06:00
|
|
|
go jc.Run(ctx)
|
2019-07-10 19:01:12 -06:00
|
|
|
go func() {
|
2020-01-21 14:01:59 -07:00
|
|
|
ctx, srv := lsp.NewServer(ctx, cache.New(app.options).NewSession(), jsonrpc2.NewHeaderStream(sr, sw))
|
2019-07-10 19:01:12 -06:00
|
|
|
srv.Run(ctx)
|
|
|
|
}()
|
2019-12-06 00:44:16 -07:00
|
|
|
if err := connection.initialize(ctx, app.options); err != nil {
|
2019-05-07 11:54:07 -06:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-05-17 08:51:19 -06:00
|
|
|
internalConnections[app.wd] = connection
|
2019-05-07 11:54:07 -06:00
|
|
|
return connection, nil
|
2019-03-28 15:17:40 -06:00
|
|
|
default:
|
2019-05-07 11:54:07 -06:00
|
|
|
connection := newConnection(app)
|
2019-02-08 14:16:57 -07:00
|
|
|
conn, err := net.Dial("tcp", app.Remote)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stream := jsonrpc2.NewHeaderStream(conn, conn)
|
2019-03-28 19:06:01 -06:00
|
|
|
var jc *jsonrpc2.Conn
|
2019-07-10 19:01:12 -06:00
|
|
|
ctx, jc, connection.Server = protocol.NewClient(ctx, stream, connection.Client)
|
2019-03-28 19:06:01 -06:00
|
|
|
go jc.Run(ctx)
|
2019-12-06 00:44:16 -07:00
|
|
|
return connection, connection.initialize(ctx, app.options)
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
}
|
2019-04-17 12:47:10 -06:00
|
|
|
|
2019-12-06 00:44:16 -07:00
|
|
|
func (c *connection) initialize(ctx context.Context, options func(*source.Options)) error {
|
2019-11-17 12:29:15 -07:00
|
|
|
params := &protocol.ParamInitialize{}
|
2019-05-17 08:51:19 -06:00
|
|
|
params.RootURI = string(span.FileURI(c.Client.app.wd))
|
2019-04-08 07:16:48 -06:00
|
|
|
params.Capabilities.Workspace.Configuration = true
|
2019-12-06 00:44:16 -07:00
|
|
|
|
|
|
|
// Make sure to respect configured options when sending initialize request.
|
|
|
|
opts := source.DefaultOptions
|
|
|
|
if options != nil {
|
|
|
|
options(&opts)
|
|
|
|
}
|
2019-11-17 12:29:15 -07:00
|
|
|
params.Capabilities.TextDocument.Hover = protocol.HoverClientCapabilities{
|
2019-12-06 00:44:16 -07:00
|
|
|
ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat},
|
2019-09-24 15:51:00 -06:00
|
|
|
}
|
2019-12-06 00:44:16 -07:00
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
if _, err := c.Server.Initialize(ctx, params); err != nil {
|
|
|
|
return err
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
if err := c.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
|
|
|
|
return err
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type connection struct {
|
|
|
|
protocol.Server
|
|
|
|
Client *cmdClient
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
type cmdClient struct {
|
2019-02-08 14:16:57 -07:00
|
|
|
protocol.Server
|
2019-05-07 11:54:07 -06:00
|
|
|
app *Application
|
|
|
|
fset *token.FileSet
|
|
|
|
|
2020-01-10 15:54:47 -07:00
|
|
|
diagnosticsMu sync.Mutex
|
|
|
|
diagnosticsDone chan struct{}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
filesMu sync.Mutex
|
|
|
|
files map[span.URI]*cmdFile
|
|
|
|
}
|
|
|
|
|
|
|
|
type cmdFile struct {
|
2020-01-10 15:54:47 -07:00
|
|
|
uri span.URI
|
|
|
|
mapper *protocol.ColumnMapper
|
|
|
|
err error
|
|
|
|
added bool
|
|
|
|
diagnostics []protocol.Diagnostic
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
func newConnection(app *Application) *connection {
|
|
|
|
return &connection{
|
|
|
|
Client: &cmdClient{
|
|
|
|
app: app,
|
|
|
|
fset: token.NewFileSet(),
|
|
|
|
files: make(map[span.URI]*cmdFile),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cmdClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
|
|
|
|
|
|
|
|
func (c *cmdClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
|
2019-02-08 14:16:57 -07:00
|
|
|
return nil, nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
|
|
|
func (c *cmdClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
|
2019-03-29 17:04:29 -06:00
|
|
|
switch p.Type {
|
|
|
|
case protocol.Error:
|
|
|
|
log.Print("Error:", p.Message)
|
|
|
|
case protocol.Warning:
|
|
|
|
log.Print("Warning:", p.Message)
|
|
|
|
case protocol.Info:
|
2019-05-10 16:50:29 -06:00
|
|
|
if c.app.Verbose {
|
|
|
|
log.Print("Info:", p.Message)
|
|
|
|
}
|
2019-03-29 17:04:29 -06:00
|
|
|
case protocol.Log:
|
2019-05-10 16:50:29 -06:00
|
|
|
if c.app.Verbose {
|
|
|
|
log.Print("Log:", p.Message)
|
|
|
|
}
|
2019-03-29 17:04:29 -06:00
|
|
|
default:
|
2019-05-10 16:50:29 -06:00
|
|
|
if c.app.Verbose {
|
|
|
|
log.Print(p.Message)
|
|
|
|
}
|
2019-03-29 17:04:29 -06:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
|
|
|
func (c *cmdClient) Event(ctx context.Context, t *interface{}) error { return nil }
|
|
|
|
|
|
|
|
func (c *cmdClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
|
2019-02-08 14:16:57 -07:00
|
|
|
return nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
|
|
|
func (c *cmdClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
|
2019-02-08 14:16:57 -07:00
|
|
|
return nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
|
|
|
func (c *cmdClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
|
2019-02-08 14:16:57 -07:00
|
|
|
return nil, nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
2019-11-17 12:29:15 -07:00
|
|
|
func (c *cmdClient) Configuration(ctx context.Context, p *protocol.ParamConfiguration) ([]interface{}, error) {
|
2019-02-08 14:16:57 -07:00
|
|
|
results := make([]interface{}, len(p.Items))
|
|
|
|
for i, item := range p.Items {
|
|
|
|
if item.Section != "gopls" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
env := map[string]interface{}{}
|
2019-05-17 08:51:19 -06:00
|
|
|
for _, value := range c.app.env {
|
2019-02-08 14:16:57 -07:00
|
|
|
l := strings.SplitN(value, "=", 2)
|
|
|
|
if len(l) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
env[l[0]] = l[1]
|
|
|
|
}
|
2019-04-27 20:45:06 -06:00
|
|
|
results[i] = map[string]interface{}{
|
2019-10-22 15:15:19 -06:00
|
|
|
"env": env,
|
|
|
|
"go-diff": true,
|
2019-04-27 20:45:06 -06:00
|
|
|
}
|
2018-12-17 11:52:58 -07:00
|
|
|
}
|
2019-02-08 14:16:57 -07:00
|
|
|
return results, nil
|
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
|
|
|
|
func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResponse, error) {
|
2019-04-23 08:05:23 -06:00
|
|
|
return &protocol.ApplyWorkspaceEditResponse{Applied: false, FailureReason: "not implemented"}, nil
|
2019-02-08 14:16:57 -07:00
|
|
|
}
|
2019-04-01 18:08:14 -06:00
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
|
2020-01-10 15:54:47 -07:00
|
|
|
if p.URI == "gopls://diagnostics-done" {
|
|
|
|
close(c.diagnosticsDone)
|
|
|
|
}
|
2019-11-20 12:26:02 -07:00
|
|
|
// Don't worry about diagnostics without versions.
|
|
|
|
if p.Version == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
c.filesMu.Lock()
|
|
|
|
defer c.filesMu.Unlock()
|
2019-11-20 12:26:02 -07:00
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
uri := span.URI(p.URI)
|
|
|
|
file := c.getFile(ctx, uri)
|
|
|
|
file.diagnostics = p.Diagnostics
|
|
|
|
return nil
|
2019-04-01 18:08:14 -06:00
|
|
|
}
|
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
|
|
|
|
file, found := c.files[uri]
|
2019-08-01 14:31:02 -06:00
|
|
|
if !found || file.err != nil {
|
2019-05-07 11:54:07 -06:00
|
|
|
file = &cmdFile{
|
2020-01-10 15:54:47 -07:00
|
|
|
uri: uri,
|
2019-05-07 11:54:07 -06:00
|
|
|
}
|
|
|
|
c.files[uri] = file
|
2019-04-01 18:08:14 -06:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
if file.mapper == nil {
|
2019-06-06 11:51:00 -06:00
|
|
|
fname := uri.Filename()
|
2019-05-07 11:54:07 -06:00
|
|
|
content, err := ioutil.ReadFile(fname)
|
|
|
|
if err != nil {
|
2019-08-06 13:13:11 -06:00
|
|
|
file.err = errors.Errorf("getFile: %v: %v", uri, err)
|
2019-05-07 11:54:07 -06:00
|
|
|
return file
|
|
|
|
}
|
|
|
|
f := c.fset.AddFile(fname, -1, len(content))
|
|
|
|
f.SetLinesForContent(content)
|
2019-09-09 22:36:39 -06:00
|
|
|
converter := span.NewContentConverter(fname, content)
|
|
|
|
file.mapper = &protocol.ColumnMapper{
|
|
|
|
URI: uri,
|
|
|
|
Converter: converter,
|
|
|
|
Content: content,
|
|
|
|
}
|
2019-04-01 18:08:14 -06:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *connection) AddFile(ctx context.Context, uri span.URI) *cmdFile {
|
|
|
|
c.Client.filesMu.Lock()
|
|
|
|
defer c.Client.filesMu.Unlock()
|
2019-06-27 12:59:09 -06:00
|
|
|
|
2019-05-07 11:54:07 -06:00
|
|
|
file := c.Client.getFile(ctx, uri)
|
2019-08-01 14:31:02 -06:00
|
|
|
// This should never happen.
|
|
|
|
if file == nil {
|
|
|
|
return &cmdFile{
|
|
|
|
uri: uri,
|
|
|
|
err: fmt.Errorf("no file found for %s", uri),
|
|
|
|
}
|
|
|
|
}
|
2019-08-03 11:21:48 -06:00
|
|
|
if file.err != nil || file.added {
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
file.added = true
|
2019-11-20 12:26:02 -07:00
|
|
|
p := &protocol.DidOpenTextDocumentParams{
|
|
|
|
TextDocument: protocol.TextDocumentItem{
|
|
|
|
URI: protocol.NewURI(uri),
|
|
|
|
LanguageID: source.DetectLanguage("", file.uri.Filename()).String(),
|
|
|
|
Version: 1,
|
|
|
|
Text: string(file.mapper.Content),
|
|
|
|
},
|
|
|
|
}
|
2019-08-03 11:21:48 -06:00
|
|
|
if err := c.Server.DidOpen(ctx, p); err != nil {
|
2019-08-06 13:13:11 -06:00
|
|
|
file.err = errors.Errorf("%v: %v", uri, err)
|
2019-04-01 18:08:14 -06:00
|
|
|
}
|
2019-05-07 11:54:07 -06:00
|
|
|
return file
|
2019-04-01 18:08:14 -06:00
|
|
|
}
|
2019-05-02 08:55:04 -06:00
|
|
|
|
2020-01-10 15:54:47 -07:00
|
|
|
func (c *connection) diagnoseFiles(ctx context.Context, files []span.URI) error {
|
|
|
|
c.Client.diagnosticsMu.Lock()
|
|
|
|
defer c.Client.diagnosticsMu.Unlock()
|
|
|
|
|
|
|
|
c.Client.diagnosticsDone = make(chan struct{})
|
|
|
|
_, err := c.Server.NonstandardRequest(ctx, "gopls/diagnoseFiles", map[string]interface{}{"files": files})
|
|
|
|
<-c.Client.diagnosticsDone
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-05-02 08:55:04 -06:00
|
|
|
func (c *connection) terminate(ctx context.Context) {
|
|
|
|
if c.Client.app.Remote == "internal" {
|
|
|
|
// internal connections need to be left alive for the next test
|
|
|
|
return
|
|
|
|
}
|
|
|
|
//TODO: do we need to handle errors on these calls?
|
|
|
|
c.Shutdown(ctx)
|
|
|
|
//TODO: right now calling exit terminates the process, we should rethink that
|
|
|
|
//server.Exit(ctx)
|
|
|
|
}
|