/man.go

https://code.google.com/p/mango-doc/ · Go · 310 lines · 282 code · 19 blank · 9 comment · 73 complexity · 344f6628416776b74d9933c845123ba4 MD5 · raw file

  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "go/ast"
  6. "go/doc"
  7. "go/token"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. //BUG(jmf): Quotation marks are all wrong in postscript output.
  14. func ovr_map(in []*section) map[string][]interface{} {
  15. out := map[string][]interface{}{}
  16. for _, s := range in {
  17. out[s.name] = s.paras
  18. }
  19. return out
  20. }
  21. type M struct {
  22. *F
  23. name, version, sec string
  24. descr []byte //short description
  25. sections, overd, end []*section
  26. overm map[string][]interface{}
  27. refs []string
  28. pkg *ast.Package
  29. docs *doc.Package
  30. }
  31. func NewManPage(pkg *ast.Package, docs *doc.Package, overd []*section) *M {
  32. //break up the package document, extract a short description
  33. dvec := unstring([]byte(docs.Doc))
  34. var fs []byte //first sentence.
  35. if dvec != nil && len(dvec) > 0 {
  36. if p, ok := dvec[0].([][]byte); ok && len(p) > 0 {
  37. fs = p[0]
  38. //if the first paragraph is one sentence, only use it in description
  39. //otherwise we leave it where it is to repeat.
  40. if len(p) == 1 {
  41. dvec = dvec[1:]
  42. }
  43. }
  44. }
  45. m := &M{
  46. F: Formatter(),
  47. name: *name,
  48. version: grep_version(pkg),
  49. descr: fs,
  50. sections: sections(dvec),
  51. overd: overd,
  52. overm: ovr_map(overd),
  53. pkg: pkg,
  54. docs: docs,
  55. }
  56. h := -1
  57. for i, sec := range m.sections {
  58. if sec.name == "HISTORY" {
  59. h = i
  60. break
  61. }
  62. }
  63. if hs := get_section(m, "HISTORY", h); hs != nil {
  64. m.end = []*section{&section{"HISTORY", hs}}
  65. }
  66. m.WriteString(".\\\" Automatically generated by mango(1)")
  67. return m
  68. }
  69. func lit(x interface{}) []byte {
  70. if b, ok := x.(*ast.BasicLit); ok {
  71. v := b.Value
  72. switch b.Kind {
  73. case token.CHAR, token.STRING:
  74. v, _ = strconv.Unquote(v)
  75. }
  76. return []byte(v)
  77. }
  78. return nil
  79. }
  80. func grep_version(pkg *ast.Package) string {
  81. if *version != "" {
  82. return *version
  83. }
  84. for _, file := range pkg.Files {
  85. for _, decl := range file.Decls {
  86. if g, ok := decl.(*ast.GenDecl); ok {
  87. if g.Tok == token.CONST || g.Tok == token.VAR {
  88. for _, s := range g.Specs {
  89. if v, ok := s.(*ast.ValueSpec); ok {
  90. for i, n := range v.Names {
  91. if n.Name == "Version" {
  92. t := v.Values[i]
  93. if b, ok := t.(*ast.BasicLit); ok {
  94. return string(lit(b))
  95. }
  96. }
  97. }
  98. }
  99. }
  100. }
  101. }
  102. }
  103. }
  104. return ""
  105. }
  106. func flatten(docs *doc.Package, extras []string) <-chan string {
  107. out := make(chan string)
  108. var sub func(interface{})
  109. sub = func(x interface{}) {
  110. switch t := x.(type) {
  111. case []*doc.Value:
  112. for _, v := range t {
  113. out <- v.Doc
  114. }
  115. case []*doc.Func:
  116. for _, v := range t {
  117. out <- v.Doc
  118. }
  119. case []*doc.Type:
  120. for _, v := range t {
  121. out <- v.Doc
  122. sub(v.Consts)
  123. sub(v.Vars)
  124. sub(v.Funcs)
  125. sub(v.Methods)
  126. }
  127. }
  128. }
  129. go func() {
  130. for _, x := range extras {
  131. out <- x
  132. }
  133. for _, bug := range docs.Bugs {
  134. out <- bug
  135. }
  136. out <- docs.Doc
  137. sub(docs.Consts)
  138. sub(docs.Types)
  139. sub(docs.Vars)
  140. sub(docs.Funcs)
  141. close(out)
  142. }()
  143. return out
  144. }
  145. func (m *M) find_refs(extras []string) {
  146. var acc []string
  147. seen := map[string]bool{}
  148. seen[m.name+"("+m.sec+")"] = true //don't want recursive references
  149. for str := range flatten(m.docs, extras) {
  150. for _, word := range strings.Fields(str) {
  151. if !refrx.MatchString(word) {
  152. continue
  153. }
  154. switch word[strings.Index(word, "(")+1] { //check the part in the ()
  155. case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
  156. 'n', 'o', 'l', 'x', 'p':
  157. //okay, even though most of these are unlikely
  158. //and some deprecated
  159. default:
  160. //not a man page
  161. continue
  162. }
  163. if !seen[word] {
  164. seen[word] = true
  165. acc = append(acc, word)
  166. }
  167. }
  168. }
  169. sort.Strings(acc)
  170. m.refs = acc
  171. }
  172. func (m *M) do_header(kind string) {
  173. tm := time.Now().Format("2006-01-02")
  174. version := m.version
  175. if version == "" {
  176. version = tm
  177. }
  178. if *manual != "" {
  179. kind = *manual
  180. }
  181. m.WriteString(
  182. fmt.Sprintf("\n.TH \"%s\" %s \"%s\" \"version %s\" \"%s\"",
  183. m.name,
  184. m.sec,
  185. tm,
  186. version,
  187. kind,
  188. ))
  189. }
  190. func (m *M) do_name() {
  191. m.section("NAME")
  192. m.WriteString(m.name)
  193. s := bytes.TrimSpace(m.descr)
  194. if len(s) > 0 {
  195. m.WriteString(" \\- ")
  196. m.Write(s) //first sentence
  197. }
  198. }
  199. func get_section(m *M, nm string, i int) (ps []interface{}) {
  200. ok := false
  201. if ps, ok = m.overm[nm]; ok {
  202. delete(m.overm, nm)
  203. } else if i != -1 {
  204. ps = m.sections[i].paras
  205. }
  206. //regardless of where it comes from, remove from m.sections given valid i
  207. switch {
  208. case i == -1:
  209. return
  210. case i == 0:
  211. m.sections = m.sections[1:]
  212. case i == len(m.sections)-1:
  213. m.sections = m.sections[:len(m.sections)-1]
  214. default:
  215. copy(m.sections[i:], m.sections[i+1:])
  216. m.sections = m.sections[:len(m.sections)-1]
  217. }
  218. return
  219. }
  220. func (m *M) do_description() {
  221. i := -1
  222. if len(m.sections) > 0 {
  223. i = 0
  224. }
  225. ps := get_section(m, "", i)
  226. if ps != nil && len(ps) > 0 {
  227. m.section("DESCRIPTION")
  228. m.paras(ps)
  229. }
  230. }
  231. func (m *M) user_sections(sx ...string) {
  232. for _, req := range sx {
  233. for i, sc := range m.sections {
  234. if sc.name != req {
  235. i = -1
  236. }
  237. if ps := get_section(m, req, i); ps != nil {
  238. m.section(req)
  239. m.paras(ps)
  240. }
  241. }
  242. }
  243. }
  244. func (m *M) remaining_user_sections() {
  245. for _, sec := range m.sections {
  246. m.section(sec.name)
  247. m.paras(sec.paras)
  248. }
  249. //this is horrible but beats deleting the overd sections as we go
  250. for _, s := range m.overd {
  251. if _, ok := m.overm[s.name]; ok {
  252. m.section(s.name)
  253. m.paras(s.paras)
  254. }
  255. }
  256. }
  257. func (m *M) do_endmatter() {
  258. for _, sec := range m.end {
  259. m.section(sec.name)
  260. m.paras(sec.paras)
  261. }
  262. }
  263. func (m *M) do_bugs() {
  264. bs := m.docs.Bugs
  265. if len(bs) > 0 {
  266. m.section("BUGS")
  267. m.text(bytes.TrimSpace([]byte(bs[0])))
  268. for _, b := range bs[1:] {
  269. m.PP()
  270. m.text(bytes.TrimSpace([]byte(b)))
  271. }
  272. }
  273. }
  274. func (m *M) _seealso1(s string) {
  275. m.WriteString(".BR ")
  276. piv := strings.Index(s, "(")
  277. m.Write(escape([]byte(s[:piv])))
  278. m.WriteByte(' ')
  279. m.WriteString(s[piv:])
  280. }
  281. func (m *M) do_see_also() {
  282. if len(m.refs) > 0 {
  283. m.section("SEE ALSO")
  284. last := len(m.refs) - 1
  285. for _, s := range m.refs[:last] {
  286. m._seealso1(s)
  287. m.WriteString(",\n")
  288. }
  289. m._seealso1(m.refs[last])
  290. }
  291. }