OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package formatter | 5 package formatter |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "fmt" | 9 "fmt" |
10 "mojom/mojom_parser/lexer" | 10 "mojom/mojom_parser/lexer" |
(...skipping 28 matching lines...) Expand all Loading... |
39 // eolComment is the comment to be printed at the end of the current lin
e. | 39 // eolComment is the comment to be printed at the end of the current lin
e. |
40 eolComment *lexer.Token | 40 eolComment *lexer.Token |
41 | 41 |
42 // linePos is the number of runes that have been written to the current
line. | 42 // linePos is the number of runes that have been written to the current
line. |
43 linePos int | 43 linePos int |
44 | 44 |
45 // maxLineLength is the maximum number of runes that should be printed o
n a line. | 45 // maxLineLength is the maximum number of runes that should be printed o
n a line. |
46 // A negative maxLineLength indicates no maximum line length should be e
nforced. | 46 // A negative maxLineLength indicates no maximum line length should be e
nforced. |
47 // This is currently only used to decide when to break up a method on di
fferent lines. | 47 // This is currently only used to decide when to break up a method on di
fferent lines. |
48 maxLineLength int | 48 maxLineLength int |
| 49 |
| 50 // mojomFile is the file being printed. |
| 51 mojomFile *mojom.MojomFile |
| 52 |
| 53 // noFormat indicates we are in a block where formatting has been disabl
ed. |
| 54 noFormat bool |
| 55 |
| 56 // token that begins a block where formatting has been disabled. |
| 57 noFormatStart lexer.Token |
| 58 |
| 59 // indentSize at the time noFormat is set. |
| 60 noFormatStartIndent int |
49 } | 61 } |
50 | 62 |
51 // newPrinter is a constructor for printer. | 63 // newPrinter is a constructor for printer. |
52 func newPrinter() (p *printer) { | 64 func newPrinter() (p *printer) { |
53 p = new(printer) | 65 p = new(printer) |
54 p.maxLineLength = 100 | 66 p.maxLineLength = 100 |
55 return | 67 return |
56 } | 68 } |
57 | 69 |
58 // result returns the pretty-printed version of what was given to the printer. | 70 // result returns the pretty-printed version of what was given to the printer. |
59 // Usually, this is a pretty-printed MojomFile. | 71 // Usually, this is a pretty-printed MojomFile. |
60 func (p *printer) result() string { | 72 func (p *printer) result() string { |
61 return p.buffer.String() | 73 return p.buffer.String() |
62 } | 74 } |
63 | 75 |
64 // writeMojomFile is the entry point of the pretty printer. | 76 // writeMojomFile is the entry point of the pretty printer. |
65 // It takes a mojom file with attached comments and creates a pretty-printed | 77 // It takes a mojom file with attached comments and creates a pretty-printed |
66 // representation. | 78 // representation. |
67 func (p *printer) writeMojomFile(mojomFile *mojom.MojomFile) { | 79 func (p *printer) writeMojomFile(mojomFile *mojom.MojomFile) { |
68 if p.used { | 80 if p.used { |
69 panic("A printer can only be used once!") | 81 panic("A printer can only be used once!") |
70 } | 82 } |
71 p.used = true | 83 p.used = true |
| 84 p.mojomFile = mojomFile |
72 | 85 |
73 p.writeAttributes(mojomFile.Attributes) | 86 p.writeAttributes(mojomFile.Attributes) |
74 p.writeModuleNamespace(mojomFile.ModuleNamespace) | 87 p.writeModuleNamespace(mojomFile.ModuleNamespace) |
75 | 88 |
76 if len(mojomFile.Imports) > 0 { | 89 if len(mojomFile.Imports) > 0 { |
77 p.writeImportedFiles(mojomFile.Imports) | 90 p.writeImportedFiles(mojomFile.Imports) |
78 } | 91 } |
79 | 92 |
80 for _, declaredObject := range mojomFile.DeclaredObjects { | 93 for _, declaredObject := range mojomFile.DeclaredObjects { |
81 p.writeDeclaredObject(declaredObject) | 94 p.writeDeclaredObject(declaredObject) |
82 p.nl() | 95 p.nl() |
83 } | 96 } |
84 | 97 |
85 if mojomFile.FinalComments != nil { | 98 if mojomFile.FinalComments != nil { |
86 finalComments := trimEmptyLines(mojomFile.FinalComments) | 99 finalComments := trimEmptyLines(mojomFile.FinalComments) |
87 if len(finalComments) > 0 { | 100 if len(finalComments) > 0 { |
88 p.nl() | 101 p.nl() |
89 p.writeCommentBlocks(finalComments, true) | 102 p.writeCommentBlocks(finalComments, true) |
90 } | 103 } |
91 } | 104 } |
| 105 |
| 106 p.eofNoFormat() |
92 } | 107 } |
93 | 108 |
94 // writeModuleNamespace writes a mojom file's module statement and associated | 109 // writeModuleNamespace writes a mojom file's module statement and associated |
95 // comments. | 110 // comments. |
96 func (p *printer) writeModuleNamespace(module *mojom.ModuleNamespace) { | 111 func (p *printer) writeModuleNamespace(module *mojom.ModuleNamespace) { |
97 if module == nil || module.Identifier == "" { | 112 if module == nil || module.Identifier == "" { |
98 return | 113 return |
99 } | 114 } |
100 p.writeBeforeComments(module) | 115 p.writeBeforeComments(module) |
101 p.writef("module %s;", module.Identifier) | 116 p.writef("module %s;", module.Identifier) |
(...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
564 p.writef(" %s", comment.Text) | 579 p.writef(" %s", comment.Text) |
565 } | 580 } |
566 } | 581 } |
567 | 582 |
568 // writeCommentBlocks writes a slice of comments. If finalEol is true, a new | 583 // writeCommentBlocks writes a slice of comments. If finalEol is true, a new |
569 // line is written after all the comments are written. | 584 // line is written after all the comments are written. |
570 func (p *printer) writeCommentBlocks(comments []lexer.Token, finalEol bool) { | 585 func (p *printer) writeCommentBlocks(comments []lexer.Token, finalEol bool) { |
571 for i, comment := range comments { | 586 for i, comment := range comments { |
572 switch comment.Kind { | 587 switch comment.Kind { |
573 case lexer.SingleLineComment: | 588 case lexer.SingleLineComment: |
| 589 if comment.Text == "// no-format" { |
| 590 p.startNoFormat(comment) |
| 591 } else if comment.Text == "// end-no-format" { |
| 592 p.endNoFormat(comment) |
| 593 } |
574 p.write(comment.Text) | 594 p.write(comment.Text) |
575 if finalEol || i < len(comments)-1 { | 595 if finalEol || i < len(comments)-1 { |
576 p.nl() | 596 p.nl() |
577 } | 597 } |
578 case lexer.MultiLineComment: | 598 case lexer.MultiLineComment: |
579 p.writeMultiLineComment(comment) | 599 p.writeMultiLineComment(comment) |
580 if finalEol || i < len(comments)-1 { | 600 if finalEol || i < len(comments)-1 { |
581 p.nl() | 601 p.nl() |
582 } | 602 } |
583 case lexer.EmptyLine: | 603 case lexer.EmptyLine: |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
628 // write writes the provided string to the buffer. | 648 // write writes the provided string to the buffer. |
629 // If nothing has been written yet on the current line write also writes the | 649 // If nothing has been written yet on the current line write also writes the |
630 // current indentation level. | 650 // current indentation level. |
631 func (p *printer) write(s string) { | 651 func (p *printer) write(s string) { |
632 if strings.ContainsRune(s, '\n') { | 652 if strings.ContainsRune(s, '\n') { |
633 panic(fmt.Sprintf("Only the nl method can write a new line: %q",
s)) | 653 panic(fmt.Sprintf("Only the nl method can write a new line: %q",
s)) |
634 } | 654 } |
635 | 655 |
636 // We only print the indentation if the line is not empty. | 656 // We only print the indentation if the line is not empty. |
637 if p.linePos == 0 { | 657 if p.linePos == 0 { |
638 » » p.buffer.WriteString(strings.Repeat(" ", p.indentSize)) | 658 » » if !p.noFormat { |
| 659 » » » p.buffer.WriteString(strings.Repeat(" ", p.indentSize)) |
| 660 » » } |
639 p.linePos += p.indentSize | 661 p.linePos += p.indentSize |
640 } | 662 } |
641 p.linePos += len(s) | 663 p.linePos += len(s) |
642 » p.buffer.WriteString(s) | 664 » if !p.noFormat { |
| 665 » » p.buffer.WriteString(s) |
| 666 » } |
643 } | 667 } |
644 | 668 |
645 // nl writes a new line. Before writing the new line, nl writes the last | 669 // nl writes a new line. Before writing the new line, nl writes the last |
646 // comment on the line. | 670 // comment on the line. |
647 func (p *printer) nl() { | 671 func (p *printer) nl() { |
648 // Before going to the next line, print the last comment on the line. | 672 // Before going to the next line, print the last comment on the line. |
649 if p.eolComment != nil { | 673 if p.eolComment != nil { |
650 » » p.writef(" %s", p.eolComment.Text) | 674 » » if !p.noFormat { |
| 675 » » » p.writef(" %s", p.eolComment.Text) |
| 676 » » } |
651 p.eolComment = nil | 677 p.eolComment = nil |
652 } | 678 } |
653 | 679 |
654 » p.buffer.WriteString("\n") | 680 » if !p.noFormat { |
| 681 » » p.buffer.WriteString("\n") |
| 682 » } |
655 p.linePos = 0 | 683 p.linePos = 0 |
656 } | 684 } |
657 | 685 |
658 func (p *printer) incIndent() { | 686 func (p *printer) incIndent() { |
659 p.indentSize += 2 | 687 p.indentSize += 2 |
660 } | 688 } |
661 | 689 |
662 func (p *printer) decIndent() { | 690 func (p *printer) decIndent() { |
663 p.indentSize -= 2 | 691 p.indentSize -= 2 |
664 if p.indentSize < 0 { | 692 if p.indentSize < 0 { |
(...skipping 21 matching lines...) Expand all Loading... |
686 // to the line yet, it returns the indent size. | 714 // to the line yet, it returns the indent size. |
687 // The purpose of lineLenght is to answer the question: If I write something now | 715 // The purpose of lineLenght is to answer the question: If I write something now |
688 // how far on the current line will it be written? | 716 // how far on the current line will it be written? |
689 func (p *printer) lineLength() int { | 717 func (p *printer) lineLength() int { |
690 if p.linePos == 0 { | 718 if p.linePos == 0 { |
691 return p.indentSize | 719 return p.indentSize |
692 } | 720 } |
693 return p.linePos | 721 return p.linePos |
694 } | 722 } |
695 | 723 |
| 724 // startNoFormat disables writes to the buffer (future writes are discarded). |
| 725 func (p *printer) startNoFormat(startToken lexer.Token) { |
| 726 if p.noFormat { |
| 727 return |
| 728 } |
| 729 |
| 730 p.noFormat = true |
| 731 p.noFormatStart = startToken |
| 732 p.noFormatStartIndent = p.indentSize |
| 733 } |
| 734 |
| 735 // endNoFormat re-enables writes to the buffer and writes the source starting |
| 736 // at the beginning of p.startToken and ending right before endToken. |
| 737 func (p *printer) endNoFormat(endToken lexer.Token) { |
| 738 if !p.noFormat { |
| 739 return |
| 740 } |
| 741 |
| 742 fileContents := p.mojomFile.FileContents() |
| 743 notFormatted := fileContents[p.noFormatStart.SourcePosBytes:endToken.Sou
rcePosBytes] |
| 744 |
| 745 // Remove any unformatted indentation right before the end of the unform
atted block. |
| 746 notFormatted = strings.TrimRight(notFormatted, " \t") |
| 747 p.buffer.WriteString(strings.Repeat(" ", p.noFormatStartIndent)) |
| 748 p.buffer.WriteString(notFormatted) |
| 749 p.noFormat = false |
| 750 } |
| 751 |
| 752 // eofNoFormat handles the case where formatting had been disabled but not |
| 753 // re-enabled. It writes everything after the formatting was ended. |
| 754 func (p *printer) eofNoFormat() { |
| 755 if !p.noFormat { |
| 756 return |
| 757 } |
| 758 |
| 759 fileContents := p.mojomFile.FileContents() |
| 760 p.endNoFormat(lexer.Token{SourcePosBytes: len(fileContents)}) |
| 761 } |
| 762 |
696 // Standalone utilities that do not operate on the buffer. | 763 // Standalone utilities that do not operate on the buffer. |
697 | 764 |
698 // isNewBlock determines if an empty line should be printed above the element | 765 // isNewBlock determines if an empty line should be printed above the element |
699 // under consideration. The purpose is to respect user-intention to separate | 766 // under consideration. The purpose is to respect user-intention to separate |
700 // elements in a mojom file. | 767 // elements in a mojom file. |
701 // | 768 // |
702 // If the element has attributes, and the first "comment" attached above the | 769 // If the element has attributes, and the first "comment" attached above the |
703 // attributes is an empty line, the element is a new block. | 770 // attributes is an empty line, the element is a new block. |
704 // If the element has no attributes and the first "comment" attached above the | 771 // If the element has no attributes and the first "comment" attached above the |
705 // element itself is an empty line, the element is a new block. | 772 // element itself is an empty line, the element is a new block. |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
768 | 835 |
769 // See sort.Interface. | 836 // See sort.Interface. |
770 func (ifs *importedFilesSorter) Less(i, j int) bool { | 837 func (ifs *importedFilesSorter) Less(i, j int) bool { |
771 return ifs.imports[i].SpecifiedName < ifs.imports[j].SpecifiedName | 838 return ifs.imports[i].SpecifiedName < ifs.imports[j].SpecifiedName |
772 } | 839 } |
773 | 840 |
774 // See sort.Interface. | 841 // See sort.Interface. |
775 func (ifs *importedFilesSorter) Swap(i, j int) { | 842 func (ifs *importedFilesSorter) Swap(i, j int) { |
776 ifs.imports[i], ifs.imports[j] = ifs.imports[j], ifs.imports[i] | 843 ifs.imports[i], ifs.imports[j] = ifs.imports[j], ifs.imports[i] |
777 } | 844 } |
OLD | NEW |