Ruplacer - Find and replace text in source files

Overview

crates.io image

Ruplacer

Find and replace text in source files:

$ ruplacer old new src/
Patching src/a_dir/sub/foo.txt
-- old is everywhere, old is old
++ new is everywhere, new is new

Patching src/top.txt
-- old is nice
++ new is nice

Would perform 2 replacements on 2 matching files.
Re-run ruplacer with --go to write these changes to disk

Note

This project was originally hosted on the TankerHQ GitHub organization, which was my employer from 2016 to 2021. They kindly agreed to give me back ownership of this project. Thanks!

Installing with cargo

Install rust and cargo, for example with rustup.

Then run:

cargo install ruplacer

Alternative installation methods

  • Pre-compiled binaries for Linux, macOS, and Windows are available as assets of the latest release.

  • ruplacer can also be installed from homebrew:

brew install TankerHQ/homebrew-repo/ruplacer

Basic usage

ruplacer pattern replacement [path]

If the path is not given, it defaults to the current working directory.

Ruplacer will then walk through every file in while honoring .gitignore files found on the way.

Binary files and text files containing non-UTF8 characters will be skipped. Then for every remaining file, it will read the contents, replace all lines matching the pattern by the replacement, and print the difference.

If you are OK with the replacements, re-run ruplacer with the --go option to actually write the changes to disk.

Regex

By default, pattern will be compiled into a Rust regex.

Note that it's slightly different from Perl-style regular expressions. Also, you must use $1, $2 to reference groups captured from pattern inside replacement.

For instance, this replaces 'last, first' by 'first last':

ruplacer '(\w+), (\w+)' '$2 $1'

(note the use of single quotes to avoid any processing by the shell)

If you don't want the pattern to be used as a regex, use the --no-regex command line flag.

This makes it possible to look for special characters without escaping them:

# This is a regex that matches the letter a
# or the letter o
$ ruplacer '(a|o)' u
- tata toto
+ tutu tutu
- (a|o)
+ (u|u)

# This is the literal string: '(a|o)'
$ ruplacer --no-regex '(a|o)' u
# or
$ ruplacer '\(a\|o|)' u
- (a|o)
+ u

Subvert mode

Ruplacer has a --subvert option which works across a variety of case styles (lower case, snake case, and so on):

$ ruplacer --subvert foo_bar spam_eggs
Patching src/foo.txt
-- foo_bar, FooBar, and FOO_BAR!
++ spam_eggs, SpamEggs, and SPAM_EGGS!

Filter files by type or glob patterns

Inspired by ripgrep, you can also select or ignore certain "file types" or glob patterns:

# Select only C++ files
$ ruplacer old new --type cpp
# Select only *.foo files
$ ruplacer old new --type *.foo
# Select only files that match foo*bar.c
$ ruplacer old new --type foo*bar.c

# Ignore all js files
$ ruplacer old new --type-not js
# Ignore all *.bar files
$ ruplacer old new --type-not *.bar
# Ignore all files that match foo*bar.c
$ ruplacer old new --type-not foo*bar.c

Each "file type" is just a list of glob pattern. For instance: the cpp file type matches *.C, *.H, *.cc, *.cpp and so on ...

You can see the whole list by using ruplacer --file-types.

Comments
  • Better diff algorithm

    Better diff algorithm

    Right now if you replace foo_bar by spam_eggs, the underscore won't get colored when printing the diff.

    I've tried and fail to fix the algorithm, so maybe we should use an other diff library instead.

    Maybe this one: https://docs.rs/diff/0.1.11/diff/index.html ?

    Or we port diff-so-fancy from Perl to Rust :P

    opened by dmerejkowsky 9
  • Rewrite core functionality without using the difference crate

    Rewrite core functionality without using the difference crate

    Instead of just using string.replace or re.replace_all, actually do the work ourselves and compute a list of input and output fragments

    Each fragment is a struct containing some text (either added or removed) and some position

    This allows use to display the exact diff.

    Implementation notes:

    • Replace line_patcher module by just replacer

    • Compute the list of patters and replacements much earlier (and just one) when using --subvert

    • Also, implement Train-Case (also known as Http-Case) handling

    • Display the patch while the source files are read. This means less shuffling of data around

    • Tweak output

    Old:

    Patching foo/bar.js --- old is old +++ new is new

    New:

    foo/bar.js 3: old is old foo/bar.js 3: new is new

    • We no longer allocate n*n bytes when patching a line of size 'n', so the fix for issue #52 is no longer needed

    Fix #15

    opened by dmerejkowsky 7
  • Outdated version in brew

    Outdated version in brew

    While trying to apply "--ignored" option I discovered that my version is 0.4.1 while newest is 0.6.4. I can't install if from assets (macOS warns me about it), but will try to install from source. It would be much easier to me if you will update your brew package version. Thanks.

    opened by Masynchin 5
  • subvert does not replace all lower/upper case matches

    subvert does not replace all lower/upper case matches

    Steps to reproduce:

    echo 'stuffStuff stuffstuff STUFFSTUFF' > f
    ruplacer stuffStuff fooFoo --subvert
    

    Obtained result: only the first "stuffStuff" is replaced Expected result: all three should be replaced

    opened by blastrock 5
  • pattern/replacement starting with dashes

    pattern/replacement starting with dashes

    Hi,

    Is there a way to have a pattern or replacement starting with dashes? Right now ruplacer think of them as invalid options. Would it be possible to add "explicit" options? Something like ruplacer <-p|--pattern> --my-pattern-starting-with-double-dashes <-r|--replacement> -my-replacement-with-only-one-dash-at-the-beginning ?

    opened by s-d-m 4
  • select 'ruplacement`by piping them  though fzf or other selector

    select 'ruplacement`by piping them though fzf or other selector

    Hi ! First, thank for this amazing tool ! I use it almost on a daily basis :) Sometime ruplacer over-deliver replacement candidate, and I wold like to select only a subset of them.
    Would it be possible to pipe the candidate into a selector like fzf to achieve this purpose ?

    If not, could you give me some pointer on how to achieve it ?

    opened by sucrecacao 4
  • Add `-V`, `--version` option

    Add `-V`, `--version` option

    I find it very helpful to have a tool tell me it's version so I can consider upgrading to a newer version or track down bugs.

    This change adds the common --version option/switch (along with -V). Fortunately, Clap makes this very easy by using a magic command attribute: https://github.com/clap-rs/clap/blob/v3.1.15/examples/derive_ref/README.md#command-attributes

    Output of ruplacer --version:

    ruplacer 0.6.4
    

    Additional output of ruplacer --help:

    ruplacer 0.6.4
    
    […]
    
        -V, --version
                Print version information
    
    […]
    
    opened by homeworkprod 3
  • Handle trailing newlines consistently in FilePatcher

    Handle trailing newlines consistently in FilePatcher

    Previously, FilePatcher could not differentiate between files with and without trailing newlines, and always wrote files with trailing newlines. As a result, files without trailing newlines that were otherwise unchanged would be modified.

    This commit introduces a new LineIterator that preserves the trailing delimiter, if present, and ensures that files are written with the same newline structure they had when read.

    opened by LawnGnome 3
  • Add support for glob pattern

    Add support for glob pattern

    Workaround for https://github.com/dmerejkowsky/ruplacer/issues/56. Instead of regex, I think glob pattern would be more suited because the ignore crate already supported it.

    opened by ndtoan96 3
  • Fix typo in main.rs

    Fix typo in main.rs

    Hello, I'm Hayashi-Yudai.

    I use your repository and learn from the codes. This time, I found very tiny typo in help message. I'm grad if you merge my pull request.

    Sincerely.

    opened by Hayashi-Yudai 3
  • Replacement with empty string

    Replacement with empty string

    Thanks for creating ruplacer! It's been great for renaming variables across source files.

    Is there a way to replace matches with the empty string? For example,

    ruplacer "string" ""
    

    fails with

    error: The following required arguments were not provided:
        <replacement>
    

    I've been working around this by just adding an empty capture group () and then replacing with '$1'.

    opened by gpoore 2
  • Tool seems to try to (re)write files that don't contain matches

    Tool seems to try to (re)write files that don't contain matches

    $ echo "hello world" > a.txt
    $ echo "foo bar baz" > b.txt
    $ chmod 400 b.txt
    $ ruplacer 'hello' 'goodbye'
    ./a.txt:1 - hello world
    ./a.txt:1 + goodbye world
    
    Would perform 1 replacement on 1 matching file
    Re-run ruplacer with --go to write these changes to the filesystem
    $ ruplacer 'hello' 'goodbye' --go
    Error: Could not write ./b.txt
    
    Caused by:
        Permission denied (os error 13)
    
    opened by 08d2 0
  • Use capturing group next to literal

    Use capturing group next to literal

    Proposed tag: question.

    I want to make all class {Name}Test to be internal class {Name}Test, so I try to run:

    ruplacer 'class (\w+)Test' 'internal class $1Test'
    

    But it doesn't work as expected. I avoided it with

    ruplacer 'class (\w+)(Test)' 'internal class $1$2'
    

    But how can solve my issue without adding second capturing group?

    opened by Masynchin 2
  • Add multi-threading support

    Add multi-threading support

    This (currently WIP) PR proposes switching to ignore's parallel walker API. It's not complete yet (threads are still only set to 1), but I figured that I should start discussion with y'all here upstream before I got too carried away, since there's actually quite a few design considerations here, and I haven't even validated that this is something y'all want in the first place! :)

    See individual commits for more details on changes. Note that this is based on #93, and commits specifically for this PR start with the commit titled refactor: make `Stats` multithread-friendly.

    opened by ErichDonGubler 0
  • Allow multiple search-and-replace paths

    Allow multiple search-and-replace paths

    Intended to resolve #51, pending discussion (there's some trade-offs!). See individual commits for more details on changes.

    Open questions:

    • [ ] What new tests should be added? Cases that I see:
      • [ ] The case mentioned in the first commit (refactor: skip duplicate canonicalized paths) is a good candidate.
    opened by ErichDonGubler 0
  • `--ignored` seems not to work

    `--ignored` seems not to work

    Problem

    I want to patch code of ignored files (dependencies of my python project). But there is no difference with or without --ignored - Error: nothing found to replace. At the same time ripgrep correctly finds provided pattern.

    To reproduce

    Libs used:

    • python==3.10.2
    • ripgrep==13.0.0 (or rg)
    • ruplacer==0.6.4

    It might be overcomplicated, but it is my real situation. Steps to reproduce:

    mkdir reproduce
    cd reproduce
    
    git init
    echo venv/ > .gitignore
    
    python3 -m venv venv
    source venv/bin/activate
    pip install "Django==1.11.29"
    
    # no any output since all files ignored by gitignore:
    rg 'from collections import Iterable'
    ruplacer 'from collections import Iterable' 'from collections.abc import Iterable'
    
    # shows expected
    rg 'from collections import Iterable' --no-ignore
    
    # doesn't shows expected
    ruplacer 'from collections import Iterable' 'from collections.abc import Iterable' --ignored
    
    opened by Masynchin 3
