Skip to content

Commit 873c6d5

Browse files
committed
feat: add a max-batch-size option for formatters
This is part of #334
1 parent 0144305 commit 873c6d5

6 files changed

Lines changed: 64 additions & 8 deletions

File tree

cmd/format/format.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ import (
2222
bolt "go.etcd.io/bbolt"
2323
)
2424

25-
const (
26-
BatchSize = 1024
27-
)
28-
2925
var ErrFailOnChange = errors.New("unexpected changes detected, --fail-on-change is enabled")
3026

3127
func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string) error {
@@ -123,7 +119,7 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
123119
}
124120

125121
// create a composite formatter which will handle applying the correct formatters to each file we traverse
126-
formatter, err := format.NewCompositeFormatter(cfg, statz, BatchSize)
122+
formatter, err := format.NewCompositeFormatter(cfg, statz, format.BatchSize)
127123
if err != nil {
128124
return fmt.Errorf("failed to create composite formatter: %w", err)
129125
}
@@ -135,7 +131,7 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
135131
}
136132

137133
// start traversing
138-
files := make([]*walk.File, BatchSize)
134+
files := make([]*walk.File, format.BatchSize)
139135

140136
var (
141137
n int

cmd/root_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,6 +2501,35 @@ func TestConcurrentInvocation(t *testing.T) {
25012501
as.NoError(eg.Wait())
25022502
}
25032503

2504+
func TestMaxBatchSize(t *testing.T) {
2505+
tempDir := test.TempExamples(t)
2506+
configPath := filepath.Join(tempDir, "/treefmt.toml")
2507+
2508+
test.ChangeWorkDir(t, tempDir)
2509+
2510+
maxBatchSize := 1
2511+
cfg := &config.Config{
2512+
FormatterConfigs: map[string]*config.Formatter{
2513+
"echo": {
2514+
Command: "test-fmt-only-one-file-at-a-time",
2515+
Includes: []string{"*"},
2516+
MaxBatchSize: &maxBatchSize,
2517+
},
2518+
},
2519+
}
2520+
2521+
treefmt(t,
2522+
withConfig(configPath, cfg),
2523+
withNoError(t),
2524+
withStats(t, map[stats.Type]int{
2525+
stats.Traversed: 33,
2526+
stats.Matched: 33,
2527+
stats.Formatted: 33,
2528+
stats.Changed: 33,
2529+
}),
2530+
)
2531+
}
2532+
25042533
type options struct {
25052534
args []string
25062535
env map[string]string

config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ type Formatter struct {
6262
Excludes []string `mapstructure:"excludes,omitempty" toml:"excludes,omitempty"`
6363
// Indicates the order of precedence when executing this Formatter in a sequence of Formatters.
6464
Priority int `mapstructure:"priority,omitempty" toml:"priority,omitempty"`
65+
// The maximum number of files we should pass to this Formatter at once.
66+
MaxBatchSize *int `mapstructure:"max-batch-size" toml:"max-batch-size"`
6567
}
6668

6769
// SetFlags appends our flags to the provided flag set.

format/formatter.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020
"mvdan.cc/sh/v3/interp"
2121
)
2222

23+
const (
24+
BatchSize = 1024
25+
)
26+
2327
var (
2428
ErrInvalidName = errors.New("formatter name must only contain alphanumeric characters, `_` or `-`")
2529
// ErrCommandNotFound is returned when the Command for a Formatter is not available.
@@ -46,6 +50,14 @@ func (f *Formatter) Name() string {
4650
return f.name
4751
}
4852

53+
func (f *Formatter) MaxBatchSize() int {
54+
if f.config.MaxBatchSize == nil {
55+
return BatchSize
56+
}
57+
58+
return *f.config.MaxBatchSize
59+
}
60+
4961
func (f *Formatter) Priority() int {
5062
return f.config.Priority
5163
}
@@ -78,6 +90,10 @@ func (f *Formatter) Hash(h hash.Hash) error {
7890
}
7991

8092
func (f *Formatter) Apply(ctx context.Context, files []*walk.File) error {
93+
if len(files) > f.MaxBatchSize() {
94+
return fmt.Errorf("formatter cannot format %d files at once (max batch size: %d)", len(files), f.MaxBatchSize())
95+
}
96+
8197
start := time.Now()
8298

8399
// construct args, starting with config

format/scheduler.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ func (s *scheduler) schedule(ctx context.Context, key batchKey, batch []*walk.Fi
145145
for _, name := range key.sequence() {
146146
formatter := s.formatters[name]
147147

148-
if err := formatter.Apply(ctx, batch); err != nil {
149-
formatErrors = append(formatErrors, err)
148+
for chunk := range slices.Chunk(batch, formatter.MaxBatchSize()) {
149+
if err := formatter.Apply(ctx, chunk); err != nil {
150+
formatErrors = append(formatErrors, err)
151+
}
150152
}
151153
}
152154

nix/packages/treefmt/formatters.nix

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,15 @@ with pkgs; [
5858
test-fmt-append "$@"
5959
'';
6060
})
61+
(pkgs.writeShellApplication {
62+
name = "test-fmt-only-one-file-at-a-time";
63+
text = ''
64+
if [ $# -ne 1 ]; then
65+
echo "I only support formatting exactly 1 file at a time"
66+
exit 1
67+
fi
68+
69+
test-fmt-append "suffix" "$1"
70+
'';
71+
})
6172
]

0 commit comments

Comments
 (0)