mirror of
https://github.com/golang/go
synced 2024-11-18 09:04:49 -07:00
internal/lsp: clean out the debug handling
This removes all the cache/session/view hooks from the lsp and instead uses normal introspection from the client to find all the entities rather than keeping shadow copies of the object graph in the debug page handler. This required the addition only of the ability to view the sessions open on a cache and exposing the unique identifier for all the objects, both of which are useful and reasonable things to have in the API anyway. Change-Id: Ic19903e7b19832ca79290954ec373d4177789742 Reviewed-on: https://go-review.googlesource.com/c/tools/+/236197 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
7147197327
commit
ce53dc4445
14
internal/lsp/cache/cache.go
vendored
14
internal/lsp/cache/cache.go
vendored
@ -13,7 +13,6 @@ import (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/debug"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/memoize"
|
||||
"golang.org/x/tools/internal/span"
|
||||
@ -27,9 +26,6 @@ func New(ctx context.Context, options func(*source.Options)) *Cache {
|
||||
fset: token.NewFileSet(),
|
||||
options: options,
|
||||
}
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
di.State.AddCache(debugCache{c})
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@ -84,9 +80,6 @@ func (c *Cache) NewSession(ctx context.Context) *Session {
|
||||
options: source.DefaultOptions(),
|
||||
overlays: make(map[span.URI]*overlay),
|
||||
}
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
di.State.AddSession(DebugSession{s})
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@ -119,8 +112,5 @@ func hashContents(contents []byte) string {
|
||||
|
||||
var cacheIndex, sessionIndex, viewIndex int64
|
||||
|
||||
type debugCache struct{ *Cache }
|
||||
|
||||
func (c *Cache) ID() string { return c.id }
|
||||
func (c debugCache) FileSet() *token.FileSet { return c.fset }
|
||||
func (c debugCache) MemStats() map[reflect.Type]int { return c.store.Stats() }
|
||||
func (c *Cache) ID() string { return c.id }
|
||||
func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }
|
||||
|
64
internal/lsp/cache/debug.go
vendored
64
internal/lsp/cache/debug.go
vendored
@ -1,64 +0,0 @@
|
||||
// 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 cache
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/debug"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
type debugView struct{ *view }
|
||||
|
||||
func (v debugView) ID() string { return v.id }
|
||||
func (v debugView) Session() debug.Session { return DebugSession{v.session} }
|
||||
func (v debugView) Env() []string { return v.Options().Env }
|
||||
|
||||
type DebugSession struct{ *Session }
|
||||
|
||||
func (s DebugSession) ID() string { return s.id }
|
||||
func (s DebugSession) Cache() debug.Cache { return debugCache{s.cache} }
|
||||
func (s DebugSession) Files() []*debug.File {
|
||||
var files []*debug.File
|
||||
seen := make(map[span.URI]*debug.File)
|
||||
s.overlayMu.Lock()
|
||||
defer s.overlayMu.Unlock()
|
||||
for _, overlay := range s.overlays {
|
||||
f, ok := seen[overlay.uri]
|
||||
if !ok {
|
||||
f = &debug.File{Session: s, URI: overlay.uri}
|
||||
seen[overlay.uri] = f
|
||||
files = append(files, f)
|
||||
}
|
||||
f.Data = string(overlay.text)
|
||||
f.Error = nil
|
||||
f.Hash = overlay.hash
|
||||
}
|
||||
sort.Slice(files, func(i int, j int) bool {
|
||||
return files[i].URI < files[j].URI
|
||||
})
|
||||
return files
|
||||
}
|
||||
|
||||
func (s DebugSession) File(hash string) *debug.File {
|
||||
s.overlayMu.Lock()
|
||||
defer s.overlayMu.Unlock()
|
||||
for _, overlay := range s.overlays {
|
||||
if overlay.hash == hash {
|
||||
return &debug.File{
|
||||
Session: s,
|
||||
URI: overlay.uri,
|
||||
Data: string(overlay.text),
|
||||
Error: nil,
|
||||
Hash: overlay.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
return &debug.File{
|
||||
Session: s,
|
||||
Hash: hash,
|
||||
}
|
||||
}
|
57
internal/lsp/cache/session.go
vendored
57
internal/lsp/cache/session.go
vendored
@ -13,7 +13,6 @@ import (
|
||||
|
||||
"golang.org/x/tools/internal/event"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/lsp/debug"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/xcontext"
|
||||
@ -27,8 +26,8 @@ type Session struct {
|
||||
options source.Options
|
||||
|
||||
viewMu sync.Mutex
|
||||
views []*view
|
||||
viewMap map[span.URI]*view
|
||||
views []*View
|
||||
viewMap map[span.URI]*View
|
||||
|
||||
overlayMu sync.Mutex
|
||||
overlays map[span.URI]*overlay
|
||||
@ -60,10 +59,17 @@ func (o *overlay) Identity() source.FileIdentity {
|
||||
Kind: o.kind,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *overlay) Read(ctx context.Context) ([]byte, string, error) {
|
||||
return o.text, o.hash, nil
|
||||
}
|
||||
|
||||
func (o *overlay) Session() source.Session { return o.session }
|
||||
func (o *overlay) Saved() bool { return o.saved }
|
||||
func (o *overlay) Data() []byte { return o.text }
|
||||
|
||||
func (s *Session) ID() string { return s.id }
|
||||
|
||||
func (s *Session) Options() source.Options {
|
||||
return s.options
|
||||
}
|
||||
@ -80,9 +86,6 @@ func (s *Session) Shutdown(ctx context.Context) {
|
||||
}
|
||||
s.views = nil
|
||||
s.viewMap = nil
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
di.State.DropSession(DebugSession{s})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) Cache() source.Cache {
|
||||
@ -98,18 +101,18 @@ func (s *Session) NewView(ctx context.Context, name string, folder span.URI, opt
|
||||
}
|
||||
s.views = append(s.views, v)
|
||||
// we always need to drop the view map
|
||||
s.viewMap = make(map[span.URI]*view)
|
||||
s.viewMap = make(map[span.URI]*View)
|
||||
return v, snapshot, nil
|
||||
}
|
||||
|
||||
func (s *Session) createView(ctx context.Context, name string, folder span.URI, options source.Options, snapshotID uint64) (*view, *snapshot, error) {
|
||||
func (s *Session) createView(ctx context.Context, name string, folder span.URI, options source.Options, snapshotID uint64) (*View, *snapshot, error) {
|
||||
index := atomic.AddInt64(&viewIndex, 1)
|
||||
// We want a true background context and not a detached context here
|
||||
// the spans need to be unrelated and no tag values should pollute it.
|
||||
baseCtx := event.Detach(xcontext.Detach(ctx))
|
||||
backgroundCtx, cancel := context.WithCancel(baseCtx)
|
||||
|
||||
v := &view{
|
||||
v := &View{
|
||||
session: s,
|
||||
initialized: make(chan struct{}),
|
||||
id: strconv.FormatInt(index, 10),
|
||||
@ -148,10 +151,6 @@ func (s *Session) createView(ctx context.Context, name string, folder span.URI,
|
||||
|
||||
// Initialize the view without blocking.
|
||||
go v.initialize(xcontext.Detach(ctx), v.snapshot)
|
||||
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
di.State.AddView(debugView{v})
|
||||
}
|
||||
return v, v.snapshot, nil
|
||||
}
|
||||
|
||||
@ -173,7 +172,7 @@ func (s *Session) ViewOf(uri span.URI) (source.View, error) {
|
||||
return s.viewOf(uri)
|
||||
}
|
||||
|
||||
func (s *Session) viewOf(uri span.URI) (*view, error) {
|
||||
func (s *Session) viewOf(uri span.URI) (*View, error) {
|
||||
s.viewMu.Lock()
|
||||
defer s.viewMu.Unlock()
|
||||
|
||||
@ -190,11 +189,11 @@ func (s *Session) viewOf(uri span.URI) (*view, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (s *Session) viewsOf(uri span.URI) []*view {
|
||||
func (s *Session) viewsOf(uri span.URI) []*View {
|
||||
s.viewMu.Lock()
|
||||
defer s.viewMu.Unlock()
|
||||
|
||||
var views []*view
|
||||
var views []*View
|
||||
for _, view := range s.views {
|
||||
if strings.HasPrefix(string(uri), string(view.Folder())) {
|
||||
views = append(views, view)
|
||||
@ -215,12 +214,12 @@ func (s *Session) Views() []source.View {
|
||||
|
||||
// bestView finds the best view to associate a given URI with.
|
||||
// viewMu must be held when calling this method.
|
||||
func (s *Session) bestView(uri span.URI) (*view, error) {
|
||||
func (s *Session) bestView(uri span.URI) (*View, error) {
|
||||
if len(s.views) == 0 {
|
||||
return nil, errors.Errorf("no views in the session")
|
||||
}
|
||||
// we need to find the best view for this file
|
||||
var longest *view
|
||||
var longest *View
|
||||
for _, view := range s.views {
|
||||
if longest != nil && len(longest.Folder()) > len(view.Folder()) {
|
||||
continue
|
||||
@ -236,7 +235,7 @@ func (s *Session) bestView(uri span.URI) (*view, error) {
|
||||
return s.views[0], nil
|
||||
}
|
||||
|
||||
func (s *Session) removeView(ctx context.Context, view *view) error {
|
||||
func (s *Session) removeView(ctx context.Context, view *View) error {
|
||||
s.viewMu.Lock()
|
||||
defer s.viewMu.Unlock()
|
||||
i, err := s.dropView(ctx, view)
|
||||
@ -251,7 +250,7 @@ func (s *Session) removeView(ctx context.Context, view *view) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) updateView(ctx context.Context, view *view, options source.Options) (*view, *snapshot, error) {
|
||||
func (s *Session) updateView(ctx context.Context, view *View, options source.Options) (*View, *snapshot, error) {
|
||||
s.viewMu.Lock()
|
||||
defer s.viewMu.Unlock()
|
||||
i, err := s.dropView(ctx, view)
|
||||
@ -276,9 +275,9 @@ func (s *Session) updateView(ctx context.Context, view *view, options source.Opt
|
||||
return v, snapshot, nil
|
||||
}
|
||||
|
||||
func (s *Session) dropView(ctx context.Context, v *view) (int, error) {
|
||||
func (s *Session) dropView(ctx context.Context, v *View) (int, error) {
|
||||
// we always need to drop the view map
|
||||
s.viewMap = make(map[span.URI]*view)
|
||||
s.viewMap = make(map[span.URI]*View)
|
||||
for i := range s.views {
|
||||
if v == s.views[i] {
|
||||
// we found the view, drop it and return the index it was found at
|
||||
@ -291,7 +290,7 @@ func (s *Session) dropView(ctx context.Context, v *view) (int, error) {
|
||||
}
|
||||
|
||||
func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModification) ([]source.Snapshot, error) {
|
||||
views := make(map[*view]map[span.URI]source.FileHandle)
|
||||
views := make(map[*View]map[span.URI]source.FileHandle)
|
||||
|
||||
overlays, err := s.updateOverlays(ctx, changes)
|
||||
if err != nil {
|
||||
@ -442,15 +441,13 @@ func (s *Session) readOverlay(uri span.URI) *overlay {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) UnsavedFiles() []span.URI {
|
||||
func (s *Session) Overlays() []source.Overlay {
|
||||
s.overlayMu.Lock()
|
||||
defer s.overlayMu.Unlock()
|
||||
|
||||
var unsaved []span.URI
|
||||
for uri, overlay := range s.overlays {
|
||||
if !overlay.saved {
|
||||
unsaved = append(unsaved, uri)
|
||||
}
|
||||
overlays := make([]source.Overlay, 0, len(s.overlays))
|
||||
for _, overlay := range s.overlays {
|
||||
overlays = append(overlays, overlay)
|
||||
}
|
||||
return unsaved
|
||||
return overlays
|
||||
}
|
||||
|
4
internal/lsp/cache/snapshot.go
vendored
4
internal/lsp/cache/snapshot.go
vendored
@ -29,7 +29,7 @@ import (
|
||||
|
||||
type snapshot struct {
|
||||
id uint64
|
||||
view *view
|
||||
view *View
|
||||
|
||||
// mu guards all of the maps in the snapshot.
|
||||
mu sync.Mutex
|
||||
@ -667,7 +667,7 @@ func (s *snapshot) orphanedFileScopes() []interface{} {
|
||||
return scopes
|
||||
}
|
||||
|
||||
func contains(views []*view, view *view) bool {
|
||||
func contains(views []*View, view *View) bool {
|
||||
for _, v := range views {
|
||||
if v == view {
|
||||
return true
|
||||
|
82
internal/lsp/cache/view.go
vendored
82
internal/lsp/cache/view.go
vendored
@ -24,7 +24,6 @@ import (
|
||||
"golang.org/x/tools/internal/event/keys"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/imports"
|
||||
"golang.org/x/tools/internal/lsp/debug"
|
||||
"golang.org/x/tools/internal/lsp/debug/tag"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/memoize"
|
||||
@ -33,7 +32,7 @@ import (
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type view struct {
|
||||
type View struct {
|
||||
session *Session
|
||||
id string
|
||||
|
||||
@ -137,7 +136,7 @@ type fileBase struct {
|
||||
uris []span.URI
|
||||
fname string
|
||||
|
||||
view *view
|
||||
view *View
|
||||
}
|
||||
|
||||
func (f *fileBase) URI() span.URI {
|
||||
@ -153,29 +152,31 @@ func (f *fileBase) addURI(uri span.URI) int {
|
||||
return len(f.uris)
|
||||
}
|
||||
|
||||
func (v *view) ValidBuildConfiguration() bool {
|
||||
func (v *View) ID() string { return v.id }
|
||||
|
||||
func (v *View) ValidBuildConfiguration() bool {
|
||||
return v.hasValidBuildConfiguration
|
||||
}
|
||||
|
||||
func (v *view) ModFiles() (span.URI, span.URI) {
|
||||
func (v *View) ModFiles() (span.URI, span.URI) {
|
||||
return v.realMod, v.tempMod
|
||||
}
|
||||
|
||||
func (v *view) Session() source.Session {
|
||||
func (v *View) Session() source.Session {
|
||||
return v.session
|
||||
}
|
||||
|
||||
// Name returns the user visible name of this view.
|
||||
func (v *view) Name() string {
|
||||
func (v *View) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
// Folder returns the root of this view.
|
||||
func (v *view) Folder() span.URI {
|
||||
func (v *View) Folder() span.URI {
|
||||
return v.folder
|
||||
}
|
||||
|
||||
func (v *view) Options() source.Options {
|
||||
func (v *View) Options() source.Options {
|
||||
v.optionsMu.Lock()
|
||||
defer v.optionsMu.Unlock()
|
||||
return v.options
|
||||
@ -193,7 +194,7 @@ func minorOptionsChange(a, b source.Options) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *view) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
|
||||
func (v *View) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
|
||||
// no need to rebuild the view if the options were not materially changed
|
||||
v.optionsMu.Lock()
|
||||
if minorOptionsChange(v.options, options) {
|
||||
@ -206,12 +207,12 @@ func (v *view) SetOptions(ctx context.Context, options source.Options) (source.V
|
||||
return newView, err
|
||||
}
|
||||
|
||||
func (v *view) Rebuild(ctx context.Context) (source.Snapshot, error) {
|
||||
func (v *View) Rebuild(ctx context.Context) (source.Snapshot, error) {
|
||||
_, snapshot, err := v.session.updateView(ctx, v, v.Options())
|
||||
return snapshot, err
|
||||
}
|
||||
|
||||
func (v *view) LookupBuiltin(ctx context.Context, name string) (*ast.Object, error) {
|
||||
func (v *View) LookupBuiltin(ctx context.Context, name string) (*ast.Object, error) {
|
||||
v.awaitInitialized(ctx)
|
||||
|
||||
if v.builtin == nil {
|
||||
@ -241,7 +242,7 @@ func (v *view) LookupBuiltin(ctx context.Context, name string) (*ast.Object, err
|
||||
return astObj, nil
|
||||
}
|
||||
|
||||
func (v *view) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
|
||||
func (v *View) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
|
||||
if len(goFiles) != 1 {
|
||||
return errors.Errorf("only expected 1 file, got %v", len(goFiles))
|
||||
}
|
||||
@ -271,7 +272,7 @@ func (v *view) buildBuiltinPackage(ctx context.Context, goFiles []string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) WriteEnv(ctx context.Context, w io.Writer) error {
|
||||
func (v *View) WriteEnv(ctx context.Context, w io.Writer) error {
|
||||
v.optionsMu.Lock()
|
||||
env, buildFlags := v.envLocked()
|
||||
v.optionsMu.Unlock()
|
||||
@ -291,7 +292,7 @@ func (v *view) WriteEnv(ctx context.Context, w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
|
||||
func (v *View) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
|
||||
v.importsMu.Lock()
|
||||
defer v.importsMu.Unlock()
|
||||
|
||||
@ -339,7 +340,7 @@ func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) refreshProcessEnv() {
|
||||
func (v *View) refreshProcessEnv() {
|
||||
start := time.Now()
|
||||
|
||||
v.importsMu.Lock()
|
||||
@ -361,7 +362,7 @@ func (v *view) refreshProcessEnv() {
|
||||
v.importsMu.Unlock()
|
||||
}
|
||||
|
||||
func (v *view) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error) {
|
||||
func (v *View) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error) {
|
||||
v.optionsMu.Lock()
|
||||
env, buildFlags := v.envLocked()
|
||||
localPrefix, verboseOutput := v.options.LocalPrefix, v.options.VerboseOutput
|
||||
@ -403,7 +404,7 @@ func (v *view) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error)
|
||||
return processEnv, nil
|
||||
}
|
||||
|
||||
func (v *view) envLocked() ([]string, []string) {
|
||||
func (v *View) envLocked() ([]string, []string) {
|
||||
// We want to run the go commands with the -modfile flag if the version of go
|
||||
// that we are using supports it.
|
||||
buildFlags := v.options.BuildFlags
|
||||
@ -415,11 +416,11 @@ func (v *view) envLocked() ([]string, []string) {
|
||||
return env, buildFlags
|
||||
}
|
||||
|
||||
func (v *view) contains(uri span.URI) bool {
|
||||
func (v *View) contains(uri span.URI) bool {
|
||||
return strings.HasPrefix(string(uri), string(v.folder))
|
||||
}
|
||||
|
||||
func (v *view) mapFile(uri span.URI, f *fileBase) {
|
||||
func (v *View) mapFile(uri span.URI, f *fileBase) {
|
||||
v.filesByURI[uri] = f
|
||||
if f.addURI(uri) == 1 {
|
||||
basename := basename(f.filename())
|
||||
@ -431,7 +432,7 @@ func basename(filename string) string {
|
||||
return strings.ToLower(filepath.Base(filename))
|
||||
}
|
||||
|
||||
func (v *view) relevantChange(c source.FileModification) bool {
|
||||
func (v *View) relevantChange(c source.FileModification) bool {
|
||||
// If the file is known to the view, the change is relevant.
|
||||
known := v.knownFile(c.URI)
|
||||
|
||||
@ -444,7 +445,7 @@ func (v *view) relevantChange(c source.FileModification) bool {
|
||||
return v.contains(c.URI) || known
|
||||
}
|
||||
|
||||
func (v *view) knownFile(uri span.URI) bool {
|
||||
func (v *View) knownFile(uri span.URI) bool {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
|
||||
@ -454,7 +455,7 @@ func (v *view) knownFile(uri span.URI) bool {
|
||||
|
||||
// getFile returns a file for the given URI. It will always succeed because it
|
||||
// adds the file to the managed set if needed.
|
||||
func (v *view) getFile(uri span.URI) (*fileBase, error) {
|
||||
func (v *View) getFile(uri span.URI) (*fileBase, error) {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
|
||||
@ -476,7 +477,7 @@ func (v *view) getFile(uri span.URI) (*fileBase, error) {
|
||||
//
|
||||
// An error is only returned for an irreparable failure, for example, if the
|
||||
// filename in question does not exist.
|
||||
func (v *view) findFile(uri span.URI) (*fileBase, error) {
|
||||
func (v *View) findFile(uri span.URI) (*fileBase, error) {
|
||||
if f := v.filesByURI[uri]; f != nil {
|
||||
// a perfect match
|
||||
return f, nil
|
||||
@ -507,11 +508,11 @@ func (v *view) findFile(uri span.URI) (*fileBase, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (v *view) Shutdown(ctx context.Context) {
|
||||
func (v *View) Shutdown(ctx context.Context) {
|
||||
v.session.removeView(ctx, v)
|
||||
}
|
||||
|
||||
func (v *view) shutdown(ctx context.Context) {
|
||||
func (v *View) shutdown(ctx context.Context) {
|
||||
// TODO: Cancel the view's initialization.
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
@ -523,14 +524,11 @@ func (v *view) shutdown(ctx context.Context) {
|
||||
os.Remove(v.tempMod.Filename())
|
||||
os.Remove(tempSumFile(v.tempMod.Filename()))
|
||||
}
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
di.State.DropView(debugView{v})
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore checks if the given URI is a URI we ignore.
|
||||
// As of right now, we only ignore files in the "builtin" package.
|
||||
func (v *view) Ignore(uri span.URI) bool {
|
||||
func (v *View) Ignore(uri span.URI) bool {
|
||||
v.ignoredURIsMu.Lock()
|
||||
defer v.ignoredURIsMu.Unlock()
|
||||
|
||||
@ -545,32 +543,32 @@ func (v *view) Ignore(uri span.URI) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v *view) addIgnoredFile(uri span.URI) {
|
||||
func (v *View) addIgnoredFile(uri span.URI) {
|
||||
v.ignoredURIsMu.Lock()
|
||||
defer v.ignoredURIsMu.Unlock()
|
||||
|
||||
v.ignoredURIs[uri] = struct{}{}
|
||||
}
|
||||
|
||||
func (v *view) BackgroundContext() context.Context {
|
||||
func (v *View) BackgroundContext() context.Context {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
|
||||
return v.backgroundCtx
|
||||
}
|
||||
|
||||
func (v *view) Snapshot() source.Snapshot {
|
||||
func (v *View) Snapshot() source.Snapshot {
|
||||
return v.getSnapshot()
|
||||
}
|
||||
|
||||
func (v *view) getSnapshot() *snapshot {
|
||||
func (v *View) getSnapshot() *snapshot {
|
||||
v.snapshotMu.Lock()
|
||||
defer v.snapshotMu.Unlock()
|
||||
|
||||
return v.snapshot
|
||||
}
|
||||
|
||||
func (v *view) initialize(ctx context.Context, s *snapshot) {
|
||||
func (v *View) initialize(ctx context.Context, s *snapshot) {
|
||||
v.initializeOnce.Do(func() {
|
||||
defer close(v.initialized)
|
||||
|
||||
@ -581,7 +579,7 @@ func (v *view) initialize(ctx context.Context, s *snapshot) {
|
||||
})
|
||||
}
|
||||
|
||||
func (v *view) awaitInitialized(ctx context.Context) {
|
||||
func (v *View) awaitInitialized(ctx context.Context) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-v.initialized:
|
||||
@ -591,7 +589,7 @@ func (v *view) awaitInitialized(ctx context.Context) {
|
||||
// invalidateContent invalidates the content of a Go file,
|
||||
// including any position and type information that depends on it.
|
||||
// It returns true if we were already tracking the given file, false otherwise.
|
||||
func (v *view) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle, forceReloadMetadata bool) source.Snapshot {
|
||||
func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle, forceReloadMetadata bool) source.Snapshot {
|
||||
// Detach the context so that content invalidation cannot be canceled.
|
||||
ctx = xcontext.Detach(ctx)
|
||||
|
||||
@ -610,7 +608,7 @@ func (v *view) invalidateContent(ctx context.Context, uris map[span.URI]source.F
|
||||
return v.snapshot
|
||||
}
|
||||
|
||||
func (v *view) cancelBackground() {
|
||||
func (v *View) cancelBackground() {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if v.cancel == nil {
|
||||
@ -621,7 +619,7 @@ func (v *view) cancelBackground() {
|
||||
v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
|
||||
}
|
||||
|
||||
func (v *view) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
|
||||
func (v *View) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
|
||||
if err := checkPathCase(folder.Filename()); err != nil {
|
||||
return fmt.Errorf("invalid workspace configuration: %w", err)
|
||||
}
|
||||
@ -721,7 +719,7 @@ func isSubdirectory(root, leaf string) bool {
|
||||
// getGoEnv sets the view's build information's GOPATH, GOCACHE, GOPRIVATE, and
|
||||
// GOPACKAGESDRIVER values. It also returns the view's GOMOD value, which need
|
||||
// not be cached.
|
||||
func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
|
||||
func (v *View) getGoEnv(ctx context.Context, env []string) (string, error) {
|
||||
var gocache, gopath, gopackagesdriver, goprivate bool
|
||||
isGoCommand := func(gopackagesdriver string) bool {
|
||||
return gopackagesdriver == "" || gopackagesdriver == "off"
|
||||
@ -792,7 +790,7 @@ func (v *view) getGoEnv(ctx context.Context, env []string) (string, error) {
|
||||
|
||||
}
|
||||
|
||||
func (v *view) IsGoPrivatePath(target string) bool {
|
||||
func (v *View) IsGoPrivatePath(target string) bool {
|
||||
return globsMatchPath(v.goprivate, target)
|
||||
}
|
||||
|
||||
@ -840,7 +838,7 @@ func globsMatchPath(globs, target string) bool {
|
||||
|
||||
// This function will return the main go.mod file for this folder if it exists and whether the -modfile
|
||||
// flag exists for this version of go.
|
||||
func (v *view) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
|
||||
func (v *View) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
|
||||
// Check the go version by running "go list" with modules off.
|
||||
// Borrowed from internal/imports/mod.go:620.
|
||||
const format = `{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}`
|
||||
|
@ -23,7 +23,14 @@ import (
|
||||
func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
|
||||
switch params.Command {
|
||||
case source.CommandTest:
|
||||
if len(s.session.UnsavedFiles()) != 0 {
|
||||
unsaved := false
|
||||
for _, overlay := range s.session.Overlays() {
|
||||
if !overlay.Saved() {
|
||||
unsaved = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if unsaved {
|
||||
return nil, s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
||||
Type: protocol.Error,
|
||||
Message: "could not run tests, there are unsaved files in the view",
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
@ -18,7 +17,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
rpprof "runtime/pprof"
|
||||
"strconv"
|
||||
@ -34,9 +32,9 @@ import (
|
||||
"golang.org/x/tools/internal/event/export/prometheus"
|
||||
"golang.org/x/tools/internal/event/keys"
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
"golang.org/x/tools/internal/lsp/cache"
|
||||
"golang.org/x/tools/internal/lsp/debug/tag"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@ -67,12 +65,9 @@ type Instance struct {
|
||||
|
||||
// State holds debugging information related to the server state.
|
||||
type State struct {
|
||||
mu sync.Mutex
|
||||
caches objset
|
||||
sessions objset
|
||||
views objset
|
||||
clients objset
|
||||
servers objset
|
||||
mu sync.Mutex
|
||||
clients objset
|
||||
servers objset
|
||||
}
|
||||
|
||||
type ider interface {
|
||||
@ -107,38 +102,75 @@ func (s *objset) find(id string) ider {
|
||||
}
|
||||
|
||||
// Caches returns the set of Cache objects currently being served.
|
||||
func (st *State) Caches() []Cache {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
caches := make([]Cache, len(st.caches.objs))
|
||||
for i, c := range st.caches.objs {
|
||||
caches[i] = c.(Cache)
|
||||
func (st *State) Caches() []*cache.Cache {
|
||||
var caches []*cache.Cache
|
||||
seen := make(map[string]struct{})
|
||||
for _, client := range st.Clients() {
|
||||
cache, ok := client.Session().Cache().(*cache.Cache)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, found := seen[cache.ID()]; found {
|
||||
continue
|
||||
}
|
||||
seen[cache.ID()] = struct{}{}
|
||||
caches = append(caches, cache)
|
||||
}
|
||||
return caches
|
||||
}
|
||||
|
||||
// Cache returns the Cache that matches the supplied id.
|
||||
func (st *State) Cache(id string) *cache.Cache {
|
||||
for _, c := range st.Caches() {
|
||||
if c.ID() == id {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sessions returns the set of Session objects currently being served.
|
||||
func (st *State) Sessions() []Session {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
sessions := make([]Session, len(st.sessions.objs))
|
||||
for i, s := range st.sessions.objs {
|
||||
sessions[i] = s.(Session)
|
||||
func (st *State) Sessions() []*cache.Session {
|
||||
var sessions []*cache.Session
|
||||
for _, client := range st.Clients() {
|
||||
sessions = append(sessions, client.Session())
|
||||
}
|
||||
return sessions
|
||||
}
|
||||
|
||||
// Session returns the Session that matches the supplied id.
|
||||
func (st *State) Session(id string) *cache.Session {
|
||||
for _, s := range st.Sessions() {
|
||||
if s.ID() == id {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Views returns the set of View objects currently being served.
|
||||
func (st *State) Views() []View {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
views := make([]View, len(st.views.objs))
|
||||
for i, v := range st.views.objs {
|
||||
views[i] = v.(View)
|
||||
func (st *State) Views() []*cache.View {
|
||||
var views []*cache.View
|
||||
for _, s := range st.Sessions() {
|
||||
for _, v := range s.Views() {
|
||||
if cv, ok := v.(*cache.View); ok {
|
||||
views = append(views, cv)
|
||||
}
|
||||
}
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
// View returns the View that matches the supplied id.
|
||||
func (st *State) View(id string) *cache.View {
|
||||
for _, v := range st.Views() {
|
||||
if v.ID() == id {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clients returns the set of Clients currently being served.
|
||||
func (st *State) Clients() []Client {
|
||||
st.mu.Lock()
|
||||
@ -164,7 +196,7 @@ func (st *State) Servers() []Server {
|
||||
// A Client is an incoming connection from a remote client.
|
||||
type Client interface {
|
||||
ID() string
|
||||
Session() Session
|
||||
Session() *cache.Session
|
||||
DebugAddress() string
|
||||
Logfile() string
|
||||
ServerID() string
|
||||
@ -178,80 +210,6 @@ type Server interface {
|
||||
ClientID() string
|
||||
}
|
||||
|
||||
// A Cache is an in-memory cache.
|
||||
type Cache interface {
|
||||
ID() string
|
||||
FileSet() *token.FileSet
|
||||
MemStats() map[reflect.Type]int
|
||||
}
|
||||
|
||||
// A Session is an LSP serving session.
|
||||
type Session interface {
|
||||
ID() string
|
||||
Cache() Cache
|
||||
Files() []*File
|
||||
File(hash string) *File
|
||||
}
|
||||
|
||||
// A View is a root directory within a Session.
|
||||
type View interface {
|
||||
ID() string
|
||||
Name() string
|
||||
Folder() span.URI
|
||||
Session() Session
|
||||
}
|
||||
|
||||
// A File is a file within a session.
|
||||
type File struct {
|
||||
Session Session
|
||||
URI span.URI
|
||||
Data string
|
||||
Error error
|
||||
Hash string
|
||||
}
|
||||
|
||||
// AddCache adds a cache to the set being served.
|
||||
func (st *State) AddCache(cache Cache) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.caches.add(cache)
|
||||
}
|
||||
|
||||
// DropCache drops a cache from the set being served.
|
||||
func (st *State) DropCache(cache Cache) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.caches.drop(cache)
|
||||
}
|
||||
|
||||
// AddSession adds a session to the set being served.
|
||||
func (st *State) AddSession(session Session) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.sessions.add(session)
|
||||
}
|
||||
|
||||
// DropSession drops a session from the set being served.
|
||||
func (st *State) DropSession(session Session) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.sessions.drop(session)
|
||||
}
|
||||
|
||||
// AddView adds a view to the set being served.
|
||||
func (st *State) AddView(view View) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.views.add(view)
|
||||
}
|
||||
|
||||
// DropView drops a view from the set being served.
|
||||
func (st *State) DropView(view View) {
|
||||
st.mu.Lock()
|
||||
defer st.mu.Unlock()
|
||||
st.views.drop(view)
|
||||
}
|
||||
|
||||
// AddClient adds a client to the set being served.
|
||||
func (st *State) AddClient(client Client) {
|
||||
st.mu.Lock()
|
||||
@ -282,52 +240,11 @@ func (st *State) DropServer(server Server) {
|
||||
}
|
||||
|
||||
func (i *Instance) getCache(r *http.Request) interface{} {
|
||||
i.State.mu.Lock()
|
||||
defer i.State.mu.Unlock()
|
||||
id := path.Base(r.URL.Path)
|
||||
c, ok := i.State.caches.find(id).(Cache)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result := struct {
|
||||
Cache
|
||||
Sessions []Session
|
||||
}{
|
||||
Cache: c,
|
||||
}
|
||||
|
||||
// now find all the views that belong to this session
|
||||
for _, vd := range i.State.sessions.objs {
|
||||
v := vd.(Session)
|
||||
if v.Cache().ID() == id {
|
||||
result.Sessions = append(result.Sessions, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return i.State.Cache(path.Base(r.URL.Path))
|
||||
}
|
||||
|
||||
func (i *Instance) getSession(r *http.Request) interface{} {
|
||||
i.State.mu.Lock()
|
||||
defer i.State.mu.Unlock()
|
||||
id := path.Base(r.URL.Path)
|
||||
s, ok := i.State.sessions.find(id).(Session)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result := struct {
|
||||
Session
|
||||
Views []View
|
||||
}{
|
||||
Session: s,
|
||||
}
|
||||
// now find all the views that belong to this session
|
||||
for _, vd := range i.State.views.objs {
|
||||
v := vd.(View)
|
||||
if v.Session().ID() == id {
|
||||
result.Views = append(result.Views, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return i.State.Session(path.Base(r.URL.Path))
|
||||
}
|
||||
|
||||
func (i Instance) getClient(r *http.Request) interface{} {
|
||||
@ -353,26 +270,22 @@ func (i Instance) getServer(r *http.Request) interface{} {
|
||||
}
|
||||
|
||||
func (i Instance) getView(r *http.Request) interface{} {
|
||||
i.State.mu.Lock()
|
||||
defer i.State.mu.Unlock()
|
||||
id := path.Base(r.URL.Path)
|
||||
v, ok := i.State.views.find(id).(View)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
return i.State.View(path.Base(r.URL.Path))
|
||||
}
|
||||
|
||||
func (i *Instance) getFile(r *http.Request) interface{} {
|
||||
i.State.mu.Lock()
|
||||
defer i.State.mu.Unlock()
|
||||
hash := path.Base(r.URL.Path)
|
||||
identifier := path.Base(r.URL.Path)
|
||||
sid := path.Base(path.Dir(r.URL.Path))
|
||||
s, ok := i.State.sessions.find(sid).(Session)
|
||||
if !ok {
|
||||
s := i.State.Session(sid)
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.File(hash)
|
||||
for _, o := range s.Overlays() {
|
||||
if o.Identity().Identifier == identifier {
|
||||
return o
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Instance) getInfo(r *http.Request) interface{} {
|
||||
@ -625,6 +538,10 @@ func fuint32(v uint32) string {
|
||||
return commas(strconv.FormatUint(uint64(v), 10))
|
||||
}
|
||||
|
||||
func fcontent(v []byte) string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
var baseTemplate = template.Must(template.New("").Parse(`
|
||||
<html>
|
||||
<head>
|
||||
@ -664,10 +581,11 @@ Unknown page
|
||||
{{define "serverlink"}}<a href="/server/{{.}}">Server {{.}}</a>{{end}}
|
||||
{{define "sessionlink"}}<a href="/session/{{.}}">Session {{.}}</a>{{end}}
|
||||
{{define "viewlink"}}<a href="/view/{{.}}">View {{.}}</a>{{end}}
|
||||
{{define "filelink"}}<a href="/file/{{.Session.ID}}/{{.Hash}}">{{.URI}}</a>{{end}}
|
||||
{{define "filelink"}}<a href="/file/{{.SessionID}}/{{.Identifier}}">{{.URI}}</a>{{end}}
|
||||
`)).Funcs(template.FuncMap{
|
||||
"fuint64": fuint64,
|
||||
"fuint32": fuint32,
|
||||
"fuint64": fuint64,
|
||||
"fuint32": fuint32,
|
||||
"fcontent": fcontent,
|
||||
"localAddress": func(s string) string {
|
||||
// Try to translate loopback addresses to localhost, both for cosmetics and
|
||||
// because unspecified ipv6 addresses can break links on Windows.
|
||||
@ -785,8 +703,8 @@ var sessionTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
|
||||
From: <b>{{template "cachelink" .Cache.ID}}</b><br>
|
||||
<h2>Views</h2>
|
||||
<ul>{{range .Views}}<li>{{.Name}} is {{template "viewlink" .ID}} in {{.Folder}}</li>{{end}}</ul>
|
||||
<h2>Files</h2>
|
||||
<ul>{{range .Files}}<li>{{template "filelink" .}}</li>{{end}}</ul>
|
||||
<h2>Overlays</h2>
|
||||
<ul>{{range .Overlays}}<li>{{template "filelink" .Identity}}</li>{{end}}</ul>
|
||||
{{end}}
|
||||
`))
|
||||
|
||||
@ -797,18 +715,21 @@ Name: <b>{{.Name}}</b><br>
|
||||
Folder: <b>{{.Folder}}</b><br>
|
||||
From: <b>{{template "sessionlink" .Session.ID}}</b><br>
|
||||
<h2>Environment</h2>
|
||||
<ul>{{range .Env}}<li>{{.}}</li>{{end}}</ul>
|
||||
<ul>{{range .Options.Env}}<li>{{.}}</li>{{end}}</ul>
|
||||
{{end}}
|
||||
`))
|
||||
|
||||
var fileTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
|
||||
{{define "title"}}File {{.Hash}}{{end}}
|
||||
{{define "title"}}Overlay {{.Identity.Identifier}}{{end}}
|
||||
{{define "body"}}
|
||||
From: <b>{{template "sessionlink" .Session.ID}}</b><br>
|
||||
URI: <b>{{.URI}}</b><br>
|
||||
Hash: <b>{{.Hash}}</b><br>
|
||||
Error: <b>{{.Error}}</b><br>
|
||||
{{with .Identity}}
|
||||
From: <b>{{template "sessionlink" .SessionID}}</b><br>
|
||||
URI: <b>{{.URI}}</b><br>
|
||||
Identifier: <b>{{.Identifier}}</b><br>
|
||||
Version: <b>{{.Version}}</b><br>
|
||||
Kind: <b>{{.Kind}}</b><br>
|
||||
{{end}}
|
||||
<h3>Contents</h3>
|
||||
<pre>{{.Data}}</pre>
|
||||
<pre>{{fcontent .Data}}</pre>
|
||||
{{end}}
|
||||
`))
|
||||
|
@ -1,54 +0,0 @@
|
||||
// Copyright 2020 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 debug
|
||||
|
||||
import "testing"
|
||||
|
||||
type fakeCache struct {
|
||||
Cache
|
||||
|
||||
id string
|
||||
}
|
||||
|
||||
func (c fakeCache) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
c1 := fakeCache{id: "1"}
|
||||
c2 := fakeCache{id: "2"}
|
||||
c3 := fakeCache{id: "3"}
|
||||
|
||||
var s State
|
||||
s.AddCache(c1)
|
||||
s.AddCache(c2)
|
||||
s.AddCache(c3)
|
||||
|
||||
compareCaches := func(desc string, want []fakeCache) {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
caches := s.Caches()
|
||||
if gotLen, wantLen := len(caches), len(want); gotLen != wantLen {
|
||||
t.Fatalf("len(Caches) = %d, want %d", gotLen, wantLen)
|
||||
}
|
||||
for i, got := range caches {
|
||||
if got != want[i] {
|
||||
t.Errorf("Caches[%d] = %v, want %v", i, got, want[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
compareCaches("initial load", []fakeCache{c1, c2, c3})
|
||||
s.DropCache(c2)
|
||||
compareCaches("dropped cache 2", []fakeCache{c1, c3})
|
||||
s.DropCache(c2)
|
||||
compareCaches("duplicate drop", []fakeCache{c1, c3})
|
||||
s.AddCache(c2)
|
||||
compareCaches("re-add cache 2", []fakeCache{c1, c3, c2})
|
||||
s.DropCache(c1)
|
||||
s.DropCache(c2)
|
||||
s.DropCache(c3)
|
||||
compareCaches("drop all", []fakeCache{})
|
||||
}
|
@ -95,8 +95,8 @@ type debugClient struct {
|
||||
serverID string
|
||||
}
|
||||
|
||||
func (c debugClient) Session() debug.Session {
|
||||
return cache.DebugSession{Session: c.session}
|
||||
func (c debugClient) Session() *cache.Session {
|
||||
return c.session
|
||||
}
|
||||
|
||||
func (c debugClient) ServerID() string {
|
||||
@ -462,7 +462,7 @@ func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler)
|
||||
client.goplsPath = req.GoplsPath
|
||||
resp := handshakeResponse{
|
||||
ClientID: client.id,
|
||||
SessionID: cache.DebugSession{Session: client.session}.ID(),
|
||||
SessionID: client.session.ID(),
|
||||
GoplsPath: goplsPath,
|
||||
}
|
||||
if di := debug.GetInstance(ctx); di != nil {
|
||||
|
@ -189,8 +189,8 @@ type Session interface {
|
||||
// It returns the resulting snapshots, a guaranteed one per view.
|
||||
DidModifyFiles(ctx context.Context, changes []FileModification) ([]Snapshot, error)
|
||||
|
||||
// UnsavedFiles returns a slice of open but unsaved files in the session.
|
||||
UnsavedFiles() []span.URI
|
||||
// Overlays returns a slice of file overlays for the session.
|
||||
Overlays() []Overlay
|
||||
|
||||
// Options returns a copy of the SessionOptions for this session.
|
||||
Options() Options
|
||||
@ -199,6 +199,21 @@ type Session interface {
|
||||
SetOptions(Options)
|
||||
}
|
||||
|
||||
// Overlay is the type for a file held in memory on a session.
|
||||
type Overlay interface {
|
||||
// Session returns the session this overlay belongs to.
|
||||
Session() Session
|
||||
|
||||
// Identity returns the FileIdentity for the overlay.
|
||||
Identity() FileIdentity
|
||||
|
||||
// Saved returns whether this overlay has been saved to disk.
|
||||
Saved() bool
|
||||
|
||||
// Data is the contents of the overlay held in memory.
|
||||
Data() []byte
|
||||
}
|
||||
|
||||
// FileModification represents a modification to a file.
|
||||
type FileModification struct {
|
||||
URI span.URI
|
||||
|
Loading…
Reference in New Issue
Block a user