pyDiffTools 0.1.22__tar.gz → 0.1.24__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.
Files changed (87) hide show
  1. {pydifftools-0.1.22/pyDiffTools.egg-info → pydifftools-0.1.24}/PKG-INFO +1 -1
  2. {pydifftools-0.1.22 → pydifftools-0.1.24/pyDiffTools.egg-info}/PKG-INFO +1 -1
  3. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/command_line.py +41 -1
  4. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/comment_toggle.js +19 -3
  5. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/comments.css +38 -11
  6. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/continuous.py +76 -14
  7. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/flowchart/watch_graph.py +51 -12
  8. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyproject.toml +1 -1
  9. {pydifftools-0.1.22 → pydifftools-0.1.24}/tests/test_continuous_shutdown.py +2 -1
  10. {pydifftools-0.1.22 → pydifftools-0.1.24}/LICENSE.md +0 -0
  11. {pydifftools-0.1.22 → pydifftools-0.1.24}/MANIFEST.in +0 -0
  12. {pydifftools-0.1.22 → pydifftools-0.1.24}/README.rst +0 -0
  13. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/_quarto.yml +0 -0
  14. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
  15. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
  16. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
  17. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
  18. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
  19. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
  20. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
  21. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
  22. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
  23. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
  24. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
  25. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
  26. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
  27. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
  28. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
  29. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
  30. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
  31. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
  32. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
  33. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
  34. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
  35. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/.jupyter_cache/global.db +0 -0
  36. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/example.qmd +0 -0
  37. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/example.tex +0 -0
  38. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/index.qmd +0 -0
  39. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
  40. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
  41. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
  42. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
  43. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/independent.qmd +0 -0
  44. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/index.qmd +0 -0
  45. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/tasks.qmd +0 -0
  46. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/test_include.qmd +0 -0
  47. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
  48. {pydifftools-0.1.22 → pydifftools-0.1.24}/example_notebook/project1/test_include.qmd +0 -0
  49. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyDiffTools.egg-info/SOURCES.txt +0 -0
  50. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyDiffTools.egg-info/dependency_links.txt +0 -0
  51. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyDiffTools.egg-info/entry_points.txt +0 -0
  52. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyDiffTools.egg-info/requires.txt +0 -0
  53. {pydifftools-0.1.22 → pydifftools-0.1.24}/pyDiffTools.egg-info/top_level.txt +0 -0
  54. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/__init__.py +0 -0
  55. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/browser_lifecycle.py +0 -0
  56. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/check_numbers.py +0 -0
  57. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/command_registry.py +0 -0
  58. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/comment_functions.py +0 -0
  59. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/comment_tags.lua +0 -0
  60. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/copy_files.py +0 -0
  61. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/diff-doc.js +0 -0
  62. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/doc_contents.py +0 -0
  63. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/flowchart/__init__.py +0 -0
  64. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/flowchart/dot_to_yaml.py +0 -0
  65. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/flowchart/graph.py +0 -0
  66. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/html_comments.py +0 -0
  67. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/html_uncomments.py +0 -0
  68. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/match_spaces.py +0 -0
  69. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/notebook/__init__.py +0 -0
  70. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/notebook/fast_build.py +0 -0
  71. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/notebook/tex_to_qmd.py +0 -0
  72. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/onewordify.py +0 -0
  73. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/onewordify_undo.py +0 -0
  74. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/outline.py +0 -0
  75. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/rearrange_tex.py +0 -0
  76. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/searchacro.py +0 -0
  77. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/separate_comments.py +0 -0
  78. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/split_conflict.py +0 -0
  79. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/unseparate_comments.py +0 -0
  80. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/update_check.py +0 -0
  81. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/wrap_sentences.py +0 -0
  82. {pydifftools-0.1.22 → pydifftools-0.1.24}/pydifftools/xml2xlsx.vbs +0 -0
  83. {pydifftools-0.1.22 → pydifftools-0.1.24}/setup.cfg +0 -0
  84. {pydifftools-0.1.22 → pydifftools-0.1.24}/tests/test_browser_lifecycle.py +0 -0
  85. {pydifftools-0.1.22 → pydifftools-0.1.24}/tests/test_rrng.py +0 -0
  86. {pydifftools-0.1.22 → pydifftools-0.1.24}/tests/test_tex_to_qmd.py +0 -0
  87. {pydifftools-0.1.22 → pydifftools-0.1.24}/tests/test_update_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyDiffTools
