| # Copyright 2017 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # snipmd inserts code snippets from Go source files into a markdown file. |
| # |
| # Call with one or more .go files and a .md file: |
| # |
| # awk -f snipmd.awk foo.go bar.go template.md |
| # |
| # In the Go files, start a snippet with |
| # //[ NAME |
| # and end it with |
| # //] |
| # |
| # In the markdown, write |
| # [snip]:# NAME |
| # to insert the snippet NAME just below that line. |
| # If there is already a code block after the [snip]:# line, it will be |
| # replaced, so a previous output can be used as input. |
| # |
| # The following transformations are made to the Go code: |
| # - The first tab of each line is removed. |
| # - Trailing blank lines are removed. |
| # - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...` |
| |
| |
| /^[ \t]*\/\/\[/ { # start snippet in Go file |
| if (inGo()) { |
| if ($2 == "") { |
| die("missing snippet name") |
| } |
| curSnip = $2 |
| next |
| } |
| } |
| |
| /^[ \t]*\/\/]/ { # end snippet in Go file |
| if (inGo()) { |
| if (curSnip != "") { |
| # Remove all but one trailing newline. |
| gsub(/\n+$/, "\n", snips[curSnip]) |
| curSnip = "" |
| next |
| } else { |
| die("//] without corresponding //[") |
| } |
| } |
| } |
| |
| ENDFILE { |
| if (curSnip != "") { |
| die("unclosed snippet: " curSnip) |
| } |
| } |
| |
| # Skip code blocks in the input that immediately follow [snip]:# lines, |
| # because we just inserted the snippet. Supports round-tripping. |
| /^```go$/,/^```$/ { |
| if (inMarkdown() && afterSnip) { |
| next |
| } |
| } |
| |
| # Matches every line. |
| { |
| if (curSnip != "") { |
| line = $0 |
| # Remove initial tab, if any. |
| if (line ~ /^\t/) { |
| line = substr(line, 2) |
| } |
| # Replace ELLIPSIS. |
| gsub(/_ = ELLIPSIS/, "...", line) |
| gsub(/ELLIPSIS/, "...", line) |
| |
| snips[curSnip] = snips[curSnip] line "\n" |
| } else if (inMarkdown()) { |
| afterSnip = 0 |
| # Copy .md to output. |
| print |
| } |
| } |
| |
| $1 ~ /\[snip\]:#/ { # Snippet marker in .md file. |
| if (inMarkdown()) { |
| # We expect '[snip]:#' to be followed by '(NAME)' |
| if ($2 !~ /\(.*\)/) { |
| die("bad snip spec: " $0) |
| } |
| name = substr($2, 2, length($2)-2) |
| if (snips[name] == "") { |
| die("no snippet named " name) |
| } |
| printf("```go\n%s```\n", snips[name]) |
| afterSnip = 1 |
| } |
| } |
| |
| |
| function inMarkdown() { |
| return match(FILENAME, /\.md$/) |
| } |
| |
| function inGo() { |
| return match(FILENAME, /\.go$/) |
| } |
| |
| |
| function die(msg) { |
| printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr" |
| exit 1 |
| } |