internal/lsp: enable incrementalSync by default

This change also leaves in an opt-out setting (noIncrementalSync), just
in case we need to disable it at some point.

Change-Id: I3575efe942294b764c35d9259ce75d124b590e98
Reviewed-on: https://go-review.googlesource.com/c/tools/+/182468
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-06-17 13:07:16 -04:00
parent cd2ed857af
commit 7ef8a99cf3
3 changed files with 39 additions and 32 deletions

View File

@ -200,15 +200,14 @@ func shortestEditSequence(a, b []string) ([][]int, int) {
// Return if we've exceeded the maximum values. // Return if we've exceeded the maximum values.
if x == M && y == N { if x == M && y == N {
// Save the state of the array, and exit function // Makes sure to save the state of the array before returning.
copy(copyV, V) copy(copyV, V)
trace[d] = copyV trace[d] = copyV
return trace, offset return trace, offset
} }
} }
// Save the state of the array, and continue loop // Save the state of the array.
copy(copyV, V) copy(copyV, V)
trace[d] = copyV trace[d] = copyV
} }

View File

@ -26,11 +26,11 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara
} }
s.isInitialized = true // mark server as initialized now s.isInitialized = true // mark server as initialized now
// TODO(iancottrell): Change this default to protocol.Incremental and remove the option // TODO: Remove the option once we are certain there are no issues here.
s.textDocumentSyncKind = protocol.Full
if opts, ok := params.InitializationOptions.(map[string]interface{}); ok {
if opt, ok := opts["incrementalSync"].(bool); ok && opt {
s.textDocumentSyncKind = protocol.Incremental s.textDocumentSyncKind = protocol.Incremental
if opts, ok := params.InitializationOptions.(map[string]interface{}); ok {
if opt, ok := opts["noIncrementalSync"].(bool); ok && opt {
s.textDocumentSyncKind = protocol.Full
} }
} }

View File

@ -7,6 +7,7 @@ package lsp
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
@ -25,23 +26,29 @@ func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDo
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided") return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
} }
var text string uri := span.NewURI(params.TextDocument.URI)
// Check if the client sent the full content of the file.
// We accept a full content change even if the server expected incremental changes.
text, isFullChange := fullChange(params.ContentChanges)
// We only accept an incremental change if the server expected it.
if !isFullChange {
switch s.textDocumentSyncKind { switch s.textDocumentSyncKind {
case protocol.Full:
return fmt.Errorf("expected a full content change, received incremental changes for %s", uri)
case protocol.Incremental: case protocol.Incremental:
// Determine the new file content.
var err error var err error
text, err = s.applyChanges(ctx, params) text, err = s.applyChanges(ctx, uri, params.ContentChanges)
if err != nil { if err != nil {
return err 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))
// Cache the new file content and send fresh diagnostics.
return s.cacheAndDiagnose(ctx, uri, []byte(text))
} }
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []byte) error { func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []byte) error {
@ -56,23 +63,24 @@ func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []b
return nil return nil
} }
func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) { func fullChange(changes []protocol.TextDocumentContentChangeEvent) (string, bool) {
if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil { if len(changes) > 1 {
// If range is empty, we expect the full content of file, i.e. a single change with no range. return "", false
change := params.ContentChanges[0]
if change.RangeLength != 0 {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "unexpected change range provided")
} }
return change.Text, nil // The length of the changes must be 1 at this point.
if changes[0].Range == nil && changes[0].RangeLength == 0 {
return changes[0].Text, true
}
return "", false
} }
uri := span.NewURI(params.TextDocument.URI) func (s *Server) applyChanges(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) (string, error) {
content, _, err := s.session.GetFile(uri).Read(ctx) content, _, err := s.session.GetFile(uri).Read(ctx)
if err != nil { if err != nil {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found") return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found")
} }
fset := s.session.Cache().FileSet() fset := s.session.Cache().FileSet()
for _, change := range params.ContentChanges { for _, change := range changes {
// Update column mapper along with the content. // Update column mapper along with the content.
m := protocol.NewColumnMapper(uri, uri.Filename(), fset, nil, content) m := protocol.NewColumnMapper(uri, uri.Filename(), fset, nil, content)