3
- Version: 0.1.22
3
+ Version: 0.1.24
4
4
  Summary: Diff tools
5
5
  Author: J M Franck
6
6
  License: Copyright (c) 2015, jmfranck
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyDiffTools
3
- Version: 0.1.22
3
+ Version: 0.1.24
4
4
  Summary: Diff tools
5
5
  Author: J M Franck
6
6
  License: Copyright (c) 2015, jmfranck
@@ -796,8 +796,35 @@ def pmd(arguments):
796
796
  p1.wait()
797
797
 
798
798
 
799
+ def _subcommand_help_hint(prog):
800
+ return (
801
+ f"*** Run '{prog} --help <subcommand>' to learn about "
802
+ "subcommand options. ***"
803
+ )
804
+
805
+
806
+ class PyDiffArgumentParser(argparse.ArgumentParser):
807
+ """ArgumentParser with a clearer root-level missing-subcommand hint."""
808
+
809
+ def __init__(self, *args, is_root_parser=False, **kwargs):
810
+ super().__init__(*args, **kwargs)
811
+ self._pydifft_is_root_parser = is_root_parser
812
+
813
+ def error(self, message):
814
+ if self._pydifft_is_root_parser:
815
+ self.print_usage(sys.stderr)
816
+ hint = _subcommand_help_hint(self.prog)
817
+ self.exit(2, f"{self.prog}: error: {message}\n{hint}\n")
818
+ super().error(message)
819
+
820
+
799
821
  def build_parser():
800
- parser = argparse.ArgumentParser()
822
+ parser = PyDiffArgumentParser(
823
+ formatter_class=argparse.RawDescriptionHelpFormatter,
824
+ is_root_parser=True,
825
+ )
826
+ parser.epilog = _subcommand_help_hint(parser.prog)
827
+ parser._pydifft_subparsers = {}
801
828
  subparsers = parser.add_subparsers(dest="command")
802
829
  subparsers.required = True
803
830
  for name, spec in _COMMAND_SPECS.items():
@@ -823,10 +850,18 @@ def build_parser():
823
850
  action.completer = FilesCompleter(
824
851
  allowednames=["*.yaml", "*.yml"]
825
852
  )
853
+ if (
854
+ FilesCompleter is not None
855
+ and name == "cpb"
856
+ and action.dest == "filename"
857
+ ):
858
+ # Provide Markdown-only completions for continuous pandoc build.
859
+ action.completer = FilesCompleter(allowednames=["*.md"])
826
860
  if name == "wgrph" and action.dest == "t":
827
861
  # Offer case-insensitive completions for incomplete task names.
828
862
  action.completer = wgrph_task_completer
829
863
  subparser.set_defaults(_handler=spec["handler"])
864
+ parser._pydifft_subparsers[name] = subparser
830
865
  return parser
831
866
 
832
867
 
@@ -859,6 +894,11 @@ def main(argv=None):
859
894
  if not argv:
860
895
  parser.print_help()
861
896
  return
897
+ if argv[0] in ("-h", "--help") and len(argv) > 1:
898
+ subcommand = argv[1]
899
+ if subcommand in parser._pydifft_subparsers:
900
+ parser._pydifft_subparsers[subcommand].print_help()
901
+ return
862
902
  namespace = parser.parse_args(argv)
863
903
  handler = namespace._handler
864
904
  handler_kwargs = dict(vars(namespace))
