blob: 0a60571c18aa7e81231a726f76189c72b0c628fc [file] [log] [blame]
// 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 server defines gopls' implementation of the LSP server
// interface, [protocol.Server]. Call [New] to create an instance.
package server
import (
"context"
"fmt"
"os"
"sync"
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/progress"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/internal/event"
)
// New creates an LSP server and binds it to handle incoming client
// messages on the supplied stream.
func New(session *cache.Session, client protocol.ClientCloser, options *settings.Options) protocol.Server {
const concurrentAnalyses = 1
// If this assignment fails to compile after a protocol
// upgrade, it means that one or more new methods need new
// stub declarations in unimplemented.go.
return &server{
diagnostics: map[protocol.DocumentURI]*fileReports{},
watchedGlobPatterns: nil, // empty
changedFiles: make(map[protocol.DocumentURI]struct{}),
session: session,
client: client,
diagnosticsSema: make(chan struct{}, concurrentAnalyses),
progress: progress.NewTracker(client),
options: options,
}
}
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.ClientCloser
stateMu sync.Mutex
state serverState
// notifications generated before serverInitialized
notifications []*protocol.ShowMessageParams
session *cache.Session
tempDir string
// changedFiles tracks files for which there has been a textDocument/didChange.
changedFilesMu sync.Mutex
changedFiles map[protocol.DocumentURI]struct{}
// folders is only valid between initialize and initialized, and holds the
// set of folders to build views for when we are ready.
// Each has a valid, non-empty 'file'-scheme URI.
pendingFolders []protocol.WorkspaceFolder
// watchedGlobPatterns is the set of glob patterns that we have requested
// the client watch on disk. It will be updated as the set of directories
// that the server should watch changes.
// The map field may be reassigned but the map is immutable.
watchedGlobPatternsMu sync.Mutex
watchedGlobPatterns map[string]struct{}
watchRegistrationCount int
diagnosticsMu sync.Mutex
diagnostics map[protocol.DocumentURI]*fileReports
// diagnosticsSema limits the concurrency of diagnostics runs, which can be
// expensive.
diagnosticsSema chan struct{}
progress *progress.Tracker
// When the workspace fails to load, we show its status through a progress
// report with an error message.
criticalErrorStatusMu sync.Mutex
criticalErrorStatus *progress.WorkDone
// Track an ongoing CPU profile created with the StartProfile command and
// terminated with the StopProfile command.
ongoingProfileMu sync.Mutex
ongoingProfile *os.File // if non-nil, an ongoing profile is writing to this file
// Track most recently requested options.
optionsMu sync.Mutex
options *settings.Options
}
func (s *server) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error {
ctx, done := event.Start(ctx, "lsp.Server.workDoneProgressCancel")
defer done()
return s.progress.Cancel(params.Token)
}