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.
5 // Package lsp implements LSP for gopls.
13 "golang.org/x/tools/internal/jsonrpc2"
14 "golang.org/x/tools/internal/lsp/protocol"
15 "golang.org/x/tools/internal/lsp/source"
16 "golang.org/x/tools/internal/span"
17 errors "golang.org/x/xerrors"
20 const concurrentAnalyses = 1
22 // NewServer creates an LSP server and binds it to handle incoming client
23 // messages on on the supplied stream.
24 func NewServer(session source.Session, client protocol.Client) *Server {
26 delivered: make(map[span.URI]sentDiagnostics),
27 gcOptimizatonDetails: make(map[span.URI]struct{}),
28 watchedDirectories: make(map[span.URI]struct{}),
29 changedFiles: make(map[span.URI]struct{}),
32 diagnosticsSema: make(chan struct{}, concurrentAnalyses),
33 progress: newProgressTracker(client),
34 debouncer: newDebouncer(),
41 serverCreated = serverState(iota)
42 serverInitializing // set once the server has received "initialize" request
43 serverInitialized // set once the server has received "initialized" request
47 func (s serverState) String() string {
51 case serverInitializing:
53 case serverInitialized:
58 return fmt.Sprintf("(unknown state: %d)", int(s))
61 // Server implements the protocol.Server interface.
63 client protocol.Client
68 session source.Session
70 // notifications generated before serverInitialized
71 notifications []*protocol.ShowMessageParams
73 // changedFiles tracks files for which there has been a textDocument/didChange.
74 changedFilesMu sync.Mutex
75 changedFiles map[span.URI]struct{}
77 // folders is only valid between initialize and initialized, and holds the
78 // set of folders to build views for when we are ready
79 pendingFolders []protocol.WorkspaceFolder
81 // watchedDirectories is the set of directories that we have requested that
82 // the client watch on disk. It will be updated as the set of directories
83 // that the server should watch changes.
84 watchedDirectoriesMu sync.Mutex
85 watchedDirectories map[span.URI]struct{}
86 watchRegistrationCount uint64
88 // delivered is a cache of the diagnostics that the server has sent.
89 deliveredMu sync.Mutex
90 delivered map[span.URI]sentDiagnostics
92 // gcOptimizationDetails describes the packages for which we want
93 // optimization details to be included in the diagnostics. The key is the
94 // directory of the package.
95 gcOptimizationDetailsMu sync.Mutex
96 gcOptimizatonDetails map[span.URI]struct{}
98 // diagnosticsSema limits the concurrency of diagnostics runs, which can be expensive.
99 diagnosticsSema chan struct{}
101 progress *progressTracker
103 // debouncer is used for debouncing diagnostics.
107 // sentDiagnostics is used to cache diagnostics that have been sent for a given file.
108 type sentDiagnostics struct {
109 id source.VersionedFileIdentity
110 sorted []*source.Diagnostic
115 func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error {
116 return s.progress.cancel(ctx, params.Token)
119 func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
120 paramMap := params.(map[string]interface{})
121 if method == "gopls/diagnoseFiles" {
122 for _, file := range paramMap["files"].([]interface{}) {
123 snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind)
129 fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI())
133 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
134 URI: protocol.URIFromSpanURI(fh.URI()),
135 Diagnostics: toProtocolDiagnostics(diagnostics),
136 Version: fileID.Version,
141 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
142 URI: "gopls://diagnostics-done",
146 return struct{}{}, nil
148 return nil, notImplemented(method)
151 func notImplemented(method string) error {
152 return errors.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method)
155 //go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .