pyDiffTools 0.1.34__tar.gz → 0.1.35__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 (112) hide show
  1. {pydifftools-0.1.34/pyDiffTools.egg-info → pydifftools-0.1.35}/PKG-INFO +1 -1
  2. {pydifftools-0.1.34 → pydifftools-0.1.35/pyDiffTools.egg-info}/PKG-INFO +1 -1
  3. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/continuous.py +122 -3
  4. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/wrap_sentences.py +70 -66
  5. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyproject.toml +1 -1
  6. {pydifftools-0.1.34 → pydifftools-0.1.35}/LICENSE.md +0 -0
  7. {pydifftools-0.1.34 → pydifftools-0.1.35}/MANIFEST.in +0 -0
  8. {pydifftools-0.1.34 → pydifftools-0.1.35}/README.rst +0 -0
  9. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/_quarto.yml +0 -0
  10. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
  11. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
  12. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
  13. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
  14. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
  15. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
  16. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
  17. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
  18. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
  19. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
  20. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
  21. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
  22. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
  23. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
  24. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
  25. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
  26. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
  27. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
  28. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
  29. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
  30. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
  31. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/.jupyter_cache/global.db +0 -0
  32. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/example.qmd +0 -0
  33. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/example.tex +0 -0
  34. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/index.qmd +0 -0
  35. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
  36. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
  37. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
  38. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
  39. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/independent.qmd +0 -0
  40. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/index.qmd +0 -0
  41. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/tasks.qmd +0 -0
  42. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/test_include.qmd +0 -0
  43. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
  44. {pydifftools-0.1.34 → pydifftools-0.1.35}/example_notebook/project1/test_include.qmd +0 -0
  45. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyDiffTools.egg-info/SOURCES.txt +0 -0
  46. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyDiffTools.egg-info/dependency_links.txt +0 -0
  47. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyDiffTools.egg-info/entry_points.txt +0 -0
  48. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyDiffTools.egg-info/requires.txt +0 -0
  49. {pydifftools-0.1.34 → pydifftools-0.1.35}/pyDiffTools.egg-info/top_level.txt +0 -0
  50. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/__init__.py +0 -0
  51. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/bibliography.py +0 -0
  52. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/browser_lifecycle.py +0 -0
  53. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/check_numbers.py +0 -0
  54. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/citation_dialog.py +0 -0
  55. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/command_line.py +0 -0
  56. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/command_registry.py +0 -0
  57. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comment_functions.py +0 -0
  58. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comment_tags.lua +0 -0
  59. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comment_tags_margin.lua +0 -0
  60. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comment_tags_no_comments.lua +0 -0
  61. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comment_toggle.js +0 -0
  62. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/comments.css +0 -0
  63. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/copy_files.py +0 -0
  64. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/diff-doc.js +0 -0
  65. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/doc_contents.py +0 -0
  66. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/__init__.py +0 -0
  67. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/comparison.py +0 -0
  68. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/dot_to_yaml.py +0 -0
  69. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/graph.py +0 -0
  70. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/svg-pan-zoom.min.js +0 -0
  71. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/watch_graph.py +0 -0
  72. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/flowchart/wgrph_view.js +0 -0
  73. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/forward_search.py +0 -0
  74. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/git_gd.py +0 -0
  75. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/git_gd_history.py +0 -0
  76. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/git_gd_image.py +0 -0
  77. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/git_gd_qt.py +0 -0
  78. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/html_comments.py +0 -0
  79. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/html_uncomments.py +0 -0
  80. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/log_example.py +0 -0
  81. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/match_spaces.py +0 -0
  82. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/notebook/__init__.py +0 -0
  83. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/notebook/display.py +0 -0
  84. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/notebook/fast_build.py +0 -0
  85. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/notebook/tex_to_qmd.py +0 -0
  86. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/onewordify.py +0 -0
  87. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/onewordify_undo.py +0 -0
  88. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/outline.py +0 -0
  89. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/rearrange_tex.py +0 -0
  90. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/searchacro.py +0 -0
  91. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/separate_comments.py +0 -0
  92. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/split_conflict.py +0 -0
  93. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/unseparate_comments.py +0 -0
  94. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/update_check.py +0 -0
  95. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/xml2xlsx.vbs +0 -0
  96. {pydifftools-0.1.34 → pydifftools-0.1.35}/pydifftools/zotero.py +0 -0
  97. {pydifftools-0.1.34 → pydifftools-0.1.35}/setup.cfg +0 -0
  98. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_bibliography.py +0 -0
  99. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_browser_lifecycle.py +0 -0
  100. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_check_single_use.py +0 -0
  101. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_command_line_help.py +0 -0
  102. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_continuous_shutdown.py +0 -0
  103. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_forward_search.py +0 -0
  104. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_git_gd_history.py +0 -0
  105. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_git_gd_image.py +0 -0
  106. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_git_gd_qt.py +0 -0
  107. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_rrng.py +0 -0
  108. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_tex_to_qmd.py +0 -0
  109. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_update_check.py +0 -0
  110. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_wrap_check.py +0 -0
  111. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_wrapping.py +0 -0
  112. {pydifftools-0.1.34 → pydifftools-0.1.35}/tests/test_zotero.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyDiffTools
