mirror of
https://github.com/golang/go
synced 2024-11-19 00:54:42 -07:00
80b1e8742c
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>
122 lines
3.5 KiB
Go
122 lines
3.5 KiB
Go
// 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]
|
||
}
|