Releases(v0.8.1)
Owner
Dimitri Merejkowsky
Software Writer - Blogger - Teacher
Dimitri Merejkowsky
Rust library to scan files and expand multi-file crates source code as a single tree

syn-file-expand This library allows you to load full source code of multi-file crates into a single syn::File. Features: Based on syn crate. Handling

Vitaly Shukela 11 Jul 27, 2022
A program written in Rust, that allows the user to find the current location of the International Space Station and see it on a map.

ISS Location ViewFinder A program written in Rust, that allows the user to find the current location of the International Space Station and see it on

Suvaditya Mukherjee 2 Nov 8, 2021
A service for helping your cat find other cats

Check back later! Discord Self-hosting This is an open-source service! Feel free to host you own private instances. All we ask is you credit us and li

ibx34 4 Oct 31, 2021
Rust library to generate word cloud images from text and images !

wordcloud-rs A Rust library to generate word-clouds from text and images! Example Code use std::collections::HashMap; use std::fs; use lazy_static::la

Teo Orthlieb 2 Dec 8, 2022
A tool that generates a Sublime Text project file that helps you get started using Scoggle.

README A tool that generates a Sublime Text project file that helps you get started using Scoggle. While Scoggle-Gen may not find every single source

Sanjiv Sahayam 0 Jan 10, 2022
Small library for text to image steganography.

