Left: | ||
Right: |
OLD | NEW |
---|---|
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" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
54 var doc string | 54 var doc string |
55 if f.Doc != nil { | 55 if f.Doc != nil { |
56 doc = f.Doc.Text() | 56 doc = f.Doc.Text() |
57 } | 57 } |
58 flist = append(flist, &Example{ | 58 flist = append(flist, &Example{ |
59 Name: name[len("Example"):], | 59 Name: name[len("Example"):], |
60 Doc: doc, | 60 Doc: doc, |
61 Code: f.Body, | 61 Code: f.Body, |
62 Play: playExample(file, f.Body), | 62 Play: playExample(file, f.Body), |
63 Comments: file.Comments, | 63 Comments: file.Comments, |
64 » » » » Output: exampleOutput(f, file.Comments), | 64 » » » » Output: exampleOutput(f.Body, file.Comments), |
65 }) | 65 }) |
66 } | 66 } |
67 if !hasTests && numDecl > 1 && len(flist) == 1 { | 67 if !hasTests && numDecl > 1 && len(flist) == 1 { |
68 // If this file only has one example function, some | 68 // If this file only has one example function, some |
69 // other top-level declarations, and no tests or | 69 // other top-level declarations, and no tests or |
70 // benchmarks, use the whole file as the example. | 70 // benchmarks, use the whole file as the example. |
71 flist[0].Code = file | 71 flist[0].Code = file |
72 } | 72 } |
73 list = append(list, flist...) | 73 list = append(list, flist...) |
74 } | 74 } |
75 sort.Sort(exampleByName(list)) | 75 sort.Sort(exampleByName(list)) |
76 return list | 76 return list |
77 } | 77 } |
78 | 78 |
79 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) | 79 var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) |
80 | 80 |
81 func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string { | 81 func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) string { |
82 // find the last comment in the function | 82 // find the last comment in the function |
83 var last *ast.CommentGroup | 83 var last *ast.CommentGroup |
84 for _, cg := range comments { | 84 for _, cg := range comments { |
85 » » if cg.Pos() < fun.Pos() { | 85 » » if cg.Pos() < b.Pos() { |
86 continue | 86 continue |
87 } | 87 } |
88 » » if cg.End() > fun.End() { | 88 » » if cg.End() > b.End() { |
89 break | 89 break |
90 } | 90 } |
91 last = cg | 91 last = cg |
92 } | 92 } |
93 if last != nil { | 93 if last != nil { |
94 // test that it begins with the correct prefix | 94 // test that it begins with the correct prefix |
95 text := last.Text() | 95 text := last.Text() |
96 if loc := outputPrefix.FindStringIndex(text); loc != nil { | 96 if loc := outputPrefix.FindStringIndex(text); loc != nil { |
97 » » » return strings.TrimSpace(text[loc[1]:]) | 97 » » » return strings.TrimSpace(last.Text()[loc[1]:]) |
gri
2012/09/28 18:03:38
Why is text here not ok? it there's a reason, it n
adg
2012/10/01 22:35:49
This was an accident of some experimentation I did
| |
98 } | 98 } |
99 } | 99 } |
100 return "" // no suitable comment found | 100 return "" // no suitable comment found |
101 } | 101 } |
102 | 102 |
103 // isTest tells whether name looks like a test, example, or benchmark. | 103 // isTest tells whether name looks like a test, example, or benchmark. |
104 // It is a Test (say) if there is a character after Test that is not a | 104 // It is a Test (say) if there is a character after Test that is not a |
105 // lower-case letter. (We don't want Testiness.) | 105 // lower-case letter. (We don't want Testiness.) |
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) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 n = s.Name.Name | 156 n = s.Name.Name |
157 } | 157 } |
158 for _, id := range unresolved { | 158 for _, id := range unresolved { |
159 if n == id.Name { | 159 if n == id.Name { |
160 imports[n] = p | 160 imports[n] = p |
161 break | 161 break |
162 } | 162 } |
163 } | 163 } |
164 } | 164 } |
165 | 165 |
166 // TODO(adg): look for other unresolved identifiers and, if found, give up. | |
167 | |
168 // Synthesize new imports. | 166 // Synthesize new imports. |
169 importDecl := &ast.GenDecl{ | 167 importDecl := &ast.GenDecl{ |
170 Tok: token.IMPORT, | 168 Tok: token.IMPORT, |
171 Lparen: 1, // Need non-zero Lparen and Rparen so that printer | 169 Lparen: 1, // Need non-zero Lparen and Rparen so that printer |
172 Rparen: 1, // treats this as a factored import. | 170 Rparen: 1, // treats this as a factored import. |
173 } | 171 } |
174 for n, p := range imports { | 172 for n, p := range imports { |
175 s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p) }} | 173 s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p) }} |
176 if path.Base(p) != n { | 174 if path.Base(p) != n { |
177 s.Name = ast.NewIdent(n) | 175 s.Name = ast.NewIdent(n) |
178 } | 176 } |
179 importDecl.Specs = append(importDecl.Specs, s) | 177 importDecl.Specs = append(importDecl.Specs, s) |
180 } | 178 } |
181 | 179 |
182 » // Synthesize main function. | 180 » // TODO(adg): look for other unresolved identifiers and, if found, give up. |
183 » funcDecl := &ast.FuncDecl{ | |
184 » » Name: ast.NewIdent("main"), | |
185 » » Type: &ast.FuncType{}, | |
186 » » Body: body, | |
187 » } | |
188 | 181 |
189 // Filter out comments that are outside the function body. | 182 // Filter out comments that are outside the function body. |
190 var comments []*ast.CommentGroup | 183 var comments []*ast.CommentGroup |
191 for _, c := range file.Comments { | 184 for _, c := range file.Comments { |
192 if c.Pos() < body.Pos() || c.Pos() >= body.End() { | 185 if c.Pos() < body.Pos() || c.Pos() >= body.End() { |
193 continue | 186 continue |
194 } | 187 } |
195 comments = append(comments, c) | 188 comments = append(comments, c) |
196 } | 189 } |
197 | 190 |
191 // Strip "Output:" commment and adjust body end position. | |
192 if len(comments) > 0 { | |
193 last := comments[len(comments)-1] | |
194 if outputPrefix.MatchString(last.Text()) { | |
195 comments = comments[:len(comments)-1] | |
196 // Copy body, as the original may be used elsewhere. | |
197 body = &ast.BlockStmt{ | |
198 Lbrace: body.Pos(), | |
199 List: body.List, | |
200 Rbrace: last.Pos(), | |
201 } | |
202 } | |
203 } | |
204 | |
205 // Synthesize main function. | |
206 funcDecl := &ast.FuncDecl{ | |
207 Name: ast.NewIdent("main"), | |
208 Type: &ast.FuncType{}, | |
209 Body: body, | |
210 } | |
211 | |
198 // Synthesize file. | 212 // Synthesize file. |
199 f := &ast.File{ | 213 f := &ast.File{ |
200 Name: ast.NewIdent("main"), | 214 Name: ast.NewIdent("main"), |
201 Decls: []ast.Decl{importDecl, funcDecl}, | 215 Decls: []ast.Decl{importDecl, funcDecl}, |
202 Comments: comments, | 216 Comments: comments, |
203 } | 217 } |
204 | 218 |
205 // TODO(adg): look for resolved identifiers declared outside function sc ope | 219 // TODO(adg): look for resolved identifiers declared outside function sc ope |
206 // and include their declarations in the new file. | 220 // and include their declarations in the new file. |
207 | 221 |
208 return f | 222 return f |
209 } | 223 } |
OLD | NEW |