Skip to content

Commit

Permalink
exclude patterns, not directories
Browse files Browse the repository at this point in the history
this makes the exclude synax much more flexible
  • Loading branch information
justbuchanan committed Jan 8, 2018
1 parent f55c218 commit a696213
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .stylize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
formatters:
.py: yapf
.go: gofmt
exclude_dirs:
exclude:
- testdata
clang_style: google
yapf_style: pep8
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
// This type defines the structure of the yml config file for stylize.
type Config struct {
FormattersByExt map[string]string `yaml:"formatters"`
ExcludeDirs []string `yaml:"exclude_dirs"`
ExcludePatterns []string `yaml:"exclude"`

// TODO: do better
ClangStyle string `yaml:"clang_style"`
Expand Down
25 changes: 11 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func main() {
patchFileFlag := flag.String("patch_output", "", "Path to output patch to. If '-', writes to stdout.")
configFileFlag := flag.String("config", ".stylize.yml", "Optional config file (defaults to .stylize.yml).")
dirFlag := flag.String("dir", ".", "Directory to recursively format.")
excludeDirFlag := flag.String("exclude_dirs", "", "Directories to exclude (comma-separated).")
excludeFlag := flag.String("exclude", "", "A list of exclude patterns (comma-separated).")
diffbaseFlag := flag.String("git_diffbase", "", "If provided, stylize only looks at files that differ from the given commit/branch.")
parallelismFlag := flag.Int("j", 8, "Number of files to process in parallel.")
flag.Parse()
Expand All @@ -31,7 +31,10 @@ func main() {
log.Printf("Loaded config from file %s", *configFileFlag)
}

rootDir := absPathOrFail(*dirFlag)
rootDir, err := filepath.Abs(*dirFlag)
if err != nil {
log.Fatal(err)
}

// set style configs from config file
// TODO: do better
Expand All @@ -44,20 +47,14 @@ func main() {
}
}

var excludeDirs []string
var excludePatterns []string
// exclude dirs from config
if cfg != nil {
excludeDirs = append(excludeDirs, cfg.ExcludeDirs...)
excludePatterns = append(excludePatterns, cfg.ExcludePatterns...)
}
// exclude dirs from flag
if len(*excludeDirFlag) > 0 {
excludeDirs = append(excludeDirs, strings.Split(*excludeDirFlag, ",")...)
}
// make exclude dirs absolute - if they're not already, they're assumed to be relative to the root directory
for i, edir := range excludeDirs {
if !filepath.IsAbs(edir) {
excludeDirs[i] = filepath.Join(rootDir, edir)
}
if len(*excludeFlag) > 0 {
excludePatterns = append(excludePatterns, strings.Split(*excludeFlag, ",")...)
}

// setup formatters
Expand Down Expand Up @@ -86,9 +83,9 @@ func main() {
defer patchFileOut.Close()
log.Printf("Writing patch to file %s", *patchFileFlag)
}
stats = StylizeMain(formatters, rootDir, excludeDirs, *diffbaseFlag, patchOut, *inPlaceFlag, *parallelismFlag)
stats = StylizeMain(formatters, rootDir, excludePatterns, *diffbaseFlag, patchOut, *inPlaceFlag, *parallelismFlag)
} else {
stats = StylizeMain(formatters, rootDir, excludeDirs, *diffbaseFlag, nil, *inPlaceFlag, *parallelismFlag)
stats = StylizeMain(formatters, rootDir, excludePatterns, *diffbaseFlag, nil, *inPlaceFlag, *parallelismFlag)
}

if stats.Error != 0 {
Expand Down
52 changes: 24 additions & 28 deletions stylize.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,10 @@ type FormattingResult struct {
Error error
}

func fileIsExcluded(file string, excludeDirs []string) bool {
for _, eDir := range excludeDirs {
if filepath.HasPrefix(file, eDir) {
return true
}
}
return false
}

// Walks the given directory and sends all non-excluded files to the returned channel.
// @param rootDir absolute path to root directory
// @return file paths relative to rootDir
func IterateAllFiles(rootDir string, excludeDirs []string) <-chan string {
func IterateAllFiles(rootDir string, exclude []string) <-chan string {
files := make(chan string)

go func() {
Expand All @@ -44,16 +35,21 @@ func IterateAllFiles(rootDir string, excludeDirs []string) <-chan string {
return nil
}

excludeDirs = append(excludeDirs, absPathOrFail(".git"), absPathOrFail(".hg"))
if fi.IsDir() && fileIsExcluded(path, excludeDirs) {
relPath, _ := filepath.Rel(rootDir, path)

exclude = append(exclude, ".git", ".hg")
if fi.IsDir() && fileIsExcluded(relPath, exclude) {
return filepath.SkipDir
}

if fileIsExcluded(relPath, exclude) {
return nil
}

if fi.IsDir() {
return nil
}

relPath, _ := filepath.Rel(rootDir, path)
files <- relPath

return nil
Expand All @@ -66,7 +62,7 @@ func IterateAllFiles(rootDir string, excludeDirs []string) <-chan string {
// Finds files that have been modified since the common ancestor of HEAD and
// diffbase and sends them onto the returned channel.
// @return file paths relative to rootDir
func IterateGitChangedFiles(rootDir string, excludeDirs []string, diffbase string) (<-chan string, error) {
func IterateGitChangedFiles(rootDir string, exclude []string, diffbase string) (<-chan string, error) {
changedFiles, err := gitChangedFiles(rootDir, diffbase)
if err != nil {
return nil, err
Expand All @@ -90,8 +86,14 @@ func IterateGitChangedFiles(rootDir string, excludeDirs []string, diffbase strin

for _, file := range changedFiles {
absPath := filepath.Join(gitRoot, file)
if fileIsExcluded(absPath, excludeDirs) {
// log.Printf("Excluding file: %s", absPath)

// get file path relative to root directory
relPath, err := filepath.Rel(rootDir, absPath)
if err != nil {
log.Fatal(err)
}

if fileIsExcluded(relPath, exclude) {
continue
}

Expand All @@ -102,12 +104,6 @@ func IterateGitChangedFiles(rootDir string, excludeDirs []string, diffbase strin
continue
}

// get file path relative to root directory
relPath, err := filepath.Rel(rootDir, absPath)
if err != nil {
log.Fatal(err)
}

files <- relPath
}
}()
Expand Down Expand Up @@ -263,30 +259,30 @@ func LogActionsAndCollectStats(results <-chan FormattingResult, inPlace bool) Ru
// diffbase. Otherwise looks at all files.
// @param formatters A map of file extension -> formatter
// @return (changeCount, totalCount, errCount)
func StylizeMain(formatters map[string]Formatter, rootDir string, excludeDirs []string, gitDiffbase string, patchOut io.Writer, inPlace bool, parallelism int) RunStats {
func StylizeMain(formatters map[string]Formatter, rootDir string, exclude []string, gitDiffbase string, patchOut io.Writer, inPlace bool, parallelism int) RunStats {
if inPlace && patchOut != nil {
log.Fatal("Patch output writer should only be provided in non-inplace runs")
}
if !filepath.IsAbs(rootDir) {
log.Fatalf("root directory should be an absolute path: '%s'", rootDir)
}

for _, excl := range excludeDirs {
if !filepath.IsAbs(excl) {
log.Fatal("exclude directories should be absolute")
for _, excl := range exclude {
if filepath.IsAbs(excl) {
log.Fatal("exclude directories should not be absolute")
}
}

// setup file source
var err error
var fileChan <-chan string
if len(gitDiffbase) > 0 {
fileChan, err = IterateGitChangedFiles(rootDir, excludeDirs, gitDiffbase)
fileChan, err = IterateGitChangedFiles(rootDir, exclude, gitDiffbase)
if err != nil {
log.Fatal(err)
}
} else {
fileChan = IterateAllFiles(rootDir, excludeDirs)
fileChan = IterateAllFiles(rootDir, exclude)
}

// run formatter on all files
Expand Down
40 changes: 29 additions & 11 deletions stylize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}

func expectMatch(t *testing.T, match bool, pattern, file string) {
m := filePatternMatch(pattern, file)
if m != match {
t.Logf("'%s' '%s'", pattern, file)
if match {
t.Error("Expected match, but didn't get one")
} else {
t.Error("It matched, but wasnt supposed to")
}
}
}

func TestMatch(t *testing.T) {
expectMatch(t, true, "exclude/", "exclude/file.cpp")
expectMatch(t, true, "exclude", "exclude/file.cpp")
expectMatch(t, false, "exclud", "exclude/file.cpp")
expectMatch(t, false, "bbb", "bbb.cpp")
expectMatch(t, true, "*", "bad.cpp")
expectMatch(t, true, "*", "files/bad.cpp")
}

func TestCreatePatch(t *testing.T) {
goldenFile := "testdata/patch.golden"

Expand All @@ -42,7 +63,8 @@ func TestCreatePatch(t *testing.T) {
patchOut = &patchBuffer
}

StylizeMain(LoadDefaultFormatters(), absPathOrFail("testdata"), []string{absPathOrFail("testdata/exclude")}, "", patchOut, false, PARALLELISM)
absDirPath, _ := filepath.Abs("testdata")
StylizeMain(LoadDefaultFormatters(), absDirPath, []string{"exclude"}, "", patchOut, false, PARALLELISM)

if !*generateGoldens {
assertGoldenMatch(t, goldenFile, patchBuffer.String())
Expand All @@ -60,7 +82,7 @@ func TestInPlace(t *testing.T) {
tmp := mktmp(t)
dir := copyTestData(t, tmp)

exclude := []string{path.Join(dir, "exclude")}
exclude := []string{"exclude"}
t.Log("exclude: " + strings.Join(exclude, ","))

// run in-place formatting
Expand All @@ -83,30 +105,26 @@ func TestInPlaceWithConfig(t *testing.T) {
dir := copyTestData(t, tmp)

cfgPath := path.Join(dir, ".stylize.yml")
err := ioutil.WriteFile(cfgPath, []byte("---\nformatters:\n .py: yapf\nexclude_dirs:\n - exclude"), 0644)
err := ioutil.WriteFile(cfgPath, []byte("---\nformatters:\n .py: yapf\nexclude:\n - exclude"), 0644)
tCheckErr(t, err)
t.Logf("Wrote config file: %s", cfgPath)

cfg, err := LoadConfig(cfgPath)
tCheckErr(t, err)
t.Log("Read config file")

for i, edir := range cfg.ExcludeDirs {
cfg.ExcludeDirs[i] = filepath.Join(dir, edir)
}
t.Log("exclude: " + strings.Join(cfg.ExcludeDirs, ","))
t.Log("exclude: " + strings.Join(cfg.ExcludePatterns, ","))

formatters := LoadFormattersFromMapping(cfg.FormattersByExt)

// run in-place formatting
stats := StylizeMain(formatters, dir, cfg.ExcludeDirs, "", nil, true, PARALLELISM)
stats := StylizeMain(formatters, dir, cfg.ExcludePatterns, "", nil, true, PARALLELISM)
t.Logf("Stylize results: %d, %d, %d", stats.Change, stats.Total, stats.Error)

if stats.Change != 1 {
t.Fatal("One file should have changed")
}

stats = StylizeMain(formatters, dir, cfg.ExcludeDirs, "", nil, true, PARALLELISM)
stats = StylizeMain(formatters, dir, cfg.ExcludePatterns, "", nil, true, PARALLELISM)
t.Logf("Stylize results: %d, %d, %d", stats.Change, stats.Total, stats.Error)

if stats.Change != 0 {
Expand Down Expand Up @@ -135,7 +153,7 @@ func TestGitDiffbase(t *testing.T) {
runCmd(t, dir, "git", "add", ".")
runCmd(t, dir, "git", "commit", "-m", "added files")

exclude := []string{path.Join(dir, "exclude")}
exclude := []string{"exclude"}
t.Log("exclude: " + strings.Join(exclude, ","))

// run stylize with diffbase provided
Expand Down
43 changes: 33 additions & 10 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package main
import (
"bytes"
"fmt"
"github.com/danwakefield/fnmatch"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unsafe"
Expand All @@ -30,14 +29,6 @@ func runIOCommand(args []string, in io.Reader, out io.Writer) error {
return nil
}

func absPathOrFail(path string) string {
absPath, err := filepath.Abs(path)
if err != nil {
log.Fatal(err)
}
return absPath
}

type winsize struct {
Row uint16
Col uint16
Expand Down Expand Up @@ -90,3 +81,35 @@ func gitChangedFiles(rootDir, diffbase string) ([]string, error) {

return changedFiles, nil
}

// TODO: this implementation has a lot of flaws
// It would be nice to do something similar to gitignore
func filePatternMatch(pattern, file string) bool {
if fnmatch.Match(pattern, file, fnmatch.FNM_PATHNAME|fnmatch.FNM_LEADING_DIR) {
return true
}

// TODO: this is a hack
hasGlobChars := strings.ContainsAny(pattern, "*?")
if hasGlobChars {
return false
}

// If pattern ends in '/', ignore all files in that directory recursively
if strings.HasSuffix(pattern, "/") {
if strings.HasPrefix(file, pattern) {
return true
}
}

return false
}

func fileIsExcluded(file string, exclude []string) bool {
for _, e := range exclude {
if filePatternMatch(e, file) {
return true
}
}
return false
}

0 comments on commit a696213

Please sign in to comment.