diff --git a/docs/command-usage.md b/docs/command-usage.md index 6f4a3e1..5002bc8 100644 --- a/docs/command-usage.md +++ b/docs/command-usage.md @@ -90,7 +90,7 @@ The string array flags can be a bit confusing. See the [String Array Flags](#str | KYAML | `-kyaml` | bool | `yamlfmat -kyaml` | Enable the alternate [KYAML formatter](./config-file.md#kyaml-formatter). Note that using this option will completely override any formatter configuration from detected config file. | | Formatter Config | `-formatter` | []string | `yamlfmt -formatter indent=2,include_document_start=true` | Provide configuration values for the formatter. See [Formatter Configuration Options](./config-file.md#basic-formatter) for options. Each field is specified as `configkey=value`. | | Debug Logging | `-debug` | []string | `yamlfmt -debug paths,config` | Enable debug logging. See [Debug Logging](#debug-logging) for more information. | -| Output Format | `-output_format` | `default`, `line` | `yamlfmt -output_format line` | Choose a different output format. Defaults to `default`. See [Output docs](./output.md) for more information. | +| Output Format | `-output_format` | `default`, `line`, `slim`, `gitlab` | `yamlfmt -output_format slim` | Choose a different output format. Defaults to `default`. See [Output docs](./output.md) for more information. | #### String Array Flags diff --git a/docs/config-file.md b/docs/config-file.md index 73b78c8..36966cb 100644 --- a/docs/config-file.md +++ b/docs/config-file.md @@ -47,7 +47,7 @@ The command package defines the main command engine that `cmd/yamlfmt` uses. It | `regex_exclude` | []string | [] | Regex patterns to match file contents for, if the file content matches the regex the file will be excluded. Use [Go regexes](https://regex101.com/). | | `extensions` | []string | [] | The extensions to use for standard mode path collection. See [Specifying Paths][] for more details. | | `formatter` | map[string]any | `type: basic` | Formatter settings. See [Formatter](#formatter) for more details. | -| `output_format` | `default` or `line` | `default` | The output format to use. See [Output docs](./output.md) for more details. | +| `output_format` | `default`, `line`, `slim`, or `gitlab` | `default` | The output format to use. See [Output docs](./output.md) for more details. | ## Formatter diff --git a/docs/output.md b/docs/output.md index 08c04a3..6cc0c9f 100644 --- a/docs/output.md +++ b/docs/output.md @@ -33,6 +33,22 @@ y.yaml: formatting difference found z.yaml: formatting difference found ``` +## `slim` + +Prints a unified diff with nearby context lines instead of the full side-by-side file view. + +Example: +``` +The following formatting differences were found: + +--- x.yaml ++++ x.yaml +@@ -1,2 +1,2 @@ + a: +- b:1 ++ b: 1 +``` + ## `gitlab` Generates a [GitLab Code Quality report](https://docs.gitlab.com/ee/ci/testing/code_quality.html#code-quality-report-format). diff --git a/engine.go b/engine.go index 7666c43..4a6e3ec 100644 --- a/engine.go +++ b/engine.go @@ -113,7 +113,7 @@ func (fds FileDiffs) Add(diff *FileDiff) error { func (fds FileDiffs) StrOutput() string { result := "" - sortedPaths := fds.sortedPaths() + sortedPaths := fds.SortedPaths() for _, path := range sortedPaths { fd := fds[path] if fd.Diff.Changed() { @@ -125,7 +125,7 @@ func (fds FileDiffs) StrOutput() string { func (fds FileDiffs) StrOutputQuiet() string { result := "" - sortedPaths := fds.sortedPaths() + sortedPaths := fds.SortedPaths() for _, path := range sortedPaths { fd := fds[path] if fd.Diff.Changed() { @@ -155,7 +155,7 @@ func (fds FileDiffs) ChangedCount() int { return changed } -func (fds FileDiffs) sortedPaths() []string { +func (fds FileDiffs) SortedPaths() []string { pathKeys := []string{} for path := range fds { pathKeys = append(pathKeys, path) diff --git a/engine/output.go b/engine/output.go index c6a2e9e..f6ab391 100644 --- a/engine/output.go +++ b/engine/output.go @@ -22,6 +22,9 @@ import ( "github.com/google/yamlfmt" "github.com/google/yamlfmt/internal/gitlab" + "github.com/hexops/gotextdiff" + "github.com/hexops/gotextdiff/myers" + "github.com/hexops/gotextdiff/span" ) type EngineOutputFormat string @@ -29,6 +32,7 @@ type EngineOutputFormat string const ( EngineOutputDefault EngineOutputFormat = "default" EngineOutputSingeLine EngineOutputFormat = "line" + EngineOutputSlim EngineOutputFormat = "slim" EngineOutputGitlab EngineOutputFormat = "gitlab" ) @@ -38,6 +42,8 @@ func getEngineOutput(t EngineOutputFormat, operation yamlfmt.Operation, files ya return engineOutput{Operation: operation, Files: files, Quiet: quiet, Verbose: verbose}, nil case EngineOutputSingeLine: return engineOutputSingleLine{Operation: operation, Files: files, Quiet: quiet}, nil + case EngineOutputSlim: + return engineOutputSlim{Operation: operation, Files: files, Quiet: quiet, Verbose: verbose}, nil case EngineOutputGitlab: return engineOutputGitlab{Operation: operation, Files: files, Compact: quiet}, nil @@ -104,6 +110,70 @@ func (eosl engineOutputSingleLine) String() string { return msg } +type engineOutputSlim struct { + Operation yamlfmt.Operation + Files yamlfmt.FileDiffs + Quiet bool + Verbose bool +} + +func (eos engineOutputSlim) String() string { + var msg string + switch eos.Operation { + case yamlfmt.OperationFormat: + // Formatting only produces output in verbose mode. + if !eos.Verbose { + return "" + } + msg = "The following files were modified:" + case yamlfmt.OperationLint: + msg = "The following formatting differences were found:" + if eos.Quiet { + msg = "The following files had formatting differences:" + } + case yamlfmt.OperationDry: + if eos.Files.ChangedCount() == 0 { + return "No files will be formatted." + } + msg = "The following files would be formatted:" + } + + var b strings.Builder + if msg != "" { + b.WriteString(msg + "\n\n") + } + + if eos.Quiet { + b.WriteString(eos.Files.StrOutputQuiet()) + return b.String() + } + + for _, fileDiff := range eos.changedFileDiffs() { + output := slimFileDiff(fileDiff) + b.WriteString(output) + } + return b.String() +} + +func (eos engineOutputSlim) changedFileDiffs() []*yamlfmt.FileDiff { + changed := make([]*yamlfmt.FileDiff, 0, eos.Files.ChangedCount()) + for _, path := range eos.Files.SortedPaths() { + fileDiff := eos.Files[path] + if !fileDiff.Diff.Changed() { + continue + } + changed = append(changed, fileDiff) + } + return changed +} + +func slimFileDiff(fileDiff *yamlfmt.FileDiff) string { + original := fileDiff.Diff.GetOriginal() + formatted := fileDiff.Diff.GetFormatted() + edits := myers.ComputeEdits(span.URIFromPath(fileDiff.Path), original, formatted) + return fmt.Sprint(gotextdiff.ToUnified(fileDiff.Path, fileDiff.Path, original, edits)) +} + type engineOutputGitlab struct { Operation yamlfmt.Operation Files yamlfmt.FileDiffs diff --git a/go.mod b/go.mod index f0f079d..87108f7 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ toolchain go1.24.8 require ( github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/google/go-cmp v0.6.0 + github.com/hexops/gotextdiff v1.0.3 github.com/mitchellh/mapstructure v1.5.0 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 diff --git a/go.sum b/go.sum index 8abf35d..0154c6e 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/integrationtest/command/command_test.go b/integrationtest/command/command_test.go index 562e0fe..1500939 100644 --- a/integrationtest/command/command_test.go +++ b/integrationtest/command/command_test.go @@ -93,6 +93,15 @@ func TestDry(t *testing.T) { }.Run(t) } +func TestSlimOutput(t *testing.T) { + TestCase{ + Dir: "slim_output", + Command: yamlfmtWithArgs("-lint -output_format slim ."), + Update: *updateFlag, + IsError: true, + }.Run(t) +} + func TestDryQuiet(t *testing.T) { TestCase{ Dir: "dry_quiet", diff --git a/integrationtest/command/testdata/slim_output/after/x.yaml b/integrationtest/command/testdata/slim_output/after/x.yaml new file mode 100644 index 0000000..d07b3ae --- /dev/null +++ b/integrationtest/command/testdata/slim_output/after/x.yaml @@ -0,0 +1,2 @@ +a: + b : 1 diff --git a/integrationtest/command/testdata/slim_output/before/x.yaml b/integrationtest/command/testdata/slim_output/before/x.yaml new file mode 100644 index 0000000..d07b3ae --- /dev/null +++ b/integrationtest/command/testdata/slim_output/before/x.yaml @@ -0,0 +1,2 @@ +a: + b : 1 diff --git a/integrationtest/command/testdata/slim_output/stdout/stderr.txt b/integrationtest/command/testdata/slim_output/stdout/stderr.txt new file mode 100644 index 0000000..eef9d36 --- /dev/null +++ b/integrationtest/command/testdata/slim_output/stdout/stderr.txt @@ -0,0 +1,8 @@ +The following formatting differences were found: + +--- x.yaml ++++ x.yaml +@@ -1,2 +1,2 @@ + a: +- b : 1 ++ b: 1 diff --git a/integrationtest/command/testdata/slim_output/stdout/stdout.txt b/integrationtest/command/testdata/slim_output/stdout/stdout.txt new file mode 100644 index 0000000..e69de29 diff --git a/schema.json b/schema.json index 65f5038..6d73066 100644 --- a/schema.json +++ b/schema.json @@ -83,7 +83,9 @@ "type": "string", "enum": [ "default", - "line" + "line", + "slim", + "gitlab" ], "default": "default", "description": "The output format to use. See Output docs for more details."