pyDiffTools 0.1.51__tar.gz → 0.1.52__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.
- {pydifftools-0.1.51/pyDiffTools.egg-info → pydifftools-0.1.52}/PKG-INFO +56 -4
- {pydifftools-0.1.51 → pydifftools-0.1.52}/README.rst +55 -3
- {pydifftools-0.1.51 → pydifftools-0.1.52/pyDiffTools.egg-info}/PKG-INFO +56 -4
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyDiffTools.egg-info/SOURCES.txt +4 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/command_line.py +3 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/source_jump.js +24 -9
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/watch_graph.py +2 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/wgrph_view.js +61 -27
- pydifftools-0.1.52/pydifftools/git_aliases.py +30 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/git_gd.py +19 -13
- pydifftools-0.1.52/pydifftools/pdf_diff.py +579 -0
- pydifftools-0.1.52/pydifftools/source_jump.py +203 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyproject.toml +1 -1
- pydifftools-0.1.52/tests/test_git_pdf_diff.py +424 -0
- pydifftools-0.1.52/tests/test_pdf_diff.py +495 -0
- pydifftools-0.1.52/tests/test_source_jump.py +424 -0
- pydifftools-0.1.51/pydifftools/source_jump.py +0 -128
- pydifftools-0.1.51/tests/test_source_jump.py +0 -85
- {pydifftools-0.1.51 → pydifftools-0.1.52}/LICENSE.md +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/MANIFEST.in +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/_quarto.yml +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/example.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/example.tex +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/index.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/independent.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/index.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/tasks.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/test_include.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/example_notebook/project1/test_include.qmd +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyDiffTools.egg-info/dependency_links.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyDiffTools.egg-info/entry_points.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyDiffTools.egg-info/requires.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pyDiffTools.egg-info/top_level.txt +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/__init__.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/bibliography.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/browser_lifecycle.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/check_numbers.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/citation_dialog.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/command_registry.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_filter_history.json +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_functions.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_migration.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_tags.lua +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_tags_margin.lua +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_tags_no_comments.lua +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comment_toggle.js +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comments.css +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/comments_author_colors.css +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/continuous.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/copy_files.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/diff-doc.js +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/doc_contents.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/__init__.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/comparison.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/dot_to_yaml.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/graph.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/flowchart/svg-pan-zoom.min.js +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/forward_search.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/git_gd_history.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/git_gd_image.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/git_gd_qt.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/html_comments.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/html_uncomments.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/line_alignment.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/log_example.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/match_spaces.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/notebook/__init__.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/notebook/display.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/notebook/fast_build.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/notebook/tex_to_qmd.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/onewordify.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/onewordify_undo.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/outline.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/rearrange_tex.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/searchacro.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/separate_comments.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/split_conflict.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/unseparate_comments.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/update_check.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/wrap_sentences.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/wrapping_options.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/xml2xlsx.vbs +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/pydifftools/zotero.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/setup.cfg +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_bibliography.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_browser_lifecycle.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_check_single_use.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_command_line_help.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_comment_filters.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_comment_migration.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_continuous_shutdown.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_forward_search.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_git_gd_history.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_git_gd_image.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_git_gd_qt.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_line_alignment.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_rrng.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_tex_to_qmd.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_update_check.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_wrap_check.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_wrapping.py +0 -0
- {pydifftools-0.1.51 → pydifftools-0.1.52}/tests/test_zotero.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyDiffTools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.52
|
|
4
4
|
Summary: Diff tools
|
|
5
5
|
Author: J M Franck
|
|
6
6
|
License: Copyright (c) 2015, jmfranck
|
|
@@ -185,9 +185,13 @@ included are (listed in order of fun/utility):
|
|
|
185
185
|
clearing the entire query shows the full plan, including completed and
|
|
186
186
|
undated tasks. Each embedded SVG and live refresh uses the same URL mode.
|
|
187
187
|
The search button beside Home and Zoom finds text in the visible graph,
|
|
188
|
-
highlights matches, and centers
|
|
189
|
-
advances to the next match; Shift+Tab or Shift+Enter goes
|
|
190
|
-
search. Phrases can span wrapped lines and formatted
|
|
188
|
+
highlights matches, and centers the containing task at your chosen zoom.
|
|
189
|
+
Tab or Enter advances to the next match; Shift+Tab or Shift+Enter goes
|
|
190
|
+
back. Esc closes search. Phrases can span wrapped lines and formatted
|
|
191
|
+
text. Resizing the window scales the current view with the viewport.
|
|
192
|
+
The ``1:1`` button displays fonts at their declared pixel sizes, including
|
|
193
|
+
correction for GraphViz's SVG scaling. Right-click a task and select
|
|
194
|
+
Jump to source to open its YAML definition in gvim.
|
|
191
195
|
Comparison uses the same repository-relative YAML path; a file absent
|
|
192
196
|
from the revision is treated as an empty plan. Historical file renames
|
|
193
197
|
are not followed, and comparison markup is never written to YAML.
|
|
@@ -227,6 +231,10 @@ included are (listed in order of fun/utility):
|
|
|
227
231
|
changes, an additional red/blue alpha-difference view is included.
|
|
228
232
|
Run ``pydifft gd --install`` to add the matching ``git gd`` alias to
|
|
229
233
|
your global git config.
|
|
234
|
+
``pydifft gd --add-to-git`` offers the same installation, retaining the
|
|
235
|
+
exact command of an existing global ``gd`` alias. If none is configured,
|
|
236
|
+
it installs ``!f() { pydifft gd "$@"; }; f``. Alias installation is shared
|
|
237
|
+
with ``pd`` and ``tree``.
|
|
230
238
|
- `pydifft qmdinit [directory]` scaffolds a new Quarto-style project using
|
|
231
239
|
the bundled templates and example ``project1`` hierarchy, then downloads
|
|
232
240
|
MathJax into ``_template/mathjax`` so the builder can run immediately.
|
|
@@ -262,6 +270,50 @@ included are (listed in order of fun/utility):
|
|
|
262
270
|
this rule. The option is shared by ``cpb``, ``wmatch``, ``wr`` and
|
|
263
271
|
``wrchk``; ``--punctuation-slop`` remains an alias.
|
|
264
272
|
|
|
273
|
+
- ``pydifft pd OLD NEW [--no-compile]`` ("PDF diff") compares two ``.md``
|
|
274
|
+
files or two ``.tex`` files. It writes ``NEW_diff.tex`` beside the newer
|
|
275
|
+
input and, by default, compiles ``NEW_diff.pdf`` there with ``pdflatex``.
|
|
276
|
+
For example, ``pydifft pd old.md paper.md`` produces ``paper_diff.tex``
|
|
277
|
+
and ``paper_diff.pdf``. ``--no-compile`` retains the diff TeX without
|
|
278
|
+
running a compiler and supports TeX fragments as well as full documents.
|
|
279
|
+
|
|
280
|
+
Both formats use ``mylatexdiff-preamble.sty`` and the existing custom
|
|
281
|
+
``latexdiff`` options and postprocessing. The preamble is searched for
|
|
282
|
+
in the current directory, beside NEW, then through ``kpsewhich``.
|
|
283
|
+
Tools must be available on the caller's PATH, including any TeX Live
|
|
284
|
+
directory configured in your shell. Markdown additionally requires
|
|
285
|
+
Pandoc and pandoc-crossref; conversion honors declared bibliography and
|
|
286
|
+
CSL metadata, renders citations and cross-references, and resolves local
|
|
287
|
+
images beside each source. Converted TeX intermediates are temporary;
|
|
288
|
+
the Markdown sources are unchanged.
|
|
289
|
+
|
|
290
|
+
Markdown code blocks retain Pandoc syntax highlighting. Changed lines
|
|
291
|
+
get a colored background and a ``+`` or ``-`` marker, with each line
|
|
292
|
+
processed independently so highlighted code remains valid TeX.
|
|
293
|
+
Prose keeps the custom preamble styling.
|
|
294
|
+
|
|
295
|
+
Python runs ``pdflatex`` beside NEW until references and the table of
|
|
296
|
+
contents settle (at most six passes), running ``bibtex`` or ``biber``
|
|
297
|
+
when required. ``latexmk`` and its configuration are not used.
|
|
298
|
+
Compilation artifacts and the diff TeX remain available if compilation
|
|
299
|
+
fails. Existing generated outputs can be replaced on a successful rerun.
|
|
300
|
+
The former ``git-latexdiffnocompile.sh`` is a compatibility wrapper for
|
|
301
|
+
``pydifft pd --no-compile``.
|
|
302
|
+
|
|
303
|
+
Run ``pydifft pd --add-to-git`` to install the global ``git pd`` alias.
|
|
304
|
+
It accepts Git diff arguments: ``git pd 00ffaa -- filename.md`` compares
|
|
305
|
+
that revision with the working file; bare ``git pd`` compares unstaged
|
|
306
|
+
changes; ``git pd --cached`` compares staged changes; and
|
|
307
|
+
``git pd OLD NEW -- filename.md`` compares two revisions. Ranges and
|
|
308
|
+
Git pathspecs work as usual, including from repository subdirectories.
|
|
309
|
+
Each changed Markdown or TeX file produces its own ``NAME_diff.tex`` and
|
|
310
|
+
PDF beside the document's working-tree path. ``git pd --no-compile``
|
|
311
|
+
generates only TeX. Other file types are skipped, and unchanged files
|
|
312
|
+
produce no output. Additions, deletions, and renames are supported;
|
|
313
|
+
unresolved merge conflicts must be resolved first. Historical document
|
|
314
|
+
snapshots use the current project's bibliography, images, and TeX
|
|
315
|
+
includes. The shared installer edits only the requested global Git alias.
|
|
316
|
+
|
|
265
317
|
- `pydifft wd` ("word diff"): generate "track changes" word files starting from pandoc markdown in a git history. Assuming that you have copied diff-doc.js (copied + licensed from elsewhere) into your home directory, this will use pandoc to convert the markdown files to MS Word, then use the MS Word comparison tool to generate a document where all relevant changes are shown with "track changes."
|
|
266
318
|
|
|
267
319
|
* by default, this uses the file `template.docx` in the current directory as a pandoc word template
|
|
@@ -130,9 +130,13 @@ included are (listed in order of fun/utility):
|
|
|
130
130
|
clearing the entire query shows the full plan, including completed and
|
|
131
131
|
undated tasks. Each embedded SVG and live refresh uses the same URL mode.
|
|
132
132
|
The search button beside Home and Zoom finds text in the visible graph,
|
|
133
|
-
highlights matches, and centers
|
|
134
|
-
advances to the next match; Shift+Tab or Shift+Enter goes
|
|
135
|
-
search. Phrases can span wrapped lines and formatted
|
|
133
|
+
highlights matches, and centers the containing task at your chosen zoom.
|
|
134
|
+
Tab or Enter advances to the next match; Shift+Tab or Shift+Enter goes
|
|
135
|
+
back. Esc closes search. Phrases can span wrapped lines and formatted
|
|
136
|
+
text. Resizing the window scales the current view with the viewport.
|
|
137
|
+
The ``1:1`` button displays fonts at their declared pixel sizes, including
|
|
138
|
+
correction for GraphViz's SVG scaling. Right-click a task and select
|
|
139
|
+
Jump to source to open its YAML definition in gvim.
|
|
136
140
|
Comparison uses the same repository-relative YAML path; a file absent
|
|
137
141
|
from the revision is treated as an empty plan. Historical file renames
|
|
138
142
|
are not followed, and comparison markup is never written to YAML.
|
|
@@ -172,6 +176,10 @@ included are (listed in order of fun/utility):
|
|
|
172
176
|
changes, an additional red/blue alpha-difference view is included.
|
|
173
177
|
Run ``pydifft gd --install`` to add the matching ``git gd`` alias to
|
|
174
178
|
your global git config.
|
|
179
|
+
``pydifft gd --add-to-git`` offers the same installation, retaining the
|
|
180
|
+
exact command of an existing global ``gd`` alias. If none is configured,
|
|
181
|
+
it installs ``!f() { pydifft gd "$@"; }; f``. Alias installation is shared
|
|
182
|
+
with ``pd`` and ``tree``.
|
|
175
183
|
- `pydifft qmdinit [directory]` scaffolds a new Quarto-style project using
|
|
176
184
|
the bundled templates and example ``project1`` hierarchy, then downloads
|
|
177
185
|
MathJax into ``_template/mathjax`` so the builder can run immediately.
|
|
@@ -207,6 +215,50 @@ included are (listed in order of fun/utility):
|
|
|
207
215
|
this rule. The option is shared by ``cpb``, ``wmatch``, ``wr`` and
|
|
208
216
|
``wrchk``; ``--punctuation-slop`` remains an alias.
|
|
209
217
|
|
|
218
|
+
- ``pydifft pd OLD NEW [--no-compile]`` ("PDF diff") compares two ``.md``
|
|
219
|
+
files or two ``.tex`` files. It writes ``NEW_diff.tex`` beside the newer
|
|
220
|
+
input and, by default, compiles ``NEW_diff.pdf`` there with ``pdflatex``.
|
|
221
|
+
For example, ``pydifft pd old.md paper.md`` produces ``paper_diff.tex``
|
|
222
|
+
and ``paper_diff.pdf``. ``--no-compile`` retains the diff TeX without
|
|
223
|
+
running a compiler and supports TeX fragments as well as full documents.
|
|
224
|
+
|
|
225
|
+
Both formats use ``mylatexdiff-preamble.sty`` and the existing custom
|
|
226
|
+
``latexdiff`` options and postprocessing. The preamble is searched for
|
|
227
|
+
in the current directory, beside NEW, then through ``kpsewhich``.
|
|
228
|
+
Tools must be available on the caller's PATH, including any TeX Live
|
|
229
|
+
directory configured in your shell. Markdown additionally requires
|
|
230
|
+
Pandoc and pandoc-crossref; conversion honors declared bibliography and
|
|
231
|
+
CSL metadata, renders citations and cross-references, and resolves local
|
|
232
|
+
images beside each source. Converted TeX intermediates are temporary;
|
|
233
|
+
the Markdown sources are unchanged.
|
|
234
|
+
|
|
235
|
+
Markdown code blocks retain Pandoc syntax highlighting. Changed lines
|
|
236
|
+
get a colored background and a ``+`` or ``-`` marker, with each line
|
|
237
|
+
processed independently so highlighted code remains valid TeX.
|
|
238
|
+
Prose keeps the custom preamble styling.
|
|
239
|
+
|
|
240
|
+
Python runs ``pdflatex`` beside NEW until references and the table of
|
|
241
|
+
contents settle (at most six passes), running ``bibtex`` or ``biber``
|
|
242
|
+
when required. ``latexmk`` and its configuration are not used.
|
|
243
|
+
Compilation artifacts and the diff TeX remain available if compilation
|
|
244
|
+
fails. Existing generated outputs can be replaced on a successful rerun.
|
|
245
|
+
The former ``git-latexdiffnocompile.sh`` is a compatibility wrapper for
|
|
246
|
+
``pydifft pd --no-compile``.
|
|
247
|
+
|
|
248
|
+
Run ``pydifft pd --add-to-git`` to install the global ``git pd`` alias.
|
|
249
|
+
It accepts Git diff arguments: ``git pd 00ffaa -- filename.md`` compares
|
|
250
|
+
that revision with the working file; bare ``git pd`` compares unstaged
|
|
251
|
+
changes; ``git pd --cached`` compares staged changes; and
|
|
252
|
+
``git pd OLD NEW -- filename.md`` compares two revisions. Ranges and
|
|
253
|
+
Git pathspecs work as usual, including from repository subdirectories.
|
|
254
|
+
Each changed Markdown or TeX file produces its own ``NAME_diff.tex`` and
|
|
255
|
+
PDF beside the document's working-tree path. ``git pd --no-compile``
|
|
256
|
+
generates only TeX. Other file types are skipped, and unchanged files
|
|
257
|
+
produce no output. Additions, deletions, and renames are supported;
|
|
258
|
+
unresolved merge conflicts must be resolved first. Historical document
|
|
259
|
+
snapshots use the current project's bibliography, images, and TeX
|
|
260
|
+
includes. The shared installer edits only the requested global Git alias.
|
|
261
|
+
|
|
210
262
|
- `pydifft wd` ("word diff"): generate "track changes" word files starting from pandoc markdown in a git history. Assuming that you have copied diff-doc.js (copied + licensed from elsewhere) into your home directory, this will use pandoc to convert the markdown files to MS Word, then use the MS Word comparison tool to generate a document where all relevant changes are shown with "track changes."
|
|
211
263
|
|
|
212
264
|
* by default, this uses the file `template.docx` in the current directory as a pandoc word template
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyDiffTools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.52
|
|
4
4
|
Summary: Diff tools
|
|
5
5
|
Author: J M Franck
|
|
6
6
|
License: Copyright (c) 2015, jmfranck
|
|
@@ -185,9 +185,13 @@ included are (listed in order of fun/utility):
|
|
|
185
185
|
clearing the entire query shows the full plan, including completed and
|
|
186
186
|
undated tasks. Each embedded SVG and live refresh uses the same URL mode.
|
|
187
187
|
The search button beside Home and Zoom finds text in the visible graph,
|
|
188
|
-
highlights matches, and centers
|
|
189
|
-
advances to the next match; Shift+Tab or Shift+Enter goes
|
|
190
|
-
search. Phrases can span wrapped lines and formatted
|
|
188
|
+
highlights matches, and centers the containing task at your chosen zoom.
|
|
189
|
+
Tab or Enter advances to the next match; Shift+Tab or Shift+Enter goes
|
|
190
|
+
back. Esc closes search. Phrases can span wrapped lines and formatted
|
|
191
|
+
text. Resizing the window scales the current view with the viewport.
|
|
192
|
+
The ``1:1`` button displays fonts at their declared pixel sizes, including
|
|
193
|
+
correction for GraphViz's SVG scaling. Right-click a task and select
|
|
194
|
+
Jump to source to open its YAML definition in gvim.
|
|
191
195
|
Comparison uses the same repository-relative YAML path; a file absent
|
|
192
196
|
from the revision is treated as an empty plan. Historical file renames
|
|
193
197
|
are not followed, and comparison markup is never written to YAML.
|
|
@@ -227,6 +231,10 @@ included are (listed in order of fun/utility):
|
|
|
227
231
|
changes, an additional red/blue alpha-difference view is included.
|
|
228
232
|
Run ``pydifft gd --install`` to add the matching ``git gd`` alias to
|
|
229
233
|
your global git config.
|
|
234
|
+
``pydifft gd --add-to-git`` offers the same installation, retaining the
|
|
235
|
+
exact command of an existing global ``gd`` alias. If none is configured,
|
|
236
|
+
it installs ``!f() { pydifft gd "$@"; }; f``. Alias installation is shared
|
|
237
|
+
with ``pd`` and ``tree``.
|
|
230
238
|
- `pydifft qmdinit [directory]` scaffolds a new Quarto-style project using
|
|
231
239
|
the bundled templates and example ``project1`` hierarchy, then downloads
|
|
232
240
|
MathJax into ``_template/mathjax`` so the builder can run immediately.
|
|
@@ -262,6 +270,50 @@ included are (listed in order of fun/utility):
|
|
|
262
270
|
this rule. The option is shared by ``cpb``, ``wmatch``, ``wr`` and
|
|
263
271
|
``wrchk``; ``--punctuation-slop`` remains an alias.
|
|
264
272
|
|
|
273
|
+
- ``pydifft pd OLD NEW [--no-compile]`` ("PDF diff") compares two ``.md``
|
|
274
|
+
files or two ``.tex`` files. It writes ``NEW_diff.tex`` beside the newer
|
|
275
|
+
input and, by default, compiles ``NEW_diff.pdf`` there with ``pdflatex``.
|
|
276
|
+
For example, ``pydifft pd old.md paper.md`` produces ``paper_diff.tex``
|
|
277
|
+
and ``paper_diff.pdf``. ``--no-compile`` retains the diff TeX without
|
|
278
|
+
running a compiler and supports TeX fragments as well as full documents.
|
|
279
|
+
|
|
280
|
+
Both formats use ``mylatexdiff-preamble.sty`` and the existing custom
|
|
281
|
+
``latexdiff`` options and postprocessing. The preamble is searched for
|
|
282
|
+
in the current directory, beside NEW, then through ``kpsewhich``.
|
|
283
|
+
Tools must be available on the caller's PATH, including any TeX Live
|
|
284
|
+
directory configured in your shell. Markdown additionally requires
|
|
285
|
+
Pandoc and pandoc-crossref; conversion honors declared bibliography and
|
|
286
|
+
CSL metadata, renders citations and cross-references, and resolves local
|
|
287
|
+
images beside each source. Converted TeX intermediates are temporary;
|
|
288
|
+
the Markdown sources are unchanged.
|
|
289
|
+
|
|
290
|
+
Markdown code blocks retain Pandoc syntax highlighting. Changed lines
|
|
291
|
+
get a colored background and a ``+`` or ``-`` marker, with each line
|
|
292
|
+
processed independently so highlighted code remains valid TeX.
|
|
293
|
+
Prose keeps the custom preamble styling.
|
|
294
|
+
|
|
295
|
+
Python runs ``pdflatex`` beside NEW until references and the table of
|
|
296
|
+
contents settle (at most six passes), running ``bibtex`` or ``biber``
|
|
297
|
+
when required. ``latexmk`` and its configuration are not used.
|
|
298
|
+
Compilation artifacts and the diff TeX remain available if compilation
|
|
299
|
+
fails. Existing generated outputs can be replaced on a successful rerun.
|
|
300
|
+
The former ``git-latexdiffnocompile.sh`` is a compatibility wrapper for
|
|
301
|
+
``pydifft pd --no-compile``.
|
|
302
|
+
|
|
303
|
+
Run ``pydifft pd --add-to-git`` to install the global ``git pd`` alias.
|
|
304
|
+
It accepts Git diff arguments: ``git pd 00ffaa -- filename.md`` compares
|
|
305
|
+
that revision with the working file; bare ``git pd`` compares unstaged
|
|
306
|
+
changes; ``git pd --cached`` compares staged changes; and
|
|
307
|
+
``git pd OLD NEW -- filename.md`` compares two revisions. Ranges and
|
|
308
|
+
Git pathspecs work as usual, including from repository subdirectories.
|
|
309
|
+
Each changed Markdown or TeX file produces its own ``NAME_diff.tex`` and
|
|
310
|
+
PDF beside the document's working-tree path. ``git pd --no-compile``
|
|
311
|
+
generates only TeX. Other file types are skipped, and unchanged files
|
|
312
|
+
produce no output. Additions, deletions, and renames are supported;
|
|
313
|
+
unresolved merge conflicts must be resolved first. Historical document
|
|
314
|
+
snapshots use the current project's bibliography, images, and TeX
|
|
315
|
+
includes. The shared installer edits only the requested global Git alias.
|
|
316
|
+
|
|
265
317
|
- `pydifft wd` ("word diff"): generate "track changes" word files starting from pandoc markdown in a git history. Assuming that you have copied diff-doc.js (copied + licensed from elsewhere) into your home directory, this will use pandoc to convert the markdown files to MS Word, then use the MS Word comparison tool to generate a document where all relevant changes are shown with "track changes."
|
|
266
318
|
|
|
267
319
|
* by default, this uses the file `template.docx` in the current directory as a pandoc word template
|
|
@@ -65,6 +65,7 @@ pydifftools/copy_files.py
|
|
|
65
65
|
pydifftools/diff-doc.js
|
|
66
66
|
pydifftools/doc_contents.py
|
|
67
67
|
pydifftools/forward_search.py
|
|
68
|
+
pydifftools/git_aliases.py
|
|
68
69
|
pydifftools/git_gd.py
|
|
69
70
|
pydifftools/git_gd_history.py
|
|
70
71
|
pydifftools/git_gd_image.py
|
|
@@ -77,6 +78,7 @@ pydifftools/match_spaces.py
|
|
|
77
78
|
pydifftools/onewordify.py
|
|
78
79
|
pydifftools/onewordify_undo.py
|
|
79
80
|
pydifftools/outline.py
|
|
81
|
+
pydifftools/pdf_diff.py
|
|
80
82
|
pydifftools/rearrange_tex.py
|
|
81
83
|
pydifftools/searchacro.py
|
|
82
84
|
pydifftools/separate_comments.py
|
|
@@ -111,7 +113,9 @@ tests/test_forward_search.py
|
|
|
111
113
|
tests/test_git_gd_history.py
|
|
112
114
|
tests/test_git_gd_image.py
|
|
113
115
|
tests/test_git_gd_qt.py
|
|
116
|
+
tests/test_git_pdf_diff.py
|
|
114
117
|
tests/test_line_alignment.py
|
|
118
|
+
tests/test_pdf_diff.py
|
|
115
119
|
tests/test_rrng.py
|
|
116
120
|
tests/test_source_jump.py
|
|
117
121
|
tests/test_tex_to_qmd.py
|
|
@@ -36,6 +36,7 @@ from .copy_files import copy_image_files
|
|
|
36
36
|
from .searchacro import replace_acros
|
|
37
37
|
from .rearrange_tex import run as rearrange_tex_run
|
|
38
38
|
from .git_gd import gd # registers git difftool review command
|
|
39
|
+
from .pdf_diff import pd, git_pd # registers PDF diff command
|
|
39
40
|
from .flowchart.watch_graph import wgrph
|
|
40
41
|
from .flowchart.graph import load_graph_yaml
|
|
41
42
|
from .notebook.tex_to_qmd import tex2qmd
|
|
@@ -933,6 +934,8 @@ def build_parser():
|
|
|
933
934
|
def main(argv=None):
|
|
934
935
|
if argv is None:
|
|
935
936
|
argv = sys.argv[1:]
|
|
937
|
+
if argv[:2] == ["pd", "--git"]:
|
|
938
|
+
return git_pd(argv[2:])
|
|
936
939
|
if argv and argv[0] in ("--ver", "--version"):
|
|
937
940
|
build_parser().parse_args(argv)
|
|
938
941
|
return
|
|
@@ -28,12 +28,21 @@
|
|
|
28
28
|
|
|
29
29
|
let phrase = '';
|
|
30
30
|
|
|
31
|
+
// Preserve HTML tag boundaries without adding spaces to words such as
|
|
32
|
+
// H<sub>2</sub>O. Python treats this reserved marker as a formatting gap.
|
|
33
|
+
function normalizeSourceText(text, trimEnd = true) {
|
|
34
|
+
const normalized = text.replace(/[\s\uE000]+/g, function (gap) {
|
|
35
|
+
return /\s/.test(gap) ? ' ' : '\uE000';
|
|
36
|
+
}).replace(/^[\s\uE000]+/, '');
|
|
37
|
+
return trimEnd ? normalized.replace(/[\s\uE000]+$/, '') : normalized;
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
function hideMenu() {
|
|
32
41
|
menu.style.display = 'none';
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
function showMenu(event, text) {
|
|
36
|
-
phrase = (text || '')
|
|
45
|
+
phrase = normalizeSourceText(text || '');
|
|
37
46
|
if (!phrase) {
|
|
38
47
|
hideMenu();
|
|
39
48
|
return;
|
|
@@ -71,8 +80,8 @@
|
|
|
71
80
|
const block = targetElement.closest(
|
|
72
81
|
'p,li,blockquote,h1,h2,h3,h4,h5,h6,dt,dd,td,th'
|
|
73
82
|
) || targetElement;
|
|
74
|
-
// Drop rendered math and
|
|
75
|
-
//
|
|
83
|
+
// Drop rendered math and figure-reference links, whose displayed text
|
|
84
|
+
// differs from their Markdown source.
|
|
76
85
|
let text = '';
|
|
77
86
|
let clickedOffset = 0;
|
|
78
87
|
let found = false;
|
|
@@ -93,11 +102,18 @@
|
|
|
93
102
|
text += ' ';
|
|
94
103
|
return;
|
|
95
104
|
}
|
|
105
|
+
const tagBoundary = node !== block && node.nodeType === Node.ELEMENT_NODE;
|
|
106
|
+
if (tagBoundary) {
|
|
107
|
+
text += '\uE000';
|
|
108
|
+
}
|
|
96
109
|
node.childNodes.forEach(collectText);
|
|
110
|
+
if (tagBoundary) {
|
|
111
|
+
text += '\uE000';
|
|
112
|
+
}
|
|
97
113
|
}
|
|
98
114
|
collectText(block);
|
|
99
115
|
const rawText = text;
|
|
100
|
-
text = rawText
|
|
116
|
+
text = normalizeSourceText(rawText);
|
|
101
117
|
if (!text) {
|
|
102
118
|
return '';
|
|
103
119
|
}
|
|
@@ -107,10 +123,9 @@
|
|
|
107
123
|
|
|
108
124
|
// The raw DOM offset is used to find sentence bounds before whitespace is
|
|
109
125
|
// collapsed. Recompute its visible offset after normalization.
|
|
110
|
-
const normalizedPrefix =
|
|
111
|
-
.slice(0, clickedOffset)
|
|
112
|
-
|
|
113
|
-
.replace(/^\s+/, '');
|
|
126
|
+
const normalizedPrefix = normalizeSourceText(
|
|
127
|
+
rawText.slice(0, clickedOffset), false
|
|
128
|
+
);
|
|
114
129
|
clickedOffset = normalizedPrefix.length;
|
|
115
130
|
const before = text.slice(0, clickedOffset);
|
|
116
131
|
const after = text.slice(clickedOffset);
|
|
@@ -119,7 +134,7 @@
|
|
|
119
134
|
before.lastIndexOf('!'),
|
|
120
135
|
before.lastIndexOf('?')
|
|
121
136
|
) + 1;
|
|
122
|
-
const endMatch = after.match(/[.!?](?:\s|$)/);
|
|
137
|
+
const endMatch = after.match(/[.!?]\uE000*(?:\s|$)/);
|
|
123
138
|
const end = endMatch ? clickedOffset + endMatch.index + 1 : text.length;
|
|
124
139
|
return text.slice(start, end).trim() || text;
|
|
125
140
|
}
|
|
@@ -239,6 +239,8 @@ def _watch_html(
|
|
|
239
239
|
"</button>"
|
|
240
240
|
"<button id='wgrph-zoom-in' title='Zoom in'>+</button>"
|
|
241
241
|
"<button id='wgrph-zoom-out' title='Zoom out'>−</button>"
|
|
242
|
+
"<button id='wgrph-actual-size' title='Actual size: native font size'"
|
|
243
|
+
" aria-label='Actual size'>1:1</button>"
|
|
242
244
|
"<button id='wgrph-search' title='Search graph (Ctrl+F)'"
|
|
243
245
|
" aria-label='Search graph' aria-expanded='false'"
|
|
244
246
|
" aria-controls='wgrph-search-panel'>🔍</button>"
|
|
@@ -43,19 +43,23 @@
|
|
|
43
43
|
matchIndex = (matchIndex + step + matches.length) % matches.length;
|
|
44
44
|
searchStatus.textContent = `${matchIndex + 1} of ${matches.length}`;
|
|
45
45
|
const sizes = panZoom.getSizes();
|
|
46
|
-
const
|
|
46
|
+
const viewport = svgDoc.querySelector('.svg-pan-zoom_viewport');
|
|
47
|
+
// DOM ranges reflect the last rendered frame, which can lag behind the
|
|
48
|
+
// pan/zoom state while typing. Use that frame's matrix for highlights.
|
|
49
|
+
const matrix = viewport.getScreenCTM();
|
|
47
50
|
const highlight = svgDoc.createElementNS(
|
|
48
51
|
'http://www.w3.org/2000/svg', 'g'
|
|
49
52
|
);
|
|
50
53
|
highlight.id = 'wgrph-search-highlight';
|
|
51
54
|
highlight.setAttribute('pointer-events', 'none');
|
|
52
|
-
const
|
|
55
|
+
const match = matches[matchIndex];
|
|
56
|
+
match.ranges.forEach(function (range) {
|
|
53
57
|
const bounds = range.getBoundingClientRect();
|
|
54
58
|
const box = {
|
|
55
|
-
x: (bounds.x -
|
|
56
|
-
y: (bounds.y -
|
|
57
|
-
width: bounds.width /
|
|
58
|
-
height: bounds.height /
|
|
59
|
+
x: (bounds.x - matrix.e) / matrix.a,
|
|
60
|
+
y: (bounds.y - matrix.f) / matrix.d,
|
|
61
|
+
width: bounds.width / matrix.a,
|
|
62
|
+
height: bounds.height / matrix.d,
|
|
59
63
|
};
|
|
60
64
|
const rect = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
61
65
|
for (const key of ['x', 'y', 'width', 'height']) {
|
|
@@ -66,25 +70,20 @@
|
|
|
66
70
|
rect.setAttribute('stroke', '#b77900');
|
|
67
71
|
rect.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
68
72
|
highlight.appendChild(rect);
|
|
69
|
-
return box;
|
|
70
73
|
});
|
|
71
|
-
|
|
74
|
+
viewport.appendChild(highlight);
|
|
72
75
|
if (focus) {
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
const zoom = panZoom.getZoom() * 18 / fontSize / sizes.realZoom;
|
|
82
|
-
panZoom.setMaxZoom(Math.max(50, zoom));
|
|
83
|
-
panZoom.zoom(zoom);
|
|
84
|
-
const realZoom = panZoom.getSizes().realZoom;
|
|
76
|
+
// Center the containing task while keeping the user's chosen scale.
|
|
77
|
+
const bounds = match.node.getBBox();
|
|
78
|
+
const point = svgDoc.documentElement.createSVGPoint();
|
|
79
|
+
point.x = bounds.x + bounds.width / 2;
|
|
80
|
+
point.y = bounds.y + bounds.height / 2;
|
|
81
|
+
const center = point.matrixTransform(
|
|
82
|
+
matrix.inverse().multiply(match.node.getScreenCTM())
|
|
83
|
+
);
|
|
85
84
|
panZoom.pan({
|
|
86
|
-
x: sizes.width / 2 -
|
|
87
|
-
y: sizes.height / 2 -
|
|
85
|
+
x: sizes.width / 2 - center.x * sizes.realZoom,
|
|
86
|
+
y: sizes.height / 2 - center.y * sizes.realZoom,
|
|
88
87
|
});
|
|
89
88
|
}
|
|
90
89
|
}
|
|
@@ -115,7 +114,7 @@
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
if (ranges.length) {
|
|
118
|
-
matches.push(ranges);
|
|
117
|
+
matches.push({ ranges, node: entry.node });
|
|
119
118
|
}
|
|
120
119
|
start = entry.text.indexOf(query, start + query.length);
|
|
121
120
|
}
|
|
@@ -232,9 +231,11 @@
|
|
|
232
231
|
home();
|
|
233
232
|
} else {
|
|
234
233
|
const restore = savedView;
|
|
235
|
-
panZoom.
|
|
236
|
-
|
|
237
|
-
);
|
|
234
|
+
const zoom = (restore.realZoom / panZoom.getSizes().realZoom) *
|
|
235
|
+
panZoom.getZoom();
|
|
236
|
+
panZoom.setMinZoom(Math.min(0.1, zoom));
|
|
237
|
+
panZoom.setMaxZoom(Math.max(50, zoom));
|
|
238
|
+
panZoom.zoom(zoom);
|
|
238
239
|
panZoom.pan(restore.pan);
|
|
239
240
|
}
|
|
240
241
|
saveView();
|
|
@@ -264,7 +265,7 @@
|
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
|
-
return { text, positions };
|
|
268
|
+
return { text, positions, node };
|
|
268
269
|
});
|
|
269
270
|
// }}}
|
|
270
271
|
search(false);
|
|
@@ -390,11 +391,44 @@
|
|
|
390
391
|
init();
|
|
391
392
|
window.addEventListener('resize', function () {
|
|
392
393
|
if (panZoom !== null) {
|
|
394
|
+
// Keep the same magnification relative to the whole-graph fit.
|
|
395
|
+
const before = panZoom.getSizes();
|
|
396
|
+
const pan = panZoom.getPan();
|
|
397
|
+
const zoom = panZoom.getZoom();
|
|
398
|
+
const atHome = Math.abs(zoom - 1) < 1e-6 &&
|
|
399
|
+
Math.abs(pan.x + before.viewBox.x * before.realZoom -
|
|
400
|
+
(before.width - before.viewBox.width * before.realZoom) / 2) < 0.5 &&
|
|
401
|
+
Math.abs(pan.y + before.viewBox.y * before.realZoom) < 0.5;
|
|
393
402
|
panZoom.resize();
|
|
403
|
+
if (atHome) {
|
|
404
|
+
home();
|
|
405
|
+
} else {
|
|
406
|
+
panZoom.zoom(zoom);
|
|
407
|
+
const after = panZoom.getSizes();
|
|
408
|
+
const factor = after.realZoom / before.realZoom;
|
|
409
|
+
panZoom.pan({
|
|
410
|
+
x: after.width / 2 + (pan.x - before.width / 2) * factor,
|
|
411
|
+
y: after.height / 2 + (pan.y - before.height / 2) * factor,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
saveView();
|
|
394
415
|
}
|
|
395
416
|
});
|
|
396
417
|
document.addEventListener('keydown', handleKeys);
|
|
397
418
|
document.getElementById('wgrph-home').addEventListener('click', home);
|
|
419
|
+
document.getElementById('wgrph-actual-size').addEventListener('click', function () {
|
|
420
|
+
// Graphviz may scale its graph group before viewer zoom is applied.
|
|
421
|
+
const viewport = initializedDoc.querySelector('.svg-pan-zoom_viewport');
|
|
422
|
+
const text = initializedDoc.querySelector('g.node text');
|
|
423
|
+
const matrix = text ? viewport.getScreenCTM().inverse().multiply(
|
|
424
|
+
text.getScreenCTM()
|
|
425
|
+
) : { a: 1, b: 0 };
|
|
426
|
+
const zoom = panZoom.getZoom() / panZoom.getSizes().realZoom /
|
|
427
|
+
Math.hypot(matrix.a, matrix.b);
|
|
428
|
+
panZoom.setMinZoom(Math.min(0.1, zoom));
|
|
429
|
+
panZoom.setMaxZoom(Math.max(50, zoom));
|
|
430
|
+
panZoom.zoom(zoom);
|
|
431
|
+
});
|
|
398
432
|
document
|
|
399
433
|
.getElementById('wgrph-zoom-in')
|
|
400
434
|
.addEventListener('click', function () {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Shared installation of the package's global Git aliases."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def install_git_alias(name, command, preserve_existing=False):
|
|
7
|
+
"""Install an alias, optionally retaining its exact configured command."""
|
|
8
|
+
if preserve_existing:
|
|
9
|
+
current = subprocess.run(
|
|
10
|
+
["git", "config", "--global", "--null", "--get", f"alias.{name}"],
|
|
11
|
+
capture_output=True,
|
|
12
|
+
text=True,
|
|
13
|
+
)
|
|
14
|
+
if current.returncode == 0 and current.stdout:
|
|
15
|
+
# Remove only Git's terminator, preserving command whitespace.
|
|
16
|
+
command = current.stdout[:-1]
|
|
17
|
+
elif current.returncode not in (0, 1):
|
|
18
|
+
raise SystemExit(
|
|
19
|
+
f"Cannot read global alias.{name}: {current.stderr.strip()}"
|
|
20
|
+
)
|
|
21
|
+
try:
|
|
22
|
+
subprocess.run(
|
|
23
|
+
["git", "config", "--global", f"alias.{name}", command],
|
|
24
|
+
check=True,
|
|
25
|
+
)
|
|
26
|
+
except (OSError, subprocess.CalledProcessError) as exc:
|
|
27
|
+
raise SystemExit(
|
|
28
|
+
f"Cannot install global alias.{name}: {exc}"
|
|
29
|
+
) from None
|
|
30
|
+
print(f"Installed global git alias: alias.{name} -> {command}")
|
|
@@ -24,6 +24,7 @@ from pathlib import Path
|
|
|
24
24
|
from typing import Sequence
|
|
25
25
|
|
|
26
26
|
from .command_registry import register_command
|
|
27
|
+
from .git_aliases import install_git_alias
|
|
27
28
|
|
|
28
29
|
INSTALL_ALIAS_VALUE = '!f() { pydifft gd "$@"; }; f'
|
|
29
30
|
DIFFTOOL_NAME = "mygvim"
|
|
@@ -186,6 +187,7 @@ def changed_paths(
|
|
|
186
187
|
return [entry.path for entry in changed_entries(diff_args, pathspec)]
|
|
187
188
|
|
|
188
189
|
|
|
190
|
+
# also used by: git_gd_qt.py and tests/test_git_gd_history.py.
|
|
189
191
|
def numstat_for_paths(diff_args: Sequence[str], paths: Sequence[str]):
|
|
190
192
|
cmd = [
|
|
191
193
|
"diff",
|
|
@@ -435,7 +437,7 @@ def main(argv: Sequence[str]) -> int:
|
|
|
435
437
|
"With no arguments, review unstaged changes, like git diff.\n"
|
|
436
438
|
"Use pydifft tree to browse Git history.\n\n"
|
|
437
439
|
"Install the matching git alias automatically with:\n"
|
|
438
|
-
" pydifft gd --install\n\n"
|
|
440
|
+
" pydifft gd --add-to-git (or --install)\n\n"
|
|
439
441
|
"or add it yourself with:\n"
|
|
440
442
|
" git config --global alias.gd '!f() { pydifft gd \"$@\"; }; f'\n\n"
|
|
441
443
|
"This command shells out to git difftool --tool=mygvim, so keep\n"
|
|
@@ -446,19 +448,26 @@ def main(argv: Sequence[str]) -> int:
|
|
|
446
448
|
"runs this subcommand."
|
|
447
449
|
),
|
|
448
450
|
},
|
|
451
|
+
argument_options={
|
|
452
|
+
"add_to_git": {
|
|
453
|
+
"help": (
|
|
454
|
+
"Install the global git gd alias, preserving its current "
|
|
455
|
+
"command."
|
|
456
|
+
),
|
|
457
|
+
},
|
|
458
|
+
},
|
|
449
459
|
)
|
|
450
|
-
def gd(arguments, install=False):
|
|
460
|
+
def gd(arguments, install=False, add_to_git=False):
|
|
451
461
|
"""Mirror ``git_gd_qt.py`` and optionally install ``git gd``."""
|
|
452
462
|
|
|
453
|
-
if install:
|
|
463
|
+
if install or add_to_git:
|
|
454
464
|
if arguments:
|
|
455
|
-
|
|
465
|
+
flag = "--add-to-git" if add_to_git else "--install"
|
|
466
|
+
raise SystemExit(f"pydifft gd {flag} does not take diff args")
|
|
456
467
|
# {{{ install the Git alias and check difftool configuration
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
check=True,
|
|
468
|
+
install_git_alias(
|
|
469
|
+
"gd", INSTALL_ALIAS_VALUE, preserve_existing=True,
|
|
460
470
|
)
|
|
461
|
-
print("Installed global git alias: alias.gd -> pydifft gd")
|
|
462
471
|
tool_cmd = subprocess.run(
|
|
463
472
|
["git", "config", "--global", "--get", "difftool.mygvim.cmd"],
|
|
464
473
|
capture_output=True,
|
|
@@ -490,12 +499,9 @@ def tree(install=False):
|
|
|
490
499
|
"""Open the history browser or install its Git alias."""
|
|
491
500
|
try:
|
|
492
501
|
if install:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
'!f() { pydifft tree "$@"; }; f'],
|
|
496
|
-
check=True,
|
|
502
|
+
install_git_alias(
|
|
503
|
+
"tree", '!f() { pydifft tree "$@"; }; f',
|
|
497
504
|
)
|
|
498
|
-
print("Installed global git alias: alias.tree -> pydifft tree")
|
|
499
505
|
return
|
|
500
506
|
from PySide6.QtWidgets import QApplication
|
|
501
507
|
from .git_gd_history import HistoryWindow, load_history
|