# 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
}
