github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/format/node.go (about) 1 // Copyright 2018 The CUE Authors 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 format 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/joomcode/cue/cue/ast" 22 "github.com/joomcode/cue/cue/literal" 23 "github.com/joomcode/cue/cue/scanner" 24 "github.com/joomcode/cue/cue/token" 25 "github.com/joomcode/cue/internal" 26 ) 27 28 func printNode(node interface{}, f *printer) error { 29 s := newFormatter(f) 30 31 ls := labelSimplifier{scope: map[string]bool{}} 32 33 // format node 34 f.allowed = nooverride // gobble initial whitespace. 35 switch x := node.(type) { 36 case *ast.File: 37 if f.cfg.simplify { 38 ls.markReferences(x) 39 } 40 s.file(x) 41 case ast.Expr: 42 if f.cfg.simplify { 43 ls.markReferences(x) 44 } 45 s.expr(x) 46 case ast.Decl: 47 if f.cfg.simplify { 48 ls.markReferences(x) 49 } 50 s.decl(x) 51 // case ast.Node: // TODO: do we need this? 52 // s.walk(x) 53 case []ast.Decl: 54 if f.cfg.simplify { 55 ls.processDecls(x) 56 } 57 s.walkDeclList(x) 58 default: 59 goto unsupported 60 } 61 62 return s.errs 63 64 unsupported: 65 return fmt.Errorf("cue/format: unsupported node type %T", node) 66 } 67 68 func isRegularField(tok token.Token) bool { 69 return tok == token.ILLEGAL || tok == token.COLON 70 } 71 72 // Helper functions for common node lists. They may be empty. 73 74 func nestDepth(f *ast.Field) int { 75 d := 1 76 if s, ok := f.Value.(*ast.StructLit); ok { 77 switch { 78 case len(s.Elts) != 1: 79 d = 0 80 default: 81 if f, ok := s.Elts[0].(*ast.Field); ok { 82 d += nestDepth(f) 83 } 84 } 85 } 86 return d 87 } 88 89 // TODO: be more accurate and move to astutil 90 func hasDocComments(d ast.Decl) bool { 91 if len(d.Comments()) > 0 { 92 return true 93 } 94 switch x := d.(type) { 95 case *ast.Field: 96 return len(x.Label.Comments()) > 0 97 case *ast.Alias: 98 return len(x.Ident.Comments()) > 0 99 case *ast.LetClause: 100 return len(x.Ident.Comments()) > 0 101 } 102 return false 103 } 104 105 func (f *formatter) walkDeclList(list []ast.Decl) { 106 f.before(nil) 107 d := 0 108 hasEllipsis := false 109 for i, x := range list { 110 if i > 0 { 111 f.print(declcomma) 112 nd := 0 113 if f, ok := x.(*ast.Field); ok { 114 nd = nestDepth(f) 115 } 116 if f.current.parentSep == newline && (d == 0 || nd != d) { 117 f.print(f.formfeed()) 118 } 119 if hasDocComments(x) { 120 switch x := list[i-1].(type) { 121 case *ast.Field: 122 if x.Token == token.ISA || internal.IsDefinition(x.Label) { 123 f.print(newsection) 124 } 125 126 default: 127 f.print(newsection) 128 } 129 } 130 } 131 if f.printer.cfg.simplify && internal.IsEllipsis(x) { 132 hasEllipsis = true 133 continue 134 } 135 f.decl(x) 136 d = 0 137 if f, ok := x.(*ast.Field); ok { 138 d = nestDepth(f) 139 } 140 if j := i + 1; j < len(list) { 141 switch x := list[j].(type) { 142 case *ast.Field: 143 switch x := x.Value.(type) { 144 case *ast.StructLit: 145 // TODO: not entirely correct: could have multiple elements, 146 // not have a valid Lbrace, and be marked multiline. This 147 // cannot occur for ASTs resulting from a parse, though. 148 if x.Lbrace.IsValid() || len(x.Elts) != 1 { 149 f.print(f.formfeed()) 150 continue 151 } 152 case *ast.ListLit: 153 f.print(f.formfeed()) 154 continue 155 } 156 } 157 } 158 f.print(f.current.parentSep) 159 } 160 if hasEllipsis { 161 f.decl(&ast.Ellipsis{}) 162 f.print(f.current.parentSep) 163 } 164 f.after(nil) 165 } 166 167 func (f *formatter) walkSpecList(list []*ast.ImportSpec) { 168 f.before(nil) 169 for _, x := range list { 170 f.before(x) 171 f.importSpec(x) 172 f.after(x) 173 } 174 f.after(nil) 175 } 176 177 func (f *formatter) walkClauseList(list []ast.Clause, ws whiteSpace) { 178 f.before(nil) 179 for _, x := range list { 180 f.before(x) 181 f.print(ws) 182 f.clause(x) 183 f.after(x) 184 } 185 f.after(nil) 186 } 187 188 func (f *formatter) walkListElems(list []ast.Expr) { 189 f.before(nil) 190 for _, x := range list { 191 f.before(x) 192 switch n := x.(type) { 193 case *ast.Comprehension: 194 f.walkClauseList(n.Clauses, blank) 195 f.print(blank, nooverride) 196 f.expr(n.Value) 197 198 case *ast.Ellipsis: 199 f.ellipsis(n) 200 201 case *ast.Alias: 202 f.expr(n.Ident) 203 f.print(n.Equal, token.BIND) 204 f.expr(n.Expr) 205 206 // TODO: ast.CommentGroup: allows comment groups in ListLits. 207 208 case ast.Expr: 209 f.exprRaw(n, token.LowestPrec, 1) 210 } 211 f.print(comma, blank) 212 f.after(x) 213 } 214 f.after(nil) 215 } 216 217 func (f *formatter) walkArgsList(list []ast.Expr, depth int) { 218 f.before(nil) 219 for _, x := range list { 220 f.before(x) 221 f.exprRaw(x, token.LowestPrec, depth) 222 f.print(comma, blank) 223 f.after(x) 224 } 225 f.after(nil) 226 } 227 228 func (f *formatter) file(file *ast.File) { 229 f.before(file) 230 f.walkDeclList(file.Decls) 231 f.after(file) 232 f.print(token.EOF) 233 } 234 235 func (f *formatter) inlineField(n *ast.Field) *ast.Field { 236 regular := internal.IsRegularField(n) 237 // shortcut single-element structs. 238 // If the label has a valid position, we assume that an unspecified 239 // Lbrace signals the intend to collapse fields. 240 if !n.Label.Pos().IsValid() && !(f.printer.cfg.simplify && regular) { 241 return nil 242 } 243 244 obj, ok := n.Value.(*ast.StructLit) 245 if !ok || len(obj.Elts) != 1 || 246 (obj.Lbrace.IsValid() && !f.printer.cfg.simplify) || 247 (obj.Lbrace.IsValid() && hasDocComments(n)) || 248 len(n.Attrs) > 0 { 249 return nil 250 } 251 252 mem, ok := obj.Elts[0].(*ast.Field) 253 if !ok || len(mem.Attrs) > 0 { 254 return nil 255 } 256 257 if hasDocComments(mem) { 258 // TODO: this inserts curly braces even in spaces where this 259 // may not be desirable, such as: 260 // a: 261 // // foo 262 // b: 3 263 return nil 264 } 265 return mem 266 } 267 268 func (f *formatter) decl(decl ast.Decl) { 269 if decl == nil { 270 return 271 } 272 defer f.after(decl) 273 if !f.before(decl) { 274 return 275 } 276 277 switch n := decl.(type) { 278 case *ast.Field: 279 f.label(n.Label, n.Optional != token.NoPos) 280 281 regular := isRegularField(n.Token) 282 if regular { 283 f.print(noblank, nooverride, n.TokenPos, token.COLON) 284 } else { 285 f.print(blank, nooverride, n.Token) 286 } 287 288 if mem := f.inlineField(n); mem != nil { 289 switch { 290 default: 291 fallthrough 292 293 case regular && f.cfg.simplify: 294 f.print(blank, nooverride) 295 f.decl(mem) 296 297 case mem.Label.Pos().IsNewline(): 298 f.print(indent, formfeed) 299 f.decl(mem) 300 f.indent-- 301 } 302 return 303 } 304 305 nextFF := f.nextNeedsFormfeed(n.Value) 306 tab := vtab 307 if nextFF { 308 tab = blank 309 } 310 311 f.print(tab) 312 313 if n.Value != nil { 314 switch n.Value.(type) { 315 case *ast.ListLit, *ast.StructLit: 316 f.expr(n.Value) 317 default: 318 f.print(indent) 319 f.expr(n.Value) 320 f.markUnindentLine() 321 } 322 } else { 323 f.current.pos++ 324 f.visitComments(f.current.pos) 325 } 326 327 space := tab 328 for _, a := range n.Attrs { 329 if f.before(a) { 330 f.print(space, a.At, a) 331 } 332 f.after(a) 333 space = blank 334 } 335 336 if nextFF { 337 f.print(formfeed) 338 } 339 340 case *ast.BadDecl: 341 f.print(n.From, "*bad decl*", declcomma) 342 343 case *ast.Package: 344 f.print(n.PackagePos, "package") 345 f.print(blank, n.Name, newsection, nooverride) 346 347 case *ast.ImportDecl: 348 f.print(n.Import, "import") 349 if len(n.Specs) == 0 { 350 f.print(blank, n.Lparen, token.LPAREN, n.Rparen, token.RPAREN, newline) 351 break 352 } 353 switch { 354 case len(n.Specs) == 1 && len(n.Specs[0].Comments()) == 0: 355 if !n.Lparen.IsValid() { 356 f.print(blank) 357 f.walkSpecList(n.Specs) 358 break 359 } 360 fallthrough 361 default: 362 f.print(blank, n.Lparen, token.LPAREN, newline, indent) 363 f.walkSpecList(n.Specs) 364 f.print(unindent, newline, n.Rparen, token.RPAREN, newline) 365 } 366 f.print(newsection, nooverride) 367 368 case *ast.LetClause: 369 if !decl.Pos().HasRelPos() || decl.Pos().RelPos() >= token.Newline { 370 f.print(formfeed) 371 } 372 f.print(n.Let, token.LET, blank, nooverride) 373 f.expr(n.Ident) 374 f.print(blank, nooverride, n.Equal, token.BIND, blank) 375 f.expr(n.Expr) 376 f.print(declcomma) // implied 377 378 case *ast.EmbedDecl: 379 if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline { 380 f.print(formfeed) 381 } 382 f.expr(n.Expr) 383 f.print(newline, noblank) 384 385 case *ast.Attribute: 386 f.print(n.At, n) 387 388 case *ast.CommentGroup: 389 f.printComment(n) 390 f.print(newsection) 391 392 case ast.Expr: 393 f.embedding(n) 394 } 395 } 396 397 func (f *formatter) embedding(decl ast.Expr) { 398 switch n := decl.(type) { 399 case *ast.Comprehension: 400 if !n.Pos().HasRelPos() || n.Pos().RelPos() >= token.Newline { 401 f.print(formfeed) 402 } 403 f.walkClauseList(n.Clauses, blank) 404 f.print(blank, nooverride) 405 f.expr(n.Value) 406 407 case *ast.Ellipsis: 408 f.ellipsis(n) 409 410 case *ast.Alias: 411 if !decl.Pos().HasRelPos() || decl.Pos().RelPos() >= token.Newline { 412 f.print(formfeed) 413 } 414 f.expr(n.Ident) 415 f.print(blank, n.Equal, token.BIND, blank) 416 f.expr(n.Expr) 417 f.print(declcomma) // implied 418 419 // TODO: ast.CommentGroup: allows comment groups in ListLits. 420 421 case ast.Expr: 422 f.exprRaw(n, token.LowestPrec, 1) 423 } 424 } 425 426 func (f *formatter) nextNeedsFormfeed(n ast.Expr) bool { 427 switch x := n.(type) { 428 case *ast.StructLit: 429 return true 430 case *ast.BasicLit: 431 return strings.IndexByte(x.Value, '\n') >= 0 432 case *ast.ListLit: 433 return true 434 } 435 return false 436 } 437 438 func (f *formatter) importSpec(x *ast.ImportSpec) { 439 if x.Name != nil { 440 f.label(x.Name, false) 441 f.print(blank) 442 } else { 443 f.current.pos++ 444 f.visitComments(f.current.pos) 445 } 446 f.expr(x.Path) 447 f.print(newline) 448 } 449 450 func isValidIdent(ident string) bool { 451 var scan scanner.Scanner 452 scan.Init(token.NewFile("check", -1, len(ident)), []byte(ident), nil, 0) 453 454 _, tok, lit := scan.Scan() 455 if tok == token.IDENT || tok.IsKeyword() { 456 return lit == ident 457 } 458 return false 459 } 460 461 func (f *formatter) label(l ast.Label, optional bool) { 462 f.before(l) 463 defer f.after(l) 464 switch n := l.(type) { 465 case *ast.Alias: 466 f.expr(n) 467 468 case *ast.Ident: 469 // Escape an identifier that has invalid characters. This may happen, 470 // if the AST is not generated by the parser. 471 name := n.Name 472 if !ast.IsValidIdent(name) { 473 name = literal.String.Quote(n.Name) 474 } 475 f.print(n.NamePos, name) 476 477 case *ast.BasicLit: 478 str := n.Value 479 // Allow any CUE string in the AST, but ensure it is formatted 480 // according to spec. 481 if strings.HasPrefix(str, `"""`) || strings.HasPrefix(str, "#") { 482 if u, err := literal.Unquote(str); err == nil { 483 str = literal.String.Quote(u) 484 } 485 } 486 f.print(n.ValuePos, str) 487 488 case *ast.ListLit: 489 f.expr(n) 490 491 case *ast.ParenExpr: 492 f.expr(n) 493 494 case *ast.Interpolation: 495 f.expr(n) 496 497 default: 498 panic(fmt.Sprintf("unknown label type %T", n)) 499 } 500 if optional { 501 f.print(token.OPTION) 502 } 503 } 504 505 func (f *formatter) ellipsis(x *ast.Ellipsis) { 506 f.print(x.Ellipsis, token.ELLIPSIS) 507 if x.Type != nil && !isTop(x.Type) { 508 f.expr(x.Type) 509 } 510 } 511 512 func (f *formatter) expr(x ast.Expr) { 513 const depth = 1 514 f.expr1(x, token.LowestPrec, depth) 515 } 516 517 func (f *formatter) expr0(x ast.Expr, depth int) { 518 f.expr1(x, token.LowestPrec, depth) 519 } 520 521 func (f *formatter) expr1(expr ast.Expr, prec1, depth int) { 522 if f.before(expr) { 523 f.exprRaw(expr, prec1, depth) 524 } 525 f.after(expr) 526 } 527 528 func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) { 529 530 switch x := expr.(type) { 531 case *ast.BadExpr: 532 f.print(x.From, "_|_") 533 534 case *ast.BottomLit: 535 f.print(x.Bottom, token.BOTTOM) 536 537 case *ast.Alias: 538 // Aliases in expression positions are printed in short form. 539 f.label(x.Ident, false) 540 f.print(x.Equal, token.BIND) 541 f.expr(x.Expr) 542 543 case *ast.Ident: 544 f.print(x.NamePos, x) 545 546 case *ast.BinaryExpr: 547 if depth < 1 { 548 f.internalError("depth < 1:", depth) 549 depth = 1 550 } 551 f.binaryExpr(x, prec1, cutoff(x, depth), depth) 552 553 case *ast.UnaryExpr: 554 const prec = token.UnaryPrec 555 if prec < prec1 { 556 // parenthesis needed 557 f.print(token.LPAREN, nooverride) 558 f.expr(x) 559 f.print(token.RPAREN) 560 } else { 561 // no parenthesis needed 562 f.print(x.OpPos, x.Op, nooverride) 563 f.expr1(x.X, prec, depth) 564 } 565 566 case *ast.BasicLit: 567 f.print(x.ValuePos, x) 568 569 case *ast.Interpolation: 570 f.before(nil) 571 for _, x := range x.Elts { 572 f.expr0(x, depth+1) 573 } 574 f.after(nil) 575 576 case *ast.ParenExpr: 577 if _, hasParens := x.X.(*ast.ParenExpr); hasParens { 578 // don't print parentheses around an already parenthesized expression 579 // TODO: consider making this more general and incorporate precedence levels 580 f.expr0(x.X, depth) 581 } else { 582 f.print(x.Lparen, token.LPAREN) 583 f.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth 584 f.print(x.Rparen, token.RPAREN) 585 } 586 587 case *ast.SelectorExpr: 588 f.selectorExpr(x, depth) 589 590 case *ast.IndexExpr: 591 f.expr1(x.X, token.HighestPrec, 1) 592 f.print(x.Lbrack, token.LBRACK) 593 f.expr0(x.Index, depth+1) 594 f.print(x.Rbrack, token.RBRACK) 595 596 case *ast.SliceExpr: 597 f.expr1(x.X, token.HighestPrec, 1) 598 f.print(x.Lbrack, token.LBRACK) 599 indices := []ast.Expr{x.Low, x.High} 600 for i, y := range indices { 601 if i > 0 { 602 // blanks around ":" if both sides exist and either side is a binary expression 603 x := indices[i-1] 604 if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) { 605 f.print(blank, token.COLON, blank) 606 } else { 607 f.print(token.COLON) 608 } 609 } 610 if y != nil { 611 f.expr0(y, depth+1) 612 } 613 } 614 f.print(x.Rbrack, token.RBRACK) 615 616 case *ast.CallExpr: 617 if len(x.Args) > 1 { 618 depth++ 619 } 620 wasIndented := f.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) 621 f.print(x.Lparen, token.LPAREN) 622 f.walkArgsList(x.Args, depth) 623 f.print(trailcomma, noblank, x.Rparen, token.RPAREN) 624 if wasIndented { 625 f.print(unindent) 626 } 627 628 case *ast.StructLit: 629 var l line 630 ws := noblank 631 ff := f.formfeed() 632 633 switch { 634 case len(x.Elts) == 0: 635 if !x.Rbrace.HasRelPos() { 636 // collapse curly braces if the body is empty. 637 ffAlt := blank | nooverride 638 for _, c := range x.Comments() { 639 if c.Position == 1 { 640 ffAlt = ff 641 } 642 } 643 ff = ffAlt 644 } 645 case !x.Rbrace.HasRelPos() || !x.Elts[0].Pos().HasRelPos(): 646 ws |= newline | nooverride 647 } 648 f.print(x.Lbrace, token.LBRACE, &l, ws, ff, indent) 649 650 f.walkDeclList(x.Elts) 651 f.matchUnindent() 652 653 ws = noblank 654 if f.lineout != l { 655 ws |= newline 656 if f.lastTok != token.RBRACE && f.lastTok != token.RBRACK { 657 ws |= nooverride 658 } 659 } 660 f.print(ws, x.Rbrace, token.RBRACE) 661 662 case *ast.ListLit: 663 f.print(x.Lbrack, token.LBRACK, indent) 664 f.walkListElems(x.Elts) 665 f.print(trailcomma, noblank) 666 f.visitComments(f.current.pos) 667 f.matchUnindent() 668 f.print(noblank, x.Rbrack, token.RBRACK) 669 670 case *ast.Ellipsis: 671 f.ellipsis(x) 672 673 default: 674 panic(fmt.Sprintf("unimplemented type %T", x)) 675 } 676 } 677 678 func (f *formatter) clause(clause ast.Clause) { 679 switch n := clause.(type) { 680 case *ast.ForClause: 681 f.print(n.For, "for", blank) 682 f.print(indent) 683 if n.Key != nil { 684 f.label(n.Key, false) 685 f.print(n.Colon, token.COMMA, blank) 686 } else { 687 f.current.pos++ 688 f.visitComments(f.current.pos) 689 } 690 f.label(n.Value, false) 691 f.print(blank, n.In, "in", blank) 692 f.expr(n.Source) 693 f.markUnindentLine() 694 695 case *ast.IfClause: 696 f.print(n.If, "if", blank) 697 f.print(indent) 698 f.expr(n.Condition) 699 f.markUnindentLine() 700 701 case *ast.LetClause: 702 f.print(n.Let, token.LET, blank, nooverride) 703 f.print(indent) 704 f.expr(n.Ident) 705 f.print(blank, nooverride, n.Equal, token.BIND, blank) 706 f.expr(n.Expr) 707 f.markUnindentLine() 708 709 default: 710 panic("unknown clause type") 711 } 712 } 713 714 func walkBinary(e *ast.BinaryExpr) (has6, has7, has8 bool, maxProblem int) { 715 switch e.Op.Precedence() { 716 case 6: 717 has6 = true 718 case 7: 719 has7 = true 720 case 8: 721 has8 = true 722 } 723 724 switch l := e.X.(type) { 725 case *ast.BinaryExpr: 726 if l.Op.Precedence() < e.Op.Precedence() { 727 // parens will be inserted. 728 // pretend this is an *syntax.ParenExpr and do nothing. 729 break 730 } 731 h6, h7, h8, mp := walkBinary(l) 732 has6 = has6 || h6 733 has7 = has7 || h7 734 has8 = has8 || h8 735 if maxProblem < mp { 736 maxProblem = mp 737 } 738 } 739 740 switch r := e.Y.(type) { 741 case *ast.BinaryExpr: 742 if r.Op.Precedence() <= e.Op.Precedence() { 743 // parens will be inserted. 744 // pretend this is an *syntax.ParenExpr and do nothing. 745 break 746 } 747 h6, h7, h8, mp := walkBinary(r) 748 has6 = has6 || h6 749 has7 = has7 || h7 750 has8 = has8 || h8 751 if maxProblem < mp { 752 maxProblem = mp 753 } 754 755 case *ast.UnaryExpr: 756 switch e.Op.String() + r.Op.String() { 757 case "/*": 758 maxProblem = 8 759 case "++", "--": 760 if maxProblem < 6 { 761 maxProblem = 6 762 } 763 } 764 } 765 return 766 } 767 768 func cutoff(e *ast.BinaryExpr, depth int) int { 769 has6, has7, has8, maxProblem := walkBinary(e) 770 if maxProblem > 0 { 771 return maxProblem + 1 772 } 773 if (has6 || has7) && has8 { 774 if depth == 1 { 775 return 8 776 } 777 if has7 { 778 return 7 779 } 780 return 6 781 } 782 if has6 && has7 { 783 if depth == 1 { 784 return 7 785 } 786 return 6 787 } 788 if depth == 1 { 789 return 8 790 } 791 return 6 792 } 793 794 func diffPrec(expr ast.Expr, prec int) int { 795 x, ok := expr.(*ast.BinaryExpr) 796 if !ok || prec != x.Op.Precedence() { 797 return 1 798 } 799 return 0 800 } 801 802 func reduceDepth(depth int) int { 803 depth-- 804 if depth < 1 { 805 depth = 1 806 } 807 return depth 808 } 809 810 // Format the binary expression: decide the cutoff and then format. 811 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode. 812 // (Algorithm suggestion by Russ Cox.) 813 // 814 // The precedences are: 815 // 7 * / % quo rem div mod 816 // 6 + - 817 // 5 == != < <= > >= 818 // 4 && 819 // 3 || 820 // 2 & 821 // 1 | 822 // 823 // The only decision is whether there will be spaces around levels 6 and 7. 824 // There are never spaces at level 8 (unary), and always spaces at levels 5 and below. 825 // 826 // To choose the cutoff, look at the whole expression but excluding primary 827 // expressions (function calls, parenthesized exprs), and apply these rules: 828 // 829 // 1) If there is a binary operator with a right side unary operand 830 // that would clash without a space, the cutoff must be (in order): 831 // 832 // /* 8 833 // ++ 7 // not necessary, but to avoid confusion 834 // -- 7 835 // 836 // (Comparison operators always have spaces around them.) 837 // 838 // 2) If there is a mix of level 7 and level 6 operators, then the cutoff 839 // is 7 (use spaces to distinguish precedence) in Normal mode 840 // and 6 (never use spaces) in Compact mode. 841 // 842 // 3) If there are no level 6 operators or no level 7 operators, then the 843 // cutoff is 8 (always use spaces) in Normal mode 844 // and 6 (never use spaces) in Compact mode. 845 // 846 func (f *formatter) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) { 847 f.nestExpr++ 848 defer func() { f.nestExpr-- }() 849 850 prec := x.Op.Precedence() 851 if prec < prec1 { 852 // parenthesis needed 853 // Note: The parser inserts an syntax.ParenExpr node; thus this case 854 // can only occur if the AST is created in a different way. 855 // defer p.pushComment(nil).pop() 856 f.print(token.LPAREN, nooverride) 857 f.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth 858 f.print(token.RPAREN) 859 return 860 } 861 862 printBlank := prec < cutoff 863 864 f.expr1(x.X, prec, depth+diffPrec(x.X, prec)) 865 f.print(nooverride) 866 if printBlank { 867 f.print(blank) 868 } 869 f.print(x.OpPos, x.Op) 870 if x.Y.Pos().IsNewline() { 871 // at least one line break, but respect an extra empty line 872 // in the source 873 f.print(formfeed) 874 printBlank = false // no blank after line break 875 } else { 876 f.print(nooverride) 877 } 878 if printBlank { 879 f.print(blank) 880 } 881 f.expr1(x.Y, prec+1, depth+1) 882 } 883 884 func isBinary(expr ast.Expr) bool { 885 _, ok := expr.(*ast.BinaryExpr) 886 return ok 887 } 888 889 func (f *formatter) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool { 890 if x, ok := expr.(*ast.SelectorExpr); ok { 891 return f.selectorExpr(x, depth) 892 } 893 f.expr1(expr, prec1, depth) 894 return false 895 } 896 897 // selectorExpr handles an *syntax.SelectorExpr node and returns whether x spans 898 // multiple lines. 899 func (f *formatter) selectorExpr(x *ast.SelectorExpr, depth int) bool { 900 f.expr1(x.X, token.HighestPrec, depth) 901 f.print(token.PERIOD) 902 if x.Sel.Pos().IsNewline() { 903 f.print(indent, formfeed) 904 f.expr(x.Sel.(ast.Expr)) 905 f.print(unindent) 906 return true 907 } 908 f.print(noblank) 909 f.expr(x.Sel.(ast.Expr)) 910 return false 911 } 912 913 func isTop(e ast.Expr) bool { 914 ident, ok := e.(*ast.Ident) 915 return ok && ident.Name == "_" 916 }