vimtabdiff/vimtabdiff.py
Balakrishnan Balasubramanian da5e24260a Use shlex to split custom vim command
This allows to include spaces in vim command
Add example to change default diff algorithm
Move to first line on each file
2022-12-20 13:59:12 -05:00

94 lines
2.9 KiB
Python
Executable File

#!/usr/bin/python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import argparse
import itertools
import tempfile
import subprocess
import shlex
from pathlib import Path
from typing import Callable, TypeVar
from collections.abc import Iterator, Sequence
T = TypeVar('T')
def star(f: Callable[..., T]) -> Callable[[Sequence], T]:
""" see https://stackoverflow.com/q/21892989 """
return lambda args: f(*args)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Show diff of files from two directories in vim tabs",
epilog="See https://github.com/balki/vimtabdiff for more info")
parser.add_argument("pathA", type=Path)
parser.add_argument("pathB", type=Path)
parser.add_argument("--vim", help="vim command to run", default="vim")
return parser.parse_args()
def get_dir_info(dirpath: Path | None) -> tuple[list[Path], list[Path]]:
if not dirpath:
return [], []
dirs, files = [], []
for p in dirpath.iterdir():
if p.is_dir():
dirs.append(p)
else:
files.append(p)
return dirs, files
def get_pairs(aItems: list[Path],
bItems: list[Path]) -> Iterator[tuple[Path | None, Path | None]]:
aItems = [(item, 'A') for item in aItems]
bItems = [(item, 'B') for item in bItems]
abItems = aItems + bItems
abItems.sort(key=star(lambda item, tag: (item.name, tag)))
for _, items in itertools.groupby(abItems,
key=star(lambda item, _: item.name)):
match list(items):
case [(aItem, _), (bItem, _)]:
yield aItem, bItem
case [(item, 'A'),]:
yield item, None
case [(item, 'B'),]:
yield None, item
def get_file_pairs(a: Path,
b: Path) -> Iterator[tuple[Path | None, Path | None]]:
aDirs, aFiles = get_dir_info(a)
bDirs, bFiles = get_dir_info(b)
yield from get_pairs(aFiles, bFiles)
for aDir, bDir in get_pairs(aDirs, bDirs):
yield from get_file_pairs(aDir, bDir)
def main() -> None:
args = parse_args()
vimCmdFile = tempfile.NamedTemporaryFile(mode='w', delete=False)
with vimCmdFile:
for a, b in get_file_pairs(args.pathA, args.pathB):
aPath = a.resolve() if a else os.devnull
bPath = b.resolve() if b else os.devnull
print(
f"tabedit {aPath} | vsp {bPath} | windo diffthis | windo diffupdate",
file=vimCmdFile)
cmds = f"""
tabdo windo :1
tabfirst | tabclose
call delete("{vimCmdFile.name}")
"""
print(cmds, file=vimCmdFile)
subprocess.run(shlex.split(args.vim) + ["-S", vimCmdFile.name])
if __name__ == '__main__':
main()