// 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" "strings" "golang.org/x/tools/internal/lsp" "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" ) // 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 (f *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 := f.app.connect(ctx) if err != nil { return err } 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() // this cannot fail, already checked in AddFile above loc, err := file.mapper.Location(spn) if err != nil { return err } if loc.Range.Start != loc.Range.End { return fmt.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 fmt.Errorf("%v: %v", spn, err) } sedits, err := lsp.FromProtocolEdits(file.mapper, edits) if err != nil { return fmt.Errorf("%v: %v", spn, err) } ops := source.EditsToDiff(sedits) lines := diff.SplitLines(string(file.mapper.Content)) formatted := strings.Join(diff.ApplyEdits(lines, ops), "") printIt := true if f.List { printIt = false if len(edits) > 0 { fmt.Println(filename) } } if f.Write { printIt = false if len(edits) > 0 { ioutil.WriteFile(filename, []byte(formatted), 0644) } } if f.Diff { printIt = false u := diff.ToUnified(filename+".orig", filename, lines, ops) fmt.Print(u) } if printIt { fmt.Print(formatted) } } return nil }