godoc: Order package results by import count.

Allow searching for packages by directory components.

LGTM=sameer
R=sameer, bradfitz
CC=golang-codereviews
https://golang.org/cl/104220043
This commit is contained in:
Brad Garcia 2014-06-27 12:47:39 -04:00
parent 936715c71c
commit 345b6437fc
2 changed files with 103 additions and 29 deletions

View File

@ -358,25 +358,47 @@ type Ident struct {
Doc string // e.g. "NewRequest returns a new Request..." Doc string // e.g. "NewRequest returns a new Request..."
} }
type byPackage []Ident // byImportCount sorts the given slice of Idents by the import
// counts of the packages to which they belong.
func (s byPackage) Len() int { return len(s) } type byImportCount struct {
func (s byPackage) Less(i, j int) bool { Idents []Ident
if s[i].Package == s[j].Package { ImportCount map[string]int
return s[i].Path < s[j].Path
}
return s[i].Package < s[j].Package
} }
func (s byPackage) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Filter creates a new Ident list where the results match the given func (ic byImportCount) Len() int {
return len(ic.Idents)
}
func (ic byImportCount) Less(i, j int) bool {
ri := ic.ImportCount[ic.Idents[i].Path]
rj := ic.ImportCount[ic.Idents[j].Path]
if ri == rj {
return ic.Idents[i].Path < ic.Idents[j].Path
}
return ri > rj
}
func (ic byImportCount) Swap(i, j int) {
ic.Idents[i], ic.Idents[j] = ic.Idents[j], ic.Idents[i]
}
func (ic byImportCount) String() string {
buf := bytes.NewBuffer([]byte("["))
for _, v := range ic.Idents {
buf.WriteString(fmt.Sprintf("\n\t%s, %s (%d)", v.Path, v.Name, ic.ImportCount[v.Path]))
}
buf.WriteString("\n]")
return buf.String()
}
// filter creates a new Ident list where the results match the given
// package name. // package name.
func (s byPackage) filter(pakname string) []Ident { func (ic byImportCount) filter(pakname string) []Ident {
if s == nil { if ic.Idents == nil {
return nil return nil
} }
var res []Ident var res []Ident
for _, i := range s { for _, i := range ic.Idents {
if i.Package == pakname { if i.Package == pakname {
res = append(res, i) res = append(res, i)
} }
@ -384,6 +406,14 @@ func (s byPackage) filter(pakname string) []Ident {
return res return res
} }
// top returns the top n identifiers.
func (ic byImportCount) top(n int) []Ident {
if len(ic.Idents) > n {
return ic.Idents[:n]
}
return ic.Idents
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Indexer // Indexer
@ -719,23 +749,45 @@ func (x *Indexer) indexDocs(dirname string, filename string, astFile *ast.File)
Doc: doc.Synopsis(docstr), Doc: doc.Synopsis(docstr),
}) })
} }
foundPkg := false
if x.idents[PackageClause] != nil { if x.idents[PackageClause] == nil {
pkgs := x.idents[PackageClause][docPkg.Name] x.idents[PackageClause] = make(map[string][]Ident)
}
// List of words under which the package identifier will be stored.
// This includes the package name and the components of the directory
// in which it resides.
words := strings.Split(pathpkg.Dir(pkgPath), "/")
if words[0] == "." {
words = []string{}
}
name := x.intern(docPkg.Name)
synopsis := doc.Synopsis(docPkg.Doc)
words = append(words, name)
pkgIdent := Ident{
Path: pkgPath,
Package: pkgName,
Name: name,
Doc: synopsis,
}
for _, word := range words {
word = x.intern(word)
found := false
pkgs := x.idents[PackageClause][word]
for i, p := range pkgs { for i, p := range pkgs {
if p.Path == pkgPath { if p.Path == pkgPath {
foundPkg = true
if docPkg.Doc != "" { if docPkg.Doc != "" {
p.Doc = doc.Synopsis(docPkg.Doc) p.Doc = synopsis
pkgs[i] = p pkgs[i] = p
} }
found = true
break break
} }
} }
if !found {
x.idents[PackageClause][word] = append(x.idents[PackageClause][word], pkgIdent)
} }
if !foundPkg {
addIdent(PackageClause, docPkg.Name, docPkg.Doc)
} }
for _, c := range docPkg.Consts { for _, c := range docPkg.Consts {
for _, name := range c.Names { for _, name := range c.Names {
addIdent(ConstDecl, name, c.Doc) addIdent(ConstDecl, name, c.Doc)
@ -1027,9 +1079,10 @@ func (c *Corpus) NewIndex() *Index {
suffixes = suffixarray.New(x.sources.Bytes()) suffixes = suffixarray.New(x.sources.Bytes())
} }
// sort idents by the number of imports of their respective packages
for _, idMap := range x.idents { for _, idMap := range x.idents {
for _, ir := range idMap { for _, ir := range idMap {
sort.Sort(byPackage(ir)) sort.Sort(byImportCount{ir, x.importCount})
} }
} }
@ -1238,7 +1291,9 @@ func (x *Index) Lookup(query string) (*SearchResult, error) {
rslt.Pak = rslt.Hit.Others.filter(ident) rslt.Pak = rslt.Hit.Others.filter(ident)
} }
for k, v := range x.idents { for k, v := range x.idents {
rslt.Idents[k] = v[ident] const rsltLimit = 50
ids := byImportCount{v[ident], x.importCount}
rslt.Idents[k] = ids.top(rsltLimit)
} }
case 2: case 2:
@ -1252,7 +1307,8 @@ func (x *Index) Lookup(query string) (*SearchResult, error) {
rslt.Hit = &LookupResult{decls, others} rslt.Hit = &LookupResult{decls, others}
} }
for k, v := range x.idents { for k, v := range x.idents {
rslt.Idents[k] = byPackage(v[ident]).filter(pakname) ids := byImportCount{v[ident], x.importCount}
rslt.Idents[k] = ids.filter(pakname)
} }
default: default:

View File

@ -235,6 +235,7 @@ func checkIdents(t *testing.T, c *Corpus, ix *Index) {
{"other/bar", "bar", "bar", "Package bar is another bar package."}, {"other/bar", "bar", "bar", "Package bar is another bar package."},
}, },
"foo": []Ident{{"foo", "foo", "foo", "Package foo is an example."}}, "foo": []Ident{{"foo", "foo", "foo", "Package foo is an example."}},
"other": []Ident{{"other/bar", "bar", "bar", "Package bar is another bar package."}},
}, },
ConstDecl: map[string][]Ident{ ConstDecl: map[string][]Ident{
"Pi": []Ident{{"foo", "foo", "Pi", ""}}, "Pi": []Ident{{"foo", "foo", "Pi", ""}},
@ -257,6 +258,11 @@ func checkIdents(t *testing.T, c *Corpus, ix *Index) {
} }
func TestIdentResultSort(t *testing.T) { func TestIdentResultSort(t *testing.T) {
ic := map[string]int{
"/a/b/pkg1": 10,
"/a/b/pkg2": 2,
"/b/d/pkg3": 20,
}
for _, tc := range []struct { for _, tc := range []struct {
ir []Ident ir []Ident
exp []Ident exp []Ident
@ -268,19 +274,30 @@ func TestIdentResultSort(t *testing.T) {
{"/a/b/pkg1", "pkg1", "MyFunc1", ""}, {"/a/b/pkg1", "pkg1", "MyFunc1", ""},
}, },
exp: []Ident{ exp: []Ident{
{"/b/d/pkg3", "pkg3", "MyFunc3", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""}, {"/a/b/pkg1", "pkg1", "MyFunc1", ""},
{"/a/b/pkg2", "pkg2", "MyFunc2", ""}, {"/a/b/pkg2", "pkg2", "MyFunc2", ""},
{"/b/d/pkg3", "pkg3", "MyFunc3", ""}, },
},
{
ir: []Ident{
{"/a/a/pkg1", "pkg1", "MyFunc1", ""},
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
},
exp: []Ident{
{"/a/b/pkg1", "pkg1", "MyFunc1", ""},
{"/a/a/pkg1", "pkg1", "MyFunc1", ""},
}, },
}, },
} { } {
if sort.Sort(byPackage(tc.ir)); !reflect.DeepEqual(tc.ir, tc.exp) { if sort.Sort(byImportCount{tc.ir, ic}); !reflect.DeepEqual(tc.ir, tc.exp) {
t.Errorf("got: %v, want %v", tc.ir, tc.exp) t.Errorf("got: %v, want %v", tc.ir, tc.exp)
} }
} }
} }
func TestIdentPackageFilter(t *testing.T) { func TestIdentFilter(t *testing.T) {
ic := map[string]int{}
for _, tc := range []struct { for _, tc := range []struct {
ir []Ident ir []Ident
pak string pak string
@ -298,7 +315,8 @@ func TestIdentPackageFilter(t *testing.T) {
}, },
}, },
} { } {
if res := byPackage(tc.ir).filter(tc.pak); !reflect.DeepEqual(res, tc.exp) { res := byImportCount{tc.ir, ic}.filter(tc.pak)
if !reflect.DeepEqual(res, tc.exp) {
t.Errorf("got: %v, want %v", res, tc.exp) t.Errorf("got: %v, want %v", res, tc.exp)
} }
} }