1
0
mirror of https://github.com/golang/go synced 2024-11-18 17:44:47 -07:00
go/internal/lsp/workspace.go
Ian Cottrell 80b1e8742c internal/lsp: support dynamic workspace folder changes
This also required us to change the way we map files to a view, as it may change
over time.

Fixes golang/go#31635

Change-Id: Ic82467a1185717081487389f4c25ad69df1af290
Reviewed-on: https://go-review.googlesource.com/c/tools/+/175477
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-05-10 21:22:50 +00:00

122 lines
3.5 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2019 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 lsp
import (
"context"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/cache"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
)
func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFoldersChangeEvent) error {
s.log.Infof(ctx, "change folders")
for _, folder := range event.Removed {
if err := s.removeView(ctx, folder.Name, span.NewURI(folder.URI)); err != nil {
return err
}
}
for _, folder := range event.Added {
if err := s.addView(ctx, folder.Name, span.NewURI(folder.URI)); err != nil {
return err
}
}
return nil
}
func (s *Server) addView(ctx context.Context, name string, uri span.URI) error {
s.viewMu.Lock()
defer s.viewMu.Unlock()
// We need a "detached" context so it does not get timeout cancelled.
// TODO(iancottrell): Do we need to copy any values across?
viewContext := context.Background()
s.log.Infof(viewContext, "add view %v as %v", name, uri)
folderPath, err := uri.Filename()
if err != nil {
return err
}
s.views = append(s.views, cache.NewView(viewContext, s.log, name, uri, &packages.Config{
Context: viewContext,
Dir: folderPath,
Env: os.Environ(),
Mode: packages.LoadImports,
Fset: token.NewFileSet(),
Overlay: make(map[string][]byte),
ParseFile: func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
},
Tests: true,
}))
// we always need to drop the view map
s.viewMap = make(map[span.URI]*cache.View)
return nil
}
func (s *Server) removeView(ctx context.Context, name string, uri span.URI) error {
s.viewMu.Lock()
defer s.viewMu.Unlock()
// we always need to drop the view map
s.viewMap = make(map[span.URI]*cache.View)
s.log.Infof(ctx, "drop view %v as %v", name, uri)
for i, view := range s.views {
if view.Name == name {
// delete this view... we don't care about order but we do want to make
// sure we can garbage collect the view
s.views[i] = s.views[len(s.views)-1]
s.views[len(s.views)-1] = nil
s.views = s.views[:len(s.views)-1]
//TODO: shutdown the view in here
return nil
}
}
return fmt.Errorf("view %s for %v not found", name, uri)
}
// findView returns the view corresponding to the given URI.
// If the file is not already associated with a view, pick one using some heuristics.
func (s *Server) findView(ctx context.Context, uri span.URI) *cache.View {
s.viewMu.Lock()
defer s.viewMu.Unlock()
// check if we already know this file
if v, found := s.viewMap[uri]; found {
return v
}
// pick the best view for this file and memoize the result
v := s.bestView(ctx, uri)
s.viewMap[uri] = v
return v
}
// bestView finds the best view to associate a given URI with.
// viewMu must be held when calling this method.
func (s *Server) bestView(ctx context.Context, uri span.URI) *cache.View {
// we need to find the best view for this file
var longest *cache.View
for _, view := range s.views {
if longest != nil && len(longest.Folder) > len(view.Folder) {
continue
}
if strings.HasPrefix(string(uri), string(view.Folder)) {
longest = view
}
}
if longest != nil {
return longest
}
//TODO: are there any more heuristics we can use?
return s.views[0]
}