texdiff 0.2.3__tar.gz → 0.2.4__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 (40) hide show
  1. {texdiff-0.2.3/src/texdiff.egg-info → texdiff-0.2.4}/PKG-INFO +1 -1
  2. {texdiff-0.2.3 → texdiff-0.2.4}/pyproject.toml +1 -1
  3. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/__init__.py +1 -1
  4. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/emit.py +15 -2
  5. {texdiff-0.2.3 → texdiff-0.2.4/src/texdiff.egg-info}/PKG-INFO +1 -1
  6. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_tables_render.py +58 -0
  7. {texdiff-0.2.3 → texdiff-0.2.4}/LICENSE +0 -0
  8. {texdiff-0.2.3 → texdiff-0.2.4}/README.md +0 -0
  9. {texdiff-0.2.3 → texdiff-0.2.4}/setup.cfg +0 -0
  10. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/align.py +0 -0
  11. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/api.py +0 -0
  12. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/check.py +0 -0
  13. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/cli.py +0 -0
  14. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/flatten.py +0 -0
  15. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/newlines.py +0 -0
  16. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/nodes.py +0 -0
  17. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/oldlines.py +0 -0
  18. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/parse.py +0 -0
  19. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/preamble.py +0 -0
  20. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/tables.py +0 -0
  21. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff/textdiff.py +0 -0
  22. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff.egg-info/SOURCES.txt +0 -0
  23. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff.egg-info/dependency_links.txt +0 -0
  24. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff.egg-info/entry_points.txt +0 -0
  25. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff.egg-info/requires.txt +0 -0
  26. {texdiff-0.2.3 → texdiff-0.2.4}/src/texdiff.egg-info/top_level.txt +0 -0
  27. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_align.py +0 -0
  28. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_alignment.py +0 -0
  29. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_api.py +0 -0
  30. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_check.py +0 -0
  31. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_cli.py +0 -0
  32. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_corpus.py +0 -0
  33. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_emit.py +0 -0
  34. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_flatten.py +0 -0
  35. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_parse.py +0 -0
  36. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_preamble.py +0 -0
  37. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_table_head_order.py +0 -0
  38. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_tables.py +0 -0
  39. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_textdiff.py +0 -0
  40. {texdiff-0.2.3 → texdiff-0.2.4}/tests/test_verbatim.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: texdiff
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: AST-driven semantic diff for LaTeX documents
5
5
  Author: GoBobr
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "texdiff"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "AST-driven semantic diff for LaTeX documents"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -42,4 +42,4 @@ __all__ = [
42
42
  "word_diff",
43
43
  ]
44
44
 
45
- __version__ = "0.2.2"
45
+ __version__ = "0.2.4"
@@ -1556,7 +1556,13 @@ def _is_safe_inline(text: str) -> bool:
1556
1556
  return False
1557
1557
  if "\n" in text:
1558
1558
  return False
1559
- if _ARG_MACRO_RE.search(text) or _ARG_MACRO_BR_RE.search(text):
1559
+ # decoration macros (\\emph, \\textbf, ...) with pure-text
1560
+ # arguments are LR-safe inside \\DIFdel/\\DIFadd - font commands
1561
+ # survive \\sout/\\uwave. Mask them before the macro-argument
1562
+ # probe or cells like ``\\emph{valid\\_min} to \\emph{valid\\_max}``
1563
+ # degrade to the colour-only fallback: red but never struck.
1564
+ probe = _SAFE_DECOR_RE.sub(lambda m: m.group(2), text)
1565
+ if _ARG_MACRO_RE.search(probe) or _ARG_MACRO_BR_RE.search(probe):
1560
1566
  return False
1561
1567
  if _LIST_ITEM_RE.search(text):
1562
1568
  # \item / \par inside a strikeout/wave is LR-mode illegal
@@ -2239,6 +2245,11 @@ def _escape_cell_pair(old_cell: str, new_cell: str) -> str:
2239
2245
  if _is_safe_inline(new_cell):
2240
2246
  return f"\\DIFadd{{{new_cell}}}"
2241
2247
  return f"\\DIFaddbegin{{}} {new_cell} \\DIFaddend{{}}"
2248
+ if not new_cell:
2249
+ # text -> empty: strike the old cell (blue adds nothing)
2250
+ if _is_safe_inline(old_cell):
2251
+ return f"\\DIFdel{{{old_cell}}}"
2252
+ return f"\\DIFdelbegin{{}} {old_cell} \\DIFdelend{{}}"
2242
2253
  # underscore-escape-only difference: the OLD ADS generator emits