3
- Version: 0.1.34
3
+ Version: 0.1.35
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.34
3
+ Version: 0.1.35
4
4
  Summary: Diff tools
5
5
  Author: J M Franck
6
6
  License: Copyright (c) 2015, jmfranck
@@ -1,5 +1,6 @@
1
1
  """Continuous Pandoc build utility that requires geckodriver."""
2
2
 
3
+ import json
3
4
  import time
4
5
  import subprocess
5
6
  import sys
@@ -10,10 +11,10 @@ import shutil
10
11
  import threading
11
12
  import queue
12
13
  import traceback
13
- import json
14
14
  import tempfile
15
15
  from pathlib import Path
16
16
  import yaml
17
+ import textwrap
17
18
  from watchdog.events import FileSystemEventHandler
18
19
  from watchdog.observers import Observer
19
20
  from watchdog.observers.polling import PollingObserver
@@ -61,7 +62,8 @@ def _comment_filter_mode(path, packaged_filters=None):
61
62
  return "custom"
62
63
 
63
64
 
64
- def _confirm_restore_comment_filter(active_mode):
65
+ # Kept separate so its prompt choices can be unit-tested.
66
+ def confirm_restore_comment_filter(active_mode):
65
67
  if active_mode == "none":
66
68
  message = (
67
69
  "The current lua filter is the one that does not show comments, "
@@ -132,6 +134,120 @@ def run_pandoc(
132
134
  no_comments=False,
133
135
  comment_filter_session=None,
134
136
  ):
137
+ # {{{ review Markdown lint issues in a Qt walkthrough
138
+ def show_markdown_lint(filename, issues):
139
+ """Walk through cpb's Markdown style issues in a Qt window."""
140
+ dialog_script = r"""
141
+ import html
142
+ import json
143
+ import sys
144
+ from PySide6.QtCore import Qt
145
+ from PySide6.QtWidgets import (
146
+ QApplication,
147
+ QDialog,
148
+ QLabel,
149
+ QPushButton,
150
+ QVBoxLayout,
151
+ )
152
+
153
+ filename = sys.argv[1]
154
+ issues = json.loads(sys.argv[2])
155
+ app = QApplication(sys.argv[:1])
156
+ dialog = QDialog()
157
+ dialog.setWindowTitle("pydifft cpb — Markdown style")
158
+ dialog.resize(620, 260)
159
+ layout = QVBoxLayout(dialog)
160
+ heading = QLabel()
161
+ heading.setTextFormat(Qt.TextFormat.RichText)
162
+ details = QLabel()
163
+ details.setTextFormat(Qt.TextFormat.RichText)
164
+ details.setWordWrap(True)
165
+ fix = QLabel()
166
+ fix.setTextFormat(Qt.TextFormat.RichText)
167
+ fix.setWordWrap(True)
168
+ layout.addWidget(heading)
169
+ layout.addWidget(details)
170
+ layout.addWidget(fix)
171
+ button = QPushButton("Next")
172
+ layout.addWidget(button)
173
+ index = 0
174
+
175
+ def show_issue():
176
+ global index
177
+ if index >= len(issues):
178
+ heading.setText("<b>All listed issues reviewed.</b>")
179
+ details.setText(
180
+ "Edit the source if needed, then continue to check it again."
181
+ )
182
+ fix.setText("")
183
+ button.setText("Check again")
184
+ return
185
+ line, message = issues[index]
186
+ heading.setText(
187
+ f"<b>{html.escape(filename)}:{line}</b> — issue "
188
+ f"{index + 1} of {len(issues)}"
189
+ )
190
+ details.setText(html.escape(message))
191
+ if message.startswith("sentence ends mid-line"):
192
+ fix.setText(
193
+ "Fix: end the sentence on this line, then continue the next "
194
+ "sentence on a new source line.<br>"
195
+ "<span style='color:green'>↳ Enter</span> "
196
+ "<span style='color:green'><i>soft return: this newline "
197
+ "appears only in the source.</i></span>"
198
+ )
199
+ else:
200
+ fix.setText(
201
+ "Fix: add a source newline within the sentence so the prose "
202
+ "wraps at 55 characters or fewer.<br>"
203
+ "<span style='color:green'>↳ Enter</span> "
204
+ "<span style='color:green'><i>soft return: this newline "
205
+ "appears only in the source.</i></span>"
206
+ )
207
+
208
+ def advance():
209
+ global index
210
+ index += 1
211
+ show_issue()
212
+
213
+ button.clicked.connect(advance)
214
+ show_issue()
215
+ dialog.exec()
216
+ """
217
+ result = subprocess.run(
218
+ [
219
+ sys.executable,
220
+ "-c",
221
+ textwrap.dedent(dialog_script),
222
+ filename,
223
+ json.dumps(issues),
224
+ ],
225
+ capture_output=True,
226
+ text=True,
227
+ )
228
+ if result.returncode != 0:
229
+ detail = result.stderr.strip()
230
+ if detail:
231
+ raise RuntimeError(
232
+ f"cpb Markdown lint dialog failed: {detail}"
233
+ )
234
+ raise RuntimeError("cpb Markdown lint dialog failed.")
235
+ # }}}
236
+ # Keep cpb's build gate tied to wrchk's Markdown rules. Recheck after each
237
+ # walkthrough because users can edit the source in their editor while the
238
+ # Qt window is open.
239
+ from .wrap_sentences import markdown_lint_issues_from_text
240
+
241
+ while True:
242
+ with open(filename, encoding="utf-8") as fp:
243
+ markdown_text = fp.read()
244
+ issues = markdown_lint_issues_from_text(
245
+ markdown_text, wrapnumber=55
246
+ )
247
+ if not issues:
248
+ break
249
+ show_markdown_lint(filename, issues)
250
+
135
251
  if comments_to_margin:
136
252
  comment_filter_mode = "margin"
137
253
  elif no_comments:
@@ -198,9 +314,12 @@ def run_pandoc(
198
314
  ):
199
315
  show_comments = comment_filter_session["show_comments"]
200
316
  else:
201
- show_comments = _confirm_restore_comment_filter(
317
+ # {{{ ask whether to restore comments after detecting a
318
+ # locally changed comment filter
319
+ show_comments = confirm_restore_comment_filter(
202
320
  active_mode
203
321
  )
322
+ # }}}
204
323
  if comment_filter_session is not None:
205
324
  comment_filter_session["show_comments"] = (
206
325
  show_comments
@@ -826,62 +826,13 @@ def wr(filename, wrapnumber=45, punctuation_slop=20, cleanoo=False, i=-1):
826
826
  fp.write(result)
827
827
 
828
828
 
829
- def _check_fragment(
830
- fragment, frag_start, wrapnumber, punctuation_slop, base_line, stripped
831
- ):
832
- """Report line-length violations within one wrap unit (a sentence, or a
833
- piece of one split off around a LaTeX structural macro).
834
-
835
- Replays wr's own greedy word-fitting choice (_next_break) but always
836
- resumes from the *real* line break already present in the source
837
- (never from wr's own suggested break), so a shorter user break, or a
838
- word nudged onto the next line, is never flagged -- only a line that
839
- holds more words than wr's choice would allow is.
840
- """
841
- words, positions = [], []
842
- for match in re.finditer(r"\S+", fragment):
843
- words.append(match.group())
844
- positions.append(frag_start + match.start())
845
- if not words:
846
- return []
847
- ends = positions[1:] + [frag_start + len(fragment)]
848
- breaks_after = [
849
- "\n" in stripped[positions[j] + len(words[j]) : ends[j]]
850
- for j in range(len(words))
851
- ]
852
- boundaries = _math_boundaries(words)
853
- issues = []
854
- offset = 0
855
- while offset < len(words):
856
- upto = _next_break(
857
- words[offset:], wrapnumber, punctuation_slop, boundaries, offset
858
- )
859
- wr_end = offset + upto
860
- actual_end = offset
861
- while actual_end < len(words) - 1 and not breaks_after[actual_end]:
862
- actual_end += 1
863
- if actual_end > wr_end:
864
- overflow = words[wr_end + 1]
865
- line = base_line + stripped.count("\n", 0, positions[actual_end])
866
- issues.append(
867
- (
868
- line,
869
- f"line too long: wr would break after '{words[wr_end]}' "
870
- f"(before '{overflow}'); move '{overflow}' onward to "
871
- "the next line, or shorten this sentence",
872
- )
873
- )
874
- offset = actual_end + 1
875
- return issues
876
-
877
-
878
829
  def check_prose(content, wrapnumber, punctuation_slop, line_start=1):
879
830
  """Report source lines in `content` that break wr's rules.
880
831
 
881
832
  Two checks, mirroring wrap_prose's own paragraph/sentence/macro
882
833
  splitting so the two commands never disagree about what counts as a
883
834
  sentence: lines must not hold more words than wr's own greedy choice
884
- would put there (see _check_fragment), and a sentence must not end
835
+ would put there (see the fragment check below), and a sentence must not end
885
836
  in the middle of a source line. A sentence-ending period followed by
886
837
  a backslash-space (`\\ `) rather than a plain space -- the standard
887
838
  LaTeX/Pandoc way to mark an abbreviation -- is never mistaken for a
@@ -912,16 +863,54 @@ def check_prose(content, wrapnumber, punctuation_slop, line_start=1):
912
863
  frag_offset = sentence_start
913
864
  for fragment in LATEX_MACRO_SPLIT.split(sentence):
914
865
  if fragment:
915
- issues.extend(
916
- _check_fragment(
917
- fragment,
918
- frag_offset,
919
- wrapnumber,
920
- punctuation_slop,
921
- para_line,
922
- stripped,
923
- )
924
- )
866
+ # {{{ check line lengths within this sentence fragment
867
+ words, positions = [], []
868
+ for match in re.finditer(r"\S+", fragment):
869
+ words.append(match.group())
870
+ positions.append(frag_offset + match.start())
871
+ if words:
872
+ ends = positions[1:] + [frag_offset + len(fragment)]
873
+ breaks_after = [
874
+ "\n"
875
+ in stripped[
876
+ positions[j] + len(words[j]) : ends[j]
877
+ ]
878
+ for j in range(len(words))
879
+ ]
880
+ boundaries = _math_boundaries(words)
881
+ fragment_offset = 0
882
+ while fragment_offset < len(words):
883
+ upto = _next_break(
884
+ words[fragment_offset:],
885
+ wrapnumber,
886
+ punctuation_slop,
887
+ boundaries,
888
+ fragment_offset,
889
+ )
890
+ wr_end = fragment_offset + upto
891
+ actual_end = fragment_offset
892
+ while (
893
+ actual_end < len(words) - 1
894
+ and not breaks_after[actual_end]
895
+ ):
896
+ actual_end += 1
897
+ if actual_end > wr_end:
898
+ overflow = words[wr_end + 1]
899
+ issue_line = para_line + stripped.count(
900
+ "\n", 0, positions[actual_end]
901
+ )
902
+ issues.append(
903
+ (
904
+ issue_line,
905
+ "line too long: wr would break after "
906
+ f"'{words[wr_end]}' (before "
907
+ f"'{overflow}'); move '{overflow}' "
908
+ "onward to the next line, or shorten "
909
+ "this sentence",
910
+ )
911
+ )
912
+ fragment_offset = actual_end + 1
913
+ # }}}
925
914
  frag_offset += len(fragment)
926
915
  offset += len(sentence)
927
916
  if k < len(separators):
@@ -986,6 +975,19 @@ def _iter_block_spans(blocks, wrapnumber, line_start=1):
986
975
  line += span
987
976
 
988
977
 
978
+ def markdown_lint_issues_from_text(
979
+ content, wrapnumber=45, punctuation_slop=20
980
+ ):
981
+ """Return lint issues for Markdown text using wrchk's existing rules."""
982
+ return [
983
+ issue
984
+ for line, width, span in _iter_block_spans(
985
+ markdown_blocks(content), wrapnumber
986
+ )
987
+ for issue in check_prose(span, width, punctuation_slop, line)
988
+ ]
989
+
990
+
989
991
  @register_command(
990
992
  "check wrapping and sentence-break rules (for markdown or latex).",
991
993
  "check that a file already obeys wr's wrapping rules -- without\n"
@@ -1005,17 +1007,19 @@ def _iter_block_spans(blocks, wrapnumber, line_start=1):
1005
1007
  def wrchk(filename, wrapnumber=45, punctuation_slop=20, cleanoo=False, i=-1):
1006
1008
  alltext, filetype, _, display_name = _prepare(filename, cleanoo, i)
1007
1009
  if filetype == "markdown":
1008
- spans = _iter_block_spans(markdown_blocks(alltext), wrapnumber)
1010
+ issues = markdown_lint_issues_from_text(
1011
+ alltext, wrapnumber, punctuation_slop
1012
+ )
1009
1013
  else:
1010
1014
  spans = (
1011
1015
  (line, wrapnumber, content)
1012
1016
  for line, content in _iter_classified_spans(alltext, filetype)
1013
1017
  )
1014
- issues = [
1015
- issue
1016
- for line, width, content in spans
1017
- for issue in check_prose(content, width, punctuation_slop, line)
1018
- ]
1018
+ issues = [
1019
+ issue
1020
+ for line, width, content in spans
1021
+ for issue in check_prose(content, width, punctuation_slop, line)
1022
+ ]
1019
1023
  for line, message in issues:
1020
1024
  print(f"{display_name}:{line}: {message}")
1021
1025
  if issues:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyDiffTools"
7
- version = "0.1.34"
7
+ version = "0.1.35"
8
8
  authors = [{ name = "J M Franck" }]
9
9
  description = "Diff tools"
10
10
  readme = "README.rst"
File without changes
File without changes
File without changes
File without changes