Skip to content

Commit

Permalink
gopls/internal/span: identify span.URI and protocol.DocumentURI
Browse files Browse the repository at this point in the history
Before, the difference between these two types was that DocumentURI
may contain various VS Code encoding mistakes (e.g. %XX, file://)
whereas span.URI was "clean". However, there was little clarity
as to when and how often the cleaning process was applied to
DocumentURIs received off the wire.

This change makes span.URI an alias for DocumentURI;
a followup will inline it away. The clean-up is now done
during JSON decoding, ensuring that all DocumentURIs areu
cleaned once and only once.

It is not necessary to "dirty" URIs before sending them
back in responses to the client, even if they arrived dirty.

Fixes golang/go#64147

Change-Id: Iea262b1b8867c26df541e43f77c2c5ba4f7e81c0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/542616
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
  • Loading branch information
adonovan committed Nov 17, 2023
1 parent 65d7356 commit 979df5b
Show file tree
Hide file tree
Showing 19 changed files with 335 additions and 234 deletions.
27 changes: 11 additions & 16 deletions gopls/internal/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/span"
"golang.org/x/tools/internal/constraints"
"golang.org/x/tools/internal/diff"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/tool"
Expand Down Expand Up @@ -453,15 +454,6 @@ func newConnection(server protocol.Server, client *cmdClient) *connection {
}
}

// fileURI converts a DocumentURI to a file:// span.URI, panicking if it's not a file.
func fileURI(uri protocol.DocumentURI) span.URI {
sURI := uri.SpanURI()
if !sURI.IsFile() {
panic(fmt.Sprintf("%q is not a file URI", uri))
}
return sURI
}

func (c *cmdClient) CodeLensRefresh(context.Context) error { return nil }

