You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

124 lines
2.9 KiB

  1. # Copyright 2017 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # snipmd inserts code snippets from Go source files into a markdown file.
  15. #
  16. # Call with one or more .go files and a .md file:
  17. #
  18. # awk -f snipmd.awk foo.go bar.go template.md
  19. #
  20. # In the Go files, start a snippet with
  21. # //[ NAME
  22. # and end it with
  23. # //]
  24. #
  25. # In the markdown, write
  26. # [snip]:# NAME
  27. # to insert the snippet NAME just below that line.
  28. # If there is already a code block after the [snip]:# line, it will be
  29. # replaced, so a previous output can be used as input.
  30. #
  31. # The following transformations are made to the Go code:
  32. # - The first tab of each line is removed.
  33. # - Trailing blank lines are removed.
  34. # - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...`
  35. /^[ \t]*\/\/\[/ { # start snippet in Go file
  36. if (inGo()) {
  37. if ($2 == "") {
  38. die("missing snippet name")
  39. }
  40. curSnip = $2
  41. next
  42. }
  43. }
  44. /^[ \t]*\/\/]/ { # end snippet in Go file
  45. if (inGo()) {
  46. if (curSnip != "") {
  47. # Remove all but one trailing newline.
  48. gsub(/\n+$/, "\n", snips[curSnip])
  49. curSnip = ""
  50. next
  51. } else {
  52. die("//] without corresponding //[")
  53. }
  54. }
  55. }
  56. ENDFILE {
  57. if (curSnip != "") {
  58. die("unclosed snippet: " curSnip)
  59. }
  60. }
  61. # Skip code blocks in the input that immediately follow [snip]:# lines,
  62. # because we just inserted the snippet. Supports round-tripping.
  63. /^```go$/,/^```$/ {
  64. if (inMarkdown() && afterSnip) {
  65. next
  66. }
  67. }
  68. # Matches every line.
  69. {
  70. if (curSnip != "") {
  71. line = $0
  72. # Remove initial tab, if any.
  73. if (line ~ /^\t/) {
  74. line = substr(line, 2)
  75. }
  76. # Replace ELLIPSIS.
  77. gsub(/_ = ELLIPSIS/, "...", line)
  78. gsub(/ELLIPSIS/, "...", line)
  79. snips[curSnip] = snips[curSnip] line "\n"
  80. } else if (inMarkdown()) {
  81. afterSnip = 0
  82. # Copy .md to output.
  83. print
  84. }
  85. }
  86. $1 ~ /\[snip\]:#/ { # Snippet marker in .md file.
  87. if (inMarkdown()) {
  88. # We expect '[snip]:#' to be followed by '(NAME)'
  89. if ($2 !~ /\(.*\)/) {
  90. die("bad snip spec: " $0)
  91. }
  92. name = substr($2, 2, length($2)-2)
  93. if (snips[name] == "") {
  94. die("no snippet named " name)
  95. }
  96. printf("```go\n%s```\n", snips[name])
  97. afterSnip = 1
  98. }
  99. }
  100. function inMarkdown() {
  101. return match(FILENAME, /\.md$/)
  102. }
  103. function inGo() {
  104. return match(FILENAME, /\.go$/)
  105. }
  106. function die(msg) {
  107. printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr"
  108. exit 1
  109. }