@@ -26,6 +26,8 @@ import (
26
26
"log"
27
27
"strings"
28
28
"unicode"
29
+ "path"
30
+ "net/url"
29
31
30
32
"sigs.k8s.io/kubebuilder/docs/book/utils/plugin"
31
33
)
@@ -36,22 +38,62 @@ import (
36
38
// It's triggered by using the an expression like `{{#literatego ./path/to/source/file.go}}`.
37
39
// The marker `+kubebuilder:docs-gen:collapse=<string>` can be used to collapse a description/code
38
40
// pair into a details block with the given summary.
39
- type Literate struct {}
41
+ type Literate struct {
42
+ // PrettyPathPrunePrefix specifies the prefix, if any to prune off of user-visible paths
43
+ PrettyPathPrunePrefix string
44
+ // BaseSourcePath specifies the base path to internet-reachable versions of the source code used
45
+ BaseSourcePath * url.URL
46
+ }
40
47
func (_ Literate ) SupportsOutput (_ string ) bool { return true }
41
- func (_ Literate ) Process (input * plugin.Input ) error {
48
+ func (l Literate ) Process (input * plugin.Input ) error {
49
+ bookSrcDir := filepath .Join (input .Context .Root , input .Context .Config .Book .Src )
42
50
return plugin .EachCommand (& input .Book , "literatego" , func (chapter * plugin.BookChapter , relPath string ) (string , error ) {
43
- path := filepath .Join (input .Context .Root , input .Context .Config .Book .Src , filepath .Dir (chapter .Path ), relPath )
51
+ chapterDir := filepath .Dir (chapter .Path )
52
+ pathInfo := filePathInfo {
53
+ chapterRelativePath : relPath ,
54
+ chapterDir : chapterDir ,
55
+ bookSrcDir : bookSrcDir ,
56
+ }
57
+ path := pathInfo .FullPath ()
44
58
45
59
// TODO(directxman12): don't escape root?
46
60
contents , err := ioutil .ReadFile (path )
47
61
if err != nil {
48
62
return "" , fmt .Errorf ("unable to import %q: %v" , path , err )
49
63
}
50
64
51
- return extractContents (contents , path )
65
+ return l . extractContents (contents , pathInfo )
52
66
})
53
67
}
54
68
69
+ // filePathInfo stores different paths to a file, to allow for nicely
70
+ // displaying relative path information.
71
+ type filePathInfo struct {
72
+ // chapterRelativePath is the path relative to the current chapter file
73
+ chapterRelativePath string
74
+
75
+ // chapterDir is the directory of the chapter, relative to bookSrcDir
76
+ chapterDir string
77
+
78
+ // bookSrcDir is the absoulte book source path
79
+ bookSrcDir string
80
+ }
81
+
82
+ // FullPath resturns the full, absolute path to the given file on the source filesystem.
83
+ func (f filePathInfo ) FullPath () string {
84
+ return filepath .Join (f .bookSrcDir , f .chapterDir , f .chapterRelativePath )
85
+ }
86
+
87
+ // viewablePath returns the internet-viewable path to the given source file
88
+ func (f filePathInfo ) ViewablePath (baseBookSrcURL url.URL ) string {
89
+ relPath := filepath .ToSlash (filepath .Join (f .chapterDir , f .chapterRelativePath ))
90
+ outURL := baseBookSrcURL
91
+
92
+ outURL .Path = path .Join (outURL .Path , relPath )
93
+
94
+ return outURL .String ()
95
+ }
96
+
55
97
// commentCodePair represents a block of code with some text before it, optionally
56
98
// marked as collapsed with the given "collapse summary".
57
99
type commentCodePair struct {
@@ -173,13 +215,25 @@ func extractPairs(contents []byte, path string) ([]commentCodePair, error) {
173
215
174
216
// extractContents extracts comment-code pairs from the given named file
175
217
// contents, and then renders the result to markdown.
176
- func extractContents (contents []byte , path string ) (string , error ) {
177
- pairs , err := extractPairs (contents , path )
218
+ func ( l Literate ) extractContents (contents []byte , pathInfo filePathInfo ) (string , error ) {
219
+ pairs , err := extractPairs (contents , pathInfo . FullPath () )
178
220
if err != nil {
179
221
return "" , err
180
222
}
181
223
182
224
out := new (strings.Builder )
225
+
226
+ // write the source so that readers can easily find the code
227
+ sourcePath := pathInfo .ViewablePath (* l .BaseSourcePath )
228
+ prettyPath := pathInfo .chapterRelativePath
229
+ if l .PrettyPathPrunePrefix != "" {
230
+ prunedPath , err := filepath .Rel (l .PrettyPathPrunePrefix , prettyPath )
231
+ if err != nil {
232
+ return "" , fmt .Errorf ("unable to remove path prefix %q from %q: %v" , l .PrettyPathPrunePrefix , prettyPath , err )
233
+ }
234
+ prettyPath = prunedPath
235
+ }
236
+ out .WriteString (fmt .Sprintf (`<cite class="literate-source"><a href="%[1]s">%[2]s</a></cite>` , sourcePath , prettyPath ))
183
237
184
238
for _ , pair := range pairs {
185
239
if pair .collapse != "" {
@@ -218,7 +272,15 @@ func wrapWithNewlines(src string) string {
218
272
}
219
273
220
274
func main () {
221
- if err := plugin .Run (Literate {}, os .Stdin , os .Stdout , os .Args [1 :]... ); err != nil {
275
+ baseURL , err := url .Parse ("https://sigs.k8s.io/kubebuilder/docs/book/src" )
276
+ if err != nil {
277
+ log .Fatal (err .Error ())
278
+ }
279
+ cfg := Literate {
280
+ PrettyPathPrunePrefix : "testdata" ,
281
+ BaseSourcePath : baseURL ,
282
+ }
283
+ if err := plugin .Run (cfg , os .Stdin , os .Stdout , os .Args [1 :]... ); err != nil {
222
284
log .Fatal (err .Error ())
223
285
}
224
286
}
0 commit comments