github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/server.go (about)

     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.
     4  
     5  // Package lsp implements LSP for gopls.
     6  package lsp
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"sync"
    12  
    13  	"github.com/april1989/origin-go-tools/internal/jsonrpc2"
    14  	"github.com/april1989/origin-go-tools/internal/lsp/protocol"
    15  	"github.com/april1989/origin-go-tools/internal/lsp/source"
    16  	"github.com/april1989/origin-go-tools/internal/span"
    17  	errors "golang.org/x/xerrors"
    18  )
    19  
    20  const concurrentAnalyses = 1
    21  
    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 {
    25  	return &Server{
    26  		delivered:            make(map[span.URI]sentDiagnostics),
    27  		gcOptimizatonDetails: make(map[span.URI]struct{}),
    28  		watchedDirectories:   make(map[span.URI]struct{}),
    29  		session:              session,
    30  		client:               client,
    31  		diagnosticsSema:      make(chan struct{}, concurrentAnalyses),
    32  		progress:             newProgressTracker(client),
    33  	}
    34  }
    35  
    36  type serverState int
    37  
    38  const (
    39  	serverCreated      = serverState(iota)
    40  	serverInitializing // set once the server has received "initialize" request
    41  	serverInitialized  // set once the server has received "initialized" request
    42  	serverShutDown
    43  )
    44  
    45  func (s serverState) String() string {
    46  	switch s {
    47  	case serverCreated:
    48  		return "created"
    49  	case serverInitializing:
    50  		return "initializing"
    51  	case serverInitialized:
    52  		return "initialized"
    53  	case serverShutDown:
    54  		return "shutDown"
    55  	}
    56  	return fmt.Sprintf("(unknown state: %d)", int(s))
    57  }
    58  
    59  // Server implements the protocol.Server interface.
    60  type Server struct {
    61  	client protocol.Client
    62  
    63  	stateMu sync.Mutex
    64  	state   serverState
    65  
    66  	session source.Session
    67  
    68  	// changedFiles tracks files for which there has been a textDocument/didChange.
    69  	changedFilesMu sync.Mutex
    70  	changedFiles   map[span.URI]struct{}
    71  
    72  	// folders is only valid between initialize and initialized, and holds the
    73  	// set of folders to build views for when we are ready
    74  	pendingFolders []protocol.WorkspaceFolder
    75  
    76  	// watchedDirectories is the set of directories that we have requested that
    77  	// the client watch on disk. It will be updated as the set of directories
    78  	// that the server should watch changes.
    79  	watchedDirectoriesMu   sync.Mutex
    80  	watchedDirectories     map[span.URI]struct{}
    81  	watchRegistrationCount uint64
    82  
    83  	// delivered is a cache of the diagnostics that the server has sent.
    84  	deliveredMu sync.Mutex
    85  	delivered   map[span.URI]sentDiagnostics
    86  
    87  	// gcOptimizationDetails describes the packages for which we want
    88  	// optimization details to be included in the diagnostics. The key is the
    89  	// directory of the package.
    90  	gcOptimizationDetailsMu sync.Mutex
    91  	gcOptimizatonDetails    map[span.URI]struct{}
    92  
    93  	// diagnosticsSema limits the concurrency of diagnostics runs, which can be expensive.
    94  	diagnosticsSema chan struct{}
    95  
    96  	progress *progressTracker
    97  }
    98  
    99  // sentDiagnostics is used to cache diagnostics that have been sent for a given file.
   100  type sentDiagnostics struct {
   101  	id           source.VersionedFileIdentity
   102  	sorted       []*source.Diagnostic
   103  	withAnalysis bool
   104  	snapshotID   uint64
   105  }
   106  
   107  func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
   108  	paramMap := params.(map[string]interface{})
   109  	if method == "gopls/diagnoseFiles" {
   110  		for _, file := range paramMap["files"].([]interface{}) {
   111  			snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind)
   112  			defer release()
   113  			if !ok {
   114  				return nil, err
   115  			}
   116  
   117  			fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI())
   118  			if err != nil {
   119  				return nil, err
   120  			}
   121  			if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
   122  				URI:         protocol.URIFromSpanURI(fh.URI()),
   123  				Diagnostics: toProtocolDiagnostics(diagnostics),
   124  				Version:     fileID.Version,
   125  			}); err != nil {
   126  				return nil, err
   127  			}
   128  		}
   129  		if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
   130  			URI: "gopls://diagnostics-done",
   131  		}); err != nil {
   132  			return nil, err
   133  		}
   134  		return struct{}{}, nil
   135  	}
   136  	return nil, notImplemented(method)
   137  }
   138  
   139  func notImplemented(method string) error {
   140  	return errors.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method)
   141  }
   142  
   143  //go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .