// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmd import ( "context" "flag" "fmt" "io/ioutil" "golang.org/x/tools/internal/lsp/diff" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/span" errors "golang.org/x/xerrors" ) // format implements the format verb for gopls. type format struct { Diff bool `flag:"d" help:"display diffs instead of rewriting files"` Write bool `flag:"w" help:"write result to (source) file instead of stdout"` List bool `flag:"l" help:"list files whose formatting differs from gofmt's"` app *Application } func (c *format) Name() string { return "format" } func (c *format) Usage() string { return "" } func (c *format) ShortHelp() string { return "format the code according to the go standard" } func (c *format) DetailedHelp(f *flag.FlagSet) { fmt.Fprint(f.Output(), ` The arguments supplied may be simple file names, or ranges within files. Example: reformat this file: $ gopls format -w internal/lsp/cmd/check.go gopls format flags are: `) f.PrintDefaults() } // Run performs the check on the files specified by args and prints the // results to stdout. func (c *format) Run(ctx context.Context, args ...string) error { if len(args) == 0 { // no files, so no results return nil } // now we ready to kick things off conn, err := c.app.connect(ctx) if err != nil { return err } defer conn.terminate(ctx) for _, arg := range args { spn := span.Parse(arg) file := conn.AddFile(ctx, spn.URI()) if file.err != nil { return file.err } filename := spn.URI().Filename() loc, err := file.mapper.Location(spn) if err != nil { return err } if loc.Range.Start != loc.Range.End { return errors.Errorf("only full file formatting supported") } p := protocol.DocumentFormattingParams{ TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, } edits, err := conn.Formatting(ctx, &p) if err != nil { return errors.Errorf("%v: %v", spn, err) } sedits, err := source.FromProtocolEdits(file.mapper, edits) if err != nil { return errors.Errorf("%v: %v", spn, err) } formatted := diff.ApplyEdits(string(file.mapper.Content), sedits) printIt := true if c.List { printIt = false if len(edits) > 0 { fmt.Println(filename) } } if c.Write { printIt = false if len(edits) > 0 { ioutil.WriteFile(filename, []byte(formatted), 0644) } } if c.Diff { printIt = false u := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits) fmt.Print(u) } if printIt { fmt.Print(formatted) } } return nil }