Skip to content

Print changed file names to stdout; add --null and --name-delimiter - #724

Closed
mathiasrw with Copilot wants to merge 5 commits into
mainfrom
copilot/print-changed-files-to-stdout
Closed

mathiasrw with Copilot wants to merge 5 commits into
mainfrom
copilot/print-changed-files-to-stdout

Conversation

Copilot AI commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Breaking change: in-place edits now print changed paths on stdout (one per line), like grep -l / gofmt -l / prettier --write. stderr is only for errors, --verbose, and --debug.

Behavior

  • Names still suppressed by --quiet / --quiet-total
  • No names when using --output, --output-match, or piped data (nothing written in place)
  • Async finish order is unchanged; --void-async keeps list order

New flags

  • --null / -0 — NUL after each name (safe for spaces/newlines in paths)
  • --name-delimiter <string> — separator between names, no trailing delimiter; escapes and €/§ match the replacement
  • The two flags conflict

Implementation

  • printChangedName() in src/output.ts writes names to stdout; info/chat stay on stderr
  • doReplacement() in src/engine.ts calls it after successful writes
  • CLI wiring + delimiter unescaping in src/cli.ts
  • JS, fuzzy, and CLI tests updated for stdout names and the new flags
rexreplace foo bar '**/*.md' | xargs git add
rexreplace foo bar '**/*.md' --null | xargs -0 git add
rexreplace foo bar '*.md' --name-delimiter ', '
# a.md, b.md

Copilot AI linked an issue Oct 12, 2026 that may be closed by this pull request
Copilot AI and others added 2 commits October 12, 2026 16:40
Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com>
Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com>
Copilot AI changed the title [WIP] Print names of changed files to stdout Print changed file names to stdout; add --null and --name-delimiter Oct 12, 2026
Copilot AI requested a review from mathiasrw October 12, 2026 16:47
@mathiasrw

Copy link
Copy Markdown
Owner

@copilot please see updated descirtion of original issue about the absolutelpath printing

Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com>

Copilot AI commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please see updated descirtion of original issue about the absolutelpath printing

Implemented --absolute-path / -a in e0f5b8c (or the latest push commit). Default still prints the path as given; with the flag each changed name is the full path from / (same form as file in --replacement-js).

Comment thread src/cli.ts Outdated
}
}

if (undefined !== config.nameDelimiter && null !== config.nameDelimiter) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

There seems to be a misunderstanding. If --null then use \n. If config.nameDelimiter treat § €, else use \n

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8150175. nameDelimiter is now set once up front: \0 with --null, the treated custom value with --name-delimiter, otherwise \n.

Comment thread src/cli.ts Outdated
Comment on lines +451 to +460
if (!config.voidSection) {
delimiter = delimiter.replace(/§/g, '\\');
}
delimiter = unescapeString(delimiter);
if (!config.voidEuro) {
delimiter = delimiter.replace(/€/g, '$');
}
if (!config.voidSection) {
delimiter = delimiter.replace(/§/g, '\\');
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why are we doing voidSecion twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8150175. § is applied once before unescape (same as the replacement); €/§ run again only in the engine via replacePlaceholders.

Comment thread src/cli.ts
delimiter = delimiter.replace(/§/g, '\\');
}
config.nameDelimiter = delimiter;
} catch (e) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Why are we putting this in a try catch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Kept only around unescapeString, same as the replacement — invalid sequences like \x4 need a clean error instead of a stack trace. Fixed in 8150175.

Comment thread src/engine.ts Outdated
const now = new Date();

import {outputConfig, step, debug, chat, info, error, die} from './output.ts';
import {outputConfig, step, debug, chat, printChangedName, error, die} from './output.ts';

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Please dont rename. Its info keep that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8150175. Restored info; removed printChangedName.

Comment thread src/engine.ts Outdated
return fs.promises
.writeFile(_file_rr, result, _config_rr.encoding)
.then(() => info(_file_rr), error);
.then(() => printChangedName(nameToPrint), error);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Its an info.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8150175. Engine calls info(name) again after a successful write.

Comment thread src/output.ts
// Tracks whether a changed-file name has already been written, so --name-delimiter
// can separate names without a trailing delimiter after the last one.
let namePrinted = false;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

No no no. We make sure we dont process the same file multiple times.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8150175. Dropped namePrinted. Overlapping globs are deduped in getFilePaths, and info skips a path already reported.

Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com>
@mathiasrw

Copy link
Copy Markdown
Owner

Closing. The quality of this is not where it has to be.

  • It is far too bulky for what it does. The delimiter is worked out in four places and drags five extra bits of state around, for something that is a few lines.
  • --name-delimiter with --halt loses the names of the files that were already changed, because all names are held back to the very end.
  • --absolute-path puts the working directory in front of a path that is already absolute. That is the bug from A file given with an absolute path is not changed #726 made once more.
  • The review comment about not handling the same file twice was answered with two new Sets instead of none. fast-glob already gives each file once.
  • The readme is not updated with the new flags.

#703 will be done again from scratch on current main, as a small change.

@mathiasrw mathiasrw closed this Oct 14, 2026
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.

Print the names of affected files

2 participants