func (c *cmdClient) LogTrace(context.Context, *protocol.LogTraceParams) error { return nil }
Expand Down Expand Up @@ -556,23 +548,22 @@ func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEdi
// files, honoring the preferred edit mode specified by cli.app.editMode.
// (Used by rename and by ApplyEdit downcalls.)
func (cli *cmdClient) applyWorkspaceEdit(edit *protocol.WorkspaceEdit) error {
var orderedURIs []string
var orderedURIs []span.URI
edits := map[span.URI][]protocol.TextEdit{}
for _, c := range edit.DocumentChanges {
if c.TextDocumentEdit != nil {
uri := fileURI(c.TextDocumentEdit.TextDocument.URI)
uri := c.TextDocumentEdit.TextDocument.URI
edits[uri] = append(edits[uri], c.TextDocumentEdit.Edits...)
orderedURIs = append(orderedURIs, string(uri))
orderedURIs = append(orderedURIs, uri)
}
if c.RenameFile != nil {
return fmt.Errorf("client does not support file renaming (%s -> %s)",
c.RenameFile.OldURI,
c.RenameFile.NewURI)
}
}
sort.Strings(orderedURIs)
for _, u := range orderedURIs {
uri := span.URIFromURI(u)
sortSlice(orderedURIs)
for _, uri := range orderedURIs {
f := cli.openFile(uri)
if f.err != nil {
return f.err
Expand All @@ -584,6 +575,10 @@ func (cli *cmdClient) applyWorkspaceEdit(edit *protocol.WorkspaceEdit) error {
return nil
}

func sortSlice[T constraints.Ordered](slice []T) {
sort.Slice(slice, func(i, j int) bool { return slice[i] < slice[j] })
}

// applyTextEdits applies a list of edits to the mapper file content,
// using the preferred edit mode. It is a no-op if there are no edits.
func applyTextEdits(mapper *protocol.Mapper, edits []protocol.TextEdit, flags *EditFlags) error {
Expand Down Expand Up @@ -641,7 +636,7 @@ func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishD
c.filesMu.Lock()
defer c.filesMu.Unlock()

file := c.getFile(fileURI(p.URI))
file := c.getFile(p.URI)
file.diagnostics = append(file.diagnostics, p.Diagnostics...)

// Perform a crude in-place deduplication.
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (d *definition) Run(ctx context.Context, args ...string) error {
if len(locs) == 0 {
return fmt.Errorf("%v: not an identifier", from)
}
file, err = conn.openFile(ctx, fileURI(locs[0].URI))
file, err = conn.openFile(ctx, locs[0].URI)
if err != nil {
return fmt.Errorf("%v: %v", from, err)
}
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (i *implementation) Run(ctx context.Context, args ...string) error {

var spans []string
for _, impl := range implementations {
f, err := conn.openFile(ctx, fileURI(impl.URI))
f, err := conn.openFile(ctx, impl.URI)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (t *imports) Run(ctx context.Context, args ...string) error {
}
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
}
}
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (r *references) Run(ctx context.Context, args ...string) error {
}
var spans []string
for _, l := range locations {
f, err := conn.openFile(ctx, fileURI(l.URI))
f, err := conn.openFile(ctx, l.URI)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions gopls/internal/cmd/suggested_fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
if !from.HasPosition() {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
}
}
Expand All @@ -168,7 +168,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
if diag.Range.Start == rng.Start {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
}
}
Expand All @@ -181,7 +181,7 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
if len(a.Diagnostics) == 0 {
for _, c := range a.Edit.DocumentChanges {
if c.TextDocumentEdit != nil {
if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri {
if c.TextDocumentEdit.TextDocument.URI == uri {
edits = append(edits, c.TextDocumentEdit.Edits...)
}
}
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/cmd/workspace_symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (r *workspaceSymbol) Run(ctx context.Context, args ...string) error {
return err
}
for _, s := range symbols {
f, err := conn.openFile(ctx, fileURI(s.Location.URI))
f, err := conn.openFile(ctx, s.Location.URI)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions gopls/internal/lsp/general.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (s *server) initialize(ctx context.Context, params *protocol.ParamInitializ
}
}
for _, folder := range folders {
uri := span.URIFromURI(folder.URI)
uri := protocol.URIFromURI(folder.URI)
if !uri.IsFile() {
continue
}
Expand Down Expand Up @@ -305,7 +305,7 @@ func (s *server) addFolders(ctx context.Context, folders []protocol.WorkspaceFol
// Only one view gets to have a workspace.
var nsnapshots sync.WaitGroup // number of unfinished snapshot initializations
for _, folder := range folders {
uri := span.URIFromURI(folder.URI)
uri := protocol.URIFromURI(folder.URI)
// Ignore non-file URIs.
if !uri.IsFile() {
continue
Expand Down
3 changes: 3 additions & 0 deletions gopls/internal/lsp/protocol/generate/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ func genStructs(model Model) {
// file:///c:/project/readme.md
// file:///C%3A/project/readme.md
//
// This is done during JSON unmarshalling;
// see [DocumentURI.UnmarshalText] for details.
//
type DocumentURI string
`
types["URI"] = `// A URI is an arbitrary URL (e.g. https), not necessarily a file.
Expand Down
7 changes: 3 additions & 4 deletions gopls/internal/lsp/protocol/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import (
"unicode/utf8"

"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/gopls/internal/span"
)

// A Mapper wraps the content of a file and provides mapping
Expand All @@ -94,7 +93,7 @@ import (
//
// See overview comments at top of this file.
type Mapper struct {
URI span.URI
URI DocumentURI
Content []byte

// Line-number information is requested only for a tiny
Expand All @@ -109,7 +108,7 @@ type Mapper struct {
}

// NewMapper creates a new mapper for the given URI and content.
func NewMapper(uri span.URI, content []byte) *Mapper {
func NewMapper(uri DocumentURI, content []byte) *Mapper {
return &Mapper{URI: uri, Content: content}
}

Expand Down Expand Up @@ -392,7 +391,7 @@ func (mr MappedRange) Offsets() (start, end int) { return mr.start, mr.end }
// -- convenience functions --

// URI returns the URI of the range's file.
func (mr MappedRange) URI() span.URI {
func (mr MappedRange) URI() DocumentURI {
return mr.Mapper.URI
}

Expand Down
3 changes: 1 addition & 2 deletions gopls/internal/lsp/protocol/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"testing"

"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/span"
)

// This file tests Mapper's logic for converting between offsets,
Expand Down Expand Up @@ -437,7 +436,7 @@ func TestBytesOffset(t *testing.T) {

for i, test := range tests {
fname := fmt.Sprintf("test %d", i)
uri := span.URIFromPath(fname)
uri := protocol.URIFromPath(fname)
mapper := protocol.NewMapper(uri, []byte(test.text))
got, err := mapper.PositionOffset(test.pos)
if err != nil && test.want != -1 {
Expand Down
14 changes: 0 additions & 14 deletions gopls/internal/lsp/protocol/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,8 @@ package protocol
import (
"fmt"
"unicode/utf8"

"golang.org/x/tools/gopls/internal/span"
)

func URIFromSpanURI(uri span.URI) DocumentURI {
return DocumentURI(uri) // simple conversion
}

func URIFromPath(path string) DocumentURI {
return URIFromSpanURI(span.URIFromPath(path)) // normalizing conversion
}

func (u DocumentURI) SpanURI() span.URI {
return span.URIFromURI(string(u)) // normalizing conversion
}

// CompareLocation defines a three-valued comparison over locations,
// lexicographically ordered by (URI, Range).
func CompareLocation(x, y Location) int {
Expand Down
3 changes: 3 additions & 0 deletions gopls/internal/lsp/protocol/tsprotocol.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 979df5b

Please sign in to comment.