github.com/v2fly/tools@v0.100.0/internal/lsp/cmd/format.go (about)

     1  // Copyright 2019 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  package cmd
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io/ioutil"
    12  
    13  	"github.com/v2fly/tools/internal/lsp/diff"
    14  	"github.com/v2fly/tools/internal/lsp/protocol"
    15  	"github.com/v2fly/tools/internal/lsp/source"
    16  	"github.com/v2fly/tools/internal/span"
    17  	errors "golang.org/x/xerrors"
    18  )
    19  
    20  // format implements the format verb for gopls.
    21  type format struct {
    22  	Diff  bool `flag:"d" help:"display diffs instead of rewriting files"`
    23  	Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
    24  	List  bool `flag:"l" help:"list files whose formatting differs from gofmt's"`
    25  
    26  	app *Application
    27  }
    28  
    29  func (c *format) Name() string      { return "format" }
    30  func (c *format) Usage() string     { return "<filerange>" }
    31  func (c *format) ShortHelp() string { return "format the code according to the go standard" }
    32  func (c *format) DetailedHelp(f *flag.FlagSet) {
    33  	fmt.Fprint(f.Output(), `
    34  The arguments supplied may be simple file names, or ranges within files.
    35  
    36  Example: reformat this file:
    37  
    38    $ gopls format -w internal/lsp/cmd/check.go
    39  
    40  	gopls format flags are:
    41  `)
    42  	f.PrintDefaults()
    43  }
    44  
    45  // Run performs the check on the files specified by args and prints the
    46  // results to stdout.
    47  func (c *format) Run(ctx context.Context, args ...string) error {
    48  	if len(args) == 0 {
    49  		// no files, so no results
    50  		return nil
    51  	}
    52  	// now we ready to kick things off
    53  	conn, err := c.app.connect(ctx)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	defer conn.terminate(ctx)
    58  	for _, arg := range args {
    59  		spn := span.Parse(arg)
    60  		file := conn.AddFile(ctx, spn.URI())
    61  		if file.err != nil {
    62  			return file.err
    63  		}
    64  		filename := spn.URI().Filename()
    65  		loc, err := file.mapper.Location(spn)
    66  		if err != nil {
    67  			return err
    68  		}
    69  		if loc.Range.Start != loc.Range.End {
    70  			return errors.Errorf("only full file formatting supported")
    71  		}
    72  		p := protocol.DocumentFormattingParams{
    73  			TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
    74  		}
    75  		edits, err := conn.Formatting(ctx, &p)
    76  		if err != nil {
    77  			return errors.Errorf("%v: %v", spn, err)
    78  		}
    79  		sedits, err := source.FromProtocolEdits(file.mapper, edits)
    80  		if err != nil {
    81  			return errors.Errorf("%v: %v", spn, err)
    82  		}
    83  		formatted := diff.ApplyEdits(string(file.mapper.Content), sedits)
    84  		printIt := true
    85  		if c.List {
    86  			printIt = false
    87  			if len(edits) > 0 {
    88  				fmt.Println(filename)
    89  			}
    90  		}
    91  		if c.Write {
    92  			printIt = false
    93  			if len(edits) > 0 {
    94  				ioutil.WriteFile(filename, []byte(formatted), 0644)
    95  			}
    96  		}
    97  		if c.Diff {
    98  			printIt = false
    99  			u := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits)
   100  			fmt.Print(u)
   101  		}
   102  		if printIt {
   103  			fmt.Print(formatted)
   104  		}
   105  	}
   106  	return nil
   107  }