github.com/openconfig/goyang@v1.4.5/pkg/indent/indent.go (about) 1 // Copyright 2015 Google Inc. 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 // Package indent indents lines of text. 16 package indent 17 18 import ( 19 "bytes" 20 "io" 21 "strings" 22 ) 23 24 // String returns s with each line in s prefixed by indent. 25 func String(indent, s string) string { 26 if indent == "" || s == "" { 27 return s 28 } 29 lines := strings.SplitAfter(s, "\n") 30 if len(lines[len(lines)-1]) == 0 { 31 lines = lines[:len(lines)-1] 32 } 33 return strings.Join(append([]string{""}, lines...), indent) 34 } 35 36 // Bytes returns b with each line in b prefixed by indent. 37 func Bytes(indent, b []byte) []byte { 38 if len(indent) == 0 || len(b) == 0 { 39 return b 40 } 41 lines := bytes.SplitAfter(b, []byte{'\n'}) 42 if len(lines[len(lines)-1]) == 0 { 43 lines = lines[:len(lines)-1] 44 } 45 return bytes.Join(append([][]byte{{}}, lines...), indent) 46 } 47 48 // NewWriter returns an io.Writer that prefixes the lines written to it with 49 // indent and then writes them to w. The writer returns the number of bytes 50 // written to the underlying Writer. 51 func NewWriter(w io.Writer, indent string) io.Writer { 52 if indent == "" { 53 return w 54 } 55 return &iw{ 56 w: w, 57 prefix: []byte(indent), 58 } 59 } 60 61 type iw struct { 62 w io.Writer 63 prefix []byte 64 partial bool // true if next line's indent already written 65 } 66 67 // Write implements io.Writer. 68 func (w *iw) Write(buf []byte) (int, error) { 69 if len(buf) == 0 { 70 return 0, nil 71 } 72 lines := bytes.SplitAfter(buf, []byte{'\n'}) 73 if len(lines[len(lines)-1]) == 0 { 74 lines = lines[:len(lines)-1] 75 } 76 if !w.partial { 77 lines = append([][]byte{{}}, lines...) 78 } 79 joined := bytes.Join(lines, w.prefix) 80 w.partial = joined[len(joined)-1] != '\n' 81 82 n, err := w.w.Write(joined) 83 if err != nil { 84 return actualWrittenSize(n, len(w.prefix), lines), err 85 } 86 87 return len(buf), nil 88 } 89 90 func actualWrittenSize(underlay, prefix int, lines [][]byte) int { 91 actual := 0 92 remain := underlay 93 for _, line := range lines { 94 if len(line) == 0 { 95 continue 96 } 97 98 addition := remain - prefix 99 if addition <= 0 { 100 return actual 101 } 102 103 if addition <= len(line) { 104 return actual + addition 105 } 106 107 actual += len(line) 108 remain -= prefix + len(line) 109 } 110 111 return actual 112 }