@@ -3,9 +3,9 @@
3
3
  const SELECTOR_BUBBLE =
4
4
  "div.comment-left, div.comment-right, " +
5
5
  "span.comment-pin > span.comment-left, span.comment-pin > span.comment-right, " +
6
- "div.comment-overlay.comment-left, div.comment-overlay.comment-right";
6
+ ".comment-overlay.comment-left, .comment-overlay.comment-right";
7
7
 
8
- const SELECTOR_OVERLAY = "div.comment-overlay[data-comment-id]";
8
+ const SELECTOR_OVERLAY = ".comment-overlay[data-comment-id]";
9
9
  const SELECTOR_ANCHOR = 'span.comment-pin-block[data-comment-id]';
10
10
  const SELECTOR_INLINE =
11
11
  "span.comment-pin > span.comment-right, span.comment-pin > span.comment-left";
@@ -34,6 +34,7 @@
34
34
  }
35
35
 
36
36
  function positionComments() {
37
+ const useMobileFlow = window.matchMedia("(max-width: 900px)").matches;
37
38
  // Inline comment bubbles are absolutely positioned relative to a zero-width
38
39
  // pin. We nudge overlapping bubbles so adjacent <comment> tags visibly
39
40
  // separate, and we raise the bubbles so the pointer aims at the source point.
@@ -97,6 +98,15 @@
97
98
  );
98
99
  if (!anchor) return;
99
100
 
101
+ if (useMobileFlow && ov.classList.contains("comment-margin-left")) {
102
+ // Let CSS place margin-mode comments in-flow in narrow layouts.
103
+ ov.style.position = "";
104
+ ov.style.visibility = "";
105
+ ov.style.left = "";
106
+ ov.style.top = "";
107
+ return;
108
+ }
109
+
100
110
  // Ensure measurable
101
111
  ov.style.position = "absolute";
102
112
  ov.style.visibility = "hidden";
@@ -115,9 +125,15 @@
115
125
  // Anchor point in document coords
116
126
  const ax = a.left + window.scrollX;
117
127
  const ay = a.top + window.scrollY;
128
+ const bodyRect = document.body.getBoundingClientRect();
129
+ const bodyLeft = bodyRect.left + window.scrollX;
118
130
 
119
131
  let left;
