Skip to content

Commit 59cb973

Browse files
committed
Add links to each literate code file in book
This automatically adds citation links to each literate code file when the litgo plugin is used.
1 parent 19929ec commit 59cb973

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed

docs/book/src/cronjob-tutorial/basic-project.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ basic pieces of boilerplate.
77

88
First up, basic infrastructure for building your project:
99

10-
<details><summary>`go.mod`: A new Go module matching our project, with basic dependencies</summary>
10+
<details> <summary>`go.mod`: A new Go module matching our project, with
11+
basic dependencies</summary>
1112

1213
```go
1314
{{#include ./testdata/project/go.mod}}

docs/book/theme/css/general.css

+17
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,20 @@ aside.note.warning > h1::before {
439439
border-radius: 50%;
440440
border: 2px solid var(--warning-note-color, #f0ad4e);
441441
}
442+
443+
/* literate source citations */
444+
cite.literate-source {
445+
font-size: 75%;
446+
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
447+
}
448+
cite.literate-source::before {
449+
content: "$ ";
450+
font-weight: bold;
451+
font-style: normal;
452+
}
453+
454+
cite.literate-source > a::before {
455+
content: "vim ";
456+
font-style: normal;
457+
color: var(--fg);
458+
}

docs/book/utils/litgo/literate.go

+69-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"log"
2727
"strings"
2828
"unicode"
29+
"path"
30+
"net/url"
2931

3032
"sigs.k8s.io/kubebuilder/docs/book/utils/plugin"
3133
)
@@ -36,22 +38,62 @@ import (
3638
// It's triggered by using the an expression like `{{#literatego ./path/to/source/file.go}}`.
3739
// The marker `+kubebuilder:docs-gen:collapse=<string>` can be used to collapse a description/code
3840
// 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+
}
4047
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)
4250
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()
4458

4559
// TODO(directxman12): don't escape root?
4660
contents, err := ioutil.ReadFile(path)
4761
if err != nil {
4862
return "", fmt.Errorf("unable to import %q: %v", path, err)
4963
}
5064

51-
return extractContents(contents, path)
65+
return l.extractContents(contents, pathInfo)
5266
})
5367
}
5468

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+
5597
// commentCodePair represents a block of code with some text before it, optionally
5698
// marked as collapsed with the given "collapse summary".
5799
type commentCodePair struct {
@@ -173,13 +215,25 @@ func extractPairs(contents []byte, path string) ([]commentCodePair, error) {
173215

174216
// extractContents extracts comment-code pairs from the given named file
175217
// 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())
178220
if err != nil {
179221
return "", err
180222
}
181223

182224
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))
183237

184238
for _, pair := range pairs {
185239
if pair.collapse != "" {
@@ -218,7 +272,15 @@ func wrapWithNewlines(src string) string {
218272
}
219273

220274
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 {
222284
log.Fatal(err.Error())
223285
}
224286
}

0 commit comments

Comments
 (0)