webdiff 1.2.0__tar.gz → 1.3.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webdiff-1.2.0 → webdiff-1.3.0b1}/PKG-INFO +10 -5
- {webdiff-1.2.0 → webdiff-1.3.0b1}/README.md +6 -2
- {webdiff-1.2.0 → webdiff-1.3.0b1}/pyproject.toml +5 -4
- webdiff-1.3.0b1/webdiff/app.py +341 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/diff.py +14 -2
- webdiff-1.3.0b1/webdiff/static/js/file_diff.js +61 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/templates/file_diff.html +0 -8
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/util.py +19 -0
- webdiff-1.2.0/webdiff/app.py +0 -347
- webdiff-1.2.0/webdiff/static/js/file_diff.js +0 -47
- webdiff-1.2.0/webdiff/static/js/jquery-2.1.1.min.js +0 -4
- {webdiff-1.2.0 → webdiff-1.3.0b1}/LICENSE +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/__init__.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/argparser.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/dirdiff.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/github_fetcher.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/githubdiff.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/gitwebdiff.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/gitwebshow.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/localfilediff.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/options.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/__init__.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/codediff.js/codediff.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/codediff.js/googlecode.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/Inconsolata-Bold.ttf +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/Inconsolata-Regular.ttf +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/Inconsolata/OFL.txt +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/googlecode.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/inconsolata.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/style.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/a11y-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/a11y-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/agate.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/an-old-hope.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/androidstudio.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/arduino-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/arta.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/ascetic.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-dark-reasonable.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/atom-one-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/3024.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/apathy.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/apprentice.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ashes.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-cave-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-cave.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-dune-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-dune.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-estuary-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-estuary.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-forest-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-forest.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-heath-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-heath.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-lakeside-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-lakeside.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-plateau-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-plateau.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-savanna-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-savanna.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-seaside-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-seaside.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-sulphurpool-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atelier-sulphurpool.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/atlas.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/bespin.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-bathory.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-burzum.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-dark-funeral.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-gorgoroth.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-immortal.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-khold.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-marduk.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-mayhem.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-nile.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal-venom.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/black-metal.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brewer.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/bright.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brogrammer.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brush-trees-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/brush-trees.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/chalk.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/circus.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/classic-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/classic-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/codeschool.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/colors.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/cupcake.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/cupertino.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/danqing.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darcula.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dark-violet.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darkmoss.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/darktooth.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/decaf.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/default-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/default-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dirtysea.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/dracula.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/edge-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/edge-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eighties.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/embers.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-gray-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-gray-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/equilibrium-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/espresso.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eva-dim.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/eva.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/flat.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/framer.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/fruit-soda.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gigavolt.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/github.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/google-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/google-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/grayscale-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/grayscale-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/green-screen.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-hard.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-medium.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-pale.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-dark-soft.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-hard.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-medium.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/gruvbox-light-soft.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/hardcore.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/harmonic16-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/harmonic16-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/heetch-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/heetch-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/helios.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/hopscotch.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/horizon-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/horizon-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/humanoid-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/humanoid-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ia-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ia-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/icy-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ir-black.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/isotope.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/kimber.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/london-tube.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/macintosh.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/marrakesh.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/materia.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-darker.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-lighter.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-palenight.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material-vivid.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/material.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mellow-purple.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mexico-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/mocha.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/monokai.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nebula.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nord.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/nova.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ocean.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/oceanicnext.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/one-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/onedark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/outrun-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/papercolor-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/papercolor-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/paraiso.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pasque.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/phd.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pico.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/pop.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/porple.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/qualia.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/railscasts.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/rebecca.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine-dawn.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine-moon.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/ros-pine.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/sagelight.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/sandcastle.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/seti-ui.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/shapeshifter.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/silk-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/silk-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/snazzy.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solar-flare-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solar-flare.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solarized-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/solarized-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/spacemacs.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summercamp.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summerfruit-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/summerfruit-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/synth-midnight-terminal-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/synth-midnight-terminal-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tango.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tender.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tomorrow-night.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/tomorrow.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/twilight.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/unikitty-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/unikitty-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/vulcan.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-10-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-10.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-95-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-95.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-high-contrast-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-high-contrast.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-nt-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/windows-nt.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/woodland.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/xcode-dusk.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/base16/zenburn.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/brown-paper.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/brown-papersq.png +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/codepen-embed.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/color-brewer.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/default.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/devibeans.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/docco.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/far.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/felipec.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/foundation.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github-dark-dimmed.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/github.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gml.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/googlecode.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gradient-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/gradient-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/grayscale.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/hybrid.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/idea.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/intellij-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/ir-black.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/isbl-editor-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/isbl-editor-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/kimbie-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/kimbie-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/lightfair.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/lioshi.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/magula.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/mono-blue.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/monokai-sublime.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/monokai.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/night-owl.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nnfx-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nnfx-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/nord.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/obsidian.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/panda-syntax-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/panda-syntax-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/paraiso-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/paraiso-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/pojoaque.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/pojoaque.jpg +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/purebasic.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/qtcreator-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/qtcreator-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/rainbow.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/routeros.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/school-book.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/shades-of-purple.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/srcery.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/stackoverflow-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/stackoverflow-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/sunburst.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tokyo-night-dark.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tokyo-night-light.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tomorrow-night-blue.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/tomorrow-night-bright.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/vs.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/vs2015.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/xcode.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/css/themes/xt256.css +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/favicon.ico +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/sprites.png +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/img/trans_bg.gif +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/js/highlight.min.js +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/static/js/highlight.pack.min.js +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/templates/__init__.py +0 -0
- {webdiff-1.2.0 → webdiff-1.3.0b1}/webdiff/unified_diff.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: webdiff
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0b1
|
|
4
4
|
Summary: Two-column web-based git difftool
|
|
5
5
|
Home-page: https://github.com/danvk/webdiff/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -18,10 +18,11 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Topic :: Software Development :: Version Control
|
|
21
|
-
Requires-Dist: PyGithub (
|
|
21
|
+
Requires-Dist: PyGithub (>=2.3.0,<3.0.0)
|
|
22
|
+
Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
|
|
22
23
|
Requires-Dist: binaryornot
|
|
23
24
|
Requires-Dist: pillow
|
|
24
|
-
Requires-Dist: unidiff (
|
|
25
|
+
Requires-Dist: unidiff (>=0.7.4,<0.8.0)
|
|
25
26
|
Project-URL: Repository, https://github.com/danvk/webdiff/
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
|
|
@@ -55,6 +56,10 @@ Features include:
|
|
|
55
56
|
|
|
56
57
|
pip install webdiff
|
|
57
58
|
|
|
59
|
+
or using [pipx]:
|
|
60
|
+
|
|
61
|
+
pipx install webdiff
|
|
62
|
+
|
|
58
63
|
or, if you prefer [Homebrew]:
|
|
59
64
|
|
|
60
65
|
brew install danvk/webdiff/webdiff
|
|
@@ -135,8 +140,7 @@ Options are:
|
|
|
135
140
|
poetry install
|
|
136
141
|
cd ts
|
|
137
142
|
yarn
|
|
138
|
-
|
|
139
|
-
NODE_OPTIONS=--openssl-legacy-provider webpack
|
|
143
|
+
yarn build
|
|
140
144
|
|
|
141
145
|
Then from the root directory:
|
|
142
146
|
|
|
@@ -216,4 +220,5 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
|
|
|
216
220
|
[git config]: https://git-scm.com/docs/git-config
|
|
217
221
|
[themes]: http://example.com
|
|
218
222
|
[poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
|
|
223
|
+
[pipx]: https://pipx.pypa.io/stable/
|
|
219
224
|
|
|
@@ -28,6 +28,10 @@ Features include:
|
|
|
28
28
|
|
|
29
29
|
pip install webdiff
|
|
30
30
|
|
|
31
|
+
or using [pipx]:
|
|
32
|
+
|
|
33
|
+
pipx install webdiff
|
|
34
|
+
|
|
31
35
|
or, if you prefer [Homebrew]:
|
|
32
36
|
|
|
33
37
|
brew install danvk/webdiff/webdiff
|
|
@@ -108,8 +112,7 @@ Options are:
|
|
|
108
112
|
poetry install
|
|
109
113
|
cd ts
|
|
110
114
|
yarn
|
|
111
|
-
|
|
112
|
-
NODE_OPTIONS=--openssl-legacy-provider webpack
|
|
115
|
+
yarn build
|
|
113
116
|
|
|
114
117
|
Then from the root directory:
|
|
115
118
|
|
|
@@ -189,3 +192,4 @@ There's one complication involving symlinks. `git difftool -d` may fill one of t
|
|
|
189
192
|
[git config]: https://git-scm.com/docs/git-config
|
|
190
193
|
[themes]: http://example.com
|
|
191
194
|
[poetry]: https://python-poetry.org/docs/repositories/#publishable-repositories
|
|
195
|
+
[pipx]: https://pipx.pypa.io/stable/
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "webdiff"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0b1"
|
|
8
8
|
description = "Two-column web-based git difftool"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
readme = "README.md"
|
|
@@ -28,12 +28,13 @@ include = ["webdiff/static/js/file_diff.js"]
|
|
|
28
28
|
python = "^3.10"
|
|
29
29
|
binaryornot = "*"
|
|
30
30
|
pillow = "*"
|
|
31
|
-
PyGithub = "2.3.0"
|
|
32
|
-
unidiff = "
|
|
31
|
+
PyGithub = "^2.3.0"
|
|
32
|
+
unidiff = "^0.7.4"
|
|
33
|
+
aiohttp = "^3.9.5"
|
|
33
34
|
|
|
34
35
|
[tool.poetry.group.dev.dependencies]
|
|
35
36
|
pytest = "^7.1.3"
|
|
36
|
-
ruff = "
|
|
37
|
+
ruff = "0.7"
|
|
37
38
|
|
|
38
39
|
[tool.poetry.scripts]
|
|
39
40
|
webdiff = "webdiff.app:run"
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Web-based file differ.
|
|
3
|
+
|
|
4
|
+
For usage, see README.md.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import dataclasses
|
|
8
|
+
import importlib.metadata
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import mimetypes
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import signal
|
|
15
|
+
import socket
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
import webbrowser
|
|
20
|
+
|
|
21
|
+
import aiohttp
|
|
22
|
+
import aiohttp.web_request
|
|
23
|
+
from aiohttp import web
|
|
24
|
+
from binaryornot.check import is_binary
|
|
25
|
+
|
|
26
|
+
from webdiff import argparser, diff, options, util
|
|
27
|
+
|
|
28
|
+
VERSION = importlib.metadata.version('webdiff')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def determine_path():
|
|
32
|
+
"""Borrowed from wxglade.py"""
|
|
33
|
+
try:
|
|
34
|
+
root = __file__
|
|
35
|
+
if os.path.islink(root):
|
|
36
|
+
root = os.path.realpath(root)
|
|
37
|
+
return os.path.dirname(os.path.abspath(root))
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"I'm sorry, but something is wrong. Error: {e}")
|
|
40
|
+
print('There is no __file__ variable. Please contact the author.')
|
|
41
|
+
sys.exit()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
GIT_CONFIG = {}
|
|
45
|
+
DIFF = None
|
|
46
|
+
PORT = None
|
|
47
|
+
HOSTNAME = 'localhost'
|
|
48
|
+
DEBUG = os.environ.get('DEBUG')
|
|
49
|
+
WEBDIFF_DIR = determine_path()
|
|
50
|
+
|
|
51
|
+
if DEBUG:
|
|
52
|
+
handler = logging.StreamHandler()
|
|
53
|
+
handler.setLevel(logging.DEBUG)
|
|
54
|
+
formatter = logging.Formatter(
|
|
55
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
56
|
+
)
|
|
57
|
+
handler.setFormatter(formatter)
|
|
58
|
+
|
|
59
|
+
for logname in ['']:
|
|
60
|
+
log = logging.getLogger(logname)
|
|
61
|
+
log.setLevel(logging.DEBUG)
|
|
62
|
+
log.addHandler(handler)
|
|
63
|
+
logging.getLogger('github').setLevel(logging.ERROR)
|
|
64
|
+
logging.getLogger('binaryornot').setLevel(logging.ERROR)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def handle_index(request: aiohttp.web_request.Request):
|
|
68
|
+
idx = int(request.match_info.get('idx', '0'))
|
|
69
|
+
pairs = diff.get_thin_list(DIFF)
|
|
70
|
+
with open(os.path.join(WEBDIFF_DIR, 'templates/file_diff.html'), 'r') as file:
|
|
71
|
+
html = file.read()
|
|
72
|
+
html = html.replace(
|
|
73
|
+
'{{data}}',
|
|
74
|
+
json.dumps(
|
|
75
|
+
{
|
|
76
|
+
'idx': idx,
|
|
77
|
+
'has_magick': util.is_imagemagick_available(),
|
|
78
|
+
'pairs': pairs,
|
|
79
|
+
'git_config': GIT_CONFIG,
|
|
80
|
+
},
|
|
81
|
+
indent=2,
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
return web.Response(body=html, content_type='text/html', charset='utf-8')
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def handle_thick(request: aiohttp.web_request.Request):
|
|
88
|
+
idx = int(request.match_info.get('idx'))
|
|
89
|
+
return web.json_response(diff.get_thick_dict(DIFF[idx]))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def handle_get_contents(request: aiohttp.web_request.Request):
|
|
93
|
+
side = request.match_info['side']
|
|
94
|
+
form_data = await request.post()
|
|
95
|
+
path = form_data.get('path', '')
|
|
96
|
+
if not path:
|
|
97
|
+
return web.json_response({'error': 'incomplete'}, status=400)
|
|
98
|
+
should_normalize = form_data.get('normalize_json')
|
|
99
|
+
|
|
100
|
+
idx = diff.find_diff_index(DIFF, side, path)
|
|
101
|
+
if idx is None:
|
|
102
|
+
return web.json_response({'error': 'not found'}, status=400)
|
|
103
|
+
|
|
104
|
+
d = DIFF[idx]
|
|
105
|
+
abs_path = d.a_path if side == 'a' else d.b_path
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
if is_binary(abs_path):
|
|
109
|
+
size = os.path.getsize(abs_path)
|
|
110
|
+
return web.Response(text=f'Binary file ({size} bytes)')
|
|
111
|
+
else:
|
|
112
|
+
if should_normalize:
|
|
113
|
+
abs_path = util.normalize_json(abs_path)
|
|
114
|
+
return web.FileResponse(abs_path, headers={'Content-Type': 'text/plain'})
|
|
115
|
+
except Exception as e:
|
|
116
|
+
return web.json_response({'error': str(e)}, status=500)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def handle_diff_ops(request: aiohttp.web_request.Request):
|
|
120
|
+
idx = int(request.match_info.get('idx'))
|
|
121
|
+
payload = await request.json()
|
|
122
|
+
options = payload.get('options') or []
|
|
123
|
+
should_normalize = payload.get('normalize_json')
|
|
124
|
+
logging.debug([*payload.keys()])
|
|
125
|
+
logging.debug({**payload})
|
|
126
|
+
extra_args = GIT_CONFIG['webdiff']['extraFileDiffArgs']
|
|
127
|
+
if extra_args:
|
|
128
|
+
options += extra_args.split(' ')
|
|
129
|
+
diff_ops = [
|
|
130
|
+
dataclasses.asdict(op)
|
|
131
|
+
for op in diff.get_diff_ops(DIFF[idx], options, normalize_json=should_normalize)
|
|
132
|
+
]
|
|
133
|
+
return web.json_response(diff_ops, status=200)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def handle_theme(request: aiohttp.web_request.Request):
|
|
137
|
+
theme = GIT_CONFIG['webdiff']['theme']
|
|
138
|
+
theme_dir = os.path.dirname(theme)
|
|
139
|
+
theme_file = os.path.basename(theme)
|
|
140
|
+
theme_path = os.path.join(
|
|
141
|
+
WEBDIFF_DIR, 'static/css/themes', theme_dir, theme_file + '.css'
|
|
142
|
+
)
|
|
143
|
+
return web.FileResponse(theme_path)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
async def handle_favicon(request):
|
|
147
|
+
return web.FileResponse(os.path.join(WEBDIFF_DIR, 'static/img/favicon.ico'))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
async def handle_get_image(request: aiohttp.web_request.Request):
|
|
151
|
+
side = request.match_info.get('side')
|
|
152
|
+
path = request.match_info.get('path')
|
|
153
|
+
mime_type, _ = mimetypes.guess_type(path)
|
|
154
|
+
if not mime_type or not mime_type.startswith('image/'):
|
|
155
|
+
return web.json_response({'error': 'wrong type'}, status=400)
|
|
156
|
+
|
|
157
|
+
idx = diff.find_diff_index(DIFF, side, path)
|
|
158
|
+
if idx is None:
|
|
159
|
+
return web.json_response({'error': 'not found'}, status=400)
|
|
160
|
+
|
|
161
|
+
d = DIFF[idx]
|
|
162
|
+
abs_path = d.a_path if side == 'a' else d.b_path
|
|
163
|
+
return web.FileResponse(abs_path, headers={'Content-Type': mime_type})
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def handle_pdiff(request: aiohttp.web_request.Request):
|
|
167
|
+
idx = int(request.match_info.get('idx'))
|
|
168
|
+
d = DIFF[idx]
|
|
169
|
+
try:
|
|
170
|
+
_, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
|
|
171
|
+
dilated_image_path = util.generate_dilated_pdiff_image(pdiff_image)
|
|
172
|
+
return web.FileResponse(dilated_image_path)
|
|
173
|
+
except util.ImageMagickNotAvailableError:
|
|
174
|
+
return web.Response(status=501, text='ImageMagick is not available')
|
|
175
|
+
except util.ImageMagickError as e:
|
|
176
|
+
return web.Response(status=501, text=f'ImageMagick error {e}')
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def handle_pdiff_bbox(request: aiohttp.web_request.Request):
|
|
180
|
+
idx = int(request.match_info.get('idx'))
|
|
181
|
+
d = DIFF[idx]
|
|
182
|
+
try:
|
|
183
|
+
_, pdiff_image = util.generate_pdiff_image(d.a_path, d.b_path)
|
|
184
|
+
bbox = util.get_pdiff_bbox(pdiff_image)
|
|
185
|
+
return web.json_response(bbox, status=200)
|
|
186
|
+
except util.ImageMagickNotAvailableError:
|
|
187
|
+
return web.json_response('ImageMagick is not available', status=501)
|
|
188
|
+
except util.ImageMagickError as e:
|
|
189
|
+
return web.json_response(f'ImageMagick error {e}', status=501)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
async def websocket_handler(request: aiohttp.web_request.Request):
|
|
193
|
+
ws = web.WebSocketResponse()
|
|
194
|
+
await ws.prepare(request)
|
|
195
|
+
|
|
196
|
+
async for msg in ws:
|
|
197
|
+
if msg.type == aiohttp.WSMsgType.TEXT:
|
|
198
|
+
await ws.send_str(msg.data + '/answer')
|
|
199
|
+
elif msg.type == aiohttp.WSMsgType.ERROR:
|
|
200
|
+
print('ws connection closed with exception %s' % ws.exception())
|
|
201
|
+
|
|
202
|
+
maybe_shutdown()
|
|
203
|
+
return ws
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@web.middleware
|
|
207
|
+
async def request_time_middleware(request, handler):
|
|
208
|
+
note_request_time()
|
|
209
|
+
response = await handler(request)
|
|
210
|
+
return response
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
app = web.Application(middlewares=[request_time_middleware])
|
|
214
|
+
app.add_routes(
|
|
215
|
+
[
|
|
216
|
+
web.get('/', handle_index),
|
|
217
|
+
web.get(r'/{idx:\d+}', handle_index),
|
|
218
|
+
web.get('/favicon.ico', handle_favicon),
|
|
219
|
+
web.get('/theme.css', handle_theme),
|
|
220
|
+
web.static('/static', os.path.join(WEBDIFF_DIR, 'static')),
|
|
221
|
+
web.get(r'/thick/{idx:\d+}', handle_thick),
|
|
222
|
+
web.post(r'/{side:a|b}/get_contents', handle_get_contents),
|
|
223
|
+
web.post(r'/diff/{idx:\d+}', handle_diff_ops),
|
|
224
|
+
# Image diffs
|
|
225
|
+
web.get(r'/{side:a|b}/image/{path:.*}', handle_get_image),
|
|
226
|
+
web.get(r'/pdiff/{idx:\d+}', handle_pdiff),
|
|
227
|
+
web.get(r'/pdiffbbox/{idx:\d+}', handle_pdiff_bbox),
|
|
228
|
+
# Websocket for detecting when the tab is closed
|
|
229
|
+
web.get('/ws', websocket_handler),
|
|
230
|
+
]
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def note_request_time():
|
|
235
|
+
global LAST_REQUEST_MS
|
|
236
|
+
LAST_REQUEST_MS = time.time() * 1000
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def open_browser():
|
|
240
|
+
global PORT
|
|
241
|
+
global HOSTNAME
|
|
242
|
+
global GIT_CONFIG
|
|
243
|
+
if not os.environ.get('WEBDIFF_NO_OPEN') and GIT_CONFIG['webdiff']['openBrowser']:
|
|
244
|
+
webbrowser.open_new_tab('http://%s:%s' % (HOSTNAME, PORT))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def usage_and_die():
|
|
248
|
+
sys.stderr.write(argparser.USAGE)
|
|
249
|
+
sys.exit(1)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def random_port():
|
|
253
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
254
|
+
sock.bind(('localhost', 0))
|
|
255
|
+
port = sock.getsockname()[1]
|
|
256
|
+
sock.close()
|
|
257
|
+
return port
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def pick_a_port(args, webdiff_config):
|
|
261
|
+
if 'port' in args:
|
|
262
|
+
return args['port']
|
|
263
|
+
|
|
264
|
+
env_port = os.environ.get('WEBDIFF_PORT')
|
|
265
|
+
if env_port:
|
|
266
|
+
return int(env_port)
|
|
267
|
+
|
|
268
|
+
# gitconfig
|
|
269
|
+
if webdiff_config['port'] != -1:
|
|
270
|
+
return webdiff_config['port']
|
|
271
|
+
|
|
272
|
+
return random_port()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def run_http():
|
|
276
|
+
sys.stderr.write(
|
|
277
|
+
"""Serving diffs on http://%s:%s
|
|
278
|
+
Close the browser tab or hit Ctrl-C when you're done.
|
|
279
|
+
"""
|
|
280
|
+
% (HOSTNAME, PORT)
|
|
281
|
+
)
|
|
282
|
+
threading.Timer(0.1, open_browser).start()
|
|
283
|
+
|
|
284
|
+
web.run_app(app, host=HOSTNAME, port=PORT)
|
|
285
|
+
logging.debug('http server shut down')
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def maybe_shutdown():
|
|
289
|
+
"""Wait a second for new requests, then shut down the server."""
|
|
290
|
+
last_ms = LAST_REQUEST_MS
|
|
291
|
+
|
|
292
|
+
def shutdown():
|
|
293
|
+
if LAST_REQUEST_MS <= last_ms: # subsequent requests abort shutdown
|
|
294
|
+
sys.stderr.write('Shutting down...\n')
|
|
295
|
+
signal.raise_signal(signal.SIGINT)
|
|
296
|
+
else:
|
|
297
|
+
logging.debug('Received subsequent request; shutdown aborted.')
|
|
298
|
+
|
|
299
|
+
logging.debug(
|
|
300
|
+
'Received request to shut down; waiting 500ms for subsequent requests...'
|
|
301
|
+
)
|
|
302
|
+
threading.Timer(0.5, shutdown).start()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def run():
|
|
306
|
+
global DIFF, PORT, HOSTNAME, GIT_CONFIG
|
|
307
|
+
try:
|
|
308
|
+
parsed_args = argparser.parse(sys.argv[1:], VERSION)
|
|
309
|
+
except argparser.UsageError as e:
|
|
310
|
+
sys.stderr.write('Error: %s\n\n' % e)
|
|
311
|
+
usage_and_die()
|
|
312
|
+
|
|
313
|
+
GIT_CONFIG = options.get_config()
|
|
314
|
+
WEBDIFF_CONFIG = GIT_CONFIG['webdiff']
|
|
315
|
+
DIFF = argparser.diff_for_args(parsed_args, WEBDIFF_CONFIG)
|
|
316
|
+
|
|
317
|
+
if DEBUG:
|
|
318
|
+
sys.stderr.write('Invoked as: %s\n' % sys.argv)
|
|
319
|
+
sys.stderr.write('Args: %s\n' % parsed_args)
|
|
320
|
+
sys.stderr.write('Diff: %s\n' % DIFF)
|
|
321
|
+
sys.stderr.write('GitConfig: %s\n' % GIT_CONFIG)
|
|
322
|
+
|
|
323
|
+
PORT = pick_a_port(parsed_args, WEBDIFF_CONFIG)
|
|
324
|
+
HOSTNAME = (
|
|
325
|
+
parsed_args.get('host')
|
|
326
|
+
or os.environ.get('WEBDIFF_HOST')
|
|
327
|
+
or WEBDIFF_CONFIG['host']
|
|
328
|
+
)
|
|
329
|
+
if HOSTNAME == '<hostname>':
|
|
330
|
+
_hostname = platform.node()
|
|
331
|
+
# platform.node will return empty string if it can't find the hostname
|
|
332
|
+
if not _hostname:
|
|
333
|
+
sys.stderr.write('Warning: hostname could not be determined\n')
|
|
334
|
+
else:
|
|
335
|
+
HOSTNAME = _hostname
|
|
336
|
+
|
|
337
|
+
run_http()
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
if __name__ == '__main__':
|
|
341
|
+
run()
|
|
@@ -29,7 +29,13 @@ def get_thin_dict(diff):
|
|
|
29
29
|
- change type (add, delete, move, change)
|
|
30
30
|
- (in the future) diffstats
|
|
31
31
|
"""
|
|
32
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
'a': diff.a,
|
|
34
|
+
'b': diff.b,
|
|
35
|
+
'type': diff.type,
|
|
36
|
+
'num_add': diff.num_add,
|
|
37
|
+
'num_delete': diff.num_delete,
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
|
|
35
41
|
def fast_num_lines(path: str) -> int:
|
|
@@ -39,7 +45,9 @@ def fast_num_lines(path: str) -> int:
|
|
|
39
45
|
return int(subprocess.check_output(['grep', '-c', '', path]))
|
|
40
46
|
|
|
41
47
|
|
|
42
|
-
def get_diff_ops(
|
|
48
|
+
def get_diff_ops(
|
|
49
|
+
diff: LocalFileDiff, git_diff_args=None, normalize_json=False
|
|
50
|
+
) -> List[Code]:
|
|
43
51
|
"""Run git diff on the file pair and convert the results to a sequence of codes.
|
|
44
52
|
|
|
45
53
|
git_diff_args is passed directly to git diff. It can be something like ['-w'] or
|
|
@@ -48,6 +56,10 @@ def get_diff_ops(diff: LocalFileDiff, git_diff_args=None) -> List[Code]:
|
|
|
48
56
|
# git diff --no-index doesn't follow symlinks. So we help it a bit.
|
|
49
57
|
a_path = os.path.realpath(diff.a_path) if diff.a_path else ''
|
|
50
58
|
b_path = os.path.realpath(diff.b_path) if diff.b_path else ''
|
|
59
|
+
if normalize_json:
|
|
60
|
+
a_path = a_path and util.normalize_json(a_path)
|
|
61
|
+
b_path = b_path and util.normalize_json(b_path)
|
|
62
|
+
|
|
51
63
|
if a_path and b_path:
|
|
52
64
|
num_lines = fast_num_lines(b_path)
|
|
53
65
|
args = (
|