Skip to content

feat: add -l and -d flags for check-only mode - #103

Open
hefa wants to merge 3 commits into
abemedia:masterfrom
hefa:add-check-flags
Open

hefa wants to merge 3 commits into
abemedia:masterfrom
hefa:add-check-flags

Conversation

@hefa

@hefa hefa commented Apr 9, 2026 •

Copy link
Copy Markdown

Add -l and -d flags for check-only mode

Summary

  • Add -l flag that lists files whose formatting would change (without modifying them)
  • Add -d flag that prints a unified diff of what would change (without modifying them)
  • Exit code 1 when either flag detects differences, matching the gofmt convention (0 = clean, 1 = diffs found, 2 = errors)

Motivation

There's currently no way to use gocondense in CI or pre-commit hooks to check formatting without rewriting files. Tools like gofmt -l and goimports -l support this and it's become the standard pattern for Go formatting checks:

# CI check: fail if any files need condensing
gocondense -l ./... && echo "ok" || echo "needs formatting"

Without this, the workaround is piping each file through stdin and diffing manually or copying the files to a tmp dir, running gocondense, and checking for changes.

What changed

All changes are in cmd/gocondense/ (library is untouched):

  • main.go: two new flags (-l and -d); processFile returns a 3-way result (resultOK / resultChanged / resultError) instead of bool, so processArgs can distinguish "file differs" from "error" for the exit code.
  • processArgs has a //nolint:funlen because it was already near the 60-line limit and the new result handling pushes it slightly over; extracting a helper would mean refactoring the existing function (decision for the original author).
  • diff.go (new): unified diff output using Myers algorithm, pure Go with no new dependencies

Tests

  • -l lists only files that differ, leaves all files unmodified
  • -l returns exit 0 when nothing differs
  • -l works with recursive directory walking (./...)
  • -l respects generated-file skipping
  • -l returns exit 2 on parse errors
  • -d prints correct unified diff, leaves files unmodified
  • -d returns exit 0 when nothing differs
  • -d returns exit 2 on parse errors
  • All existing tests still pass

Summary by CodeRabbit

  • New Features

    • Generates unified diffs for changed files and supports a diff view output.
    • Added -l (list changed files) and -d (show diffs) CLI flags.
  • Behavior Changes

    • When -l or -d are used, diffs or filenames are written to stdout; otherwise files are updated.
    • Exit code 1 signals detected changes with those flags; exit code 2 signals errors.
  • Tests

    • Added tests for -l and -d covering multiple files, no-change cases, recursive patterns, and error handling.
  • Documentation

    • README updated to document the new -l and -d flags.

@coderabbitai

coderabbitai Bot commented Apr 9, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a40c0b58-6d88-4945-95f5-77815baff476

📥 Commits

Reviewing files that changed from the base of the PR and between 9287f64 and a066ef2.

📒 Files selected for processing (1)
  • README.md
✅ Files skipped from review due to trivial changes (1)
  • README.md

📝 Walkthrough

Walkthrough

Adds -l and -d CLI flags to gocondense, implements a new unified-diff generator (Myers line diff → edits → hunks → unified format) in cmd/gocondense/diff.go, and updates processing logic and tests to support listing changed files and printing diffs instead of overwriting files.

Changes

Cohort / File(s) Summary
Diff Generation
cmd/gocondense/diff.go
New file: implements line-splitting, a Myers diff (forward/backtrack), edit script model (=, -, +), 3-line context hunks, and unifiedDiff(filename string, old, cur []byte) string producing unified diff text or "" when no changes.
CLI Flag Handling & Flow
cmd/gocondense/main.go
Adds -l and -d flags; threads listOnly/showDiff through processArgs→processFile; replaces boolean result with tri-state (resultOK, resultChanged, resultError); adjusts output paths (print filename, print diff, or overwrite) and exit-code logic (exit 1 when diffs found with -l/-d, 2 on errors).
Tests
cmd/gocondense/main_test.go
Adds diffOutput expected unified diff constant and test cases covering -l (listing) and -d (diff printing): multiple args, recursive patterns, generated-file skipping, no-change, and parse-error scenarios.
Docs
README.md
Documents new short flags -l and -d in the CLI options table and their exit behavior.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant ProcessArgs
    participant ProcessFile
    participant UnifiedDiff

    User->>CLI: run gocondense with flags & paths
    CLI->>ProcessArgs: parse flags, expand paths
    ProcessArgs->>ProcessFile: dispatch each file (listOnly, showDiff)
    ProcessFile->>ProcessFile: read original, compute condensed output
    alt output differs
        ProcessFile->>UnifiedDiff: unifiedDiff(filename, old, cur)
        UnifiedDiff->>UnifiedDiff: split lines -> myersDiff -> backtrack -> write hunks
        UnifiedDiff-->>ProcessFile: unified diff text
        ProcessFile-->>User: print filename (if -l) or print diff (if -d)
        ProcessFile-->>ProcessArgs: return resultChanged
    else output same
        ProcessFile-->>ProcessArgs: return resultOK
    end
    ProcessArgs-->>CLI: aggregate results, determine exit code
    CLI-->>User: exit with status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through lines both old and new,
