1
0
mirror of https://github.com/golang/go synced 2024-10-01 11:18:32 -06:00
go/internal/lsp/cache/cache.go
Rob Findley 20f46356b3 internal/lsp/lsprpc: add a handshake between forwarder and remote
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>
2020-02-24 22:51:04 +00:00

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() }