/
formatter.go
143 lines (124 loc) · 3.39 KB
/
formatter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package main
import (
"bytes"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/pmezard/go-difflib/difflib"
)
// Common interface for all formatters.
//
// A new formatter can be added by implementing this interface and adding it to
// the global registry.
type Formatter interface {
Name() string
// Reads the input stream and writes a prettified version to the output.
FormatToBuffer(args []string, file string, in io.Reader, out io.Writer) error
// Reformats the given file in-place.
FormatInPlace(args []string, file string) error
// Check if the required binary is installed.
IsInstalled() bool
// A list of file extensions (including the '.') that this formatter applies to.
FileExtensions() []string
}
func FormatInPlaceAndCheckModified(F Formatter, args []string, absPath string) (bool, error) {
// record modification time before running formatter
fi, err := os.Stat(absPath)
if err != nil {
return false, err
}
mtimeBefore := fi.ModTime()
err = F.FormatInPlace(args, absPath)
if err != nil {
return false, err
}
// See if file was modified
fi, err = os.Stat(absPath)
if err != nil {
return false, err
}
mtimeAfter := fi.ModTime()
modified := mtimeAfter.After(mtimeBefore)
return modified, nil
}
func CreatePatchWithFormatter(F Formatter, args []string, wdir, file string) (string, error) {
fileContent, err := ioutil.ReadFile(filepath.Join(wdir, file))
if err != nil {
return "", err
}
var formattedOutput bytes.Buffer
err = F.FormatToBuffer(args, file, bytes.NewReader(fileContent), &formattedOutput)
if err != nil {
return "", err
}
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(fileContent)),
B: difflib.SplitLines(formattedOutput.String()),
FromFile: "a/" + file,
ToFile: "b/" + file,
Context: 3,
})
return diff, nil
}
func LookupFormatter(name string) Formatter {
for _, f := range FormatterRegistry {
if f.Name() == name {
return f
}
}
return nil
}
// Returns a map of file extension to formatter for the ones specied in the
// input mapping.
func LoadFormattersFromMapping(extToName map[string]string) map[string]Formatter {
byExt := make(map[string]Formatter)
for ext, name := range extToName {
formatter := LookupFormatter(name)
if formatter == nil {
log.Fatalf("Unknown formatter: %s", name)
}
if !formatter.IsInstalled() {
log.Fatalf("Formatter %s not installed", name)
}
if byExt[ext] != nil {
log.Fatalf("Multiple formatters for extension '%s'", ext)
}
byExt[ext] = formatter
}
return byExt
}
// Returns a map of file extension to formatter for all installed formatters in
// the registry.
func LoadDefaultFormatters() map[string]Formatter {
byExt := make(map[string]Formatter)
for _, f := range FormatterRegistry {
if !f.IsInstalled() {
log.Printf("Skipping formatter %s b/c it's not installed", f.Name())
continue
}
for _, ext := range f.FileExtensions() {
if byExt[ext] != nil {
// log.Printf("Multiple formatters for extension '%s'", ext)
continue
}
byExt[ext] = f
}
}
return byExt
}
var (
// Global list of all formatters.
// If multiple formatters apply to the same file type, their order here
// determines precedence.
FormatterRegistry = []Formatter{
&ClangFormatter{},
&UncrustifyFormatter{},
&PrettierFormatter{},
&YapfFormatter{},
&GofmtFormatter{},
&BuildifierFormatter{},
&RustfmtFormatter{},
}
)