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

View as plain text