forked from incu6us/goimports-reviser
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfs.go
139 lines (112 loc) · 2.95 KB
/
fs.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
// SPDX-FileCopyrightText: 2023 Christoph Mewes
// SPDX-License-Identifier: MIT
package main
import (
"errors"
"fmt"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
doublestarx "github.com/bmatcuk/doublestar/v4"
)
// listFiles takes a filename or directory as its start argument and returns
// a list of absolute file paths. If a filename is given, the list contains
// exactly one element, otherwise the directory is scanned recursively.
// Note that if start is a file, the skip rules are not evaluated. This allows
// users to force-format an otherwise skipped file.
func listFiles(start string, moduleRoot string, skips []string) ([]string, error) {
result := []string{}
info, err := os.Stat(start)
if err != nil {
return nil, fmt.Errorf("invalid file: %v", err)
}
if !info.IsDir() {
return []string{start}, nil
}
err = filepath.WalkDir(start, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(moduleRoot, path)
if err != nil {
return fmt.Errorf("invalid file: %v", err)
}
if isSkipped(relPath, skips) {
if d.IsDir() {
return filepath.SkipDir
} else {
return nil
}
}
if !d.IsDir() && strings.HasSuffix(path, ".go") {
result = append(result, path)
}
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
func isSkipped(relPath string, skips []string) bool {
for _, skip := range skips {
if match, _ := doublestarx.Match(skip, relPath); match {
return true
}
}
return false
}
func goModRootPath(path string) (string, error) {
// turn path into directory, if it's a file
if info, err := os.Stat(path); err == nil && !info.IsDir() {
path = filepath.Dir(path)
}
for {
if fi, err := os.Stat(filepath.Join(path, "go.mod")); err == nil && !fi.IsDir() {
return path, nil
}
d := filepath.Dir(path)
if d == path {
break
}
path = d
}
return "", errors.New("no go.mod found")
}
var (
// detect generated files by presence if this string in the first non-stripped line
generatedRe = regexp.MustCompile("(been generated|generated by|do not edit)")
)
func isGeneratedFile(filename string) (bool, error) {
content, err := os.ReadFile(filename)
if err != nil {
return false, fmt.Errorf("failed to read file: %v", err)
}
return isGeneratedCode(content)
}
func isGeneratedCode(sourceCode []byte) (bool, error) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", sourceCode, parser.ParseComments)
if err != nil {
return false, fmt.Errorf("failed to parse code: %v", err)
}
// go through all comments until we reach the package declaration
outer:
for _, commentGroup := range file.Comments {
for _, comment := range commentGroup.List {
// found the package declaration
if comment.Slash > file.Package {
break outer
}
text := []byte(strings.ToLower(comment.Text))
if generatedRe.Match(text) {
return true, nil
}
}
}
return false, nil
}