github.com/google/yamlfmt@v0.12.2-0.20240514121411-7f77800e2681/internal/hotfix/retain_line_break.go (about) 1 // Copyright 2022 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 // The features in this file are to retain line breaks. 16 // The basic idea is to insert/remove placeholder comments in the yaml document before and after the format process. 17 18 package hotfix 19 20 import ( 21 "bufio" 22 "bytes" 23 "strings" 24 25 "github.com/google/yamlfmt" 26 ) 27 28 const lineBreakPlaceholder = "#magic___^_^___line" 29 30 type paddinger struct { 31 strings.Builder 32 } 33 34 func (p *paddinger) adjust(txt string) { 35 var indentSize int 36 for i := 0; i < len(txt) && txt[i] == ' '; i++ { // yaml only allows space to indent. 37 indentSize++ 38 } 39 // Grows if the given size is larger than us and always return the max padding. 40 for diff := indentSize - p.Len(); diff > 0; diff-- { 41 p.WriteByte(' ') 42 } 43 } 44 45 func MakeFeatureRetainLineBreak(linebreakStr string, chomp bool) yamlfmt.Feature { 46 return yamlfmt.Feature{ 47 Name: "Retain Line Breaks", 48 BeforeAction: replaceLineBreakFeature(linebreakStr, chomp), 49 AfterAction: restoreLineBreakFeature(linebreakStr), 50 } 51 } 52 53 func replaceLineBreakFeature(newlineStr string, chomp bool) yamlfmt.FeatureFunc { 54 return func(content []byte) ([]byte, error) { 55 var buf bytes.Buffer 56 reader := bytes.NewReader(content) 57 scanner := bufio.NewScanner(reader) 58 var inLineBreaks bool 59 var padding paddinger 60 for scanner.Scan() { 61 txt := scanner.Text() 62 padding.adjust(txt) 63 if strings.TrimSpace(txt) == "" { // line break or empty space line. 64 if chomp && inLineBreaks { 65 continue 66 } 67 buf.WriteString(padding.String()) // prepend some padding incase literal multiline strings. 68 buf.WriteString(lineBreakPlaceholder) 69 buf.WriteString(newlineStr) 70 inLineBreaks = true 71 } else { 72 buf.WriteString(txt) 73 buf.WriteString(newlineStr) 74 inLineBreaks = false 75 } 76 } 77 return buf.Bytes(), scanner.Err() 78 } 79 } 80 81 func restoreLineBreakFeature(newlineStr string) yamlfmt.FeatureFunc { 82 return func(content []byte) ([]byte, error) { 83 var buf bytes.Buffer 84 reader := bytes.NewReader(content) 85 scanner := bufio.NewScanner(reader) 86 for scanner.Scan() { 87 txt := scanner.Text() 88 if strings.TrimSpace(txt) == "" { 89 // The basic yaml lib inserts newline when there is a comment(either placeholder or by user) 90 // followed by optional line breaks and a `---` multi-documents. 91 // To fix it, the empty line could only be inserted by us. 92 continue 93 } 94 if strings.HasPrefix(strings.TrimLeft(txt, " "), lineBreakPlaceholder) { 95 buf.WriteString(newlineStr) 96 continue 97 } 98 buf.WriteString(txt) 99 buf.WriteString(newlineStr) 100 } 101 return buf.Bytes(), scanner.Err() 102 } 103 }