summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/internal/modindex/lookup.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex/lookup.go')
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/lookup.go145
1 files changed, 145 insertions, 0 deletions
diff --git a/vendor/golang.org/x/tools/internal/modindex/lookup.go b/vendor/golang.org/x/tools/internal/modindex/lookup.go
new file mode 100644
index 000000000..29d4e3d7a
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/modindex/lookup.go
@@ -0,0 +1,145 @@
+// Copyright 2024 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 modindex
+
+import (
+ "slices"
+ "strconv"
+ "strings"
+)
+
+type Candidate struct {
+ PkgName string
+ Name string
+ Dir string
+ ImportPath string
+ Type LexType
+ // information for Funcs
+ Results int16 // how many results
+ Sig []Field // arg names and types
+}
+
+type Field struct {
+ Arg, Type string
+}
+
+type LexType int8
+
+const (
+ Const LexType = iota
+ Var
+ Type
+ Func
+)
+
+// Lookup finds all the symbols in the index with the given PkgName and name.
+// If prefix is true, it finds all of these with name as a prefix.
+func (ix *Index) Lookup(pkg, name string, prefix bool) []Candidate {
+ loc, ok := slices.BinarySearchFunc(ix.Entries, pkg, func(e Entry, pkg string) int {
+ return strings.Compare(e.PkgName, pkg)
+ })
+ if !ok {
+ return nil // didn't find the package
+ }
+ var ans []Candidate
+ // loc is the first entry for this package name, but there may be severeal
+ for i := loc; i < len(ix.Entries); i++ {
+ e := ix.Entries[i]
+ if e.PkgName != pkg {
+ break // end of sorted package names
+ }
+ nloc, ok := slices.BinarySearchFunc(e.Names, name, func(s string, name string) int {
+ if strings.HasPrefix(s, name) {
+ return 0
+ }
+ if s < name {
+ return -1
+ }
+ return 1
+ })
+ if !ok {
+ continue // didn't find the name, nor any symbols with name as a prefix
+ }
+ for j := nloc; j < len(e.Names); j++ {
+ nstr := e.Names[j]
+ // benchmarks show this makes a difference when there are a lot of Possibilities
+ flds := fastSplit(nstr)
+ if !(flds[0] == name || prefix && strings.HasPrefix(flds[0], name)) {
+ // past range of matching Names
+ break
+ }
+ if len(flds) < 2 {
+ continue // should never happen
+ }
+ px := Candidate{
+ PkgName: pkg,
+ Name: flds[0],
+ Dir: string(e.Dir),
+ ImportPath: e.ImportPath,
+ Type: asLexType(flds[1][0]),
+ }
+ if flds[1] == "F" {
+ n, err := strconv.Atoi(flds[2])
+ if err != nil {
+ continue // should never happen
+ }
+ px.Results = int16(n)
+ if len(flds) >= 4 {
+ sig := strings.Split(flds[3], " ")
+ for i := 0; i < len(sig); i++ {
+ // $ cannot otherwise occur. removing the spaces
+ // almost works, but for chan struct{}, e.g.
+ sig[i] = strings.Replace(sig[i], "$", " ", -1)
+ }
+ px.Sig = toFields(sig)
+ }
+ }
+ ans = append(ans, px)
+ }
+ }
+ return ans
+}
+
+func toFields(sig []string) []Field {
+ ans := make([]Field, len(sig)/2)
+ for i := 0; i < len(ans); i++ {
+ ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]}
+ }
+ return ans
+}
+
+// benchmarks show this is measurably better than strings.Split
+func fastSplit(x string) []string {
+ ans := make([]string, 0, 4)
+ nxt := 0
+ start := 0
+ for i := 0; i < len(x); i++ {
+ if x[i] != ' ' {
+ continue
+ }
+ ans = append(ans, x[start:i])
+ nxt++
+ start = i + 1
+ if nxt >= 3 {
+ break
+ }
+ }
+ ans = append(ans, x[start:])
+ return ans
+}
+
+func asLexType(c byte) LexType {
+ switch c {
+ case 'C':
+ return Const
+ case 'V':
+ return Var
+ case 'T':
+ return Type
+ case 'F':
+ return Func
+ }
+ return -1
+}