1
0
mirror of https://github.com/golang/go synced 2024-10-01 03:38:32 -06:00
go/internal/lsp/text_synchronization.go
Ian Cottrell b9584148ef internal/lsp: add structured layers to the cache
This is primarily to separate the levels because they have different cache
lifetimes and sharability.
This will allow us to share results between views and even between servers.

Change-Id: I280ca19d17a6ea8a15e48637d4445e2b6cf04769
Reviewed-on: https://go-review.googlesource.com/c/tools/+/177518
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-05-16 21:30:38 +00:00

103 lines
3.2 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 (
"bytes"
"context"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
)
func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), []byte(params.TextDocument.Text))
}
func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
if len(params.ContentChanges) < 1 {
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
}
var text string
switch s.textDocumentSyncKind {
case protocol.Incremental:
var err error
text, err = s.applyChanges(ctx, params)
if err != nil {
return err
}
case protocol.Full:
// We expect the full content of file, i.e. a single change with no range.
change := params.ContentChanges[0]
if change.RangeLength != 0 {
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "unexpected change range provided")
}
text = change.Text
}
return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), []byte(text))
}
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []byte) error {
view := s.session.ViewOf(uri)
if err := view.SetContent(ctx, uri, content); err != nil {
return err
}
go func() {
ctx := view.BackgroundContext()
s.Diagnostics(ctx, view, uri)
}()
return nil
}
func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {
if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil {
// If range is empty, we expect the full content of file, i.e. a single change with no range.
change := params.ContentChanges[0]
if change.RangeLength != 0 {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "unexpected change range provided")
}
return change.Text, nil
}
uri := span.NewURI(params.TextDocument.URI)
view := s.session.ViewOf(uri)
file, m, err := getSourceFile(ctx, view, uri)
if err != nil {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found")
}
content := file.GetContent(ctx)
for _, change := range params.ContentChanges {
spn, err := m.RangeSpan(*change.Range)
if err != nil {
return "", err
}
if !spn.HasOffset() {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "invalid range for content change")
}
start, end := spn.Start().Offset(), spn.End().Offset()
if end < start {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "invalid range for content change")
}
var buf bytes.Buffer
buf.Write(content[:start])
buf.WriteString(change.Text)
buf.Write(content[end:])
content = buf.Bytes()
}
return string(content), nil
}
func (s *Server) didSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error {
return nil // ignore
}
func (s *Server) didClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
uri := span.NewURI(params.TextDocument.URI)
view := s.session.ViewOf(uri)
return view.SetContent(ctx, uri, nil)
}