Skip to content

Commit

Permalink
feat: add notify based formatter
Browse files Browse the repository at this point in the history
shikanime committed Jan 23, 2025

Verified

This commit was signed with the committer’s verified signature.
shikanime Shikanime Deva
1 parent 45881a4 commit 150bea4
Showing 7 changed files with 326 additions and 42 deletions.
11 changes: 11 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ type Config struct {
Walk string `mapstructure:"walk" toml:"walk,omitempty"`
WorkingDirectory string `mapstructure:"working-dir" toml:"-"`
Stdin bool `mapstructure:"stdin" toml:"-"` // not allowed in config
Watch bool `mapstructure:"watch" toml:"-"` // not allowed in config

FormatterConfigs map[string]*Formatter `mapstructure:"formatter" toml:"formatter,omitempty"`

@@ -98,6 +99,10 @@ func SetFlags(fs *pflag.FlagSet) {
"stdin", false,
"Format the context passed in via stdin.",
)
fs.Bool(
"watch", false,
"Watch the filesystem for changes and apply formatters when changes are detected. (env $TREEFMT_WATCH)",
)
fs.String(
"tree-root", "",
"The root directory from which treefmt will start walking the filesystem (defaults to the directory "+
@@ -157,6 +162,7 @@ func FromViper(v *viper.Viper) (*Config, error) {
"clear-cache": false,
"no-cache": false,
"stdin": false,
"watch": false,
"working-dir": ".",
}

@@ -185,6 +191,11 @@ func FromViper(v *viper.Viper) (*Config, error) {
cfg.Walk = walk.Stdin.String()
}

// if the watch flag was passed, we force the watch walk type
if cfg.Watch {
cfg.Walk = walk.Watch.String()
}

// determine the tree root
if cfg.TreeRoot == "" {
// if none was specified, we first try with tree-root-file
37 changes: 37 additions & 0 deletions test/test.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,43 @@ import (
"golang.org/x/sys/unix"
)

//nolint:gochecknoglobals
var ExamplesPaths = []string{
"elm/elm.json",
"elm/src/Main.elm",
"emoji 🕰️/README.md",
"go/go.mod",
"go/main.go",
"haskell/CHANGELOG.md",
"haskell/Foo.hs",
"haskell/Main.hs",
"haskell/Nested/Foo.hs",
"haskell/Setup.hs",
"haskell/haskell.cabal",
"haskell/treefmt.toml",
"haskell-frontend/CHANGELOG.md",
"haskell-frontend/Main.hs",
"haskell-frontend/Setup.hs",
"haskell-frontend/haskell-frontend.cabal",
"html/index.html",
"html/scripts/.gitkeep",
"javascript/source/hello.js",
"nix/sources.nix",
"nixpkgs.toml",
"python/main.py",
"python/requirements.txt",
"python/virtualenv_proxy.py",
"ruby/bundler.rb",
"rust/Cargo.toml",
"rust/src/main.rs",
"shell/foo.sh",
"terraform/main.tf",
"terraform/two.tf",
"touch.toml",
"treefmt.toml",
"yaml/test.yaml",
}

func WriteConfig(t *testing.T, path string, cfg *config.Config) {
t.Helper()

39 changes: 1 addition & 38 deletions walk/filesystem_test.go
Original file line number Diff line number Diff line change
@@ -13,43 +13,6 @@ import (
"github.com/stretchr/testify/require"
)

//nolint:gochecknoglobals
var examplesPaths = []string{
"elm/elm.json",
"elm/src/Main.elm",
"emoji 🕰️/README.md",
"go/go.mod",
"go/main.go",
"haskell/CHANGELOG.md",
"haskell/Foo.hs",
"haskell/Main.hs",
"haskell/Nested/Foo.hs",
"haskell/Setup.hs",
"haskell/haskell.cabal",
"haskell/treefmt.toml",
"haskell-frontend/CHANGELOG.md",
"haskell-frontend/Main.hs",
"haskell-frontend/Setup.hs",
"haskell-frontend/haskell-frontend.cabal",
"html/index.html",
"html/scripts/.gitkeep",
"javascript/source/hello.js",
"nix/sources.nix",
"nixpkgs.toml",
"python/main.py",
"python/requirements.txt",
"python/virtualenv_proxy.py",
"ruby/bundler.rb",
"rust/Cargo.toml",
"rust/src/main.rs",
"shell/foo.sh",
"terraform/main.tf",
"terraform/two.tf",
"touch.toml",
"treefmt.toml",
"yaml/test.yaml",
}

func TestFilesystemReader(t *testing.T) {
as := require.New(t)

@@ -67,7 +30,7 @@ func TestFilesystemReader(t *testing.T) {
n, err := r.Read(ctx, files)

for i := count; i < count+n; i++ {
as.Equal(examplesPaths[i], files[i-count].RelPath)
as.Equal(test.ExamplesPaths[i], files[i-count].RelPath)
}

count += n
12 changes: 8 additions & 4 deletions walk/type_enum.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions walk/walk.go
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ const (
Stdin
Filesystem
Git
Watch

BatchSize = 1024
)
@@ -215,6 +216,8 @@ func NewReader(
reader = NewFilesystemReader(root, path, statz, BatchSize)
case Git:
reader, err = NewGitReader(root, path, statz)
case Watch:
reader, err = NewWatchReader(root, path, statz)

default:
return nil, fmt.Errorf("unknown walk type: %v", walkType)
110 changes: 110 additions & 0 deletions walk/walk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package walk_test

import (
"context"
"errors"
"io"
"os"
"path"
"testing"
"time"

"github.com/numtide/treefmt/v2/stats"
"github.com/numtide/treefmt/v2/test"
"github.com/numtide/treefmt/v2/walk"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
)

//nolint:gochecknoglobals
var sourceExample = `
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}`

func TestWatchReader(t *testing.T) {
as := require.New(t)

tempDir := test.TempExamples(t)
statz := stats.New()

r, err := walk.NewWatchReader(tempDir, "", &statz)
as.NoError(err)

eg := errgroup.Group{}
for _, example := range test.ExamplesPaths {
eg.Go(func() error {
filePath := path.Join(tempDir, example)
content, err := os.ReadFile(filePath)
if err != nil {
return err
}

return os.WriteFile(filePath, content, 0o644)
})
}

count := 0

for count < 33 {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)

files := make([]*walk.File, 8)
n, err := r.Read(ctx, files)

count += n

cancel()

if errors.Is(err, io.EOF) {
break
}
}

as.NoError(eg.Wait())

as.Equal(33, count)
as.Equal(33, statz.Value(stats.Traversed))
as.Equal(0, statz.Value(stats.Matched))
as.Equal(0, statz.Value(stats.Formatted))
as.Equal(0, statz.Value(stats.Changed))
}

func TestWatchReaderCreate(t *testing.T) {
as := require.New(t)

tempDir := t.TempDir()
statz := stats.New()

r, err := walk.NewWatchReader(tempDir, "", &statz)
as.NoError(err)

as.NoError(
os.WriteFile(
path.Join(tempDir, "main.go"),
[]byte(sourceExample),
0o644,
),
)

ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)

files := make([]*walk.File, 8)
n, err := r.Read(ctx, files)

cancel()

if !errors.Is(err, io.EOF) {
as.NoError(err)
}

as.Equal(1, n)
as.Equal(1, statz.Value(stats.Traversed))
as.Equal(0, statz.Value(stats.Matched))
as.Equal(0, statz.Value(stats.Formatted))
as.Equal(0, statz.Value(stats.Changed))
}
Loading

0 comments on commit 150bea4

Please sign in to comment.