LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Extract example functions from file ASTs. | 5 // Extract example functions from file ASTs. |
6 | 6 |
7 package doc | 7 package doc |
8 | 8 |
9 import ( | 9 import ( |
10 "go/ast" | 10 "go/ast" |
11 "go/token" | 11 "go/token" |
12 "path" | 12 "path" |
13 "regexp" | 13 "regexp" |
14 "sort" | |
15 "strconv" | 14 "strconv" |
16 "strings" | 15 "strings" |
17 "unicode" | 16 "unicode" |
18 "unicode/utf8" | 17 "unicode/utf8" |
19 ) | 18 ) |
20 | 19 |
21 type Example struct { | 20 type Example struct { |
22 Name string // name of the item being exemplified | 21 Name string // name of the item being exemplified |
23 Doc string // example function doc string | 22 Doc string // example function doc string |
24 Code ast.Node | 23 Code ast.Node |
25 Play *ast.File // a whole program version of the example | 24 Play *ast.File // a whole program version of the example |
26 Comments []*ast.CommentGroup | 25 Comments []*ast.CommentGroup |
27 Output string // expected output | 26 Output string // expected output |
28 EmptyOutput bool // expect empty output | 27 EmptyOutput bool // expect empty output |
| 28 Order int // original source order |
29 } | 29 } |
30 | 30 |
31 func Examples(files ...*ast.File) []*Example { | 31 func Examples(files ...*ast.File) []*Example { |
32 var list []*Example | 32 var list []*Example |
33 » for _, file := range files { | 33 » for i, file := range files { |
34 hasTests := false // file contains tests or benchmarks | 34 hasTests := false // file contains tests or benchmarks |
35 numDecl := 0 // number of non-import declarations in the fi
le | 35 numDecl := 0 // number of non-import declarations in the fi
le |
36 var flist []*Example | 36 var flist []*Example |
37 for _, decl := range file.Decls { | 37 for _, decl := range file.Decls { |
38 if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IM
PORT { | 38 if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IM
PORT { |
39 numDecl++ | 39 numDecl++ |
40 continue | 40 continue |
41 } | 41 } |
42 f, ok := decl.(*ast.FuncDecl) | 42 f, ok := decl.(*ast.FuncDecl) |
43 if !ok { | 43 if !ok { |
(...skipping 14 matching lines...) Expand all Loading... |
58 } | 58 } |
59 output, hasOutput := exampleOutput(f.Body, file.Comments
) | 59 output, hasOutput := exampleOutput(f.Body, file.Comments
) |
60 flist = append(flist, &Example{ | 60 flist = append(flist, &Example{ |
61 Name: name[len("Example"):], | 61 Name: name[len("Example"):], |
62 Doc: doc, | 62 Doc: doc, |
63 Code: f.Body, | 63 Code: f.Body, |
64 Play: playExample(file, f.Body), | 64 Play: playExample(file, f.Body), |
65 Comments: file.Comments, | 65 Comments: file.Comments, |
66 Output: output, | 66 Output: output, |
67 EmptyOutput: output == "" && hasOutput, | 67 EmptyOutput: output == "" && hasOutput, |
| 68 Order: i, |
68 }) | 69 }) |
69 } | 70 } |
70 if !hasTests && numDecl > 1 && len(flist) == 1 { | 71 if !hasTests && numDecl > 1 && len(flist) == 1 { |
71 // If this file only has one example function, some | 72 // If this file only has one example function, some |
72 // other top-level declarations, and no tests or | 73 // other top-level declarations, and no tests or |
73 // benchmarks, use the whole file as the example. | 74 // benchmarks, use the whole file as the example. |
74 flist[0].Code = file | 75 flist[0].Code = file |
75 flist[0].Play = playExampleFile(file) | 76 flist[0].Play = playExampleFile(file) |
76 } | 77 } |
77 list = append(list, flist...) | 78 list = append(list, flist...) |
78 } | 79 } |
79 sort.Sort(exampleByName(list)) | |
80 return list | 80 return list |
81 } | 81 } |
82 | 82 |
83 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) | 83 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) |
84 | 84 |
85 // Extracts the expected output and whether there was a valid output comment | 85 // Extracts the expected output and whether there was a valid output comment |
86 func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output strin
g, ok bool) { | 86 func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output strin
g, ok bool) { |
87 if _, last := lastComment(b, comments); last != nil { | 87 if _, last := lastComment(b, comments); last != nil { |
88 // test that it begins with the correct prefix | 88 // test that it begins with the correct prefix |
89 text := last.Text() | 89 text := last.Text() |
(...skipping 16 matching lines...) Expand all Loading... |
106 func isTest(name, prefix string) bool { | 106 func isTest(name, prefix string) bool { |
107 if !strings.HasPrefix(name, prefix) { | 107 if !strings.HasPrefix(name, prefix) { |
108 return false | 108 return false |
109 } | 109 } |
110 if len(name) == len(prefix) { // "Test" is ok | 110 if len(name) == len(prefix) { // "Test" is ok |
111 return true | 111 return true |
112 } | 112 } |
113 rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) | 113 rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) |
114 return !unicode.IsLower(rune) | 114 return !unicode.IsLower(rune) |
115 } | 115 } |
116 | |
117 type exampleByName []*Example | |
118 | |
119 func (s exampleByName) Len() int { return len(s) } | |
120 func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
121 func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name } | |
122 | 116 |
123 // playExample synthesizes a new *ast.File based on the provided | 117 // playExample synthesizes a new *ast.File based on the provided |
124 // file with the provided function body as the body of main. | 118 // file with the provided function body as the body of main. |
125 func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { | 119 func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { |
126 if !strings.HasSuffix(file.Name.Name, "_test") { | 120 if !strings.HasSuffix(file.Name.Name, "_test") { |
127 // We don't support examples that are part of the | 121 // We don't support examples that are part of the |
128 // greater package (yet). | 122 // greater package (yet). |
129 return nil | 123 return nil |
130 } | 124 } |
131 | 125 |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
323 if cg.Pos() < pos { | 317 if cg.Pos() < pos { |
324 continue | 318 continue |
325 } | 319 } |
326 if cg.End() > end { | 320 if cg.End() > end { |
327 break | 321 break |
328 } | 322 } |
329 i, last = j, cg | 323 i, last = j, cg |
330 } | 324 } |
331 return | 325 return |
332 } | 326 } |
LEFT | RIGHT |