Compare commits

..

No commits in common. "9616c26cb83c56e834ad2b450f8a17da6249da8d" and "de278ae0ba849c341e3e06702cc3d00443f6e29e" have entirely different histories.

4 changed files with 21 additions and 42 deletions

View File

@ -1,25 +1,9 @@
# subprompt # subprompt
Interactively change every line matching a regex in multiple files. Substitute every match of a regex in multiple files - with confirmation prompts. Usage:
```
## Installation subprompt [REGEX] [SUB] [FILES...]
Through PyPI: ```
Install it through PyPi:
``` ```
python3.9 -m pip install subprompt python3.9 -m pip install subprompt
``` ```
## Usage
```
usage: subprompt.py [-h] (-d | -r R) [-n N] REGEX FILES [FILES ...]
Modifies lines matched by a regex interactively
positional arguments:
REGEX
FILES
optional arguments:
-h, --help show this help message and exit
-d delete line
-r R replace match with expression
-n N size of lines preview (default=3)
```

View File

@ -1,3 +1 @@
colorama==0.4.3 colorama==0.4.3
build==0.9.0
twine==4.0.1

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = subprompt name = subprompt
version = 0.0.7 version = 0.0.5
author = Augusto Lenz Gunsch author = Augusto Lenz Gunsch
author_email = augusto@augustogunsch.com author_email = augusto@augustogunsch.com
description = Substitute Regex in files with prompt confirmation description = Substitute Regex in files with prompt confirmation

View File

@ -41,13 +41,13 @@ class Filter:
self.regex = re.compile(args.regex) self.regex = re.compile(args.regex)
self.sub = args.r self.sub = args.r
self.delete = args.d self.delete = args.d
self.spread = args.n+1 self.spread = args.n
self.lines_to_delete = [] self.lines_to_delete = []
def _number_lines(_, lines, start_n): def _number_lines(_, lines, start_n):
return [Fore.YELLOW + Style.BRIGHT + return [Fore.YELLOW + Style.BRIGHT +
str(n + start_n + 1) + str(n + start_n) +
Fore.RESET + Style.RESET_ALL + Fore.RESET + Style.RESET_ALL +
':' + line ':' + line
for (n, line) in enumerate(lines)] for (n, line) in enumerate(lines)]
@ -74,34 +74,33 @@ class Filter:
return replaced_match return replaced_match
start_n = max(0, line_n - self.spread) start_n = max(0, line_n - self.spread)
end_n = min(len(lines)+1, line_n + self.spread) end_n = min(len(lines), line_n + self.spread)
rel_line_n = line_n-start_n
cut = lines[start_n:end_n] cut = lines[start_n:end_n]
highlighted = line.replace(curr_match, highlighted = line.replace(curr_match,
Back.RED + curr_match + Back.RESET) Back.RED + curr_match + Back.RESET)
cut_highlighted = cut cut_highlighted = cut
cut_highlighted[rel_line_n] = highlighted cut_highlighted[line_n] = highlighted
cut_highlighted = self._number_lines(cut_highlighted, start_n) cut_highlighted = self._number_lines(cut_highlighted, start_n)
cut_replaced = cut cut_replaced = cut
if not self.delete: if not self.delete:
cut_replaced[rel_line_n] = line.replace(curr_match, cut_replaced[line_n] = line.replace(curr_match,
Back.YELLOW + Fore.BLACK + replaced_match + Back.RESET + Fore.RESET) Back.YELLOW + Fore.BLACK + replaced_match + Back.RESET + Fore.RESET)
else: else:
cut_replaced.pop(rel_line_n) cut_replaced.pop(line_n)
cut_replaced = self._number_lines(cut_replaced, start_n) cut_replaced = self._number_lines(cut_replaced, start_n)
print(Fore.GREEN + Style.BRIGHT + fname) print(Fore.GREEN + Style.BRIGHT + fname)
print(''.join(cut_highlighted)) print('\n'.join(cut_highlighted))
print('Becomes the following:') print('Becomes the following:')
print() print('\n'.join(cut_replaced))
print(''.join(cut_replaced))
print()
while True: while True:
answer = input('Confirm the change? [Y/n/a/q] ').lower() answer = input('Confirm the change? [Y/n/a/q] ').lower()
@ -136,7 +135,7 @@ class Filter:
with open(fname, 'r') as file: with open(fname, 'r') as file:
contents = file.read() contents = file.read()
lines = contents.splitlines(keepends=True) lines = contents.splitlines()
lines = [ lines = [
self.regex.sub( self.regex.sub(
@ -146,13 +145,10 @@ class Filter:
for (line_n, line) in enumerate(lines) for (line_n, line) in enumerate(lines)
] ]
if self.delete: for line in reversed(self.lines_to_delete):
for line in reversed(self.lines_to_delete): lines.pop(line)
lines.pop(line)
self.lines_to_delete = [] new_contents = '\n'.join(lines)
new_contents = ''.join(lines)
hash_old = hashlib.md5(contents.encode()) hash_old = hashlib.md5(contents.encode())
hash_new = hashlib.md5(new_contents.encode()) hash_new = hashlib.md5(new_contents.encode())
@ -160,6 +156,7 @@ class Filter:
if hash_old.digest() != hash_new.digest(): if hash_old.digest() != hash_new.digest():
with open(fname, 'w') as file: with open(fname, 'w') as file:
file.write(new_contents) file.write(new_contents)
file.write('\n')
def run(args): def run(args):
parser = argparse.ArgumentParser(description='Modifies lines matched by a regex interactively') parser = argparse.ArgumentParser(description='Modifies lines matched by a regex interactively')
@ -168,7 +165,7 @@ def run(args):
action.add_argument('-d', help='delete line', action='store_true') action.add_argument('-d', help='delete line', action='store_true')
action.add_argument('-r', help='replace match with expression', type=str) action.add_argument('-r', help='replace match with expression', type=str)
parser.add_argument('files', metavar='FILES', nargs='+', type=str) parser.add_argument('files', metavar='FILES', nargs='+', type=str)
parser.add_argument('-n', help='size of lines preview (default=3)', type=int, default=3) parser.add_argument('-n', help='size lines preview (default=3)', type=int, default=3)
args = parser.parse_args(args) args = parser.parse_args(args)