123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- // Copyright (c) 2014-2017 TSUYUSATO Kitsune
- // This software is released under the MIT License.
- // http://opensource.org/licenses/mit-license.php
- // Package heredoc provides creation of here-documents from raw strings.
- //
- // Golang supports raw-string syntax.
- // doc := `
- // Foo
- // Bar
- // `
- // But raw-string cannot recognize indentation. Thus such content is an indented string, equivalent to
- // "\n\tFoo\n\tBar\n"
- // I dont't want this!
- //
- // However this problem is solved by package heredoc.
- // doc := heredoc.Doc(`
- // Foo
- // Bar
- // `)
- // Is equivalent to
- // "Foo\nBar\n"
- package heredoc
- import (
- "fmt"
- "strings"
- "unicode"
- )
- const maxInt = int(^uint(0) >> 1)
- // Doc returns un-indented string as here-document.
- func Doc(raw string) string {
- skipFirstLine := false
- if raw[0] == '\n' {
- raw = raw[1:]
- } else {
- skipFirstLine = true
- }
- lines := strings.Split(raw, "\n")
- minIndentSize := getMinIndent(lines, skipFirstLine)
- lines = removeIndentation(lines, minIndentSize, skipFirstLine)
- return strings.Join(lines, "\n")
- }
- // getMinIndent calculates the minimum indentation in lines, excluding empty lines.
- func getMinIndent(lines []string, skipFirstLine bool) int {
- minIndentSize := maxInt
- for i, line := range lines {
- if i == 0 && skipFirstLine {
- continue
- }
- indentSize := 0
- for _, r := range []rune(line) {
- if unicode.IsSpace(r) {
- indentSize += 1
- } else {
- break
- }
- }
- if len(line) == indentSize {
- if i == len(lines)-1 && indentSize < minIndentSize {
- lines[i] = ""
- }
- } else if indentSize < minIndentSize {
- minIndentSize = indentSize
- }
- }
- return minIndentSize
- }
- // removeIndentation removes n characters from the front of each line in lines.
- // Skips first line if skipFirstLine is true, skips empty lines.
- func removeIndentation(lines []string, n int, skipFirstLine bool) []string {
- for i, line := range lines {
- if i == 0 && skipFirstLine {
- continue
- }
- if len(lines[i]) >= n {
- lines[i] = line[n:]
- }
- }
- return lines
- }
- // Docf returns unindented and formatted string as here-document.
- // Formatting is done as for fmt.Printf().
- func Docf(raw string, args ...interface{}) string {
- return fmt.Sprintf(Doc(raw), args...)
- }
|