func serveAPISearch(resp http.ResponseWriter, req *http.Request) error { q := strings.TrimSpace(req.Form.Get("q")) var pkgs []database.Package if gosrc.IsValidRemotePath(q) || (strings.Contains(q, "/") && gosrc.IsGoRepoPath(q)) { pdoc, _, err := getDoc(q, apiRequest) if e, ok := err.(gosrc.NotFoundError); ok && e.Redirect != "" { pdoc, _, err = getDoc(e.Redirect, robotRequest) } if err == nil && pdoc != nil { pkgs = []database.Package{{Path: pdoc.ImportPath, Synopsis: pdoc.Synopsis}} } } if pkgs == nil { var err error ctx := appengine.NewContext(req) pkgs, err = database.Search(ctx, q) if err != nil { return err } } var data = struct { Results []database.Package `json:"results"` }{ pkgs, } resp.Header().Set("Content-Type", jsonMIMEType) return json.NewEncoder(resp).Encode(&data) }
func serveAPISearch(resp http.ResponseWriter, req *http.Request) error { q := strings.TrimSpace(req.Form.Get("q")) var pkgs []database.Package if gosrc.IsValidRemotePath(q) || (strings.Contains(q, "/") && gosrc.IsGoRepoPath(q)) { pdoc, _, err := getDoc(q, robotRequest) if err == nil && pdoc != nil { pkgs = []database.Package{{Path: pdoc.ImportPath, Synopsis: pdoc.Synopsis}} } } if pkgs == nil { var err error pkgs, err = db.Query(q) if err != nil { return err } } var data = struct { Results []database.Package `json:"results"` }{ pkgs, } resp.Header().Set("Content-Type", jsonMIMEType) return json.NewEncoder(resp).Encode(&data) }
func (pdoc *tdoc) addExamples(obj interface{}, export, method string, examples []*doc.Example) { label := export id := export if method != "" { label += "." + method id += "-" + method } for _, e := range examples { te := &texample{ Label: label, ID: id, Example: e, obj: obj, // Only show play links for packages within the standard library. Play: e.Play != "" && gosrc.IsGoRepoPath(pdoc.ImportPath), } if e.Name != "" { te.Label += " (" + e.Name + ")" if method == "" { te.ID += "-" } te.ID += "-" + e.Name } pdoc.allExamples = append(pdoc.allExamples, te) } }
func serveHome(resp http.ResponseWriter, req *http.Request) error { if req.URL.Path != "/" { return servePackage(resp, req) } q := strings.TrimSpace(req.Form.Get("q")) if q == "" { pkgs, err := popular() if err != nil { return err } return executeTemplate(resp, "home"+templateExt(req), http.StatusOK, nil, map[string]interface{}{"Popular": pkgs}) } if path, ok := isBrowseURL(q); ok { q = path } if gosrc.IsValidRemotePath(q) || (strings.Contains(q, "/") && gosrc.IsGoRepoPath(q)) { pdoc, pkgs, err := getDoc(q, queryRequest) if e, ok := err.(gosrc.NotFoundError); ok && e.Redirect != "" { http.Redirect(resp, req, "/"+e.Redirect, http.StatusFound) return nil } if err == nil && (pdoc != nil || len(pkgs) > 0) { http.Redirect(resp, req, "/"+q, http.StatusFound) return nil } } ctx := appengine.NewContext(req) pkgs, err := database.Search(ctx, q) if err != nil { return err } if gceLogger != nil { // Log up to top 10 packages we served upon a search. logPkgs := pkgs if len(pkgs) > 10 { logPkgs = pkgs[:10] } gceLogger.LogEvent(resp, req, logPkgs) } return executeTemplate(resp, "results"+templateExt(req), http.StatusOK, nil, map[string]interface{}{"q": q, "pkgs": pkgs}) }
func serveHome(resp http.ResponseWriter, req *http.Request) error { if req.URL.Path != "/" { return servePackage(resp, req) } q := strings.TrimSpace(req.Form.Get("q")) if q == "" { pkgs, err := popular() if err != nil { return err } return executeTemplate(resp, "home"+templateExt(req), http.StatusOK, nil, map[string]interface{}{"Popular": pkgs}) } if path, ok := isBrowseURL(q); ok { q = path } if gosrc.IsValidRemotePath(q) || (strings.Contains(q, "/") && gosrc.IsGoRepoPath(q)) { pdoc, pkgs, err := getDoc(q, queryRequest) if e, ok := err.(gosrc.NotFoundError); ok && e.Redirect != "" { http.Redirect(resp, req, "/"+e.Redirect, http.StatusFound) return nil } if err == nil && (pdoc != nil || len(pkgs) > 0) { http.Redirect(resp, req, "/"+q, http.StatusFound) return nil } } pkgs, err := db.Query(q) if err != nil { return err } return executeTemplate(resp, "results"+templateExt(req), http.StatusOK, nil, map[string]interface{}{"q": q, "pkgs": pkgs}) }
func ResolveDep(importPath string) (*dep.ResolvedTarget, error) { // Look up in cache. if target := resolveCache.Get(importPath); target != nil { return target, nil } if strings.HasSuffix(importPath, "_test") { // TODO(sqs): handle xtest packages - these should not be appearing here // as import paths, but they are, so suppress errors return nil, fmt.Errorf("xtest package (%s) is not yet supported", importPath) } // Check if this import path is in this tree. if pkg, err := buildContext.Import(importPath, "", build.FindOnly); err == nil && (pathHasPrefix(pkg.Dir, cwd) || isInEffectiveConfigGOPATH(pkg.Dir)) { // TODO(sqs): do we want to link refs to vendored deps to // their vendored code inside this repo? that's what it's // doing now. The alternative is to link to the external repo // that the code was vendored from. return &dep.ResolvedTarget{ // empty ToRepoCloneURL to indicate it's from this repository ToRepoCloneURL: "", ToUnit: importPath, ToUnitType: "GoPackage", }, nil } // Handle some special (and edge) cases faster for performance and corner-cases. target := &dep.ResolvedTarget{ToUnit: importPath, ToUnitType: "GoPackage"} switch { // CGO package "C" case importPath == "C": return nil, nil // Go standard library packages case gosrc.IsGoRepoPath(importPath) || strings.HasPrefix(importPath, "debug/") || strings.HasPrefix(importPath, "cmd/"): target.ToRepoCloneURL = "https://github.com/golang/go" target.ToVersionString = runtime.Version() target.ToRevSpec = "" // TODO(sqs): fill in when graphing stdlib repo // Special-case github.com/... import paths for performance. case strings.HasPrefix(importPath, "github.com/") || strings.HasPrefix(importPath, "sourcegraph.com/"): parts := strings.SplitN(importPath, "/", 4) if len(parts) < 3 { return nil, fmt.Errorf("import path starts with '(github|sourcegraph).com/' but is not valid: %q", importPath) } target.ToRepoCloneURL = "https://" + strings.Join(parts[:3], "/") + ".git" // Special-case google.golang.org/... (e.g., /appengine) import // paths for performance and to avoid hitting GitHub rate limit. case strings.HasPrefix(importPath, "google.golang.org/"): target.ToRepoCloneURL = "https://" + strings.Replace(importPath, "google.golang.org/", "github.com/golang/", 1) + ".git" // Special-case code.google.com/p/... import paths for performance. case strings.HasPrefix(importPath, "code.google.com/p/"): parts := strings.SplitN(importPath, "/", 4) if len(parts) < 3 { return nil, fmt.Errorf("import path starts with 'code.google.com/p/' but is not valid: %q", importPath) } target.ToRepoCloneURL = "https://" + strings.Join(parts[:3], "/") // Special-case golang.org/x/... import paths for performance. case strings.HasPrefix(importPath, "golang.org/x/"): parts := strings.SplitN(importPath, "/", 4) if len(parts) < 3 { return nil, fmt.Errorf("import path starts with 'golang.org/x/' but is not valid: %q", importPath) } target.ToRepoCloneURL = "https://" + strings.Replace(strings.Join(parts[:3], "/"), "golang.org/x/", "github.com/golang/", 1) // Try to resolve everything else default: log.Printf("Resolving Go dep: %s", importPath) dir, err := gosrc.Get(http.DefaultClient, string(importPath), "") if err == nil { target.ToRepoCloneURL = strings.TrimSuffix(dir.ProjectURL, "/") } else { log.Printf("warning: unable to fetch information about Go package %q: %s", importPath, err) target.ToRepoCloneURL = importPath } } // Save in cache. resolveCache.Put(importPath, target) return target, nil }
// crawlDoc fetches the package documentation from the VCS and updates the database. func crawlDoc(source string, importPath string, pdoc *doc.Package, hasSubdirs bool, nextCrawl time.Time) (*doc.Package, error) { message := []interface{}{source} defer func() { message = append(message, importPath) log.Println(message...) }() if !nextCrawl.IsZero() { d := time.Since(nextCrawl) / time.Hour if d > 0 { message = append(message, "late:", int64(d)) } } etag := "" if pdoc != nil { etag = pdoc.Etag message = append(message, "etag:", etag) } start := time.Now() var err error if i := strings.Index(importPath, "/src/pkg/"); i > 0 && gosrc.IsGoRepoPath(importPath[i+len("/src/pkg/"):]) { // Go source tree mirror. pdoc = nil err = gosrc.NotFoundError{Message: "Go source tree mirror."} } else if i := strings.Index(importPath, "/libgo/go/"); i > 0 && gosrc.IsGoRepoPath(importPath[i+len("/libgo/go/"):]) { // Go Frontend source tree mirror. pdoc = nil err = gosrc.NotFoundError{Message: "Go Frontend source tree mirror."} } else if m := nestedProjectPat.FindStringIndex(importPath); m != nil && exists(importPath[m[0]+1:]) { pdoc = nil err = gosrc.NotFoundError{Message: "Copy of other project."} } else if blocked, e := db.IsBlocked(importPath); blocked && e == nil { pdoc = nil err = gosrc.NotFoundError{Message: "Blocked."} } else { var pdocNew *doc.Package pdocNew, err = doc.Get(httpClient, importPath, etag) message = append(message, "fetch:", int64(time.Since(start)/time.Millisecond)) if err == nil && pdocNew.Name == "" && !hasSubdirs { pdoc = nil err = gosrc.NotFoundError{Message: "No Go files or subdirs"} } else if err != gosrc.ErrNotModified { pdoc = pdocNew } } nextCrawl = start.Add(*maxAge) switch { case strings.HasPrefix(importPath, "github.com/") || (pdoc != nil && len(pdoc.Errors) > 0): nextCrawl = start.Add(*maxAge * 7) case strings.HasPrefix(importPath, "gist.github.com/"): // Don't spend time on gists. It's silly thing to do. nextCrawl = start.Add(*maxAge * 30) } switch { case err == nil: message = append(message, "put:", pdoc.Etag) if err := db.Put(pdoc, nextCrawl, false); err != nil { log.Printf("ERROR db.Put(%q): %v", importPath, err) } case err == gosrc.ErrNotModified: message = append(message, "touch") if err := db.SetNextCrawlEtag(pdoc.ProjectRoot, pdoc.Etag, nextCrawl); err != nil { log.Printf("ERROR db.SetNextCrawl(%q): %v", importPath, err) } case gosrc.IsNotFound(err): message = append(message, "notfound:", err) if err := db.Delete(importPath); err != nil { log.Printf("ERROR db.Delete(%q): %v", importPath, err) } default: message = append(message, "ERROR:", err) return nil, err } return pdoc, nil }