1package pgs 2 3import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "strings" 8 "unicode" 9 "unicode/utf8" 10) 11 12const commentPrefix = "//" 13 14// C returns a comment block, wrapping when the line's length will exceed wrap. 15func C(wrap int, args ...interface{}) string { 16 s := commentScanner(wrap, args...) 17 buf := &bytes.Buffer{} 18 19 for s.Scan() { 20 fmt.Fprintln(buf, commentPrefix, s.Text()) 21 } 22 23 return buf.String() 24} 25 26// C80 is an alias for C(80, args...) 27func C80(args ...interface{}) string { return C(80, args...) } 28 29func commentScanner(wrap int, args ...interface{}) *bufio.Scanner { 30 s := bufio.NewScanner(strings.NewReader(fmt.Sprint(args...))) 31 s.Split(splitComment(wrap - 3)) 32 return s 33} 34 35func splitComment(w int) bufio.SplitFunc { 36 return func(data []byte, atEOF bool) (advance int, token []byte, err error) { 37 var r rune 38 39 start := 0 40 lastSpace := 0 41 42 for width := 0; start < len(data); start += width { 43 r, width = utf8.DecodeRune(data[start:]) 44 if !unicode.IsSpace(r) { 45 break 46 } 47 } 48 49 for width, i := 0, start; i < len(data); i += width { 50 r, width = utf8.DecodeRune(data[i:]) 51 if unicode.IsSpace(r) { 52 if i >= w { // we are at our max comment width 53 if lastSpace == 0 { // the token cannot be broken down further, allow it to break the limit 54 return i + width, data[start:i], nil 55 } 56 return lastSpace, data[start:lastSpace], nil 57 } 58 lastSpace = i 59 } 60 } 61 62 if atEOF && len(data) > start { 63 return len(data), bytes.TrimSpace(data[start:]), nil 64 } 65 66 return start, nil, nil 67 } 68} 69