Regex Replace

by bongho
5
4
3
2
1
Score: 53/100

Description

Find and replace text using regular expressions with real-time preview and match highlighting for Obsidian

Reviews

No reviews yet.

Stats

11
stars
7,221
downloads
0
forks
104
days
4
days
4
days
15
total PRs
0
open PRs
0
closed PRs
15
merged PRs
5
total issues
0
open issues
5
closed issues
47
commits

Latest Version

5 days ago

Changelog

Vault-wide replace now runs on mobile.

1.2.2 registered the two vault commands behind !Platform.isMobile. The reason was real: the only protection against a runaway pattern is a worker the main thread can terminate, that path had never run anywhere but the desktop, and if a mobile WebView scheduled things differently the failure mode was a hung app.

Three lines of evidence now say it does not.

  • Freezing the page mid-scan takes the renderer from 93% of a core to 0% — the worker stops with the timers, not despite them. On thaw the watchdog fires with exactly the remainder of its 2000ms.
  • Blink says why. DedicatedWorker::ContextLifecycleStateChanged forwards kFrozen to the worker and deliberately skips kPaused. Backgrounding an app freezes; it does not pause.
  • The one API shaped like the dangerous case — Android's WebView.pauseTimers(), which stops only the main thread's scheduler — is unreachable in Obsidian's own APK, by direct call or by reflection.

No run on real hardware. CONTRIBUTING.md carries the full trace and names the result that should put the guard back: a phone that gets warm while backgrounded mid-scan.

Also in this release

The test suite now tests the code that ships. test.ts copies the functions it exercises out of src/, and two copies had fallen behind: preview and execute predated replacement unescaping and the fix for lookaround in the match list, so neither behaviour had ever been covered. Both are now, and npm test fails when a copy drifts (#19).

README file from

Github

Obsidian Regex Replace

Safely clean up Markdown in Obsidian with live previews, match highlighting, and reusable multi-step pipelines.

Obsidian plugin License

Preview every change before it touches your note. Run a one-off replacement or save a complete cleanup workflow for PDFs, AI-generated text, and Markdown.

Why Regex Replace?

Capability Obsidian's built-in replace Regex Replace
Regular expressions and capture groups — ✓
Before/after preview — ✓
Match highlighting — ✓
Replace within a selection — ✓
Replace across the whole vault — ✓
Reusable multi-step cleanup pipelines — ✓
Import regex-pipeline rulesets — ✓

Everything runs locally in your vault. The plugin does not send note content to external services.

Features

  • Regular Expression Support: Full JavaScript regex syntax including capture groups ($1, $2, etc.)
  • Real-time Preview: See matches highlighted before replacing
  • Match Highlighting: Visual diff showing before/after changes
  • Regex Flags: Toggle global (g), case-insensitive (i), and multiline (m) flags
  • Selection Mode: Replace only within selected text
  • Pattern History: Save and reuse recent search patterns
  • Pipeline Rulesets: Save reusable multi-step rulesets and apply them in sequence, with a step-by-step preview — and import existing regex-pipeline rulesets
  • Vault-wide Replace: Replace across every note, or one folder — review the matches, pick the files, and undo the whole run afterwards
  • Selection-only Rulesets: Mark a ruleset as selection-only and it stays that way — hotkeys included — instead of resetting every time the dialog opens
  • Dark/Light Theme: Optimized for both Obsidian themes

Copy a search and replacement into Regex Replace, review the preview, and apply it only when the result is correct for your note.

Cleanup Search Replace Flags
Collapse repeated spaces {2,} g
Remove trailing whitespace [ \t]+$ (empty) gm
Collapse 3+ blank lines \n{3,} \n\n g
Change H2 headings to H3 ^## ### gm
Convert ISO dates to day/month/year (\d{4})-(\d{2})-(\d{2}) $3/$2/$1 g
Remove Markdown bold markers \*\*([^*]+)\*\* $1 g
Convert simple wiki links to Markdown links `[[([^] ]+)]]` [$1]($1.md)

[!CAUTION] A regular expression can match more text than intended. Check the highlighted preview before applying a replacement, especially with broad patterns.

Installation

Requires Obsidian 1.13.0 or later — the settings tab uses the declarative settings API introduced in that version, so its settings appear in Obsidian's settings search. Version 1.1.5 remains available for older releases.

  1. Open Settings → Community plugins in Obsidian.
  2. Select Browse and search for Regex Replace.
  3. Select Install, then Enable.

Manual Installation

  1. Download main.js, manifest.json, and styles.css from the latest release
  2. Create a folder: <YourVault>/.obsidian/plugins/regex-replace/
  3. Copy the downloaded files into this folder
  4. Reload Obsidian and enable the plugin in Settings → Community Plugins

Usage

Open the replace dialog

  • Hotkey: Cmd/Ctrl + Shift + H
  • Command Palette: Cmd/Ctrl + P → "Open Regex Replace"

Preview before replacing

+--------------------------------------+
| Search pattern:  \b(world)\b         |
| Replace with:    WORLD               |
|                                      |
| Flags: [x] g   [ ] i   [ ] m         |
|        [ ] Replace in selection only |
|                                      |
| 2 match(es) found                    |
|                                      |
| Preview                              |
|  Before: Hello world, hello world    |
|  After:  Hello WORLD, hello WORLD    |
|                                      |
|  2 match(es):                        |
|   - "world" -> "WORLD"               |
|                                      |
|           [ Replace all ] [ Cancel ] |
+--------------------------------------+

Before highlights every match in yellow, and After highlights each replacement in green. Previously used patterns reappear in a Recent patterns dropdown below the preview.

Regex examples

Use Case Search Pattern Replace Result
Find numbers \d+ [NUM] abc123 → abc[NUM]
Date format (\d{4})-(\d{2})-(\d{2}) $3/$2/$1 2024-12-08 → 08/12/2024
Remove extra spaces \s+ Multiple spaces → single
Wiki to MD link \[\[(.+?)\]\] [$1]($1.md) [[Note]] → [Note](Note.md)
Header H2 → H3 ^## ### ## Title → ### Title
Remove bold \*\*(.+?)\*\* $1 **bold** → bold
Extract link text \[(.+?)\]\((.+?)\) $1: $2 [text](url) → text: url

Flags

Flag Name Description
g Global Replace all matches (not just the first)
i Ignore Case Case-insensitive matching
m Multiline ^ and $ match line starts/ends

Capture groups

Use parentheses () to capture groups and reference them with $1, $2, etc.:

Search:  (\w+)@(\w+)\.com
Replace: User: $1, Domain: $2

Input:   [email protected]
Output:  User: test, Domain: example

Pipeline Rulesets

A ruleset is a named list of find/replace rules applied in sequence — each rule operates on the previous rule's output. Useful for repeatable, multi-step cleanups (e.g. normalize headers, then collapse whitespace, then fix links).

Defining a ruleset

In Settings → Regex Replace → Pipeline rulesets, click Add ruleset, name it, and write rules using regex-pipeline syntax (one rule per block):

"SEARCH"->"REPLACE"
"\s+"->" "
"##\s"gm->"### "
  • Inline flags follow the search quote (e.g. "foo"gi->"bar"); without them, gm is used.
  • Replacements may span multiple lines.

Applying a ruleset

  • Command Palette: "Apply ruleset (pipeline)" → pick a ruleset → review the step-by-step preview (match count per rule) → Apply pipeline.
  • Hotkey: each ruleset also gets its own Ruleset: <name> command, bindable to a hotkey or a Commander / Editing Toolbar button.
  • A rule with an invalid regex is skipped and reported, never aborting the whole run.

Running a ruleset on the selection only

Turn on Apply to selection only — either on the ruleset itself in settings, or with the checkbox in the pipeline dialog. Both write to the same stored value, so the choice survives the next open and applies to the Ruleset: <name> hotkey too.

When such a ruleset runs with nothing selected, it does nothing and shows a notice. It never falls back to the whole note, so a mis-fired hotkey cannot rewrite the file. Apply to selection only by default seeds the flag on newly added and imported rulesets, and rulesets saved before this option existed follow it until their own toggle is set.

Importing from regex-pipeline

Click Import from regex-pipeline to read every ruleset file in <vault>/.obsidian/regex-rulesets/ and convert it into a native ruleset.

Starter pipeline: clean pasted text

This ruleset removes trailing whitespace, reduces large blank gaps, and normalizes repeated spaces. Add it under Settings → Regex Replace → Pipeline rulesets, then preview it with Apply ruleset (pipeline).

"[ \\t]+$"gm->""
"\\n{3,}"g->"\\n\\n"
" {2,}"g->" "

Replace in Vault

Run Replace in vault from the command palette. Enter a pattern, flags, and a replacement, optionally limit it to a folder, then Scan vault.

The scan reads and matches in batches, so the progress count moves as it goes and you can cancel partway. When it finishes you get one row per file with a hit and its match count, all ticked. Untick the files you don't want; click a row to see that file's matches. Apply asks once more, naming how many files and roughly how long the write will take, and only replaces on the second click.

Undoing

Undo last vault replace puts every file back. It keeps the last run only.

A file you edited after the replace is skipped rather than reverted, so an undo never overwrites work you did in between. The same check runs during apply: a file that changed between the scan and the write is skipped and reported.

Options

  • Skip frontmatter leaves the --- block alone. Read from Obsidian's own metadata, so a horizontal rule in the body is not mistaken for frontmatter.
  • Excluded paths (settings) is a glob list — Archive, Templates/**, **/*.excalidraw.md. A bare folder name covers everything under it. Hidden folders are already skipped. This is separate from Obsidian's own excluded-files setting, which plugins cannot read.
  • Match timeout (settings) is how long a batch may run before the worker is killed. A pattern that backtracks catastrophically — (\w+\s?)+$ and friends — cannot be interrupted on the thread running it, so matching happens in a worker the main thread can terminate. Raise the timeout for a big vault, not for a slow pattern.

On mobile

The vault commands were desktop-only in 1.2.2 while the worker that guards against a runaway pattern went unverified anywhere but the desktop. They run everywhere as of 1.3.0. The short version of why: backgrounding an app freezes the page, and a frozen page freezes its workers too, so the pattern stops when the watchdog does rather than burning on while nothing can kill it.

It is still the less-travelled path. If a scan ever seems to hang on a phone, lower Match timeout and report it.

Settings

Access via Settings → Regex Replace:

Setting Description Default
Default Flags Pre-selected regex flags g
Show Preview Display before/after preview true
History Limit Max saved patterns 10
Pipeline rulesets Add/edit/delete/import reusable rulesets —
Apply to selection only by default Seeds the flag on new and imported rulesets false
Vault scan: excluded paths Glob list of paths to skip when scanning the vault —
Vault scan: match timeout Milliseconds a batch may match before the worker is killed 2000
Vault replace: warn above File count past which the confirm step warns 1000

Development

# Clone the repository
git clone https://github.com/bongho/obsidian-regex-replace.git

# Install dependencies
npm install

# Build for development (watch mode)
npm run dev

# Build for production
npm run build

# Run tests
npx ts-node --transpile-only test.ts

Changelog

1.3.0

  • Vault-wide replace runs on mobile. 1.2.2 held it back because the worker that stops a runaway pattern had never been exercised outside the desktop; it has now been traced far enough to lift the guard
  • The test suite tests the shipped code. test.ts copies the functions it exercises out of src/, and two of the copies had fallen years behind — preview and execute were still the versions from before replacement unescaping and before the fix for lookaround in the match list. npm test now fails when a copy drifts (#19)

1.2.2

  • Register the vault commands on desktop only. The protection against a runaway pattern is a worker the main thread can terminate, and that path has never run on a mobile device — isDesktopOnly stays false, so the rest of the plugin is unaffected (#18)
  • Document vault-wide replace in the README: usage, what undo restores, and the three vault settings
  • Say in the plugin description that vault-wide replace is desktop-only, so mobile users are not promised a command they will not see

1.2.1

  • Describe vault-wide replace in the plugin's own description — the directory and the plugin's detail page were still advertising the 1.1.x feature set (#16)
  • Run the test suite in CI. The workflow had called npm test since it was added with no script behind it, so the 68 cases had only ever run by hand (#17)

1.2.0

  • Replace across the whole vault or a folder with the new "Replace in vault" command (#15)
  • Scan streams as it reads, so progress is real and the run can be cancelled
  • Matching runs in a worker with a timeout, so a pattern that backtracks is stopped instead of freezing Obsidian
  • Selection is per file; skip frontmatter with a toggle; exclude paths with a glob list in settings
  • Undo a whole run with "Undo last vault replace" — files edited since the replace are left alone
  • Apply names the expected cost before you confirm, and warns past a configurable file count
  • Show the "select some text first" notice in the find/replace dialog even with the preview turned off (#14)

1.1.9

  • Stop "Replace in selection only" in the find/replace dialog from rewriting the whole note when nothing is selected (#11)

1.1.8

  • Store "apply to selection only" on the ruleset, so it survives reopening the dialog (#9)
  • Honour that flag in the Ruleset: <name> commands, which previously always rewrote the whole note
  • Refuse to run a selection-only ruleset with an empty selection instead of falling back to the whole note
  • Add a global "apply to selection only by default" setting that seeds new and imported rulesets
  • Run the plugin review's lint ruleset locally (eslint 10 + eslint-plugin-obsidianmd)

1.1.7

  • Drop the imperative display() fallback; the settings tab is declarative only
  • Raise minAppVersion to 1.13.0 — vaults below it stay on 1.1.5 via versions.json

1.1.6

  • Adopt the declarative settings API, so settings are indexed by Obsidian's settings search
  • Replace remaining document.createElement calls with Obsidian's createEl helpers
  • Tighten capture-group substitution types in src/engine.ts

1.1.5

  • Add dynamic ruleset commands for direct Obsidian invocation (Ruleset: )
  • Enable integration with Commander, Editing Toolbar, and macro plugins
  • Add maintainer info to README (Korean developer, AI researcher)

1.1.4

  • Reposition the plugin around safe previews and reusable cleanup pipelines
  • Add practical Markdown cleanup recipes and a built-in feature comparison
  • Align package metadata and documentation with the 0BSD license

1.1.0

  • Pipeline rulesets: save reusable multi-step rulesets, apply in sequence with step preview
  • Import rulesets from the regex-pipeline plugin
  • New command: "Apply ruleset (pipeline)"

1.0.0

  • Initial release
  • Regex find and replace with preview
  • Real-time match highlighting
  • Pattern history
  • Selection-only mode

License

0BSD License — see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

If you find this plugin useful, consider:

  • Starring the repository on GitHub
  • Reporting issues or suggesting features
  • Contributing code improvements

About

Maintained by Bongho Lee, a Korean developer and AI researcher. Feedback, issues, and pull requests are welcome!


Made with ❤️ for the Obsidian community