internal/lsp: factor out column mapper construction from content

This eliminates some duplication, and lays the groundwork for removing
the use of token.File within ColumnMapper.

Change-Id: I54fe570bfc4f7bca0da643b8727e890dc6343208
Reviewed-on: https://go-review.googlesource.com/c/tools/+/406135
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index 37fc6e6..03fdcbf 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -55,11 +55,7 @@
 		if err != nil {
 			return &parseModData{err: err}
 		}
-		m := &protocol.ColumnMapper{
-			URI:     modFH.URI(),
-			TokFile: span.NewTokenFile(modFH.URI().Filename(), contents),
-			Content: contents,
-		}
+		m := protocol.NewColumnMapper(modFH.URI(), contents)
 		file, parseErr := modfile.Parse(modFH.URI().Filename(), contents, nil)
 		// Attempt to convert the error to a standardized parse error.
 		var parseErrors []*source.Diagnostic
@@ -133,11 +129,7 @@
 		if err != nil {
 			return &parseWorkData{err: err}
 		}
-		m := &protocol.ColumnMapper{
-			URI:     modFH.URI(),
-			TokFile: span.NewTokenFile(modFH.URI().Filename(), contents),
-			Content: contents,
-		}
+		m := protocol.NewColumnMapper(modFH.URI(), contents)
 		file, parseErr := modfile.ParseWork(modFH.URI().Filename(), contents, nil)
 		// Attempt to convert the error to a standardized parse error.
 		var parseErrors []*source.Diagnostic
diff --git a/internal/lsp/cmd/semantictokens.go b/internal/lsp/cmd/semantictokens.go
index e4b1306..7dbb7f9 100644
--- a/internal/lsp/cmd/semantictokens.go
+++ b/internal/lsp/cmd/semantictokens.go
@@ -117,12 +117,7 @@
 		// can't happen; just parsed this file
 		return fmt.Errorf("can't find %s in fset", args[0])
 	}
-	tf := span.NewTokenFile(args[0], buf)
-	colmap = &protocol.ColumnMapper{
-		URI:     span.URI(args[0]),
-		Content: buf,
-		TokFile: tf,
-	}
+	colmap = protocol.NewColumnMapper(uri, buf)
 	err = decorate(file.uri.Filename(), resp.Data)
 	if err != nil {
 		return err
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index 6e21c1b..9bc979a 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -579,11 +579,7 @@
 		return nil, err
 	}
 
-	m := &protocol.ColumnMapper{
-		URI:     fh.URI(),
-		TokFile: span.NewTokenFile(fh.URI().Filename(), oldContent),
-		Content: oldContent,
-	}
+	m := protocol.NewColumnMapper(fh.URI(), oldContent)
 	diff, err := snapshot.View().Options().ComputeEdits(uri, string(oldContent), string(newContent))
 	if err != nil {
 		return nil, err
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index c7cd530..ee364b8 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -1044,12 +1044,7 @@
 		// If we have already edited this file, we use the edited version (rather than the
 		// file in its original state) so that we preserve our initial changes.
 		if content, ok := res[uri]; ok {
-			m = &protocol.ColumnMapper{
-				URI: uri,
-				TokFile: span.NewTokenFile(
-					uri.Filename(), []byte(content)),
-				Content: []byte(content),
-			}
+			m = protocol.NewColumnMapper(uri, []byte(content))
 		} else {
 			var err error
 			if m, err = r.data.Mapper(uri); err != nil {
@@ -1284,12 +1279,7 @@
 		f := fset.AddFile(fname, -1, len(test.text))
 		f.SetLinesForContent([]byte(test.text))
 		uri := span.URIFromPath(fname)
-		tf := span.NewTokenFile(fname, []byte(test.text))
-		mapper := &protocol.ColumnMapper{
-			URI:     uri,
-			TokFile: tf,
-			Content: []byte(test.text),
-		}
+		mapper := protocol.NewColumnMapper(uri, []byte(test.text))
 		got, err := mapper.Point(test.pos)
 		if err != nil && test.want != -1 {
 			t.Errorf("unexpected error: %v", err)
diff --git a/internal/lsp/protocol/span.go b/internal/lsp/protocol/span.go
index 4a364d7..dac53cb 100644
--- a/internal/lsp/protocol/span.go
+++ b/internal/lsp/protocol/span.go
@@ -13,12 +13,24 @@
 	"golang.org/x/tools/internal/span"
 )
 
+// A ColumnMapper maps between UTF-8 oriented positions (e.g. token.Pos,
+// span.Span) and the UTF-16 oriented positions used by the LSP.
 type ColumnMapper struct {
 	URI     span.URI
 	TokFile *token.File
 	Content []byte
 }
 
+// NewColumnMapper creates a new column mapper for the given uri and content.
+func NewColumnMapper(uri span.URI, content []byte) *ColumnMapper {
+	tf := span.NewTokenFile(uri.Filename(), content)
+	return &ColumnMapper{
+		URI:     uri,
+		TokFile: tf,
+		Content: content,
+	}
+}
+
 func URIFromSpanURI(uri span.URI) DocumentURI {
 	return DocumentURI(uri)
 }
diff --git a/internal/lsp/source/rename.go b/internal/lsp/source/rename.go
index 1cfba80..6312bcb 100644
--- a/internal/lsp/source/rename.go
+++ b/internal/lsp/source/rename.go
@@ -171,12 +171,7 @@
 		if err != nil {
 			return nil, err
 		}
-		tf := span.NewTokenFile(uri.Filename(), data)
-		m := &protocol.ColumnMapper{
-			URI:     uri,
-			TokFile: tf,
-			Content: data,
-		}
+		m := protocol.NewColumnMapper(uri, data)
 		// Sort the edits first.
 		diff.SortTextEdits(edits)
 		protocolEdits, err := ToProtocolEdits(m, edits)
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 9a8b76d..8265cf2 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -997,12 +997,7 @@
 		if err != nil {
 			return nil, err
 		}
-		tf := span.NewTokenFile(uri.Filename(), content)
-		data.mappers[uri] = &protocol.ColumnMapper{
-			URI:     uri,
-			TokFile: tf,
-			Content: content,
-		}
+		data.mappers[uri] = protocol.NewColumnMapper(uri, content)
 	}
 	return data.mappers[uri], nil
 }
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index 157ae23..ff153d7 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -340,12 +340,7 @@
 	}
 	for _, change := range changes {
 		// Make sure to update column mapper along with the content.
-		tf := span.NewTokenFile(uri.Filename(), content)
-		m := &protocol.ColumnMapper{
-			URI:     uri,
-			TokFile: tf,
-			Content: content,
-		}
+		m := protocol.NewColumnMapper(uri, content)
 		if change.Range == nil {
 			return nil, fmt.Errorf("%w: unexpected nil range for change", jsonrpc2.ErrInternal)
 		}