120
- if (ov.classList.contains("comment-left")) {
132
+ if (ov.classList.contains("comment-margin-left")) {
133
+ // Margin-mode comments should live entirely in the left margin like
134
+ // floated .comment-left bubbles, not just to the left of the anchor.
135
+ left = bodyLeft - gap - ow;
136
+ } else if (ov.classList.contains("comment-left")) {
121
137
  left = ax - gap - ow;
122
138
  } else {
123
139
  // default right; add configurable shift so the arrow points back to
@@ -189,6 +189,8 @@ div.csl-indent {
189
189
  /* Shared visual style */
190
190
  div.comment-right,
191
191
  div.comment-left,
192
+ .comment-overlay.comment-right,
193
+ .comment-overlay.comment-left,
192
194
  span.comment-pin > span.comment-right,
193
195
  span.comment-pin > span.comment-left {
194
196
  box-sizing: border-box;
@@ -326,14 +328,29 @@ span.comment-pin > span.comment-left::after {
326
328
  height: auto;
327
329
  }
328
330
 
331
+ .comment-overlay.comment-margin-left {
332
+ position: static !important;
333
+ float: none !important;
334
+ clear: none !important;
335
+ width: 100% !important;
336
+ max-width: 100% !important;
337
+ display: block !important;
338
+ box-sizing: border-box;
339
+ margin: 0.4em 0 !important;
340
+ }
341
+
342
+ .comment-inline-break-marker {
343
+ display: inline !important;
344
+ }
345
+
329
346
  span.comment-pin > span.comment-right::before,
330
347
  span.comment-pin > span.comment-right::after,
331
348
  span.comment-pin > span.comment-left::before,
332
349
  span.comment-pin > span.comment-left::after,
333
- div.comment-overlay.comment-right::before,
334
- div.comment-overlay.comment-right::after,
335
- div.comment-overlay.comment-left::before,
336
- div.comment-overlay.comment-left::after {
350
+ .comment-overlay.comment-right::before,
351
+ .comment-overlay.comment-right::after,
352
+ .comment-overlay.comment-left::before,
353
+ .comment-overlay.comment-left::after {
337
354
  display: none;
338
355
  }
339
356
  }
@@ -342,7 +359,7 @@ span.comment-pin > span.comment-left::after {
342
359
  }
343
360
  /* --- Overlay block comments emitted by Lua (contain ul/paras/etc) --- */
344
361
 
345
- div.comment-overlay {
362
+ .comment-overlay {
346
363
  position: absolute;
347
364
  z-index: 100; /* above text */
348
365
  margin: 0 !important; /* do not participate in normal layout */
@@ -351,13 +368,23 @@ div.comment-overlay {
351
368
  }
352
369
 
353
370
  /* Ensure overlay uses the same visual bubble styling */
354
- div.comment-overlay.comment-right,
355
- div.comment-overlay.comment-left {
371
+ .comment-overlay.comment-right,
372
+ .comment-overlay.comment-left {
356
373
  width: 22rem; /* match your current bubble width */
357
374
  }
358
375
 
376
+ /* Margin-mode inline comments should use the narrower left-margin bubble size. */
377
+ .comment-overlay.comment-inline-margin,
378
+ .comment-overlay.comment-margin-left {
379
+ width: 11rem;
380
+ }
381
+
382
+ .comment-inline-break-marker {
383
+ display: none;
384
+ }
385
+
359
386
  /* Pointer triangles for overlay block comments */
360
- div.comment-overlay.comment-right::before {
387
+ .comment-overlay.comment-right::before {
361
388
  content: "";
362
389
  position: absolute;
363
390
  left: calc(-1 * var(--comment-left-arrow-width));
@@ -366,7 +393,7 @@ div.comment-overlay.comment-right::before {
366
393
  border-style: solid;
367
394
  border-color: transparent #5aa0ff transparent transparent;
368
395
  }
369
- div.comment-overlay.comment-right::after {
396
+ .comment-overlay.comment-right::after {
370
397
  content: "";
371
398
  position: absolute;
372
399
  left: calc(-1 * var(--comment-left-arrow-width) + 1px);
@@ -375,7 +402,7 @@ div.comment-overlay.comment-right::after {
375
402
  border-style: solid;
376
403
  border-color: transparent #eaf3ff transparent transparent;
377
404
  }
378
- div.comment-overlay.comment-left::before {
405
+ .comment-overlay.comment-left::before {
379
406
  content: "";
380
407
  position: absolute;
381
408
  right: calc(-1 * var(--comment-arrow-width));
@@ -384,7 +411,7 @@ div.comment-overlay.comment-left::before {
384
411
  border-style: solid;
385
412
  border-color: transparent transparent transparent #5aa0ff;
386
413
  }
387
- div.comment-overlay.comment-left::after {
414
+ .comment-overlay.comment-left::after {
388
415
  content: "";
389
416
  position: absolute;
390
417
  right: calc(-1 * var(--comment-arrow-width) + 1px);
@@ -16,6 +16,7 @@ from .browser_lifecycle import browser_window_is_alive, close_browser_window
16
16
 
17
17
  FORWARD_SEARCH_HOST = "127.0.0.1"
18
18
  FORWARD_SEARCH_PORT = 51235
19
+ MARGIN_COMMENTS_FILTER_MARKER = "-- PYDIFFTOOLS_SPECIAL_MARGIN_COMMENTS_FILTER"
19
20
 
20
21
 
21
22
  def forward_search_listener(stop_event, search_queue):
@@ -44,7 +45,54 @@ def forward_search_listener(stop_event, search_queue):
44
45
  server.close()
45
46
 
46
47
 
47
- def run_pandoc(filename, html_file):
48
+ def _file_contains_text(path, text):
49
+ if not os.path.exists(path):
50
+ return False
51
+ with open(path, encoding="utf-8") as fp:
52
+ return text in fp.read()
53
+
54
+
55
+ def _is_margin_comments_filter(path):
56
+ return _file_contains_text(path, MARGIN_COMMENTS_FILTER_MARKER)
57
+
58
+
59
+ def _set_comment_filter_mode(source_dir, comments_to_margin):
60
+ package_dir = os.path.dirname(os.path.abspath(__file__))
61
+ active_filter = os.path.join(source_dir, "comment_tags.lua")
62
+ inactive_filter = os.path.join(source_dir, "comment_tags.lua.inactive")
63
+ packaged_default = os.path.join(package_dir, "comment_tags.lua")
64
+ packaged_margin = os.path.join(package_dir, "comment_tags_margin.lua")
65
+
66
+ active_is_margin = _is_margin_comments_filter(active_filter)
67
+ inactive_is_margin = _is_margin_comments_filter(inactive_filter)
68
+
69
+ if comments_to_margin:
70
+ if active_is_margin:
71
+ return
72
+ if os.path.exists(active_filter):
73
+ os.replace(active_filter, inactive_filter)
74
+ shutil.copy2(packaged_margin, active_filter)
75
+ return
76
+
77
+ if active_is_margin:
78
+ if os.path.exists(inactive_filter) and not inactive_is_margin:
79
+ temp_filter = active_filter + ".swap_tmp"
80
+ os.replace(active_filter, temp_filter)
81
+ os.replace(inactive_filter, active_filter)
82
+ os.replace(temp_filter, inactive_filter)
83
+ else:
84
+ os.replace(active_filter, inactive_filter)
85
+ shutil.copy2(packaged_default, active_filter)
86
+ return
87
+
88
+ if not os.path.exists(active_filter):
89
+ if os.path.exists(inactive_filter) and not inactive_is_margin:
90
+ os.replace(inactive_filter, active_filter)
91
+ else:
92
+ shutil.copy2(packaged_default, active_filter)
93
+
94
+
95
+ def run_pandoc(filename, html_file, comments_to_margin=False):
48
96
  # Pandoc and pandoc-crossref must be installed for HTML rendering.
49
97
  if shutil.which("pandoc") is None:
50
98
  raise RuntimeError(
@@ -76,17 +124,14 @@ def run_pandoc(filename, html_file):
76
124
  markdown_text = fp.read()
77
125
  if "<comment>" in markdown_text:
78
126
  package_dir = os.path.dirname(os.path.abspath(__file__))
79
- for asset_name in [
80
- "comments.css",
81
- "comment_tags.lua",
82
- "comment_toggle.js",
83
- ]:
127
+ for asset_name in ["comments.css", "comment_toggle.js"]:
84
128
  target_path = os.path.join(source_dir, asset_name)
85
129
  if not os.path.exists(target_path):
86
130
  shutil.copy2(
87
131
  os.path.join(package_dir, asset_name),
88
132
  target_path,
89
133
  )
134
+ _set_comment_filter_mode(source_dir, comments_to_margin)
90
135
  localfiles = {}
91
136
  for k in ["csl", "bib"]:
92
137
  localfiles[k] = [
@@ -189,9 +234,10 @@ def run_pandoc(filename, html_file):
189
234
 
190
235
 
191
236
  class Handler(FileSystemEventHandler):
192
- def __init__(self, filename, observer):
237
+ def __init__(self, filename, observer, comments_to_margin=False):
193
238
  self.observer = observer
194
239
  self.filename = filename
240
+ self.comments_to_margin = comments_to_margin
195
241
  self.html_file = filename.rsplit(".", 1)[0] + ".html"
196
242
  self.init_firefox()
197
243
 
@@ -201,7 +247,11 @@ class Handler(FileSystemEventHandler):
201
247
  from selenium import webdriver
202
248
 
203
249
  self.firefox = webdriver.Chrome()
204
- run_pandoc(self.filename, self.html_file)
250
+ run_pandoc(
251
+ self.filename,
252
+ self.html_file,
253
+ comments_to_margin=self.comments_to_margin,
254
+ )
205
255
  if not os.path.exists(self.html_file):
206
256
  print("html doesn't exist")
207
257
  self.append_autorefresh()
@@ -213,7 +263,11 @@ class Handler(FileSystemEventHandler):
213
263
  if os.path.normpath(
214
264
  os.path.abspath(event.src_path)
215
265
  ) == os.path.normpath(os.path.abspath(self.filename)):
216
- run_pandoc(self.filename, self.html_file)
266
+ run_pandoc(
267
+ self.filename,
268
+ self.html_file,
269
+ comments_to_margin=self.comments_to_margin,
270
+ )
217
271
  self.append_autorefresh()
218
272
  try:
219
273
  self.firefox.refresh()
@@ -238,8 +292,8 @@ class Handler(FileSystemEventHandler):
238
292
  "div.comment-left, div.comment-right, " +
239
293
  "span.comment-pin > span.comment-left, " +
240
294
  "span.comment-pin > span.comment-right, " +
241
- "div.comment-overlay.comment-left, " +
242
- "div.comment-overlay.comment-right";
295
+ ".comment-overlay.comment-left, " +
296
+ ".comment-overlay.comment-right";
243
297
 
244
298
  // When the page is about to be unloaded, save the current scroll\
245
299
  position
@@ -337,11 +391,19 @@ position
337
391
 
338
392
  @register_command(
339
393
  "continuous pandoc build. Like latexmk, but for markdown!",
340
- help={"filename": "Markdown or TeX file to watch for changes"},
394
+ help={
395
+ "filename": "Markdown or TeX file to watch for changes",
396
+ "comments_to_margin": (
397
+ "Temporarily replace comment_tags.lua with the special margin "
398
+ "comments filter for printing."
399
+ ),
400
+ },
341
401
  )
342
- def cpb(filename):
402
+ def cpb(filename, comments_to_margin=False):
343
403
  observer = Observer()
344
- event_handler = Handler(filename, observer)
404
+ event_handler = Handler(
405
+ filename, observer, comments_to_margin=comments_to_margin
406
+ )
345
407
  search_queue = queue.Queue()
346
408
  stop_event = threading.Event()
347
409
  socket_thread = threading.Thread(
@@ -185,6 +185,48 @@ def _svg_shape_bounds(shape, namespace):
185
185
  return None
186
186
 
187
187
 
188
+ def _svg_parse_viewbox(svg_root):
189
+ viewbox = svg_root.attrib.get("viewBox")
190
+ if viewbox is None:
191
+ return None
192
+ parts = viewbox.replace(",", " ").split()
193
+ if len(parts) != 4:
194
+ return None
195
+ try:
196
+ return tuple(float(part) for part in parts)
197
+ except ValueError:
198
+ return None
199
+
200
+
201
+ def _svg_add_canvas_padding(svg_root, padding=8.0):
202
+ # Graphviz emits a tight canvas; add small fixed padding so post-processed
203
+ # outlines and anti-aliased strokes are never clipped at edges.
204
+ viewbox = _svg_parse_viewbox(svg_root)
205
+ if viewbox is None:
206
+ return
207
+
208
+ view_x, view_y, view_w, view_h = viewbox
209
+ if view_w <= 0.0 or view_h <= 0.0:
210
+ return
211
+
212
+ new_view_x = view_x - padding
213
+ new_view_y = view_y - padding
214
+ new_view_w = view_w + 2.0 * padding
215
+ new_view_h = view_h + 2.0 * padding
216
+ svg_root.set(
217
+ "viewBox",
218
+ f"{new_view_x:.2f} {new_view_y:.2f} {new_view_w:.2f} {new_view_h:.2f}",
219
+ )
220
+
221
+
222
+ def _watch_html(svg_file):
223
+ return (
224
+ "<html><body style='margin:0'><embed id='svg-view'"
225
+ " style='display:block;' type='image/svg+xml'"
226
+ f" src='{svg_file.name}?t={time.time()}'/></body></html>"
227
+ )
228
+
229
+
188
230
  def _svg_expanded_outline(
189
231
  shape, namespace, expand, stroke_color, stroke_width
190
232
  ):
@@ -249,6 +291,12 @@ def build_graph(
249
291
  ["dot", "-Tsvg", str(dot_file), "-o", str(svg_file)],
250
292
  check=True,
251
293
  )
294
+ svg_tree = ET.parse(str(svg_file))
295
+ svg_root = svg_tree.getroot()
296
+ namespace = ""
297
+ if svg_root.tag.startswith("{"):
298
+ namespace = svg_root.tag[: svg_root.tag.find("}") + 1]
299
+
252
300
  if not order_by_date:
253
301
  # In dependency view mode, each node explicitly tagged with
254
302
  # ``style: endpoint`` defines a project color. A project includes the
@@ -282,12 +330,6 @@ def build_graph(
282
330
  for parent in data["nodes"][ancestor]["parents"]:
283
331
  ancestors_to_visit.append(parent)
284
332
 
285
- svg_tree = ET.parse(str(svg_file))
286
- svg_root = svg_tree.getroot()
287
- namespace = ""
288
- if svg_root.tag.startswith("{"):
289
- namespace = svg_root.tag[: svg_root.tag.find("}") + 1]
290
-
291
333
  title_to_group = {}
292
334
  node_title_to_group = {}
293
335
  for group in svg_root.iter(f"{namespace}g"):
@@ -449,7 +491,8 @@ def build_graph(
449
491
  for insert_index, outline in reversed(inserts):
450
492
  group.insert(insert_index, outline)
451
493
 
452
- svg_tree.write(str(svg_file), encoding="utf-8", xml_declaration=True)
494
+ _svg_add_canvas_padding(svg_root, padding=24.0)
495
+ svg_tree.write(str(svg_file), encoding="utf-8", xml_declaration=True)
453
496
  return data
454
497
 
455
498
 
@@ -565,11 +608,7 @@ def wgrph(yaml, wrap_width=55, d=False, t=None):
565
608
  # Render the initial graph, optionally restricting to incomplete ancestors
566
609
  # of a target task.
567
610
  data = build_graph(yaml_file, dot_file, svg_file, wrap_width, d, None, t)
568
- html_file.write_text(
569
- "<html><body style='margin:0'><embed id='svg-view'"
570
- " type='image/svg+xml'"
571
- f" src='{svg_file.name}?t={time.time()}'/></body></html>"
572
- )
611
+ html_file.write_text(_watch_html(svg_file))
573
612
  options = Options()
574
613
  driver = start_chrome(webdriver, options, html_file)
575
614
  event_handler = GraphEventHandler(
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyDiffTools"
7
- version = "0.1.22"
7
+ version = "0.1.24"
8
8
  authors = [{ name = "J M Franck" }]
9
9
  description = "Diff tools"
10
10
  readme = "README.rst"
@@ -35,9 +35,10 @@ class FakeThread:
35
35
 
36
36
 
37
37
  class FakeHandler:
38
- def __init__(self, filename, observer):
38
+ def __init__(self, filename, observer, comments_to_margin=False, **kwargs):
39
39
  self.filename = filename
40
40
  self.observer = observer
41
+ self.comments_to_margin = comments_to_margin
41
42
  self.firefox = object()
42
43
 
43
44
  def forward_search(self, _search_text):
File without changes
File without changes
File without changes
File without changes