Files
logicalor-comfyui_multi_rep…/nodes/text_replacer.py
T
2025-12-16 09:14:51 +10:00

148 lines
5.4 KiB
Python

"""
Text Replacer Node
Applies find/replace pairs to input text.
"""
import re
from typing import Any
class TextReplacer:
"""
A node that applies find/replace pairs to an input string.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"pairs": ("FR_PAIRS", {
"tooltip": "Find/Replace pairs from the FindReplacePairs node"
}),
"input_text": ("STRING", {
"default": "",
"multiline": True,
"placeholder": "Text to perform replacements on"
}),
},
"optional": {
"input_text_connected": ("STRING", {
"forceInput": True,
"tooltip": "Connected text input (overrides widget)"
}),
"use_regex": ("BOOLEAN", {
"default": False,
"tooltip": "Treat find patterns as regular expressions"
}),
"case_sensitive": ("BOOLEAN", {
"default": True,
"tooltip": "Whether replacements are case-sensitive"
}),
"replace_all": ("BOOLEAN", {
"default": True,
"tooltip": "Replace all occurrences (vs only first)"
}),
}
}
RETURN_TYPES = ("STRING", "FR_PAIRS", "STRING", "INT",)
RETURN_NAMES = ("result", "pairs_passthrough", "changes_log", "replacement_count",)
OUTPUT_TOOLTIPS = (
"The text after all replacements have been applied",
"The find/replace pairs (passed through for chaining)",
"Log of all replacements made",
"Total number of replacements performed",
)
FUNCTION = "apply_replacements"
CATEGORY = "text/replace"
DESCRIPTION = "Applies find/replace pairs to input text. Supports regex and case-insensitive matching."
def apply_replacements(
self,
pairs: dict,
input_text: str = "",
input_text_connected: str = None,
use_regex: bool = False,
case_sensitive: bool = True,
replace_all: bool = True
):
"""
Apply all find/replace pairs to the input text.
Args:
pairs: The pairs object from FindReplacePairs node
input_text: Text widget value
input_text_connected: Connected text input (takes priority)
use_regex: Whether to use regex matching
case_sensitive: Whether matching is case-sensitive
replace_all: Whether to replace all occurrences
Returns:
Tuple of (result_text, pairs_passthrough, changes_log, replacement_count)
"""
# Use connected input if available
text = input_text_connected if input_text_connected is not None else input_text
# Get the pairs list
pairs_list = pairs.get("pairs", [])
total_replacements = 0
log_entries = []
result = text
for pair in pairs_list:
find_pattern = pair.get("find", "")
replace_with = pair.get("replace", "")
if not find_pattern:
continue
# Count occurrences before replacement
if use_regex:
flags = 0 if case_sensitive else re.IGNORECASE
try:
matches = len(re.findall(find_pattern, result, flags))
if replace_all:
result = re.sub(find_pattern, replace_with, result, flags=flags)
else:
result = re.sub(find_pattern, replace_with, result, count=1, flags=flags)
except re.error as e:
log_entries.append(f"Regex error for pattern '{find_pattern}': {e}")
continue
else:
if case_sensitive:
matches = result.count(find_pattern)
if replace_all:
result = result.replace(find_pattern, replace_with)
else:
result = result.replace(find_pattern, replace_with, 1)
else:
# Case-insensitive plain text replacement
pattern = re.escape(find_pattern)
matches = len(re.findall(pattern, result, re.IGNORECASE))
if replace_all:
result = re.sub(pattern, replace_with, result, flags=re.IGNORECASE)
else:
result = re.sub(pattern, replace_with, result, count=1, flags=re.IGNORECASE)
replacements_made = min(matches, 1) if not replace_all else matches
total_replacements += replacements_made
if replacements_made > 0:
log_entries.append(
f"Replaced '{find_pattern}' -> '{replace_with}' ({replacements_made}x)"
)
# Create changes log
changes_log = "\n".join(log_entries) if log_entries else "No replacements made"
return (result, pairs, changes_log, total_replacements)
@classmethod
def IS_CHANGED(cls, pairs, input_text="", input_text_connected=None, **kwargs):
"""Force re-evaluation when inputs change."""
text = input_text_connected if input_text_connected is not None else input_text
pairs_hash = hash(str(pairs))
return hash((text, pairs_hash, str(kwargs)))