github.com/v2fly/tools@v0.100.0/internal/lsp/workspace.go (about) 1 // Copyright 2019 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 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "path/filepath" 12 "sync/atomic" 13 14 "github.com/v2fly/tools/internal/event" 15 "github.com/v2fly/tools/internal/lsp/protocol" 16 "github.com/v2fly/tools/internal/lsp/source" 17 "github.com/v2fly/tools/internal/span" 18 errors "golang.org/x/xerrors" 19 ) 20 21 func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error { 22 event := params.Event 23 for _, folder := range event.Removed { 24 view := s.session.View(folder.Name) 25 if view != nil { 26 view.Shutdown(ctx) 27 } else { 28 return errors.Errorf("view %s for %v not found", folder.Name, folder.URI) 29 } 30 } 31 return s.addFolders(ctx, event.Added) 32 } 33 34 var wsIndex int64 35 36 func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.Snapshot, func(), error) { 37 s.stateMu.Lock() 38 state := s.state 39 s.stateMu.Unlock() 40 if state < serverInitialized { 41 return nil, func() {}, errors.Errorf("addView called before server initialized") 42 } 43 options := s.session.Options().Clone() 44 if err := s.fetchConfig(ctx, name, uri, options); err != nil { 45 return nil, func() {}, err 46 } 47 // Try to assign a persistent temp directory for tracking this view's 48 // temporary workspace. 49 var tempWorkspace span.URI 50 if s.tempDir != "" { 51 index := atomic.AddInt64(&wsIndex, 1) 52 wsDir := filepath.Join(s.tempDir, fmt.Sprintf("workspace.%d", index)) 53 if err := os.Mkdir(wsDir, 0700); err == nil { 54 tempWorkspace = span.URIFromPath(wsDir) 55 } else { 56 event.Error(ctx, "making workspace dir", err) 57 } 58 } 59 _, snapshot, release, err := s.session.NewView(ctx, name, uri, tempWorkspace, options) 60 return snapshot, release, err 61 } 62 63 func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChangeConfigurationParams) error { 64 // Apply any changes to the session-level settings. 65 options := s.session.Options().Clone() 66 semanticTokensRegistered := options.SemanticTokens 67 if err := s.fetchConfig(ctx, "", "", options); err != nil { 68 return err 69 } 70 s.session.SetOptions(options) 71 72 // Go through each view, getting and updating its configuration. 73 for _, view := range s.session.Views() { 74 options := s.session.Options().Clone() 75 if err := s.fetchConfig(ctx, view.Name(), view.Folder(), options); err != nil { 76 return err 77 } 78 view, err := view.SetOptions(ctx, options) 79 if err != nil { 80 return err 81 } 82 go func() { 83 snapshot, release := view.Snapshot(ctx) 84 defer release() 85 s.diagnoseDetached(snapshot) 86 }() 87 } 88 89 // Update any session-specific registrations or unregistrations. 90 if !semanticTokensRegistered && options.SemanticTokens { 91 if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{ 92 Registrations: []protocol.Registration{semanticTokenRegistration()}, 93 }); err != nil { 94 return err 95 } 96 } else if semanticTokensRegistered && !options.SemanticTokens { 97 if err := s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{ 98 Unregisterations: []protocol.Unregistration{ 99 { 100 ID: semanticTokenRegistration().ID, 101 Method: semanticTokenRegistration().Method, 102 }, 103 }, 104 }); err != nil { 105 return err 106 } 107 } 108 return nil 109 } 110 111 func semanticTokenRegistration() protocol.Registration { 112 return protocol.Registration{ 113 ID: "textDocument/semanticTokens", 114 Method: "textDocument/semanticTokens", 115 RegisterOptions: &protocol.SemanticTokensOptions{ 116 Legend: protocol.SemanticTokensLegend{ 117 // TODO(pjw): trim these to what we use (and an unused one 118 // at position 0 of TokTypes, to catch typos) 119 TokenTypes: SemanticTypes(), 120 TokenModifiers: SemanticModifiers(), 121 }, 122 Full: true, 123 Range: true, 124 }, 125 } 126 }