mirror of
https://github.com/golang/go
synced 2024-11-18 10:04:43 -07:00
d1954cc86c
Fixes golang/vscode-go#585 Change-Id: Icb4295f1cab5c0fe60c86dd5f911023f70d2dd62 Reviewed-on: https://go-review.googlesource.com/c/tools/+/252123 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
// 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.
|
|
|
|
// Package lsp implements LSP for gopls.
|
|
package lsp
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
"golang.org/x/tools/internal/span"
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
const concurrentAnalyses = 1
|
|
|
|
// NewServer creates an LSP server and binds it to handle incoming client
|
|
// messages on on the supplied stream.
|
|
func NewServer(session source.Session, client protocol.Client) *Server {
|
|
return &Server{
|
|
delivered: make(map[span.URI]sentDiagnostics),
|
|
gcOptimizatonDetails: make(map[span.URI]struct{}),
|
|
watchedDirectories: make(map[span.URI]struct{}),
|
|
session: session,
|
|
client: client,
|
|
diagnosticsSema: make(chan struct{}, concurrentAnalyses),
|
|
progress: newProgressTracker(client),
|
|
}
|
|
}
|
|
|
|
type serverState int
|
|
|
|
const (
|
|
serverCreated = serverState(iota)
|
|
serverInitializing // set once the server has received "initialize" request
|
|
serverInitialized // set once the server has received "initialized" request
|
|
serverShutDown
|
|
)
|
|
|
|
func (s serverState) String() string {
|
|
switch s {
|
|
case serverCreated:
|
|
return "created"
|
|
case serverInitializing:
|
|
return "initializing"
|
|
case serverInitialized:
|
|
return "initialized"
|
|
case serverShutDown:
|
|
return "shutDown"
|
|
}
|
|
return fmt.Sprintf("(unknown state: %d)", int(s))
|
|
}
|
|
|
|
// Server implements the protocol.Server interface.
|
|
type Server struct {
|
|
client protocol.Client
|
|
|
|
stateMu sync.Mutex
|
|
state serverState
|
|
|
|
session source.Session
|
|
|
|
// changedFiles tracks files for which there has been a textDocument/didChange.
|
|
changedFilesMu sync.Mutex
|
|
changedFiles map[span.URI]struct{}
|
|
|
|
// folders is only valid between initialize and initialized, and holds the
|
|
// set of folders to build views for when we are ready
|
|
pendingFolders []protocol.WorkspaceFolder
|
|
|
|
// watchedDirectories is the set of directories that we have requested that
|
|
// the client watch on disk. It will be updated as the set of directories
|
|
// that the server should watch changes.
|
|
watchedDirectoriesMu sync.Mutex
|
|
watchedDirectories map[span.URI]struct{}
|
|
watchRegistrationCount uint64
|
|
|
|
// delivered is a cache of the diagnostics that the server has sent.
|
|
deliveredMu sync.Mutex
|
|
delivered map[span.URI]sentDiagnostics
|
|
|
|
// gcOptimizationDetails describes the packages for which we want
|
|
// optimization details to be included in the diagnostics. The key is the
|
|
// directory of the package.
|
|
gcOptimizationDetailsMu sync.Mutex
|
|
gcOptimizatonDetails map[span.URI]struct{}
|
|
|
|
// diagnosticsSema limits the concurrency of diagnostics runs, which can be expensive.
|
|
diagnosticsSema chan struct{}
|
|
|
|
progress *progressTracker
|
|
}
|
|
|
|
// sentDiagnostics is used to cache diagnostics that have been sent for a given file.
|
|
type sentDiagnostics struct {
|
|
id source.VersionedFileIdentity
|
|
sorted []*source.Diagnostic
|
|
withAnalysis bool
|
|
snapshotID uint64
|
|
}
|
|
|
|
func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
|
|
paramMap := params.(map[string]interface{})
|
|
if method == "gopls/diagnoseFiles" {
|
|
for _, file := range paramMap["files"].([]interface{}) {
|
|
snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind)
|
|
defer release()
|
|
if !ok {
|
|
return nil, err
|
|
}
|
|
|
|
fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
|
|
URI: protocol.URIFromSpanURI(fh.URI()),
|
|
Diagnostics: toProtocolDiagnostics(diagnostics),
|
|
Version: fileID.Version,
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
|
|
URI: "gopls://diagnostics-done",
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return struct{}{}, nil
|
|
}
|
|
return nil, notImplemented(method)
|
|
}
|
|
|
|
func notImplemented(method string) error {
|
|
return errors.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method)
|
|
}
|
|
|
|
//go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .
|