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
15# snipdoc merges code snippets from Go source files into a template to
16# produce another go file (typically doc.go).
17#
18# Call with one or more .go files and a template file.
19#
20#    awk -f snipmd.awk foo.go bar.go doc.template
21#
22# In the Go files, start a snippet with
23#    //[ NAME
24# and end it with
25#    //]
26#
27# In the template, write
28#    [NAME]
29# on a line by itself to insert the snippet NAME on that line.
30#
31# The following transformations are made to the Go code:
32# - Trailing blank lines are removed.
33# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...`
34
35
36/^[ \t]*\/\/\[/ { # start snippet in Go file
37  if (inGo()) {
38    if ($2 == "") {
39      die("missing snippet name")
40    }
41    curSnip = $2
42    next
43  }
44}
45
46/^[ \t]*\/\/]/ {  # end snippet in Go file
47  if (inGo()) {
48    if (curSnip != "") {
49      # Remove all trailing newlines.
50      gsub(/[\t\n]+$/, "", snips[curSnip])
51      curSnip = ""
52      next
53    } else {
54      die("//] without corresponding //[")
55    }
56  }
57}
58
59ENDFILE {
60  if (curSnip != "") {
61    die("unclosed snippet: " curSnip)
62  }
63}
64
65/^\[.*\]$/ { # Snippet marker in template file.
66  if (inTemplate()) {
67    name = substr($1, 2, length($1)-2)
68    if (snips[name] == "") {
69      die("no snippet named " name)
70    }
71    printf("%s\n", snips[name])
72    afterSnip = 1
73    next
74  }
75}
76
77# Matches every line.
78{
79  if (curSnip != "") {
80    # If the first line in the snip has no indent, add the indent.
81    if (snips[curSnip] == "") {
82      if (index($0, "\t") == 1) {
83        extraIndent = ""
84      } else {
85        extraIndent = "\t"
86      }
87    }
88
89    line = $0
90    # Replace ELLIPSIS.
91    gsub(/_ = ELLIPSIS/, "...", line)
92    gsub(/ELLIPSIS/, "...", line)
93
94    snips[curSnip] = snips[curSnip] extraIndent line "\n"
95  } else if (inTemplate()) {
96    afterSnip = 0
97    # Copy to output.
98    print
99  }
100}
101
102
103
104function inTemplate() {
105  return match(FILENAME, /\.template$/)
106}
107
108function inGo() {
109  return match(FILENAME, /\.go$/)
110}
111
112
113function die(msg) {
114  printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr"
115  exit 1
116}
117