2243
2254
  # raw ``_`` in variable paths while the NEW one escapes it as
2244
2255
  # ``\_`` - both render identically in LaTeX. A cell pair that
@@ -2459,7 +2470,9 @@ def _merge_keyed_rows(
2459
2470
  parts: list[str] = []
2460
2471
  for oc, nc in zip(old_cells, new_cells):
2461
2472
  oc_s, nc_s = oc.strip(), nc.strip()
2462
- if oc_s == nc_s or not nc_s:
2473
+ if oc_s == nc_s:
2474
+ parts.append(oc)
2475
+ elif not nc_s and not oc_s:
2463
2476
  parts.append(oc)
2464
2477
  else:
2465
2478
  parts.append(_escape_cell_pair(oc_s, nc_s))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: texdiff
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: AST-driven semantic diff for LaTeX documents
5
5
  Author: GoBobr
6
6
  License: MIT License
@@ -1018,3 +1018,61 @@ class TestGroupedPairingRegressions:
1018
1018
  assert name in out[nearest : nearest + 12], (
1019
1019
  f"{unit} merged into the wrong group"
1020
1020
  )
1021
+
1022
+ def test_decorated_macro_cell_strikes_not_just_colours(self):
1023
+ r"""Range-cell case: \emph{..} cells must strike, not just colour.
1024
+
1025
+ A changed cell containing only decoration macros with
1026
+ pure-text arguments (``\emph{valid\_min} to
1027
+ \emph{valid\_max}``) is LR-safe inside \DIFdel/\DIFadd:
1028
+ font commands survive \sout/\uwave. It must render with the
1029
+ inline strike markup, not degrade to the colour-switch-only
1030
+ fallback (\DIFdelbegin..\DIFdelend), which paints the old
1031
+ text red without striking it.
1032
+ """
1033
+ old_src = (
1034
+ "\\documentclass{article}\\begin{document}\n"
1035
+ "\\begin{longtable}{ll}\n\\caption{vars}\\\\\n"
1036
+ "\\rowcolor{lightcyan} \\textbf{lat} & Latitude & float & "
1037
+ "\\emph{valid\\_min} to \\emph{valid\\_max} & d1\\\\\n"
1038
+ "\\end{longtable}\\end{document}\n"
1039
+ )
1040
+ new_src = old_src.replace(
1041
+ "\\emph{valid\\_min} to \\emph{valid\\_max} & d1",
1042
+ "defined by \\emph{valid\\_range} & lat",
1043
+ )
1044
+ out = diff_documents(old_src, new_src, inject_preamble=False).marked_up
1045
+ # the old range cell is STRUCK inside \DIFdel{..}, not merely
1046
+ # coloured by the block fallback
1047
+ assert "\\DIFdel{\\emph{valid\\_min} to \\emph{valid\\_max}}" in out
1048
+ # no colour-only degradation for the pair
1049
+ assert "\\DIFdelbegin{} \\emph{valid" not in out
1050
+ # new side waves its markup too
1051
+ assert "\\DIFadd{defined by \\emph{valid\\_range}}" in out
1052
+
1053
+ def test_text_to_empty_cell_change_is_marked(self):
1054
+ r"""Text->empty cell change must strike the old cell.
1055
+
1056
+ When a cell's content is removed entirely (the regenerated
1057
+ spec leaves the Range column blank), the per-cell merge used
1058
+ to emit the OLD cell verbatim with no markup at all: the
1059
+ retired value silently appeared as unchanged text. It must
1060
+ render struck red (and its counterpart, empty->text, wavy
1061
+ blue).
1062
+ """
1063
+ old_src = (
1064
+ "\\documentclass{article}\\begin{document}\n"
1065
+ "\\begin{longtable}{lll}\n"
1066
+ "z0 & 0 & 0 \\\\\n"
1067
+ "z1 & 1 & 1 \\\\\n"
1068
+ "\\rowcolor{lightcyan} \\textbf{day} & Date of Day & "
1069
+ "\\emph{valid\\_min} to \\emph{valid\\_max} \\\\\n"
1070
+ "w & 5 & 6 \\\\\n"
1071
+ "\\end{longtable}\\end{document}\n"
1072
+ )
1073
+ new_src = old_src.replace(
1074
+ "\\emph{valid\\_min} to \\emph{valid\\_max} \\\\",
1075
+ " \\\\",
1076
+ )
1077
+ out = diff_documents(old_src, new_src, inject_preamble=False).marked_up
1078
+ assert "\\DIFdel{\\emph{valid\\_min} to \\emph{valid\\_max}}" in out
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes