mirror of
https://github.com/golang/go
synced 2024-11-19 01:14:39 -07:00
20f46356b3
In the ideal future, users will have one or more gopls instances, each serving potentially many LSP clients. In order to have any hope of navigating this web, clients and servers must know about eachother. To allow for such an exchange of information, this CL adds an additional handler layer to the serving configured in the lsprpc package. For now, forwarders just use this layer to execute a handshake with the LSP server, communicating the location of their logs and debug addresses. Updates golang/go#34111 Change-Id: Ic7432062c01a8bbd52fb4a058a95bbf5dc26baa3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/220081 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
128 lines
2.8 KiB
Go
128 lines
2.8 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 cache
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha1"
|
|
"fmt"
|
|
"go/token"
|
|
"reflect"
|
|
"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"
|
|
)
|
|
|
|
func New(options func(*source.Options), debugState *debug.State) *Cache {
|
|
if debugState == nil {
|
|
debugState = &debug.State{}
|
|
}
|
|
index := atomic.AddInt64(&cacheIndex, 1)
|
|
c := &Cache{
|
|
fs: &nativeFileSystem{},
|
|
id: strconv.FormatInt(index, 10),
|
|
fset: token.NewFileSet(),
|
|
options: options,
|
|
debug: debugState,
|
|
}
|
|
debugState.AddCache(debugCache{c})
|
|
return c
|
|
}
|
|
|
|
type Cache struct {
|
|
fs source.FileSystem
|
|
id string
|
|
fset *token.FileSet
|
|
options func(*source.Options)
|
|
debug *debug.State
|
|
|
|
store memoize.Store
|
|
}
|
|
|
|
type fileKey struct {
|
|
identity source.FileIdentity
|
|
}
|
|
|
|
type fileHandle struct {
|
|
cache *Cache
|
|
underlying source.FileHandle
|
|
handle *memoize.Handle
|
|
}
|
|
|
|
type fileData struct {
|
|
memoize.NoCopy
|
|
bytes []byte
|
|
hash string
|
|
err error
|
|
}
|
|
|
|
func (c *Cache) GetFile(uri span.URI) source.FileHandle {
|
|
underlying := c.fs.GetFile(uri)
|
|
key := fileKey{
|
|
identity: underlying.Identity(),
|
|
}
|
|
h := c.store.Bind(key, func(ctx context.Context) interface{} {
|
|
data := &fileData{}
|
|
data.bytes, data.hash, data.err = underlying.Read(ctx)
|
|
return data
|
|
})
|
|
return &fileHandle{
|
|
cache: c,
|
|
underlying: underlying,
|
|
handle: h,
|
|
}
|
|
}
|
|
|
|
func (c *Cache) NewSession() *Session {
|
|
index := atomic.AddInt64(&sessionIndex, 1)
|
|
s := &Session{
|
|
cache: c,
|
|
id: strconv.FormatInt(index, 10),
|
|
options: source.DefaultOptions(),
|
|
overlays: make(map[span.URI]*overlay),
|
|
}
|
|
c.debug.AddSession(DebugSession{s})
|
|
return s
|
|
}
|
|
|
|
func (c *Cache) FileSet() *token.FileSet {
|
|
return c.fset
|
|
}
|
|
|
|
func (h *fileHandle) FileSystem() source.FileSystem {
|
|
return h.cache
|
|
}
|
|
|
|
func (h *fileHandle) Identity() source.FileIdentity {
|
|
return h.underlying.Identity()
|
|
}
|
|
|
|
func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) {
|
|
v := h.handle.Get(ctx)
|
|
if v == nil {
|
|
return nil, "", ctx.Err()
|
|
}
|
|
data := v.(*fileData)
|
|
return data.bytes, data.hash, data.err
|
|
}
|
|
|
|
func hashContents(contents []byte) string {
|
|
// TODO: consider whether sha1 is the best choice here
|
|
// This hash is used for internal identity detection only
|
|
return fmt.Sprintf("%x", sha1.Sum(contents))
|
|
}
|
|
|
|
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() }
|