internal/lsp: allow the diff alorithm to be specified per view

Change-Id: Ib9d44d2012253189f87bc3b15a88b400f76ae955
Reviewed-on: https://go-review.googlesource.com/c/tools/+/198378
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/diff/diff_test.go b/internal/lsp/diff/diff_test.go
index be7965b..1b532f5 100644
--- a/internal/lsp/diff/diff_test.go
+++ b/internal/lsp/diff/diff_test.go
@@ -7,9 +7,10 @@
 import (
 	"testing"
 
+	"golang.org/x/tools/internal/lsp/diff"
 	"golang.org/x/tools/internal/lsp/diff/difftest"
 )
 
 func TestDiff(t *testing.T) {
-	difftest.DiffTest(t)
+	difftest.DiffTest(t, diff.MyersComputeEdits)
 }
diff --git a/internal/lsp/diff/difftest/difftest.go b/internal/lsp/diff/difftest/difftest.go
index f7db02d..338758a 100644
--- a/internal/lsp/diff/difftest/difftest.go
+++ b/internal/lsp/diff/difftest/difftest.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/internal/span"
 )
 
-func DiffTest(t *testing.T) {
+func DiffTest(t *testing.T, compute diff.ComputeEdits) {
 	t.Helper()
 	for _, test := range []struct{ name, in, out, unified string }{{
 		name: "empty",
@@ -41,7 +41,7 @@
 		in:   "one\nthree\n",
 		out:  "one\ntwo\nthree\n",
 	}} {
-		edits := diff.ComputeEdits(span.FileURI("/"+test.name), test.in, test.out)
+		edits := compute(span.FileURI("/"+test.name), test.in, test.out)
 		got := diff.ApplyEdits(test.in, edits)
 		if got != test.out {
 			t.Logf("test %v had diff:%v\n", test.name, diff.ToUnified(test.name+".orig", test.name, test.in, edits))
diff --git a/internal/lsp/diff/hooks.go b/internal/lsp/diff/hooks.go
index 8a83057..033789b 100644
--- a/internal/lsp/diff/hooks.go
+++ b/internal/lsp/diff/hooks.go
@@ -18,9 +18,10 @@
 	NewText string
 }
 
+type ComputeEdits func(uri span.URI, before, after string) []TextEdit
+
 var (
-	ComputeEdits func(uri span.URI, before, after string) []TextEdit
-	ToUnified    func(from, to string, before string, edits []TextEdit) string
+	ToUnified func(from, to string, before string, edits []TextEdit) string
 )
 
 func SortTextEdits(d []TextEdit) {
diff --git a/internal/lsp/diff/myers.go b/internal/lsp/diff/myers.go
index a05bc42..d0d9ef3 100644
--- a/internal/lsp/diff/myers.go
+++ b/internal/lsp/diff/myers.go
@@ -13,11 +13,10 @@
 )
 
 func init() {
-	ComputeEdits = myersComputeEdits
 	ToUnified = myersToUnified
 }
 
-func myersComputeEdits(uri span.URI, before, after string) []TextEdit {
+func MyersComputeEdits(uri span.URI, before, after string) []TextEdit {
 	u := myers.SplitLines(before)
 	f := myers.SplitLines(after)
 	return myersDiffToEdits(uri, myers.Operations(u, f))
diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go
index 7f42dae..15185db 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -55,7 +55,7 @@
 		if err != nil {
 			return nil, err
 		}
-		return computeTextEdits(ctx, ph.File(), m, string(formatted))
+		return computeTextEdits(ctx, view, ph.File(), m, string(formatted))
 	}
 
 	fset := view.Session().Cache().FileSet()
@@ -68,7 +68,7 @@
 	if err := format.Node(buf, fset, file); err != nil {
 		return nil, err
 	}
-	return computeTextEdits(ctx, ph.File(), m, buf.String())
+	return computeTextEdits(ctx, view, ph.File(), m, buf.String())
 }
 
 func formatSource(ctx context.Context, s Snapshot, f File) ([]byte, error) {
@@ -134,7 +134,7 @@
 	if err != nil {
 		return nil, err
 	}
-	return computeTextEdits(ctx, ph.File(), m, string(formatted))
+	return computeTextEdits(ctx, view, ph.File(), m, string(formatted))
 }
 
 type ImportFix struct {
@@ -198,7 +198,7 @@
 		if err != nil {
 			return err
 		}
-		edits, err = computeTextEdits(ctx, ph.File(), m, string(formatted))
+		edits, err = computeTextEdits(ctx, view, ph.File(), m, string(formatted))
 		if err != nil {
 			return err
 		}
@@ -209,7 +209,7 @@
 			if err != nil {
 				return err
 			}
-			edits, err := computeTextEdits(ctx, ph.File(), m, string(formatted))
+			edits, err := computeTextEdits(ctx, view, ph.File(), m, string(formatted))
 			if err != nil {
 				return err
 			}
@@ -277,7 +277,7 @@
 	return false
 }
 
-func computeTextEdits(ctx context.Context, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
+func computeTextEdits(ctx context.Context, view View, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
 	ctx, done := trace.StartSpan(ctx, "source.computeTextEdits")
 	defer done()
 
@@ -285,7 +285,7 @@
 	if err != nil {
 		return nil, err
 	}
-	edits := diff.ComputeEdits(fh.Identity().URI, string(data), formatted)
+	edits := view.Options().ComputeEdits(fh.Identity().URI, string(data), formatted)
 	return ToProtocolEdits(m, edits)
 }
 
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index d3c95cd..7e3df1d 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -9,6 +9,7 @@
 	"os"
 	"time"
 
+	"golang.org/x/tools/internal/lsp/diff"
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/telemetry/tag"
 	errors "golang.org/x/xerrors"
@@ -40,6 +41,7 @@
 			FuzzyMatching: true,
 			Budget:        100 * time.Millisecond,
 		},
+		ComputeEdits: diff.MyersComputeEdits,
 	}
 )
 
@@ -71,6 +73,8 @@
 	TextDocumentSyncKind protocol.TextDocumentSyncKind
 
 	Completion CompletionOptions
+
+	ComputeEdits diff.ComputeEdits
 }
 
 type CompletionOptions struct {