Sniffed edits, hunks, and context too,
With -l I point the changed file's name,
With -d I show each diffing frame,
A tiny rabbit cheers the condensed view! 🎀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding -l and -d flags for check-only mode. It directly matches the primary functionality introduced across the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/gocondense/main.go (1)

155-175: ⚠️ Potential issue | 🟠 Major

Serialize -l / -d output instead of writing from worker goroutines.

These workers call processFile, and processFile writes directly to the shared stdout in check-only modes. That makes filename/diff order nondeterministic, and multi-file diffs can interleave. Collect the per-file payload from each worker and emit it from one place in walk order.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/gocondense/main.go` around lines 155 - 175, Workers spawned in
filepath.WalkDir currently call processFile which writes directly to shared
stdout/stderr (for listOnly / showDiff), causing nondeterministic interleaving;
change the workers (the goroutine created where sem.Acquire/wg.Add are used) to
capture processFile's return/result and any output into a small result struct
(e.g., {path string, result int/enum, output bytes}) and send that struct on a
channel instead of writing to stdout/stderr, keep using sem and wg for
concurrency control, and in the main goroutine (outside the WalkDir worker body)
start a collector that reads from that channel and serially writes the payloads
to stdout/stderr in the same order WalkDir yielded files (use a sequence counter
or append results to a slice keyed by traversal order and flush in order after
wg.Wait, or close the channel after wg.Wait and drain it serially) while still
setting hasErrors/hasChanged based on the reported result values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/gocondense/diff.go`:
- Around line 193-200: splitLines currently strips a final empty element so
"foo" and "foo\n" become identical; update splitLines to preserve a
trailing-newline-only change by not collapsing the final empty string when the
input ends with '\n' (e.g. detect bytes.HasSuffix(data, []byte("\n")) or check
s[len(s)-1] == '\n' and keep the last empty element), so myersDiff/unifiedDiff
will see the added empty line as a real change; ensure the function splitLines
still returns nil for empty input and that callers (myersDiff/unifiedDiff) can
handle the preserved trailing empty line.
- Line 25: The diff header uses filepath.Join which emits OS-specific
separators; replace the two filepath.Join usages used in the fmt.Fprintf call
that writes the header (the call that currently formats "--- %s\n+++ %s\n" using
filepath.Join("a", filename) and filepath.Join("b", filename")) with
path.Join("a", filepath.ToSlash(filename)) and path.Join("b",
filepath.ToSlash(filename")) so the headers always use forward slashes across
platforms; update imports to include "path" if missing and keep "filepath" for
ToSlash.

In `@cmd/gocondense/main.go`:
- Around line 240-246: The listOnly and showDiff branches call
fmt.Fprintln/fmt.Fprint but ignore their returned errors, so write failures
never change the exit code; update those branches in main (around listOnly,
showDiff and unifiedDiff usage) to capture the error from
fmt.Fprintln/Fmt.Fprint, and if non-nil return the error exit code (2) instead
of returning resultChanged; otherwise continue returning resultChanged. Ensure
the same error-propagation pattern is applied for both the listOnly branch and
the showDiff branch where unifiedDiff is written to stdout.

---

Outside diff comments:
In `@cmd/gocondense/main.go`:
- Around line 155-175: Workers spawned in filepath.WalkDir currently call
processFile which writes directly to shared stdout/stderr (for listOnly /
showDiff), causing nondeterministic interleaving; change the workers (the
goroutine created where sem.Acquire/wg.Add are used) to capture processFile's
return/result and any output into a small result struct (e.g., {path string,
result int/enum, output bytes}) and send that struct on a channel instead of
writing to stdout/stderr, keep using sem and wg for concurrency control, and in
the main goroutine (outside the WalkDir worker body) start a collector that
reads from that channel and serially writes the payloads to stdout/stderr in the
same order WalkDir yielded files (use a sequence counter or append results to a
slice keyed by traversal order and flush in order after wg.Wait, or close the
channel after wg.Wait and drain it serially) while still setting
hasErrors/hasChanged based on the reported result values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 793c70c6-5802-438c-ae74-8c0edec1ac95

📥 Commits

Reviewing files that changed from the base of the PR and between 2c810b3 and b318329.

📒 Files selected for processing (3)
  • cmd/gocondense/diff.go
  • cmd/gocondense/main.go
  • cmd/gocondense/main_test.go

Comment thread cmd/gocondense/diff.go Outdated
Comment thread cmd/gocondense/diff.go
Comment thread cmd/gocondense/main.go
@abemedia

abemedia commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Thanks a lot for the PR @hefa! I'll review properly over the weekend.

@hefa

hefa commented Apr 9, 2026

Copy link
Copy Markdown
Author

@abemedia Thank you for the helpful tool!

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants