Skip to content

Commit fb4bd11

Browse files
committed
gopls/internal/lsp/cache: move Option management to the Server
In order to move toward tracking options by Folder, not view, move them into the Server. This will also help us fix bugs related to configuration lifecycle events. For golang/go#57979 Updates golang/go#42814 Change-Id: Id281cad20697756138a7bdc67f718a7468a04d4a Reviewed-on: https://go-review.googlesource.com/c/tools/+/526417 LUCI-TryBot-Result: Go LUCI <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 882bb14 commit fb4bd11

17 files changed

+132
-115
lines changed

gopls/internal/lsp/cache/cache.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"sync/atomic"
1212
"time"
1313

14-
"golang.org/x/tools/gopls/internal/lsp/source"
1514
"golang.org/x/tools/internal/event"
1615
"golang.org/x/tools/internal/gocommand"
1716
"golang.org/x/tools/internal/memoize"
@@ -56,17 +55,12 @@ type Cache struct {
5655
// The provided optionsOverrides may be nil.
5756
//
5857
// TODO(rfindley): move this to session.go.
59-
func NewSession(ctx context.Context, c *Cache, optionsOverrides func(*source.Options)) *Session {
58+
func NewSession(ctx context.Context, c *Cache) *Session {
6059
index := atomic.AddInt64(&sessionIndex, 1)
61-
options := source.DefaultOptions().Clone()
62-
if optionsOverrides != nil {
63-
optionsOverrides(options)
64-
}
6560
s := &Session{
6661
id: strconv.FormatInt(index, 10),
6762
cache: c,
6863
gocmdRunner: &gocommand.Runner{},
69-
options: options,
7064
overlayFS: newOverlayFS(c),
7165
parseCache: newParseCache(1 * time.Minute), // keep recently parsed files for a minute, to optimize typing CPU
7266
}

gopls/internal/lsp/cache/session.go

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,9 @@ type Session struct {
3333
cache *Cache // shared cache
3434
gocmdRunner *gocommand.Runner // limits go command concurrency
3535

36-
optionsMu sync.Mutex
37-
options *source.Options
38-
3936
viewMu sync.Mutex
4037
views []*View
41-
viewMap map[span.URI]*View // map of URI->best view
38+
viewMap map[span.URI]*View // file->best view
4239

4340
parseCache *parseCache
4441

@@ -54,20 +51,6 @@ func (s *Session) GoCommandRunner() *gocommand.Runner {
5451
return s.gocmdRunner
5552
}
5653

57-
// Options returns a copy of the SessionOptions for this session.
58-
func (s *Session) Options() *source.Options {
59-
s.optionsMu.Lock()
60-
defer s.optionsMu.Unlock()
61-
return s.options
62-
}
63-
64-
// SetOptions sets the options of this session to new values.
65-
func (s *Session) SetOptions(options *source.Options) {
66-
s.optionsMu.Lock()
67-
defer s.optionsMu.Unlock()
68-
s.options = options
69-
}
70-
7154
// Shutdown the session and all views it has created.
7255
func (s *Session) Shutdown(ctx context.Context) {
7356
var views []*View
@@ -293,6 +276,7 @@ func bestViewForURI(uri span.URI, views []*View) *View {
293276
func (s *Session) RemoveView(view *View) {
294277
s.viewMu.Lock()
295278
defer s.viewMu.Unlock()
279+
296280
i := s.dropView(view)
297281
if i == -1 { // error reported elsewhere
298282
return
@@ -302,18 +286,11 @@ func (s *Session) RemoveView(view *View) {
302286
s.views = removeElement(s.views, i)
303287
}
304288

305-
// updateView recreates the view with the given options.
289+
// updateViewLocked recreates the view with the given options.
306290
//
307291
// If the resulting error is non-nil, the view may or may not have already been
308292
// dropped from the session.
309-
func (s *Session) updateView(ctx context.Context, view *View, options *source.Options) (*View, error) {
310-
s.viewMu.Lock()
311-
defer s.viewMu.Unlock()
312-
313-
return s.updateViewLocked(ctx, view, options)
314-
}
315-
316-
func (s *Session) updateViewLocked(ctx context.Context, view *View, options *source.Options) (*View, error) {
293+
func (s *Session) updateViewLocked(ctx context.Context, view *View, options *source.Options) error {
317294
// Preserve the snapshot ID if we are recreating the view.
318295
view.snapshotMu.Lock()
319296
if view.snapshot == nil {
@@ -325,7 +302,7 @@ func (s *Session) updateViewLocked(ctx context.Context, view *View, options *sou
325302

326303
i := s.dropView(view)
327304
if i == -1 {
328-
return nil, fmt.Errorf("view %q not found", view.id)
305+
return fmt.Errorf("view %q not found", view.id)
329306
}
330307

331308
v, snapshot, release, err := s.createView(ctx, view.name, view.folder, options, seqID)
@@ -334,7 +311,7 @@ func (s *Session) updateViewLocked(ctx context.Context, view *View, options *sou
334311
// this should not happen and is very bad, but we still need to clean
335312
// up the view array if it happens
336313
s.views = removeElement(s.views, i)
337-
return nil, err
314+
return err
338315
}
339316
defer release()
340317

@@ -350,7 +327,7 @@ func (s *Session) updateViewLocked(ctx context.Context, view *View, options *sou
350327

351328
// substitute the new view into the array where the old view was
352329
s.views[i] = v
353-
return v, nil
330+
return nil
354331
}
355332

356333
// removeElement removes the ith element from the slice replacing it with the last element.
@@ -457,8 +434,7 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
457434
}
458435

459436
if info != view.workspaceInformation {
460-
_, err := s.updateViewLocked(ctx, view, view.Options())
461-
if err != nil {
437+
if err := s.updateViewLocked(ctx, view, view.Options()); err != nil {
462438
// More catastrophic failure. The view may or may not still exist.
463439
// The best we can do is log and move on.
464440
event.Error(ctx, "recreating view", err)

gopls/internal/lsp/cache/view.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -482,21 +482,35 @@ func minorOptionsChange(a, b *source.Options) bool {
482482
return reflect.DeepEqual(aBuildFlags, bBuildFlags)
483483
}
484484

485-
// SetViewOptions sets the options of the given view to new values. Calling
486-
// this may cause the view to be invalidated and a replacement view added to
487-
// the session. If so the new view will be returned, otherwise the original one
488-
// will be returned.
489-
func (s *Session) SetViewOptions(ctx context.Context, v *View, options *source.Options) (*View, error) {
485+
// SetFolderOptions updates the options of each View associated with the folder
486+
// of the given URI.
487+
//
488+
// Calling this may cause each related view to be invalidated and a replacement
489+
// view added to the session.
490+
func (s *Session) SetFolderOptions(ctx context.Context, uri span.URI, options *source.Options) error {
491+
s.viewMu.Lock()
492+
defer s.viewMu.Unlock()
493+
494+
for _, v := range s.views {
495+
if v.folder == uri {
496+
if err := s.setViewOptions(ctx, v, options); err != nil {
497+
return err
498+
}
499+
}
500+
}
501+
return nil
502+
}
503+
504+
func (s *Session) setViewOptions(ctx context.Context, v *View, options *source.Options) error {
490505
// no need to rebuild the view if the options were not materially changed
491506
v.optionsMu.Lock()
492507
if minorOptionsChange(v.options, options) {
493508
v.options = options
494509
v.optionsMu.Unlock()
495-
return v, nil
510+
return nil
496511
}
497512
v.optionsMu.Unlock()
498-
newView, err := s.updateView(ctx, v, options)
499-
return newView, err
513+
return s.updateViewLocked(ctx, v, options)
500514
}
501515

502516
// viewEnv returns a string describing the environment of a newly created view.

gopls/internal/lsp/cmd/capabilities_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestCapabilities(t *testing.T) {
4949
// Send an initialize request to the server.
5050
ctx := context.Background()
5151
client := newClient(app, nil)
52-
server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil), app.options), client)
52+
server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil)), client, app.options)
5353
result, err := server.Initialize(ctx, params)
5454
if err != nil {
5555
t.Fatal(err)

gopls/internal/lsp/cmd/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ func (app *Application) connect(ctx context.Context, onProgress func(*protocol.P
316316
switch {
317317
case app.Remote == "":
318318
client := newClient(app, onProgress)
319-
server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil), app.options), client)
319+
server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil)), client, app.options)
320320
conn := newConnection(server, client)
321321
if err := conn.initialize(protocol.WithClient(ctx, client), app.options); err != nil {
322322
return nil, err

gopls/internal/lsp/code_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func (s *Server) findMatchingDiagnostics(uri span.URI, pd protocol.Diagnostic) [
299299

300300
func (s *Server) getSupportedCodeActions() []protocol.CodeActionKind {
301301
allCodeActionKinds := make(map[protocol.CodeActionKind]struct{})
302-
for _, kinds := range s.session.Options().SupportedCodeActions {
302+
for _, kinds := range s.Options().SupportedCodeActions {
303303
for kind := range kinds {
304304
allCodeActionKinds[kind] = struct{}{}
305305
}

gopls/internal/lsp/command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom
4343
defer done()
4444

4545
var found bool
46-
for _, name := range s.session.Options().SupportedCommands {
46+
for _, name := range s.Options().SupportedCommands {
4747
if name == params.Command {
4848
found = true
4949
break

gopls/internal/lsp/completion_test.go

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,8 @@ func expected(t *testing.T, test tests.Completion, items tests.CompletionItems)
144144

145145
func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) []protocol.CompletionItem {
146146
t.Helper()
147-
148-
view, err := r.server.session.ViewOf(src.URI())
149-
if err != nil {
150-
t.Fatal(err)
151-
}
152-
original := view.Options()
153-
modified := view.Options().Clone()
154-
options(modified)
155-
view, err = r.server.session.SetViewOptions(r.ctx, view, modified)
156-
if err != nil {
157-
t.Error(err)
158-
return nil
159-
}
160-
defer r.server.session.SetViewOptions(r.ctx, view, original)
147+
cleanup := r.toggleOptions(t, src.URI(), options)
148+
defer cleanup()
161149

162150
list, err := r.server.Completion(r.ctx, &protocol.CompletionParams{
163151
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
@@ -175,3 +163,21 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*sourc
175163
}
176164
return list.Items
177165
}
166+
167+
func (r *runner) toggleOptions(t *testing.T, uri span.URI, options func(*source.Options)) (reset func()) {
168+
view, err := r.server.session.ViewOf(uri)
169+
if err != nil {
170+
t.Fatal(err)
171+
}
172+
folder := view.Folder()
173+
174+
original := view.Options()
175+
modified := view.Options().Clone()
176+
options(modified)
177+
if err = r.server.session.SetFolderOptions(r.ctx, folder, modified); err != nil {
178+
t.Fatal(err)
179+
}
180+
return func() {
181+
r.server.session.SetFolderOptions(r.ctx, folder, original)
182+
}
183+
}

gopls/internal/lsp/debug/serve.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -701,9 +701,10 @@ Unknown page
701701
}
702702
return s
703703
},
704-
"options": func(s *cache.Session) []sessionOption {
705-
return showOptions(s.Options())
706-
},
704+
// TODO(rfindley): re-enable option inspection.
705+
// "options": func(s *cache.Session) []sessionOption {
706+
// return showOptions(s.Options())
707+
// },
707708
})
708709

709710
var MainTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
@@ -837,12 +838,6 @@ From: <b>{{template "cachelink" .Cache.ID}}</b><br>
837838
<li>
838839
<a href="/file/{{$session.ID}}/{{.FileIdentity.Hash}}">{{.FileIdentity.URI}}</a>
839840
</li>{{end}}</ul>
840-
<h2>Options</h2>
841-
{{range options .}}
842-
<p><b>{{.Name}}</b> {{.Type}}</p>
843-
<p><i>default:</i> {{.Default}}</p>
844-
{{if ne .Default .Current}}<p><i>current:</i> {{.Current}}</p>{{end}}
845-
{{end}}
846841
{{end}}
847842
`))
848843

gopls/internal/lsp/general.go

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ
5757
}
5858
s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress)
5959

60-
options := s.session.Options()
61-
defer func() { s.session.SetOptions(options) }()
60+
options := s.Options().Clone()
61+
// TODO(rfindley): remove the error return from handleOptionResults, and
62+
// eliminate this defer.
63+
defer func() { s.SetOptions(options) }()
6264

6365
if err := s.handleOptionResults(ctx, source.SetOptions(options, params.InitializationOptions)); err != nil {
6466
return nil, err
@@ -170,8 +172,8 @@ See https://github.com/golang/go/issues/45732 for more information.`,
170172
Range: &protocol.Or_SemanticTokensOptions_range{Value: true},
171173
Full: &protocol.Or_SemanticTokensOptions_full{Value: true},
172174
Legend: protocol.SemanticTokensLegend{
173-
TokenTypes: nonNilSliceString(s.session.Options().SemanticTypes),
174-
TokenModifiers: nonNilSliceString(s.session.Options().SemanticMods),
175+
TokenTypes: nonNilSliceString(s.Options().SemanticTypes),
176+
TokenModifiers: nonNilSliceString(s.Options().SemanticMods),
175177
},
176178
},
177179
SignatureHelpProvider: &protocol.SignatureHelpOptions{
@@ -215,9 +217,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
215217
}
216218
s.notifications = nil
217219

218-
options := s.session.Options()
219-
defer func() { s.session.SetOptions(options) }()
220-
220+
options := s.Options()
221221
if err := s.addFolders(ctx, s.pendingFolders); err != nil {
222222
return err
223223
}
@@ -348,7 +348,7 @@ func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFol
348348
viewErrors := make(map[span.URI]error)
349349

350350
var ndiagnose sync.WaitGroup // number of unfinished diagnose calls
351-
if s.session.Options().VerboseWorkDoneProgress {
351+
if s.Options().VerboseWorkDoneProgress {
352352
work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil)
353353
defer func() {
354354
go func() {
@@ -475,7 +475,7 @@ func equalURISet(m1, m2 map[string]struct{}) bool {
475475
// registrations to the client and updates s.watchedDirectories.
476476
// The caller must not subsequently mutate patterns.
477477
func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[string]struct{}) error {
478-
if !s.session.Options().DynamicWatchedFilesSupported {
478+
if !s.Options().DynamicWatchedFilesSupported {
479479
return nil
480480
}
481481
s.watchedGlobPatterns = patterns
@@ -503,9 +503,27 @@ func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, patterns
503503
return nil
504504
}
505505

506-
func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error {
507-
if !s.session.Options().ConfigurationSupported {
508-
return nil
506+
// Options returns the current server options.
507+
//
508+
// The caller must not modify the result.
509+
func (s *Server) Options() *source.Options {
510+
s.optionsMu.Lock()
511+
defer s.optionsMu.Unlock()
512+
return s.options
513+
}
514+
515+
// SetOptions sets the current server options.
516+
//
517+
// The caller must not subsequently modify the options.
518+
func (s *Server) SetOptions(opts *source.Options) {
519+
s.optionsMu.Lock()
520+
defer s.optionsMu.Unlock()
521+
s.options = opts
522+
}
523+
524+
func (s *Server) fetchFolderOptions(ctx context.Context, folder span.URI) (*source.Options, error) {
525+
if opts := s.Options(); !opts.ConfigurationSupported {
526+
return opts, nil
509527
}
510528
configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{
511529
Items: []protocol.ConfigurationItem{{
@@ -515,14 +533,16 @@ func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI,
515533
},
516534
)
517535
if err != nil {
518-
return fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
536+
return nil, fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err)
519537
}
538+
539+
folderOpts := s.Options().Clone()
520540
for _, config := range configs {
521-
if err := s.handleOptionResults(ctx, source.SetOptions(o, config)); err != nil {
522-
return err
541+
if err := s.handleOptionResults(ctx, source.SetOptions(folderOpts, config)); err != nil {
542+
return nil, err
523543
}
524544
}
525-
return nil
545+
return folderOpts, nil
526546
}
527547

528548
func (s *Server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error {

0 commit comments

Comments
 (0)