internal/lsp/protocol: add a compare function for span.URI

Add an additional check to handle URI comparisons. This fixes Hover on
Windows.

Change-Id: Ibfc816f1ec374144377a873c5b52867fafa3d7e8
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172659
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/lsp/protocol/span.go b/internal/lsp/protocol/span.go
index d4d841d..9045b2c 100644
--- a/internal/lsp/protocol/span.go
+++ b/internal/lsp/protocol/span.go
@@ -40,7 +40,7 @@
 }
 
 func (m *ColumnMapper) Range(s span.Span) (Range, error) {
-	if m.URI != s.URI() {
+	if span.CompareURI(m.URI, s.URI()) != 0 {
 		return Range{}, fmt.Errorf("column mapper is for file %q instead of %q", m.URI, s.URI())
 	}
 	s, err := s.WithAll(m.Converter)
diff --git a/internal/span/span.go b/internal/span/span.go
index 0ac35a2..b5733252 100644
--- a/internal/span/span.go
+++ b/internal/span/span.go
@@ -7,7 +7,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"strings"
 )
 
 // Span represents a source code range in standardized form.
@@ -58,7 +57,7 @@
 }
 
 func Compare(a, b Span) int {
-	if r := strings.Compare(string(a.v.URI), string(b.v.URI)); r != 0 {
+	if r := CompareURI(a.URI(), b.URI()); r != 0 {
 		return r
 	}
 	if r := comparePoint(a.v.Start, b.v.Start); r != 0 {
diff --git a/internal/span/uri.go b/internal/span/uri.go
index 33755cf..63d7ba8 100644
--- a/internal/span/uri.go
+++ b/internal/span/uri.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 	"net/url"
+	"os"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -54,6 +55,29 @@
 	return FileURI(s)
 }
 
+func CompareURI(a, b URI) int {
+	if a == b {
+		return 0
+	}
+	// If we have the same URI basename, we may still have the same file URIs.
+	if fa, err := a.Filename(); err == nil {
+		if fb, err := b.Filename(); err == nil {
+			if filepath.Base(fa) == filepath.Base(fb) {
+				// Stat the files to check if they are equal.
+				if infoa, err := os.Stat(fa); err == nil {
+					if infob, err := os.Stat(fb); err == nil {
+						if os.SameFile(infoa, infob) {
+							return 0
+						}
+					}
+				}
+			}
+			return strings.Compare(fa, fb)
+		}
+	}
+	return strings.Compare(string(a), string(b))
+}
+
 // FileURI returns a span URI for the supplied file path.
 // It will always have the file scheme.
 func FileURI(path string) URI {