Source file src/cmd/vendor/golang.org/x/mod/modfile/print.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Module file printer.
     6  
     7  package modfile
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"strings"
    13  )
    14  
    15  // Format returns a go.mod file as a byte slice, formatted in standard style.
    16  func Format(f *FileSyntax) []byte {
    17  	pr := &printer{}
    18  	pr.file(f)
    19  
    20  	// remove trailing blank lines
    21  	b := pr.Bytes()
    22  	for len(b) > 0 && b[len(b)-1] == '\n' && (len(b) == 1 || b[len(b)-2] == '\n') {
    23  		b = b[:len(b)-1]
    24  	}
    25  	return b
    26  }
    27  
    28  // A printer collects the state during printing of a file or expression.
    29  type printer struct {
    30  	bytes.Buffer           // output buffer
    31  	comment      []Comment // pending end-of-line comments
    32  	margin       int       // left margin (indent), a number of tabs
    33  }
    34  
    35  // printf prints to the buffer.
    36  func (p *printer) printf(format string, args ...interface{}) {
    37  	fmt.Fprintf(p, format, args...)
    38  }
    39  
    40  // indent returns the position on the current line, in bytes, 0-indexed.
    41  func (p *printer) indent() int {
    42  	b := p.Bytes()
    43  	n := 0
    44  	for n < len(b) && b[len(b)-1-n] != '\n' {
    45  		n++
    46  	}
    47  	return n
    48  }
    49  
    50  // newline ends the current line, flushing end-of-line comments.
    51  func (p *printer) newline() {
    52  	if len(p.comment) > 0 {
    53  		p.printf(" ")
    54  		for i, com := range p.comment {
    55  			if i > 0 {
    56  				p.trim()
    57  				p.printf("\n")
    58  				for i := 0; i < p.margin; i++ {
    59  					p.printf("\t")
    60  				}
    61  			}
    62  			p.printf("%s", strings.TrimSpace(com.Token))
    63  		}
    64  		p.comment = p.comment[:0]
    65  	}
    66  
    67  	p.trim()
    68  	if b := p.Bytes(); len(b) == 0 || (len(b) >= 2 && b[len(b)-1] == '\n' && b[len(b)-2] == '\n') {
    69  		// skip the blank line at top of file or after a blank line
    70  	} else {
    71  		p.printf("\n")
    72  	}
    73  	for i := 0; i < p.margin; i++ {
    74  		p.printf("\t")
    75  	}
    76  }
    77  
    78  // trim removes trailing spaces and tabs from the current line.
    79  func (p *printer) trim() {
    80  	// Remove trailing spaces and tabs from line we're about to end.
    81  	b := p.Bytes()
    82  	n := len(b)
    83  	for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') {
    84  		n--
    85  	}
    86  	p.Truncate(n)
    87  }
    88  
    89  // file formats the given file into the print buffer.
    90  func (p *printer) file(f *FileSyntax) {
    91  	for _, com := range f.Before {
    92  		p.printf("%s", strings.TrimSpace(com.Token))
    93  		p.newline()
    94  	}
    95  
    96  	for i, stmt := range f.Stmt {
    97  		switch x := stmt.(type) {
    98  		case *CommentBlock:
    99  			// comments already handled
   100  			p.expr(x)
   101  
   102  		default:
   103  			p.expr(x)
   104  			p.newline()
   105  		}
   106  
   107  		for _, com := range stmt.Comment().After {
   108  			p.printf("%s", strings.TrimSpace(com.Token))
   109  			p.newline()
   110  		}
   111  
   112  		if i+1 < len(f.Stmt) {
   113  			p.newline()
   114  		}
   115  	}
   116  }
   117  
   118  func (p *printer) expr(x Expr) {
   119  	// Emit line-comments preceding this expression.
   120  	if before := x.Comment().Before; len(before) > 0 {
   121  		// Want to print a line comment.
   122  		// Line comments must be at the current margin.
   123  		p.trim()
   124  		if p.indent() > 0 {
   125  			// There's other text on the line. Start a new line.
   126  			p.printf("\n")
   127  		}
   128  		// Re-indent to margin.
   129  		for i := 0; i < p.margin; i++ {
   130  			p.printf("\t")
   131  		}
   132  		for _, com := range before {
   133  			p.printf("%s", strings.TrimSpace(com.Token))
   134  			p.newline()
   135  		}
   136  	}
   137  
   138  	switch x := x.(type) {
   139  	default:
   140  		panic(fmt.Errorf("printer: unexpected type %T", x))
   141  
   142  	case *CommentBlock:
   143  		// done
   144  
   145  	case *LParen:
   146  		p.printf("(")
   147  	case *RParen:
   148  		p.printf(")")
   149  
   150  	case *Line:
   151  		p.tokens(x.Token)
   152  
   153  	case *LineBlock:
   154  		p.tokens(x.Token)
   155  		p.printf(" ")
   156  		p.expr(&x.LParen)
   157  		p.margin++
   158  		for _, l := range x.Line {
   159  			p.newline()
   160  			p.expr(l)
   161  		}
   162  		p.margin--
   163  		p.newline()
   164  		p.expr(&x.RParen)
   165  	}
   166  
   167  	// Queue end-of-line comments for printing when we
   168  	// reach the end of the line.
   169  	p.comment = append(p.comment, x.Comment().Suffix...)
   170  }
   171  
   172  func (p *printer) tokens(tokens []string) {
   173  	sep := ""
   174  	for _, t := range tokens {
   175  		if t == "," || t == ")" || t == "]" || t == "}" {
   176  			sep = ""
   177  		}
   178  		p.printf("%s%s", sep, t)
   179  		sep = " "
   180  		if t == "(" || t == "[" || t == "{" {
   181  			sep = ""
   182  		}
   183  	}
   184  }
   185  

View as plain text