github.com/gernest/nezuko@v0.1.2/internal/modfile/print.go (about)

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