hips-lib Performs text to image steganography by hinding and retrieving secret text within images or pixel arrays. This is achieved by encoding the se

hewhocopypastes 4 Feb 21, 2023
Free and open-source reimplementation of Native Mouse Lock (display_mouse_lock) in rust.

dml-rs display_mouse_lock in rust. Free, open-source reimplementation of display_mouse_lock (Native Mouse Lock) in Rust. Written because I felt like i

Tomat 4 Feb 12, 2023
Open-source Rewind.ai clone written in Rust and Vue running 100% locally with whisper.cpp

mind-overflow Open-source Rewind.AI clone built with Tauri and Vue. Leverages whisper.cpp for Speech-to-Text and (wip: llama.cpp for Text generation a

Maxime Dolores 4 Aug 9, 2023
UnTeX is both a library and an executable that allows you to manipulate and understand TeX files.

UnTeX UnTeX is both a library and an executable that allows you to manipulate and understand TeX files. Usage Executable If you wish to use the execut

Jérome Eertmans 1 Apr 5, 2022
Open Source terraform provider registry

Terustry Simple configurable proxy that implement terraform provider registry protocol, to build your own terraform provider private registry. How it

Open-Source by Veepee 53 Nov 24, 2022
Open-source NI maschine device handling

Open-source NI maschine device handling

william light 69 Dec 1, 2022
Extracting react native app source code from apk file.

extract-myreact Extracting React Native app source code from apk file.

Aan 3 Oct 5, 2022
A cargo subcommand that displays the assembly generated for Rust source code

cargo-show-asm A cargo subcommand that displays the assembly generated for Rust source code.

null 193 Dec 29, 2022
Ector is an open source async, no-alloc actor framework for embedded devices

Ector is an open source async, no-alloc actor framework for embedded devices. Ector is an open source async, no-alloc actor framework for embedded dev

Drogue IoT 11 Dec 15, 2022
Source to relay.fedi.buzz: the customizable ActivityPub relay

buzzrelay A follow-only ActivityPub relay that connects to Mastodon's Streaming API. You don't need to run this yourself, just use the instance at rel

Astro 7 Jan 23, 2023
Rust stdlib-less internal base for source engine games

source-base Rust stdlib-less and msvcrt-less internal base for source engine games Info Build with cargo build -Zbuild-std If cargo can't find link.ex

null 12 Jan 23, 2023
Demo provider, source code for the Provider tutorial.

Fiberplane "Catnip" (tutorial) provider This repository contains the final code of the provider built within the "Create a Provider" tutorial. It reli

Fiberplane 4 Feb 15, 2023
An open-source Windows client for Twitch.tv

TwitchBox is a lightweight Windows client created to enhance the Twitch.tv experience. The app uses the Tauri framework, which includes a combination

Sandun Wiratunga 3 Apr 28, 2023
Open-source Rust Runtime for the VEX V5 Platform

vexide Work in progress high level bindings for the V5 Brain VEX SDK. Unlike other libraries for the V5 Brain, vexide doesn't use an RTOS. Instead, ve

vexide 10 May 6, 2024