github.com/v2fly/tools@v0.100.0/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/v2fly/tools/internal/jsonrpc2" 14 "github.com/v2fly/tools/internal/lsp/protocol" 15 "github.com/v2fly/tools/internal/lsp/source" 16 "github.com/v2fly/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 diagnostics: map[span.URI]*fileReports{}, 27 gcOptimizationDetails: make(map[string]struct{}), 28 watchedGlobPatterns: make(map[string]struct{}), 29 changedFiles: make(map[span.URI]struct{}), 30 session: session, 31 client: client, 32 diagnosticsSema: make(chan struct{}, concurrentAnalyses), 33 progress: newProgressTracker(client), 34 debouncer: newDebouncer(), 35 } 36 } 37 38 type serverState int 39 40 const ( 41 serverCreated = serverState(iota) 42 serverInitializing // set once the server has received "initialize" request 43 serverInitialized // set once the server has received "initialized" request 44 serverShutDown 45 ) 46 47 func (s serverState) String() string { 48 switch s { 49 case serverCreated: 50 return "created" 51 case serverInitializing: 52 return "initializing" 53 case serverInitialized: 54 return "initialized" 55 case serverShutDown: 56 return "shutDown" 57 } 58 return fmt.Sprintf("(unknown state: %d)", int(s)) 59 } 60 61 // Server implements the protocol.Server interface. 62 type Server struct { 63 client protocol.Client 64 65 stateMu sync.Mutex 66 state serverState 67 // notifications generated before serverInitialized 68 notifications []*protocol.ShowMessageParams 69 70 session source.Session 71 72 tempDir string 73 74 // changedFiles tracks files for which there has been a textDocument/didChange. 75 changedFilesMu sync.Mutex 76 changedFiles map[span.URI]struct{} 77 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 81 82 // watchedGlobPatterns is the set of glob patterns that we have requested 83 // the client watch on disk. It will be updated as the set of directories 84 // that the server should watch changes. 85 watchedGlobPatternsMu sync.Mutex 86 watchedGlobPatterns map[string]struct{} 87 watchRegistrationCount int 88 89 diagnosticsMu sync.Mutex 90 diagnostics map[span.URI]*fileReports 91 92 // gcOptimizationDetails describes the packages for which we want 93 // optimization details to be included in the diagnostics. The key is the 94 // ID of the package. 95 gcOptimizationDetailsMu sync.Mutex 96 gcOptimizationDetails map[string]struct{} 97 98 // diagnosticsSema limits the concurrency of diagnostics runs, which can be 99 // expensive. 100 diagnosticsSema chan struct{} 101 102 progress *progressTracker 103 104 // debouncer is used for debouncing diagnostics. 105 debouncer *debouncer 106 107 // When the workspace fails to load, we show its status through a progress 108 // report with an error message. 109 criticalErrorStatusMu sync.Mutex 110 criticalErrorStatus *workDone 111 } 112 113 func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { 114 return s.progress.cancel(ctx, params.Token) 115 } 116 117 func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { 118 switch method { 119 case "gopls/diagnoseFiles": 120 paramMap := params.(map[string]interface{}) 121 for _, file := range paramMap["files"].([]interface{}) { 122 snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind) 123 defer release() 124 if !ok { 125 return nil, err 126 } 127 128 fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI()) 129 if err != nil { 130 return nil, err 131 } 132 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ 133 URI: protocol.URIFromSpanURI(fh.URI()), 134 Diagnostics: toProtocolDiagnostics(diagnostics), 135 Version: fileID.Version, 136 }); err != nil { 137 return nil, err 138 } 139 } 140 if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ 141 URI: "gopls://diagnostics-done", 142 }); err != nil { 143 return nil, err 144 } 145 return struct{}{}, nil 146 } 147 return nil, notImplemented(method) 148 } 149 150 func notImplemented(method string) error { 151 return errors.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method) 152 } 153 154 //go:generate helper/helper -d protocol/tsserver.go -o server_gen.go -u .