gopls/internal/lsp/source: extract InDir to a new pathutil package

Change-Id: Ib977563e100a536c7721a1cf5690f2f639e5f741
Reviewed-on: https://go-review.googlesource.com/c/tools/+/543918
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go
index 09892f4..091b46b 100644
--- a/gopls/internal/lsp/cache/load.go
+++ b/gopls/internal/lsp/cache/load.go
@@ -20,6 +20,7 @@
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/immutable"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
+	"golang.org/x/tools/gopls/internal/pathutil"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/event/tag"
 	"golang.org/x/tools/internal/gocommand"
@@ -759,7 +760,7 @@
 	if len(pkg.GoFiles) > 1 {
 		return false
 	}
-	if !InDir(gocache, pkg.GoFiles[0]) {
+	if !pathutil.InDir(gocache, pkg.GoFiles[0]) {
 		return false
 	}
 	return true
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index 5d85859..b06ddf2 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -75,7 +75,6 @@
 	RemoveIntermediateTestVariants = source.RemoveIntermediateTestVariants
 	IsCommandLineArguments         = source.IsCommandLineArguments
 	BundleQuickFixes               = source.BundleQuickFixes
-	InDir                          = source.InDir
 	SuggestedFixFromCommand        = source.SuggestedFixFromCommand
 	ToProtocolEdits                = source.ToProtocolEdits
 	NewFilterer                    = source.NewFilterer
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 91e57ee..cc38931 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -38,6 +38,7 @@
 	"golang.org/x/tools/gopls/internal/lsp/source/methodsets"
 	"golang.org/x/tools/gopls/internal/lsp/source/typerefs"
 	"golang.org/x/tools/gopls/internal/lsp/source/xrefs"
+	"golang.org/x/tools/gopls/internal/pathutil"
 	"golang.org/x/tools/gopls/internal/persistent"
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/gopls/internal/vulncheck"
@@ -905,7 +906,7 @@
 
 	// If GOWORK is outside the folder, ensure we are watching it.
 	gowork, _ := s.view.GOWORK()
-	if gowork != "" && !InDir(s.view.folder.Dir.Path(), gowork.Path()) {
+	if gowork != "" && !pathutil.InDir(s.view.folder.Dir.Path(), gowork.Path()) {
 		patterns[gowork.Path()] = struct{}{}
 	}
 
@@ -914,7 +915,7 @@
 	for _, dir := range dirs {
 		// If the directory is within the view's folder, we're already watching
 		// it with the first pattern above.
-		if InDir(s.view.folder.Dir.Path(), dir) {
+		if pathutil.InDir(s.view.folder.Dir.Path(), dir) {
 			continue
 		}
 		// TODO(rstambler): If microsoft/vscode#3025 is resolved before
@@ -955,7 +956,7 @@
 
 	s.files.Dirs().Range(func(dir string) {
 		for _, wsDir := range wsDirs {
-			if InDir(wsDir, dir) {
+			if pathutil.InDir(wsDir, dir) {
 				patterns[dir] = unit{}
 			}
 		}
@@ -1029,7 +1030,7 @@
 	}
 	var files []protocol.DocumentURI
 	s.files.Range(func(uri protocol.DocumentURI, _ file.Handle) {
-		if InDir(dir, uri.Path()) {
+		if pathutil.InDir(dir, uri.Path()) {
 			files = append(files, uri)
 		}
 	})
@@ -1139,7 +1140,7 @@
 func moduleForURI(modFiles map[protocol.DocumentURI]struct{}, uri protocol.DocumentURI) protocol.DocumentURI {
 	var match protocol.DocumentURI
 	for modURI := range modFiles {
-		if !InDir(filepath.Dir(modURI.Path()), uri.Path()) {
+		if !pathutil.InDir(filepath.Dir(modURI.Path()), uri.Path()) {
 			continue
 		}
 		if len(modURI) > len(match) {
@@ -1684,7 +1685,7 @@
 
 				// When the module is underneath the view dir, we offer
 				// "use all modules" quick-fixes.
-				inDir := InDir(viewDir, modDir)
+				inDir := pathutil.InDir(viewDir, modDir)
 
 				if rel, err := filepath.Rel(viewDir, modDir); err == nil {
 					modDir = rel
@@ -2179,7 +2180,7 @@
 
 	m.Range(func(modURI protocol.DocumentURI, _ *memoize.Promise) {
 		if len(modURI) > len(mostRelevant) {
-			if InDir(filepath.Dir(modURI.Path()), changedFile) {
+			if pathutil.InDir(filepath.Dir(modURI.Path()), changedFile) {
 				mostRelevant = modURI
 			}
 		}
diff --git a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go
index 0869624..669b016 100644
--- a/gopls/internal/lsp/cache/view.go
+++ b/gopls/internal/lsp/cache/view.go
@@ -25,6 +25,7 @@
 	"golang.org/x/mod/semver"
 	"golang.org/x/tools/gopls/internal/file"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
+	"golang.org/x/tools/gopls/internal/pathutil"
 	"golang.org/x/tools/gopls/internal/settings"
 	"golang.org/x/tools/gopls/internal/vulncheck"
 	"golang.org/x/tools/internal/event"
@@ -568,10 +569,10 @@
 	// user. It would be better to explicitly consider the set of active modules
 	// wherever relevant.
 	inGoDir := false
-	if InDir(v.goCommandDir.Path(), v.folder.Dir.Path()) {
-		inGoDir = InDir(v.goCommandDir.Path(), uri.Path())
+	if pathutil.InDir(v.goCommandDir.Path(), v.folder.Dir.Path()) {
+		inGoDir = pathutil.InDir(v.goCommandDir.Path(), uri.Path())
 	}
-	inFolder := InDir(v.folder.Dir.Path(), uri.Path())
+	inFolder := pathutil.InDir(v.folder.Dir.Path(), uri.Path())
 
 	if !inGoDir && !inFolder {
 		return false
@@ -587,7 +588,7 @@
 	filterer := buildFilterer(folderDir, v.gomodcache, v.folder.Options)
 	return func(uri protocol.DocumentURI) bool {
 		// Only filter relative to the configured root directory.
-		if InDir(folderDir, uri.Path()) {
+		if pathutil.InDir(folderDir, uri.Path()) {
 			return pathExcludedByFilter(strings.TrimPrefix(uri.Path(), folderDir), filterer)
 		}
 		return false
@@ -978,7 +979,7 @@
 
 	// Check if the workspace is within any GOPATH directory.
 	for _, gp := range filepath.SplitList(def.gopath) {
-		if InDir(filepath.Join(gp, "src"), folder.Dir.Path()) {
+		if pathutil.InDir(filepath.Join(gp, "src"), folder.Dir.Path()) {
 			def.inGOPATH = true
 			break
 		}
diff --git a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.go
index 7d05ce0..4fcd745 100644
--- a/gopls/internal/lsp/fake/editor.go
+++ b/gopls/internal/lsp/fake/editor.go
@@ -20,7 +20,7 @@
 	"golang.org/x/tools/gopls/internal/lsp/command"
 	"golang.org/x/tools/gopls/internal/lsp/glob"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
-	"golang.org/x/tools/gopls/internal/lsp/source"
+	"golang.org/x/tools/gopls/internal/pathutil"
 	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/jsonrpc2/servertest"
 	"golang.org/x/tools/internal/xcontext"
@@ -1302,7 +1302,7 @@
 
 	for path := range e.buffers {
 		abs := e.sandbox.Workdir.AbsPath(path)
-		if oldAbs == abs || source.InDir(oldAbs, abs) {
+		if oldAbs == abs || pathutil.InDir(oldAbs, abs) {
 			rel, err := filepath.Rel(oldAbs, abs)
 			if err != nil {
 				return nil, nil, fmt.Errorf("filepath.Rel(%q, %q): %v", oldAbs, abs, err)
diff --git a/gopls/internal/lsp/source/util.go b/gopls/internal/lsp/source/util.go
index 8c8f5f3..ed2adef 100644
--- a/gopls/internal/lsp/source/util.go
+++ b/gopls/internal/lsp/source/util.go
@@ -10,7 +10,6 @@
 	"go/printer"
 	"go/token"
 	"go/types"
-	"path/filepath"
 	"regexp"
 	"sort"
 	"strconv"
@@ -424,45 +423,6 @@
 	return true
 }
 
-// InDir checks whether path is in the file tree rooted at dir.
-// It checks only the lexical form of the file names.
-// It does not consider symbolic links.
-//
-// Copied from go/src/cmd/go/internal/search/search.go.
-func InDir(dir, path string) bool {
-	pv := strings.ToUpper(filepath.VolumeName(path))
-	dv := strings.ToUpper(filepath.VolumeName(dir))
-	path = path[len(pv):]
-	dir = dir[len(dv):]
-	switch {
-	default:
-		return false
-	case pv != dv:
-		return false
-	case len(path) == len(dir):
-		if path == dir {
-			return true
-		}
-		return false
-	case dir == "":
-		return path != ""
-	case len(path) > len(dir):
-		if dir[len(dir)-1] == filepath.Separator {
-			if path[:len(dir)] == dir {
-				return path[len(dir):] != ""
-			}
-			return false
-		}
-		if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
-			if len(path) == len(dir)+1 {
-				return true
-			}
-			return path[len(dir)+1:] != ""
-		}
-		return false
-	}
-}
-
 // IsValidImport returns whether importPkgPath is importable
 // by pkgPath
 func IsValidImport(pkgPath, importPkgPath PackagePath) bool {
diff --git a/gopls/internal/pathutil/util.go b/gopls/internal/pathutil/util.go
new file mode 100644
index 0000000..e19863e
--- /dev/null
+++ b/gopls/internal/pathutil/util.go
@@ -0,0 +1,49 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pathutil
+
+import (
+	"path/filepath"
+	"strings"
+)
+
+// InDir checks whether path is in the file tree rooted at dir.
+// It checks only the lexical form of the file names.
+// It does not consider symbolic links.
+//
+// Copied from go/src/cmd/go/internal/search/search.go.
+func InDir(dir, path string) bool {
+	pv := strings.ToUpper(filepath.VolumeName(path))
+	dv := strings.ToUpper(filepath.VolumeName(dir))
+	path = path[len(pv):]
+	dir = dir[len(dv):]
+	switch {
+	default:
+		return false
+	case pv != dv:
+		return false
+	case len(path) == len(dir):
+		if path == dir {
+			return true
+		}
+		return false
+	case dir == "":
+		return path != ""
+	case len(path) > len(dir):
+		if dir[len(dir)-1] == filepath.Separator {
+			if path[:len(dir)] == dir {
+				return path[len(dir):] != ""
+			}
+			return false
+		}
+		if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
+			if len(path) == len(dir)+1 {
+				return true
+			}
+			return path[len(dir)+1:] != ""
+		}
+		return false
+	}
+}