Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

244 строки
5.4 KiB

  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file or at
  5. // https://developers.google.com/open-source/licenses/bsd.
  6. package database
  7. import (
  8. "path"
  9. "regexp"
  10. "strings"
  11. "unicode"
  12. "github.com/golang/gddo/doc"
  13. "github.com/golang/gddo/gosrc"
  14. )
  15. func isStandardPackage(path string) bool {
  16. return strings.Index(path, ".") < 0
  17. }
  18. func isTermSep(r rune) bool {
  19. return unicode.IsSpace(r) ||
  20. r != '.' && unicode.IsPunct(r) ||
  21. unicode.IsSymbol(r)
  22. }
  23. func normalizeProjectRoot(projectRoot string) string {
  24. if projectRoot == "" {
  25. return "go"
  26. }
  27. return projectRoot
  28. }
  29. var synonyms = map[string]string{
  30. "redis": "redisdb", // append db to avoid stemming to 'red'
  31. "rand": "random",
  32. "postgres": "postgresql",
  33. "mongo": "mongodb",
  34. }
  35. func term(s string) string {
  36. s = strings.ToLower(s)
  37. if x, ok := synonyms[s]; ok {
  38. s = x
  39. }
  40. // Trim the trailing period at the end of any sentence.
  41. return stem(strings.TrimSuffix(s, "."))
  42. }
  43. var httpPat = regexp.MustCompile(`https?://\S+`)
  44. func collectSynopsisTerms(terms map[string]bool, synopsis string) {
  45. synopsis = httpPat.ReplaceAllLiteralString(synopsis, "")
  46. fields := strings.FieldsFunc(synopsis, isTermSep)
  47. for i := range fields {
  48. fields[i] = strings.ToLower(fields[i])
  49. }
  50. // Ignore boilerplate in the following common patterns:
  51. // Package foo ...
  52. // Command foo ...
  53. // Package foo implements ... (and provides, contains)
  54. // The foo package ...
  55. // The foo package implements ...
  56. // The foo command ...
  57. checkPackageVerb := false
  58. switch {
  59. case len(fields) >= 1 && fields[0] == "package":
  60. fields = fields[1:]
  61. checkPackageVerb = true
  62. case len(fields) >= 1 && fields[0] == "command":
  63. fields = fields[1:]
  64. case len(fields) >= 3 && fields[0] == "the" && fields[2] == "package":
  65. fields[2] = fields[1]
  66. fields = fields[2:]
  67. checkPackageVerb = true
  68. case len(fields) >= 3 && fields[0] == "the" && fields[2] == "command":
  69. fields[2] = fields[1]
  70. fields = fields[2:]
  71. }
  72. if checkPackageVerb && len(fields) >= 2 &&
  73. (fields[1] == "implements" || fields[1] == "provides" || fields[1] == "contains") {
  74. fields[1] = fields[0]
  75. fields = fields[1:]
  76. }
  77. for _, s := range fields {
  78. if !stopWord[s] {
  79. terms[term(s)] = true
  80. }
  81. }
  82. }
  83. func termSlice(terms map[string]bool) []string {
  84. result := make([]string, 0, len(terms))
  85. for term := range terms {
  86. result = append(result, term)
  87. }
  88. return result
  89. }
  90. func documentTerms(pdoc *doc.Package, score float64) []string {
  91. terms := make(map[string]bool)
  92. // Project root
  93. projectRoot := normalizeProjectRoot(pdoc.ProjectRoot)
  94. terms["project:"+projectRoot] = true
  95. if strings.HasPrefix(pdoc.ImportPath, "golang.org/x/") {
  96. terms["project:subrepo"] = true
  97. }
  98. // Imports
  99. for _, path := range pdoc.Imports {
  100. if gosrc.IsValidPath(path) {
  101. terms["import:"+path] = true
  102. }
  103. }
  104. if score > 0 {
  105. for _, term := range parseQuery(pdoc.ImportPath) {
  106. terms[term] = true
  107. }
  108. if !isStandardPackage(pdoc.ImportPath) {
  109. terms["all:"] = true
  110. for _, term := range parseQuery(pdoc.ProjectName) {
  111. terms[term] = true
  112. }
  113. for _, term := range parseQuery(pdoc.Name) {
  114. terms[term] = true
  115. }
  116. }
  117. // Synopsis
  118. collectSynopsisTerms(terms, pdoc.Synopsis)
  119. }
  120. return termSlice(terms)
  121. }
  122. // vendorPat matches the path of a vendored package.
  123. var vendorPat = regexp.MustCompile(
  124. // match directories used by tools to vendor packages.
  125. `/(?:_?third_party|vendors|Godeps/_workspace/src)/` +
  126. // match a domain name.
  127. `[^./]+\.[^/]+`)
  128. func documentScore(pdoc *doc.Package) float64 {
  129. if pdoc.Name == "" ||
  130. pdoc.Status != gosrc.Active ||
  131. len(pdoc.Errors) > 0 ||
  132. strings.HasSuffix(pdoc.ImportPath, ".go") ||
  133. strings.HasPrefix(pdoc.ImportPath, "gist.github.com/") ||
  134. strings.HasSuffix(pdoc.ImportPath, "/internal") ||
  135. strings.Contains(pdoc.ImportPath, "/internal/") ||
  136. vendorPat.MatchString(pdoc.ImportPath) {
  137. return 0
  138. }
  139. for _, p := range pdoc.Imports {
  140. if strings.HasSuffix(p, ".go") {
  141. return 0
  142. }
  143. }
  144. r := 1.0
  145. if pdoc.IsCmd {
  146. if pdoc.Doc == "" {
  147. // Do not include command in index if it does not have documentation.
  148. return 0
  149. }
  150. if !importsGoPackages(pdoc) {
  151. // Penalize commands that don't use the "go/*" packages.
  152. r *= 0.9
  153. }
  154. } else {
  155. if !pdoc.Truncated &&
  156. len(pdoc.Consts) == 0 &&
  157. len(pdoc.Vars) == 0 &&
  158. len(pdoc.Funcs) == 0 &&
  159. len(pdoc.Types) == 0 &&
  160. len(pdoc.Examples) == 0 {
  161. // Do not include package in index if it does not have exports.
  162. return 0
  163. }
  164. if pdoc.Doc == "" {
  165. // Penalty for no documentation.
  166. r *= 0.95
  167. }
  168. if path.Base(pdoc.ImportPath) != pdoc.Name {
  169. // Penalty for last element of path != package name.
  170. r *= 0.9
  171. }
  172. for i := 0; i < strings.Count(pdoc.ImportPath[len(pdoc.ProjectRoot):], "/"); i++ {
  173. // Penalty for deeply nested packages.
  174. r *= 0.99
  175. }
  176. if strings.Index(pdoc.ImportPath[len(pdoc.ProjectRoot):], "/src/") > 0 {
  177. r *= 0.95
  178. }
  179. for _, p := range pdoc.Imports {
  180. if vendorPat.MatchString(p) {
  181. // Penalize packages that import vendored packages.
  182. r *= 0.1
  183. break
  184. }
  185. }
  186. }
  187. return r
  188. }
  189. func parseQuery(q string) []string {
  190. var terms []string
  191. q = strings.ToLower(q)
  192. for _, s := range strings.FieldsFunc(q, isTermSep) {
  193. if !stopWord[s] {
  194. terms = append(terms, term(s))
  195. }
  196. }
  197. return terms
  198. }
  199. func importsGoPackages(pdoc *doc.Package) bool {
  200. for _, m := range pdoc.Imports {
  201. if strings.HasPrefix(m, "go/") {
  202. return true
  203. }
  204. }
  205. return false
  206. }