@@ -252,15 +252,22 @@ func (f *formatter) writeFileHeader() {
252252 // There aren't any header values, so we can return early.
253253 return
254254 }
255+ // We use a sentinel value to track which node was written first (since a proto file
256+ // without a syntax is valid). This is then used to ensure that we do not preserve
257+ // unnecessary leading whitespace for the first node written.
258+ first := true
255259 editionNode := f .fileNode .Edition
256260 if editionNode != nil {
257261 f .writeEdition (editionNode )
262+ first = false
258263 }
259264 if syntaxNode := f .fileNode .Syntax ; syntaxNode != nil && editionNode == nil {
260265 f .writeSyntax (syntaxNode )
266+ first = false
261267 }
262268 if packageNode != nil {
263- f .writePackage (packageNode )
269+ f .writePackage (packageNode , first )
270+ first = false
264271 }
265272 sort .Slice (importNodes , func (i , j int ) bool {
266273 iName := importNodes [i ].Name .AsString ()
@@ -297,7 +304,8 @@ func (f *formatter) writeFileHeader() {
297304 continue
298305 }
299306
300- f .writeImport (importNode , i > 0 )
307+ f .writeImport (importNode , i > 0 , first )
308+ first = false
301309 }
302310 sort .Slice (optionNodes , func (i , j int ) bool {
303311 // The default options (e.g. cc_enable_arenas) should always
@@ -320,7 +328,8 @@ func (f *formatter) writeFileHeader() {
320328 if i == 0 && f .previousNode != nil && ! f .leadingCommentsContainBlankLine (optionNode ) {
321329 f .P ("" )
322330 }
323- f .writeFileOption (optionNode , i > 0 )
331+ f .writeFileOption (optionNode , i > 0 , first )
332+ first = false
324333 }
325334}
326335
@@ -349,7 +358,10 @@ func (f *formatter) writeFileTypes() {
349358//
350359// syntax = "proto3";
351360func (f * formatter ) writeSyntax (syntaxNode * ast.SyntaxNode ) {
352- f .writeStart (syntaxNode .Keyword )
361+ // If this is the first node, we want to ignore leading whitespace, unless there are
362+ // leading comments.
363+ info := f .nodeInfo (syntaxNode )
364+ f .writeStart (syntaxNode .Keyword , info .LeadingComments ().Len () == 0 )
353365 f .Space ()
354366 f .writeInline (syntaxNode .Equals )
355367 f .Space ()
@@ -363,7 +375,10 @@ func (f *formatter) writeSyntax(syntaxNode *ast.SyntaxNode) {
363375//
364376// edition = "2023";
365377func (f * formatter ) writeEdition (editionNode * ast.EditionNode ) {
366- f .writeStart (editionNode .Keyword )
378+ // If this is the first node, we want to ignore leading whitespace, unless there are
379+ // leading comments.
380+ info := f .nodeInfo (editionNode )
381+ f .writeStart (editionNode .Keyword , info .LeadingComments ().Len () == 0 )
367382 f .Space ()
368383 f .writeInline (editionNode .Equals )
369384 f .Space ()
@@ -376,8 +391,11 @@ func (f *formatter) writeEdition(editionNode *ast.EditionNode) {
376391// For example,
377392//
378393// package acme.weather.v1;
379- func (f * formatter ) writePackage (packageNode * ast.PackageNode ) {
380- f .writeStart (packageNode .Keyword )
394+ func (f * formatter ) writePackage (packageNode * ast.PackageNode , first bool ) {
395+ // If this is the first node, we want to ignore leading whitespace, unless there are
396+ // leading comments.
397+ info := f .nodeInfo (packageNode )
398+ f .writeStart (packageNode .Keyword , first && info .LeadingComments ().Len () == 0 )
381399 f .Space ()
382400 f .writeInline (packageNode .Name )
383401 f .writeLineEnd (packageNode .Semicolon )
@@ -388,8 +406,10 @@ func (f *formatter) writePackage(packageNode *ast.PackageNode) {
388406// For example,
389407//
390408// import "google/protobuf/descriptor.proto";
391- func (f * formatter ) writeImport (importNode * ast.ImportNode , forceCompact bool ) {
392- f .writeStartMaybeCompact (importNode .Keyword , forceCompact )
409+ func (f * formatter ) writeImport (importNode * ast.ImportNode , forceCompact , first bool ) {
410+ // If this is the first node, we want to ignore leading whitespace, unless there are
411+ // leading comments.
412+ f .writeStartMaybeCompact (importNode .Keyword , forceCompact , first && ! f .importHasComment (importNode ))
393413 f .Space ()
394414 // We don't want to write the "public" and "weak" nodes
395415 // if they aren't defined. One could be set, but never both.
@@ -408,8 +428,11 @@ func (f *formatter) writeImport(importNode *ast.ImportNode, forceCompact bool) {
408428// writeFileOption writes a file option. This function is slightly
409429// different than f.writeOption because file options are sorted at
410430// the top of the file, and leading comments are adjusted accordingly.
411- func (f * formatter ) writeFileOption (optionNode * ast.OptionNode , forceCompact bool ) {
412- f .writeStartMaybeCompact (optionNode .Keyword , forceCompact )
431+ func (f * formatter ) writeFileOption (optionNode * ast.OptionNode , forceCompact , first bool ) {
432+ // If this is the first node, we want to ignore leading whitespace, unless there are
433+ // leading comments.
434+ info := f .nodeInfo (optionNode )
435+ f .writeStartMaybeCompact (optionNode .Keyword , forceCompact , first && info .LeadingComments ().Len () == 0 )
413436 f .Space ()
414437 f .writeNode (optionNode .Name )
415438 f .Space ()
@@ -481,11 +504,11 @@ func (f *formatter) writeLastCompactOption(optionNode *ast.OptionNode) {
481504func (f * formatter ) writeOptionPrefix (optionNode * ast.OptionNode ) {
482505 if optionNode .Keyword != nil {
483506 // Compact options don't have the keyword.
484- f .writeStart (optionNode .Keyword )
507+ f .writeStart (optionNode .Keyword , false )
485508 f .Space ()
486509 f .writeNode (optionNode .Name )
487510 } else {
488- f .writeStart (optionNode .Name )
511+ f .writeStart (optionNode .Name , false )
489512 }
490513 f .Space ()
491514 f .writeInline (optionNode .Equals )
@@ -562,7 +585,7 @@ func (f *formatter) writeMessage(messageNode *ast.MessageNode) {
562585 }
563586 }
564587 }
565- f .writeStart (messageNode .Keyword )
588+ f .writeStart (messageNode .Keyword , false )
566589 f .Space ()
567590 f .writeInline (messageNode .Name )
568591 f .Space ()
@@ -791,14 +814,14 @@ func (f *formatter) writeMessageFieldPrefix(messageFieldNode *ast.MessageFieldNo
791814 // normally formatted in-line (i.e. as option name components).
792815 fieldReferenceNode := messageFieldNode .Name
793816 if fieldReferenceNode .Open != nil {
794- f .writeStart (fieldReferenceNode .Open )
817+ f .writeStart (fieldReferenceNode .Open , false )
795818 if fieldReferenceNode .URLPrefix != nil {
796819 f .writeInline (fieldReferenceNode .URLPrefix )
797820 f .writeInline (fieldReferenceNode .Slash )
798821 }
799822 f .writeInline (fieldReferenceNode .Name )
800823 } else {
801- f .writeStart (fieldReferenceNode .Name )
824+ f .writeStart (fieldReferenceNode .Name , false )
802825 }
803826 if fieldReferenceNode .Close != nil {
804827 f .writeInline (fieldReferenceNode .Close )
@@ -833,7 +856,7 @@ func (f *formatter) writeEnum(enumNode *ast.EnumNode) {
833856 }
834857 }
835858 }
836- f .writeStart (enumNode .Keyword )
859+ f .writeStart (enumNode .Keyword , false )
837860 f .Space ()
838861 f .writeInline (enumNode .Name )
839862 f .Space ()
@@ -853,7 +876,7 @@ func (f *formatter) writeEnum(enumNode *ast.EnumNode) {
853876// deprecated = true
854877// ];
855878func (f * formatter ) writeEnumValue (enumValueNode * ast.EnumValueNode ) {
856- f .writeStart (enumValueNode .Name )
879+ f .writeStart (enumValueNode .Name , false )
857880 f .Space ()
858881 f .writeInline (enumValueNode .Equals )
859882 f .Space ()
@@ -879,7 +902,7 @@ func (f *formatter) writeField(fieldNode *ast.FieldNode) {
879902 // a label might not be defined, but it has the leading comments attached
880903 // to it.
881904 if fieldNode .Label .KeywordNode != nil {
882- f .writeStart (fieldNode .Label )
905+ f .writeStart (fieldNode .Label , false )
883906 f .Space ()
884907 f .writeInline (fieldNode .FldType )
885908 } else {
@@ -888,7 +911,7 @@ func (f *formatter) writeField(fieldNode *ast.FieldNode) {
888911 if compoundIdentNode , ok := fieldNode .FldType .(* ast.CompoundIdentNode ); ok {
889912 f .writeCompoundIdentForFieldName (compoundIdentNode )
890913 } else {
891- f .writeStart (fieldNode .FldType )
914+ f .writeStart (fieldNode .FldType , false )
892915 }
893916 }
894917 f .Space ()
@@ -926,7 +949,7 @@ func (f *formatter) writeMapField(mapFieldNode *ast.MapFieldNode) {
926949
927950// writeMapType writes a map type (e.g. 'map<string, string>').
928951func (f * formatter ) writeMapType (mapTypeNode * ast.MapTypeNode ) {
929- f .writeStart (mapTypeNode .Keyword )
952+ f .writeStart (mapTypeNode .Keyword , false )
930953 f .writeInline (mapTypeNode .OpenAngle )
931954 f .writeInline (mapTypeNode .KeyType )
932955 f .writeInline (mapTypeNode .Comma )
@@ -962,7 +985,7 @@ func (f *formatter) writeExtend(extendNode *ast.ExtendNode) {
962985 }
963986 }
964987 }
965- f .writeStart (extendNode .Keyword )
988+ f .writeStart (extendNode .Keyword , false )
966989 f .Space ()
967990 f .writeInline (extendNode .Extendee )
968991 f .Space ()
@@ -990,7 +1013,7 @@ func (f *formatter) writeService(serviceNode *ast.ServiceNode) {
9901013 }
9911014 }
9921015 }
993- f .writeStart (serviceNode .Keyword )
1016+ f .writeStart (serviceNode .Keyword , false )
9941017 f .Space ()
9951018 f .writeInline (serviceNode .Name )
9961019 f .Space ()
@@ -1018,7 +1041,7 @@ func (f *formatter) writeRPC(rpcNode *ast.RPCNode) {
10181041 }
10191042 }
10201043 }
1021- f .writeStart (rpcNode .Keyword )
1044+ f .writeStart (rpcNode .Keyword , false )
10221045 f .Space ()
10231046 f .writeInline (rpcNode .Name )
10241047 f .writeInline (rpcNode .Input )
@@ -1073,7 +1096,7 @@ func (f *formatter) writeOneOf(oneOfNode *ast.OneofNode) {
10731096 }
10741097 }
10751098 }
1076- f .writeStart (oneOfNode .Keyword )
1099+ f .writeStart (oneOfNode .Keyword , false )
10771100 f .Space ()
10781101 f .writeInline (oneOfNode .Name )
10791102 f .Space ()
@@ -1108,13 +1131,13 @@ func (f *formatter) writeGroup(groupNode *ast.GroupNode) {
11081131 // a label might not be defined, but it has the leading comments attached
11091132 // to it.
11101133 if groupNode .Label .KeywordNode != nil {
1111- f .writeStart (groupNode .Label )
1134+ f .writeStart (groupNode .Label , false )
11121135 f .Space ()
11131136 f .writeInline (groupNode .Keyword )
11141137 } else {
11151138 // If a label was not written, the multiline comments will be
11161139 // attached to the keyword.
1117- f .writeStart (groupNode .Keyword )
1140+ f .writeStart (groupNode .Keyword , false )
11181141 }
11191142 f .Space ()
11201143 f .writeInline (groupNode .Name )
@@ -1142,7 +1165,7 @@ func (f *formatter) writeGroup(groupNode *ast.GroupNode) {
11421165// deprecated = true
11431166// ];
11441167func (f * formatter ) writeExtensionRange (extensionRangeNode * ast.ExtensionRangeNode ) {
1145- f .writeStart (extensionRangeNode .Keyword )
1168+ f .writeStart (extensionRangeNode .Keyword , false )
11461169 f .Space ()
11471170 for i := range extensionRangeNode .Ranges {
11481171 if i > 0 {
@@ -1165,7 +1188,7 @@ func (f *formatter) writeExtensionRange(extensionRangeNode *ast.ExtensionRangeNo
11651188//
11661189// reserved 5-10, 100 to max;
11671190func (f * formatter ) writeReserved (reservedNode * ast.ReservedNode ) {
1168- f .writeStart (reservedNode .Keyword )
1191+ f .writeStart (reservedNode .Keyword , false )
11691192 // Either names or ranges will be set, but never both.
11701193 elements := make ([]ast.Node , 0 , len (reservedNode .Names )+ len (reservedNode .Ranges ))
11711194 switch {
@@ -1345,7 +1368,7 @@ func (f *formatter) writeArrayLiteral(arrayLiteralNode *ast.ArrayLiteralNode) {
13451368 f .writeLineElement (arrayLiteralNode .Elements [i ])
13461369 return
13471370 }
1348- f .writeStart (arrayLiteralNode .Elements [i ])
1371+ f .writeStart (arrayLiteralNode .Elements [i ], false )
13491372 f .writeLineEnd (arrayLiteralNode .Commas [i ])
13501373 }
13511374 }
@@ -1515,11 +1538,11 @@ func (f *formatter) writeCompoundIdent(compoundIdentNode *ast.CompoundIdentNode)
15151538// }
15161539func (f * formatter ) writeCompoundIdentForFieldName (compoundIdentNode * ast.CompoundIdentNode ) {
15171540 if compoundIdentNode .LeadingDot != nil {
1518- f .writeStart (compoundIdentNode .LeadingDot )
1541+ f .writeStart (compoundIdentNode .LeadingDot , false )
15191542 }
15201543 for i := range compoundIdentNode .Components {
15211544 if i == 0 && compoundIdentNode .LeadingDot == nil {
1522- f .writeStart (compoundIdentNode .Components [i ])
1545+ f .writeStart (compoundIdentNode .Components [i ], false )
15231546 continue
15241547 }
15251548 if i > 0 {
@@ -1560,7 +1583,7 @@ func (f *formatter) writeCompoundStringLiteral(
15601583 for i , child := range compoundStringLiteralNode .Children () {
15611584 if hasTrailingPunctuation && i == len (compoundStringLiteralNode .Children ())- 1 {
15621585 // inline because there may be a subsequent comma or punctuation from enclosing element
1563- f .writeStart (child )
1586+ f .writeStart (child , false )
15641587 break
15651588 }
15661589 f .writeLineElement (child )
@@ -1599,7 +1622,7 @@ func (f *formatter) writeCompoundStringLiteralForArray(
15991622) {
16001623 for i , child := range compoundStringLiteralNode .Children () {
16011624 if ! lastElement && i == len (compoundStringLiteralNode .Children ())- 1 {
1602- f .writeStart (child )
1625+ f .writeStart (child , false )
16031626 return
16041627 }
16051628 f .writeLineElement (child )
@@ -1626,7 +1649,7 @@ func (f *formatter) writeSignedFloatLiteralForArray(
16261649 signedFloatLiteralNode * ast.SignedFloatLiteralNode ,
16271650 lastElement bool ,
16281651) {
1629- f .writeStart (signedFloatLiteralNode .Sign )
1652+ f .writeStart (signedFloatLiteralNode .Sign , false )
16301653 if lastElement {
16311654 f .writeLineEnd (signedFloatLiteralNode .Float )
16321655 return
@@ -1671,7 +1694,7 @@ func (f *formatter) writeNegativeIntLiteralForArray(
16711694 negativeIntLiteralNode * ast.NegativeIntLiteralNode ,
16721695 lastElement bool ,
16731696) {
1674- f .writeStart (negativeIntLiteralNode .Minus )
1697+ f .writeStart (negativeIntLiteralNode .Minus , false )
16751698 if lastElement {
16761699 f .writeLineEnd (negativeIntLiteralNode .Uint )
16771700 return
@@ -1734,7 +1757,9 @@ func (f *formatter) writeNode(node ast.Node) {
17341757 case * ast.IdentNode :
17351758 f .writeIdent (element )
17361759 case * ast.ImportNode :
1737- f .writeImport (element , false )
1760+ // Make no assumptions on node ordering, set first == false to always preserve leading
1761+ // whitespace.
1762+ f .writeImport (element , false , false )
17381763 case * ast.KeywordNode :
17391764 f .writeKeyword (element )
17401765 case * ast.MapFieldNode :
@@ -1756,7 +1781,9 @@ func (f *formatter) writeNode(node ast.Node) {
17561781 case * ast.OptionNameNode :
17571782 f .writeOptionName (element )
17581783 case * ast.PackageNode :
1759- f .writePackage (element )
1784+ // Make no assumptions on node ordering, set first == false to always preserve leading
1785+ // whitespace.
1786+ f .writePackage (element , false )
17601787 case * ast.RangeNode :
17611788 f .writeRange (element )
17621789 case * ast.ReservedNode :
@@ -1818,17 +1845,26 @@ func (f *formatter) writeNode(node ast.Node) {
18181845// Note that this is one of the most complex component of the formatter - it
18191846// controls how each node should be separated from one another and preserves
18201847// newlines in the original source.
1821- func (f * formatter ) writeStart (node ast.Node ) {
1822- f .writeStartMaybeCompact (node , false )
1848+ func (f * formatter ) writeStart (node ast.Node , ignoreLeadingWhitespace bool ) {
1849+ f .writeStartMaybeCompact (node , false , ignoreLeadingWhitespace )
18231850}
18241851
1825- func (f * formatter ) writeStartMaybeCompact (node ast.Node , forceCompact bool ) {
1852+ func (f * formatter ) writeStartMaybeCompact (
1853+ node ast.Node ,
1854+ forceCompact bool ,
1855+ ignoreLeadingWhitespace bool ,
1856+ ) {
18261857 defer f .SetPreviousNode (node )
18271858 info := f .nodeInfo (node )
18281859 var (
18291860 nodeNewlineCount = newlineCount (info .LeadingWhitespace ())
18301861 compact = forceCompact || isOpenBrace (f .previousNode )
18311862 )
1863+ if ignoreLeadingWhitespace {
1864+ // If we are asked to ignore leading whitespace, then we forcibly set nodeNewlineCount
1865+ // to 0, effectively ignoring the leading whitespace line count.
1866+ nodeNewlineCount = 0
1867+ }
18321868 if length := info .LeadingComments ().Len (); length > 0 {
18331869 // If leading comments are defined, the whitespace we care about
18341870 // is attached to the first comment.
0 commit comments