diff --git a/src/bleanser/core/main.py b/src/bleanser/core/main.py index ac82afb..d81a1fe 100644 --- a/src/bleanser/core/main.py +++ b/src/bleanser/core/main.py @@ -18,6 +18,7 @@ def main(*, Normaliser: Type[BaseNormaliser]) -> None: 'max_content_width': 120, 'show_default': True, } + @click.group(context_settings=context_settings) def call_main() -> None: pass @@ -28,34 +29,35 @@ def call_main() -> None: @call_main.command(name='diff', short_help='cleanup two files and diff') @click.argument('path1' , type=str) @click.argument('path2' , default=_DEFAULT) - @click.option ('--glob', is_flag=True, default=False, help='Treat the path as glob (in the glob.glob sense)') + @click.option ('--glob' , is_flag=True, default=False , help='Treat the path as glob (in the glob.glob sense)') @click.option ('--vim' , is_flag=True, default=False , help='Use vimdiff') @click.option ('--difftool' , type=str , help='Custom difftool to use') @click.option ('--from', 'from_', type=int , default=None) - @click.option ('--to' , type=int , default=None) + @click.option ('--to' , type=int , default=None , help='non-inclusive, i.e. [from, to)') def diff(path1: str, path2: Path, *, glob: bool, from_: Optional[int], to: Optional[int], vim: bool, difftool: str) -> None: path1_: Path if glob: assert path2 is cast(Path, _DEFAULT), path2 if to is None: assert from_ is not None - to = from_ + 2 + to = from_ + 2 # by default just compare with the next adjacent element paths = _get_paths(path=path1, from_=from_, to=to, glob=glob) - assert len(paths) == 2, paths - [path1_, path2] = paths else: assert cast(str, path2) is not _DEFAULT + assert from_ is None and to is None # just for sanity path1_ = Path(path1) path2 = Path(path2) + paths = [path1_, path2] from .processor import compute_diff + # meh.. if vim: difftool = 'vimdiff' if difftool is not None: os.environ['DIFFTOOL'] = difftool - for line in compute_diff(path1_, path2, Normaliser=Normaliser): + for line in compute_diff(paths, Normaliser=Normaliser): print(line) @call_main.command(name='normalised', short_help='normalise file and dump to stdout') @@ -71,7 +73,6 @@ def normalised(path: Path, stdout: bool) -> None: click.secho(f'You can examine normalised file: {cleaned}', fg='green') click.pause(info="Press any key when you've finished") - @call_main.command(name='prune', short_help='process & prune files') @click.argument('path', type=str) @click.option('--glob', is_flag=True, default=False, help='Treat the path as glob (in the glob.glob sense)') @@ -144,7 +145,7 @@ def _get_paths(*, path: str, from_: Optional[int], to: Optional[int], sort_by: s from_ = 0 if to is None: to = len(paths) - paths = paths[from_: to] + paths = paths[from_:to] assert len(paths) > 0 logger.info('processing %d files (%s ... %s)', len(paths), paths[0], paths[-1]) diff --git a/src/bleanser/core/processor.py b/src/bleanser/core/processor.py index 48dc8ec..49f377a 100644 --- a/src/bleanser/core/processor.py +++ b/src/bleanser/core/processor.py @@ -6,6 +6,7 @@ import re import shutil import sys +import subprocess from subprocess import check_call from tempfile import TemporaryDirectory, gettempdir, NamedTemporaryFile from time import time @@ -1238,24 +1239,47 @@ def stat() -> str: # TODO write a test for this -def compute_diff(path1: Path, path2: Path, *, Normaliser: Type[BaseNormaliser]) -> List[str]: - with bleanser_tmp_directory() as base_tmp_dir: - n1 = Normaliser(original=path1, base_tmp_dir=base_tmp_dir) - n2 = Normaliser(original=path2, base_tmp_dir=base_tmp_dir) - - with n1.do_normalise() as res1, n2.do_normalise() as res2: - # ok, I guess diff_filter=None makes more sense here? - # would mean it shows the whole thing - # meh - difftool = os.environ.get('DIFFTOOL', None) - if difftool is not None: - extras: List[str] = [] - if difftool == 'vimdiff': - wrap = ['-c', 'windo set wrap'] - # wrap = [] - diffopts = ['-c', 'set diffopt=filler,context:0'] # show only diffs and hide identical lines - extras.extend(wrap) - extras.extend(diffopts) - - os.execlp(difftool, difftool, *extras, str(res1), str(res2)) - return do_diff(res1, res2, diff_filter=None) +def compute_diff(paths: List[Path], *, Normaliser: Type[BaseNormaliser]) -> List[str]: + assert len(paths) >= 2, paths + + difftool = os.environ.get('DIFFTOOL', None) + difftool_args: List[str] = [] + if difftool == 'vimdiff': + wrap = ['-c', 'windo set wrap'] + diffopts = ['-c', 'set diffopt=filler,context:0'] # show only diffs and hide identical lines + difftool_args.extend(wrap) + difftool_args.extend(diffopts) + + with bleanser_tmp_directory() as base_tmp_dir, ExitStack() as exit_stack: + results = [] + for path in paths: + pn = Normaliser(original=path, base_tmp_dir=base_tmp_dir) + results.append((path, exit_stack.enter_context(pn.do_normalise()))) + + # if > 2 paths are passed, we're treating first and last path as 'pivots', and comparing to all the stuff in the middle + first = results[0] + last = results[-1] + rest = results[1:-1] + + if len(rest) == 0: + group1 = [first] + group2 = [last] + else: + group1 = rest + group2 = [first, last] + + logger.info('comparing [ %s ] vs [ %s ]', ' '.join(str(p) for p, _ in group1), ' '.join(str(p) for p, _ in group2)) + + fs1 = FileSet([r for _, r in group1], wdir=base_tmp_dir) + fs2 = FileSet([r for _, r in group2], wdir=base_tmp_dir) + c1 = fs1.merged + c2 = fs2.merged + + if difftool is not None: + # note: we don't want to exec here, otherwise context manager won't have a chance to clean up? + subprocess.run([difftool, *difftool_args, str(c1), str(c2)]) + return [] # no need to print again + + return do_diff(c1, c2, diff_filter=None) + + return [] # ugh, mypy complains "Missing return statement" without it? try to remove later