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 gcOptimizationDetails: 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
71 // notifications generated before serverInitialized
72 notifications []*protocol.ShowMessageParams
74 // changedFiles tracks files for which there has been a textDocument/didChange.
75 changedFilesMu sync.Mutex
76 changedFiles map[span.URI]struct{}
78 // folders is only valid between initialize and initialized, and holds the
79 // set of folders to build views for when we are ready
80 pendingFolders []protocol.WorkspaceFolder
82 // watchedDirectories is the set of directories that we have requested that
83 // the client watch on disk. It will be updated as the set of directories
84 // that the server should watch changes.
85 watchedDirectoriesMu sync.Mutex
86 watchedDirectories map[span.URI]struct{}
87 watchRegistrationCount uint64
89 // delivered is a cache of the diagnostics that the server has sent.
90 deliveredMu sync.Mutex
91 delivered map[span.URI]sentDiagnostics
93 // gcOptimizationDetails describes the packages for which we want
94 // optimization details to be included in the diagnostics. The key is the
95 // directory of the package.
96 gcOptimizationDetailsMu sync.Mutex
97 gcOptimizationDetails map[span.URI]struct{}
99 // diagnosticsSema limits the concurrency of diagnostics runs, which can be expensive.
100 diagnosticsSema chan struct{}
102 progress *progressTracker
104 // debouncer is used for debouncing diagnostics.
108 // sentDiagnostics is used to cache diagnostics that have been sent for a given file.
109 type sentDiagnostics struct {
110 id source.VersionedFileIdentity
111 sorted []*source.Diagnostic
116 func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error {
117 return s.progress.cancel(ctx, params.Token)
120 func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
121 paramMap := params.(map[string]interface{})
122 if method == "gopls/diagnoseFiles" {
123 for _, file := range paramMap["files"].([]interface{}) {
124 snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind)
130 fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI())
134 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
135 URI: protocol.URIFromSpanURI(fh.URI()),
136 Diagnostics: toProtocolDiagnostics(diagnostics),
137 Version: fileID.Version,
142 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
143 URI: "gopls://diagnostics-done",
147 return struct{}{}, nil
149 return nil, notImplemented(method)
152 func notImplemented(method string) error {
153 return errors.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method)
156 //go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .