2019-06-12 05:41:31 +00:00
|
|
|
import argparse
|
|
|
|
import codecs
|
|
|
|
import pyperclip
|
2020-01-15 07:47:11 +00:00
|
|
|
import re
|
|
|
|
import sys
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-11-08 19:39:17 +00:00
|
|
|
from voussoirkit import betterhelp
|
2020-12-07 08:54:00 +00:00
|
|
|
from voussoirkit import interactive
|
2021-05-17 03:56:31 +00:00
|
|
|
from voussoirkit import pathclass
|
2020-12-01 06:05:54 +00:00
|
|
|
from voussoirkit import pipeable
|
2020-06-25 21:16:51 +00:00
|
|
|
from voussoirkit import spinal
|
2021-05-17 03:56:31 +00:00
|
|
|
from voussoirkit import vlogging
|
2020-01-16 06:47:26 +00:00
|
|
|
|
2021-05-17 03:56:31 +00:00
|
|
|
log = vlogging.getLogger(__name__, 'contentreplace')
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-05-17 03:56:31 +00:00
|
|
|
def contentreplace(file, replace_from, replace_to, autoyes=False, do_regex=False):
|
|
|
|
file = pathclass.Path(file)
|
2021-10-05 00:21:14 +00:00
|
|
|
content = file.read('r', encoding='utf-8')
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2020-01-15 07:47:11 +00:00
|
|
|
if do_regex:
|
2021-03-26 20:28:10 +00:00
|
|
|
occurances = len(re.findall(replace_from, content, flags=re.MULTILINE))
|
2020-01-15 07:47:11 +00:00
|
|
|
else:
|
|
|
|
occurances = content.count(replace_from)
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-05-17 03:56:31 +00:00
|
|
|
print(f'{file.absolute_path}: Found {occurances} occurences.')
|
2019-06-12 05:41:31 +00:00
|
|
|
if occurances == 0:
|
|
|
|
return
|
|
|
|
|
2020-12-07 08:54:00 +00:00
|
|
|
if not (autoyes or interactive.getpermission('Replace?')):
|
2019-06-12 05:41:31 +00:00
|
|
|
return
|
|
|
|
|
2020-01-15 07:47:11 +00:00
|
|
|
if do_regex:
|
2021-03-26 20:28:10 +00:00
|
|
|
content = re.sub(replace_from, replace_to, content, flags=re.MULTILINE)
|
2020-01-15 07:47:11 +00:00
|
|
|
else:
|
|
|
|
content = content.replace(replace_from, replace_to)
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-10-05 00:21:14 +00:00
|
|
|
file.write('w', content, encoding='utf-8')
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2020-12-01 06:05:54 +00:00
|
|
|
@pipeable.ctrlc_return1
|
2019-06-12 05:41:31 +00:00
|
|
|
def contentreplace_argparse(args):
|
2021-08-11 08:04:41 +00:00
|
|
|
files = spinal.walk(
|
|
|
|
glob_filenames=args.filename_glob,
|
|
|
|
recurse=args.recurse,
|
|
|
|
)
|
2019-06-12 05:41:31 +00:00
|
|
|
|
|
|
|
if args.clip_prompt:
|
|
|
|
replace_from = input('Ready from')
|
|
|
|
if not replace_from:
|
|
|
|
replace_from = pyperclip.paste()
|
|
|
|
replace_to = input('Ready to')
|
|
|
|
if not replace_to:
|
|
|
|
replace_to = pyperclip.paste()
|
|
|
|
else:
|
|
|
|
replace_from = codecs.decode(args.replace_from, 'unicode_escape')
|
2020-11-16 01:58:26 +00:00
|
|
|
if args.do_regex:
|
|
|
|
replace_to = args.replace_to
|
|
|
|
else:
|
|
|
|
replace_to = codecs.decode(args.replace_to, 'unicode_escape')
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-05-17 03:56:31 +00:00
|
|
|
for file in files:
|
|
|
|
try:
|
|
|
|
contentreplace(
|
|
|
|
file,
|
|
|
|
replace_from,
|
|
|
|
replace_to,
|
|
|
|
autoyes=args.autoyes,
|
|
|
|
do_regex=args.do_regex,
|
|
|
|
)
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
log.error('%s encountered unicode decode error.', file.absolute_path)
|
2019-06-12 05:41:31 +00:00
|
|
|
|
2021-09-24 06:42:34 +00:00
|
|
|
return 0
|
|
|
|
|
2021-06-22 05:11:19 +00:00
|
|
|
@vlogging.main_decorator
|
2019-06-12 05:41:31 +00:00
|
|
|
def main(argv):
|
2022-02-13 03:50:00 +00:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description='''
|
|
|
|
find-and-replace en masse
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'filename_glob',
|
|
|
|
help='''
|
|
|
|
A glob pattern that targets the files of interest.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'replace_from',
|
|
|
|
help='''
|
|
|
|
String to be replaced. You can use backslash-escaped symbols like
|
|
|
|
\\n for newline.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'replace_to',
|
|
|
|
help='''
|
|
|
|
String with which to replace. Can use backslash-escaped symbols.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--yes',
|
|
|
|
dest='autoyes',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
If provided, replacements will occur automatically without prompting.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--recurse',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
If provided, we will recurse into subdirectories and look for glob matches
|
|
|
|
there too. If not provided, only files in the cwd are affected.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--regex',
|
|
|
|
dest='do_regex',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
If provided, the given replace_from, replace_to will be treated as regex
|
|
|
|
strings. If not provided, we use regular str.replace.
|
|
|
|
''',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--clip_prompt',
|
|
|
|
'--clip-prompt',
|
|
|
|
action='store_true',
|
|
|
|
help='''
|
|
|
|
If you want to do contentreplace with unicode that is difficult to enter
|
|
|
|
into your terminal, or multi-line strings that don't work as command line
|
|
|
|
arguments, this option might help you. The program will wait for you to put
|
|
|
|
the text of interest into your clipboard and press Enter.
|
|
|
|
''',
|
|
|
|
)
|
2019-06-12 05:41:31 +00:00
|
|
|
parser.set_defaults(func=contentreplace_argparse)
|
|
|
|
|
2022-02-13 03:50:00 +00:00
|
|
|
return betterhelp.go(parser, argv)
|
2019-06-12 05:41:31 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
raise SystemExit(main(sys.argv[1:]))
|