diff options
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex/lookup.go')
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/lookup.go | 145 |
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 +} |