panache-cli 2.54.0__tar.gz → 2.55.0__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 (264) hide show
  1. {panache_cli-2.54.0 → panache_cli-2.55.0}/Cargo.lock +3 -3
  2. {panache_cli-2.54.0 → panache_cli-2.55.0}/Cargo.toml +3 -3
  3. {panache_cli-2.54.0 → panache_cli-2.55.0}/PKG-INFO +1 -1
  4. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/CHANGELOG.md +14 -0
  5. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/Cargo.toml +2 -2
  6. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/config.rs +1 -1
  7. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/inline.rs +17 -6
  8. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/lists.rs +75 -0
  9. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/math/STYLE.md +5 -3
  10. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/math/linebreak.rs +70 -9
  11. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/math/render.rs +19 -3
  12. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/CHANGELOG.md +5 -0
  13. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/Cargo.toml +1 -1
  14. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/core.rs +130 -0
  15. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/config/types.rs +11 -6
  16. {panache_cli-2.54.0 → panache_cli-2.55.0}/LICENSE +0 -0
  17. {panache_cli-2.54.0 → panache_cli-2.55.0}/README.md +0 -0
  18. {panache_cli-2.54.0 → panache_cli-2.55.0}/build.rs +0 -0
  19. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/README.md +0 -0
  20. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/directives.rs +0 -0
  21. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/blockquotes.rs +0 -0
  22. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/code_blocks.rs +0 -0
  23. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/core.rs +0 -0
  24. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/fenced_divs.rs +0 -0
  25. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/hashpipe.rs +0 -0
  26. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/headings.rs +0 -0
  27. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/indent_utils.rs +0 -0
  28. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/inline_layout.rs +0 -0
  29. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/math/operators.rs +0 -0
  30. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/math.rs +0 -0
  31. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/metadata.rs +0 -0
  32. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/paragraphs.rs +0 -0
  33. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/sentence_wrap.rs +0 -0
  34. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/shortcodes.rs +0 -0
  35. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/smart.rs +0 -0
  36. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/tables.rs +0 -0
  37. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/utils.rs +0 -0
  38. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/STYLE.md +0 -0
  39. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/block_map.rs +0 -0
  40. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/block_sequence.rs +0 -0
  41. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/document.rs +0 -0
  42. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/flow.rs +0 -0
  43. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/options.rs +0 -0
  44. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml/scalar.rs +0 -0
  45. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter/yaml.rs +0 -0
  46. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/formatter.rs +0 -0
  47. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/lib.rs +0 -0
  48. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/parser.rs +0 -0
  49. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/syntax.rs +0 -0
  50. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/utils.rs +0 -0
  51. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-formatter/src/yaml_engine.rs +0 -0
  52. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/README.md +0 -0
  53. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/build.rs +0 -0
  54. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/lib.rs +0 -0
  55. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/options.rs +0 -0
  56. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/pandoc_ast.rs +0 -0
  57. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/block_dispatcher.rs +0 -0
  58. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/blockquotes.rs +0 -0
  59. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/code_blocks.rs +0 -0
  60. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/container_prefix.rs +0 -0
  61. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/definition_lists.rs +0 -0
  62. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/fenced_divs.rs +0 -0
  63. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/figures.rs +0 -0
  64. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/headings.rs +0 -0
  65. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/horizontal_rules.rs +0 -0
  66. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/html_blocks.rs +0 -0
  67. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/indented_code.rs +0 -0
  68. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/latex_envs.rs +0 -0
  69. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/line_blocks.rs +0 -0
  70. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/lists.rs +0 -0
  71. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/metadata.rs +0 -0
  72. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/paragraphs.rs +0 -0
  73. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/raw_blocks.rs +0 -0
  74. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/reference_links.rs +0 -0
  75. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tables.rs +0 -0
  76. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/blanklines.rs +0 -0
  77. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/blockquotes.rs +0 -0
  78. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/code_blocks.rs +0 -0
  79. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/definition_lists.rs +0 -0
  80. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/headings.rs +0 -0
  81. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/helpers.rs +0 -0
  82. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/lists.rs +0 -0
  83. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/losslessness.rs +0 -0
  84. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks/tests/metadata_guards.rs +0 -0
  85. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/blocks.rs +0 -0
  86. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/diagnostics.rs +0 -0
  87. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/bookdown.rs +0 -0
  88. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/bracketed_spans.rs +0 -0
  89. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/citations.rs +0 -0
  90. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/code_spans.rs +0 -0
  91. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/core.rs +0 -0
  92. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/emoji.rs +0 -0
  93. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/escapes.rs +0 -0
  94. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/inline_executable.rs +0 -0
  95. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/inline_footnotes.rs +0 -0
  96. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/inline_html.rs +0 -0
  97. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/inline_ir.rs +0 -0
  98. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/latex.rs +0 -0
  99. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/links.rs +0 -0
  100. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/mark.rs +0 -0
  101. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/math.rs +0 -0
  102. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/native_spans.rs +0 -0
  103. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/raw_inline.rs +0 -0
  104. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/refdef_map.rs +0 -0
  105. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/shortcodes.rs +0 -0
  106. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/sink.rs +0 -0
  107. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/strikeout.rs +0 -0
  108. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/subscript.rs +0 -0
  109. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/superscript.rs +0 -0
  110. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/tests.rs +0 -0
  111. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/uri-schemes.csv +0 -0
  112. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines/wikilinks.rs +0 -0
  113. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/inlines.rs +0 -0
  114. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/math.rs +0 -0
  115. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/attributes.rs +0 -0
  116. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/chunk_options.rs +0 -0
  117. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/container_stack.rs +0 -0
  118. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/continuation.rs +0 -0
  119. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/helpers.rs +0 -0
  120. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/inline_emission.rs +0 -0
  121. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/list_item_buffer.rs +0 -0
  122. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/marker_utils.rs +0 -0
  123. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/text_buffer.rs +0 -0
  124. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/tree_copy.rs +0 -0
  125. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils/yaml_regions.rs +0 -0
  126. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/utils.rs +0 -0
  127. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/cooking.rs +0 -0
  128. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/events.rs +0 -0
  129. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/model.rs +0 -0
  130. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/parser.rs +0 -0
  131. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/profile.rs +0 -0
  132. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/scanner.rs +0 -0
  133. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml/validator.rs +0 -0
  134. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser/yaml.rs +0 -0
  135. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/parser.rs +0 -0
  136. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/range_utils.rs +0 -0
  137. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/alerts.rs +0 -0
  138. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/ast.rs +0 -0
  139. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/attributes.rs +0 -0
  140. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/block_quotes.rs +0 -0
  141. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/blocks.rs +0 -0
  142. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/chunk_options.rs +0 -0
  143. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/citations.rs +0 -0
  144. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/code_blocks.rs +0 -0
  145. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/crossrefs.rs +0 -0
  146. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/definitions.rs +0 -0
  147. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/fenced_divs.rs +0 -0
  148. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/headings.rs +0 -0
  149. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/inlines.rs +0 -0
  150. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/kind.rs +0 -0
  151. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/links.rs +0 -0
  152. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/lists.rs +0 -0
  153. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/math.rs +0 -0
  154. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/raw_tex.rs +0 -0
  155. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/references.rs +0 -0
  156. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/shortcodes.rs +0 -0
  157. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/tables.rs +0 -0
  158. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/yaml.rs +0 -0
  159. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax/yaml_ast.rs +0 -0
  160. {panache_cli-2.54.0 → panache_cli-2.55.0}/crates/panache-parser/src/syntax.rs +0 -0
  161. {panache_cli-2.54.0 → panache_cli-2.55.0}/pyproject.toml +0 -0
  162. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib/bibtex.rs +0 -0
  163. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib/csl_json.rs +0 -0
  164. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib/csl_yaml.rs +0 -0
  165. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib/index.rs +0 -0
  166. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib/ris.rs +0 -0
  167. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/bib.rs +0 -0
  168. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/cache.rs +0 -0
  169. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/cli.rs +0 -0
  170. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/config/formatter_presets.rs +0 -0
  171. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/config/types/schema_helpers.rs +0 -0
  172. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/config.rs +0 -0
  173. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/diagnostic_renderer.rs +0 -0
  174. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/directives.rs +0 -0
  175. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/external_formatters_common.rs +0 -0
  176. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/external_formatters_sync.rs +0 -0
  177. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/external_tools_common.rs +0 -0
  178. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/formatter.rs +0 -0
  179. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/includes.rs +0 -0
  180. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lib.rs +0 -0
  181. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/code_block_collector.rs +0 -0
  182. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/diagnostics.rs +0 -0
  183. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/clippy.rs +0 -0
  184. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/eslint.rs +0 -0
  185. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/jarl.rs +0 -0
  186. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/ruff.rs +0 -0
  187. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/shellcheck.rs +0 -0
  188. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters/staticcheck.rs +0 -0
  189. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters.rs +0 -0
  190. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/external_linters_sync.rs +0 -0
  191. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/index.rs +0 -0
  192. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/metadata_diagnostics.rs +0 -0
  193. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/offsets.rs +0 -0
  194. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/adjacent_footnote_refs.rs +0 -0
  195. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/chunk_label_spaces.rs +0 -0
  196. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/citation_keys.rs +0 -0
  197. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/crossref_as_link_target.rs +0 -0
  198. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/duplicate_references.rs +0 -0
  199. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/emoji_aliases.rs +0 -0
  200. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/empty_list_item.rs +0 -0
  201. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/figure_crossref_captions.rs +0 -0
  202. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/footnote_ref_in_footnote_def.rs +0 -0
  203. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/heading_eaten_attrs.rs +0 -0
  204. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/heading_hierarchy.rs +0 -0
  205. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/heading_strip_comments_residue.rs +0 -0
  206. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/html_entities.rs +0 -0
  207. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/link_text_is_url.rs +0 -0
  208. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/math_content.rs +0 -0
  209. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/missing_chunk_labels.rs +0 -0
  210. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/stray_fenced_div_markers.rs +0 -0
  211. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/undefined_anchor.rs +0 -0
  212. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/undefined_references.rs +0 -0
  213. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules/unused_definitions.rs +0 -0
  214. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/rules.rs +0 -0
  215. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter/runner.rs +0 -0
  216. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/linter.rs +0 -0
  217. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/config.rs +0 -0
  218. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/context.rs +0 -0
  219. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/conversions.rs +0 -0
  220. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/dispatch.rs +0 -0
  221. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/documents.rs +0 -0
  222. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/global_state.rs +0 -0
  223. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/code_actions.rs +0 -0
  224. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/completion.rs +0 -0
  225. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/diagnostics.rs +0 -0
  226. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/document_links.rs +0 -0
  227. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/document_symbols.rs +0 -0
  228. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/file_rename.rs +0 -0
  229. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/file_watcher.rs +0 -0
  230. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/folding_ranges.rs +0 -0
  231. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/footnote_conversion.rs +0 -0
  232. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/formatting.rs +0 -0
  233. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/goto_definition.rs +0 -0
  234. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/heading_link_conversion.rs +0 -0
  235. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/hover.rs +0 -0
  236. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/link_conversion.rs +0 -0
  237. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/list_conversion.rs +0 -0
  238. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/prepare_rename.rs +0 -0
  239. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/references.rs +0 -0
  240. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/rename.rs +0 -0
  241. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/shortcode_args.rs +0 -0
  242. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers/workspace_symbols.rs +0 -0
  243. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/handlers.rs +0 -0
  244. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/helpers.rs +0 -0
  245. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/navigation.rs +0 -0
  246. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/symbols.rs +0 -0
  247. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/task_pool.rs +0 -0
  248. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/testing.rs +0 -0
  249. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp/uri_ext.rs +0 -0
  250. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/lsp.rs +0 -0
  251. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/main.rs +0 -0
  252. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata/bibliography.rs +0 -0
  253. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata/citations.rs +0 -0
  254. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata/project.rs +0 -0
  255. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata/references.rs +0 -0
  256. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata/yaml.rs +0 -0
  257. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/metadata.rs +0 -0
  258. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/parser.rs +0 -0
  259. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/range_utils.rs +0 -0
  260. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/salsa.rs +0 -0
  261. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/syntax.rs +0 -0
  262. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/utils.rs +0 -0
  263. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/yaml_engine.rs +0 -0
  264. {panache_cli-2.54.0 → panache_cli-2.55.0}/src/yaml_regions.rs +0 -0
@@ -1151,7 +1151,7 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
1151
1151
 
1152
1152
  [[package]]
1153
1153
  name = "panache"
1154
- version = "2.54.0"
1154
+ version = "2.55.0"
1155
1155
  dependencies = [
1156
1156
  "annotate-snippets",
1157
1157
  "assert_cmd",
@@ -1194,7 +1194,7 @@ dependencies = [
1194
1194
 
1195
1195
  [[package]]
1196
1196
  name = "panache-formatter"
1197
- version = "0.12.0"
1197
+ version = "0.13.0"
1198
1198
  dependencies = [
1199
1199
  "insta",
1200
1200
  "log",
@@ -1209,7 +1209,7 @@ dependencies = [
1209
1209
 
1210
1210
  [[package]]
1211
1211
  name = "panache-parser"
1212
- version = "0.17.0"
1212
+ version = "0.17.1"
1213
1213
  dependencies = [
1214
1214
  "entities",
1215
1215
  "insta",
@@ -7,7 +7,7 @@ authors = ["Johan Larsson <johan@jolars.co>"]
7
7
 
8
8
  [package]
9
9
  name = "panache"
10
- version = "2.54.0"
10
+ version = "2.55.0"
11
11
  edition.workspace = true
12
12
  readme = "README.md"
13
13
  description = "An LSP, formatter, and linter for Markdown, Quarto, and R Markdown"
@@ -49,8 +49,8 @@ path = "src/main.rs"
49
49
  required-features = ["cli"]
50
50
 
51
51
  [dependencies]
52
- panache-formatter = { path = "crates/panache-formatter", version = "0.12.0" }
53
- panache-parser = { path = "crates/panache-parser", version = "0.17.0", features = [
52
+ panache-formatter = { path = "crates/panache-formatter", version = "0.13.0" }
53
+ panache-parser = { path = "crates/panache-parser", version = "0.17.1", features = [
54
54
  "serde",
55
55
  "schema",
56
56
  ] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: panache-cli
3
- Version: 2.54.0
3
+ Version: 2.55.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.0](https://github.com/jolars/panache/compare/panache-formatter-v0.12.0...panache-formatter-v0.13.0) (2026-06-15)
4
+
5
+ ### Features
6
+ - **formatter:** nest broken binary math continuations by `math-indent` ([`5948ca1`](https://github.com/jolars/panache/commit/5948ca15bb1fa1350e91dbf8fdfc5e11f0e40c32))
7
+ - **formatter:** default `math-indent` to 2 ([`3d0422b`](https://github.com/jolars/panache/commit/3d0422b8355e57b2d9e04b0ac86128da414b75b2))
8
+
9
+ ### Bug Fixes
10
+ - **formatter:** fix(formatter): charge math-indent against display line-break budget ([`754faaa`](https://github.com/jolars/panache/commit/754faaa451ae418a681237f86bc5ad3d6096c92f))
11
+ - **parser:** claim trailing caption for table-first list item ([`a09f066`](https://github.com/jolars/panache/commit/a09f066a9493f3f626b44691023c59c151caafb8))
12
+ - **formatter:** re-emit list marker for table-first item ([`9633b86`](https://github.com/jolars/panache/commit/9633b8632a2f075df3d59852cf414933c9aaba44))
13
+
14
+ ### Dependencies
15
+ - updated crates/panache-parser to v0.17.1
16
+
3
17
  ## [0.12.0](https://github.com/jolars/panache/compare/panache-formatter-v0.11.0...panache-formatter-v0.12.0) (2026-06-13)
4
18
 
5
19
  ### Features
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "panache-formatter"
3
- version = "0.12.0"
3
+ version = "0.13.0"
4
4
  edition.workspace = true
5
5
  include = [
6
6
  "/src/**/*",
@@ -18,7 +18,7 @@ keywords = ["quarto", "pandoc", "markdown", "formatter"]
18
18
  categories = ["text-processing"]
19
19
 
20
20
  [dependencies]
21
- panache-parser = { path = "../panache-parser", version = "0.17.0" }
21
+ panache-parser = { path = "../panache-parser", version = "0.17.1" }
22
22
  log = { version = "0.4.31", features = ["release_max_level_debug"] }
23
23
  rowan = "0.16.1"
24
24
  unicode-width = "0.2"
@@ -200,7 +200,7 @@ impl Default for Config {
200
200
  formatter_extensions: FormatterExtensions::for_flavor(flavor),
201
201
  line_ending: Some(LineEnding::Auto),
202
202
  line_width: 80,
203
- math_indent: 0,
203
+ math_indent: 2,
204
204
  math_delimiter_style: MathDelimiterStyle::default(),
205
205
  tab_stops: TabStopMode::Normalize,
206
206
  tab_width: 4,
@@ -536,8 +536,13 @@ pub(super) fn format_inline_node(node: &SyntaxNode, config: &Config) -> String {
536
536
  result.push_str(&math::format_math(&content, &opts));
537
537
  result.push('\n');
538
538
  } else {
539
- // Process content: trim overall, then strip common leading whitespace
540
- let trimmed_content = content.trim();
539
+ // Process content: trim surrounding newlines (NOT leading spaces)
540
+ // and trailing whitespace, strip common leading whitespace, then
541
+ // re-indent every line by `math_indent`. Trimming only leading
542
+ // newlines keeps each line's true indent visible to `min_indent`,
543
+ // so the re-indent is idempotent: a later pass strips the pad as
544
+ // common indentation before re-applying it, rather than stacking.
545
+ let trimmed_content = content.trim_start_matches(['\n', '\r']).trim_end();
541
546
  if !trimmed_content.is_empty() {
542
547
  // Find minimum indentation across all non-empty lines
543
548
  let min_indent = trimmed_content
@@ -547,12 +552,18 @@ pub(super) fn format_inline_node(node: &SyntaxNode, config: &Config) -> String {
547
552
  .min()
548
553
  .unwrap_or(0);
549
554
 
550
- // Strip common indentation from each line
555
+ let pad = " ".repeat(config.math_indent);
556
+ // Strip common indentation, then re-indent each line.
551
557
  for line in trimmed_content.lines() {
552
- if line.len() >= min_indent {
553
- result.push_str(&line[min_indent..]);
558
+ let stripped = if line.len() >= min_indent {
559
+ &line[min_indent..]
554
560
  } else {
555
- result.push_str(line);
561
+ line
562
+ };
563
+ // Skip padding blank lines to avoid trailing whitespace.
564
+ if !stripped.is_empty() {
565
+ result.push_str(&pad);
566
+ result.push_str(stripped);
556
567
  }
557
568
  result.push('\n');
558
569
  }
@@ -1,6 +1,7 @@
1
1
  use crate::config::WrapMode;
2
2
  use crate::formatter::indent_utils::{calculate_list_item_indent, is_alignable_marker};
3
3
  use crate::formatter::inline_layout::{self, WrapStrategy};
4
+ use crate::formatter::tables;
4
5
  use crate::syntax::{AstNode, BlockQuote, FencedDiv, SyntaxKind, SyntaxNode};
5
6
  use rowan::NodeOrToken;
6
7
 
@@ -1188,6 +1189,80 @@ impl Formatter {
1188
1189
  self.format_node_sync(&child, content_indent);
1189
1190
  }
1190
1191
  }
1192
+ SyntaxKind::PIPE_TABLE | SyntaxKind::GRID_TABLE => {
1193
+ // A table can be a LIST_ITEM's sole/first child (the parser
1194
+ // nests it; e.g. `- | a | b |`). The wrapping pass above
1195
+ // emits nothing for an item with no PLAIN/PARAGRAPH
1196
+ // content_node, so re-emit the marker here — otherwise it is
1197
+ // dropped and the table floats out of the list (and the
1198
+ // ordered-list re-indent breaks idempotency). The first
1199
+ // table line sits on the marker line; a bare marker with the
1200
+ // table indented below would reparse the table as a
1201
+ // paragraph.
1202
+ //
1203
+ // Captioned tables splice too: the parser now nests a
1204
+ // table-first item's trailing `: cap`/`Table: cap` as the
1205
+ // table's `TABLE_CAPTION` (matching pandoc), so the whole
1206
+ // table — caption rendered below by `format_pipe_table` /
1207
+ // `format_grid_table` at `content_indent` — goes on the
1208
+ // marker line. The splice strips only the first line's
1209
+ // indent, so the caption keeps its indentation.
1210
+ let no_content_emitted = lines.is_empty()
1211
+ && preserve_lines.is_none()
1212
+ && sentence_lines.is_none()
1213
+ && content_node.is_none()
1214
+ && !has_only_empty_nested_list;
1215
+ let prev_kind = child.prev_sibling().map(|s| s.kind());
1216
+ let is_first_real_child = !matches!(
1217
+ prev_kind,
1218
+ Some(SyntaxKind::PLAIN)
1219
+ | Some(SyntaxKind::PARAGRAPH)
1220
+ | Some(SyntaxKind::HEADING)
1221
+ | Some(SyntaxKind::CODE_BLOCK)
1222
+ | Some(SyntaxKind::BLOCK_QUOTE)
1223
+ | Some(SyntaxKind::LIST)
1224
+ | Some(SyntaxKind::HORIZONTAL_RULE)
1225
+ | Some(SyntaxKind::HTML_BLOCK)
1226
+ | Some(SyntaxKind::HTML_BLOCK_DIV)
1227
+ | Some(SyntaxKind::PIPE_TABLE)
1228
+ | Some(SyntaxKind::GRID_TABLE)
1229
+ );
1230
+ let content_indent = list_indent.hanging_indent(total_indent);
1231
+ // The marker prefix occupies exactly `content_indent`
1232
+ // columns — except under `four_space_rule` (a flat tab
1233
+ // stop) or a task checkbox (which widens the marker line);
1234
+ // in those cases the splice would misalign, so fall back.
1235
+ let prefix_width = total_indent
1236
+ + list_indent.marker_padding
1237
+ + marker.len()
1238
+ + list_indent.spaces_after;
1239
+ if no_content_emitted
1240
+ && is_first_real_child
1241
+ && checkbox.is_none()
1242
+ && prefix_width == content_indent
1243
+ {
1244
+ // First table line on the marker line. Both `prefix` and
1245
+ // the table's first line are exactly `content_indent`
1246
+ // ASCII spaces wide, so splicing is byte-safe.
1247
+ let prefix = format!(
1248
+ "{}{}{}{}",
1249
+ " ".repeat(total_indent),
1250
+ " ".repeat(list_indent.marker_padding),
1251
+ marker,
1252
+ " ".repeat(list_indent.spaces_after),
1253
+ );
1254
+ let table_str = match child.kind() {
1255
+ SyntaxKind::PIPE_TABLE => {
1256
+ tables::format_pipe_table(&child, &self.config, content_indent)
1257
+ }
1258
+ _ => tables::format_grid_table(&child, &self.config, content_indent),
1259
+ };
1260
+ self.output.push_str(&prefix);
1261
+ self.output.push_str(&table_str[content_indent..]);
1262
+ } else {
1263
+ self.format_node_sync(&child, content_indent);
1264
+ }
1265
+ }
1191
1266
  _ => {
1192
1267
  // Other block elements - format with proper indentation
1193
1268
  let content_indent = list_indent.hanging_indent(total_indent);
@@ -104,9 +104,11 @@ Returned unchanged, never reflowed:
104
104
  every later relation starts a continuation **aligned under the first
105
105
  relation's column**. Then any relation segment that is still over-width
106
106
  splits before each top-level **binary** operator, with each `+ term` nested
107
- one indent step (2 spaces) deeper, under the relation's right-hand side. It
108
- is source-cosmetic only --- math ignores whitespace, so the rendered equation
109
- is unchanged:
107
+ one indent step (2 spaces) deeper, under the relation's right-hand side. The
108
+ width budget charges the flat `math-indent` against `line-width`, so a broken
109
+ line plus its leading indent still stays within `line-width`. It is
110
+ source-cosmetic only --- math ignores whitespace, so the rendered equation is
111
+ unchanged:
110
112
 
111
113
  ```
112
114
  A = aaaaaaaaaa
@@ -52,6 +52,11 @@
52
52
  //! stays on one line, like an unbreakable long word in prose reflow. Inline and
53
53
  //! environment-body math are not line-broken.
54
54
  //!
55
+ //! On top of that bare geometry, each binary continuation is nested one extra
56
+ //! `math-indent` step (the `cont_indent` argument) so the `+ term` rows sit
57
+ //! below — not flush with — the head/right-hand side they hang under. Relation
58
+ //! continuations keep the bare alignment so relation operators stay in a column.
59
+ //!
55
60
  //! ## Idempotency
56
61
  //!
57
62
  //! Indents are derived from the *logical* row (recomputed every pass), never
@@ -78,7 +83,17 @@ struct Break {
78
83
  /// Break one logical free-display row into physical content lines (no base
79
84
  /// math-indent, no trailing `\\` — the caller adds those). A row that fits, or
80
85
  /// that has no usable relation chain, is returned on one line unchanged.
81
- pub(super) fn break_free_row(elems: &[SyntaxElement], line_width: usize) -> Vec<String> {
86
+ ///
87
+ /// `cont_indent` (the block's `math-indent`) is added to every *binary*
88
+ /// continuation line so the broken `+ term` rows nest one indent step deeper
89
+ /// than the head/right-hand side they hang under. Relation continuations are
90
+ /// not shifted, so the relation operators stay column-aligned. With
91
+ /// `cont_indent == 0` the layout is the bare alignment geometry.
92
+ pub(super) fn break_free_row(
93
+ elems: &[SyntaxElement],
94
+ line_width: usize,
95
+ cont_indent: usize,
96
+ ) -> Vec<String> {
82
97
  // The unbroken, canonical single-line form — also the exact bytes the old
83
98
  // code emitted, so a row that fits is byte-identical to before.
84
99
  let single = render::render_inline(elems).trim().to_string();
@@ -94,13 +109,14 @@ pub(super) fn break_free_row(elems: &[SyntaxElement], line_width: usize) -> Vec<
94
109
  .collect();
95
110
 
96
111
  // Zero relations: a standalone binary chain. With no relation, the chain's
97
- // first term *is* the head, so each `+ term` aligns flush under it (bin_indent
98
- // 0) — the same rule the relation cases follow (a binary continuation aligns
99
- // under the first term of its right-hand side). A row with no top-level binary
100
- // op either (e.g. a lone wide `\frac`) falls through `break_binary_segment`'s
101
- // empty-`bins` guard and stays on one (over-width) line.
112
+ // first term *is* the head, so each `+ term` aligns under it, nested by
113
+ // `cont_indent` (the block's math-indent; 0 ⇒ flush) — the same rule the
114
+ // relation cases follow (a binary continuation aligns under the first term of
115
+ // its right-hand side, plus the continuation indent). A row with no top-level
116
+ // binary op either (e.g. a lone wide `\frac`) falls through
117
+ // `break_binary_segment`'s empty-`bins` guard and stays on one (over-width) line.
102
118
  if rels.is_empty() {
103
- return break_binary_segment(elems, 0, 0, line_width);
119
+ return break_binary_segment(elems, 0, cont_indent, line_width);
104
120
  }
105
121
 
106
122
  // Relation continuations align under the first relation's column; binary
@@ -114,7 +130,7 @@ pub(super) fn break_free_row(elems: &[SyntaxElement], line_width: usize) -> Vec<
114
130
  } else {
115
131
  prefix_width + 1
116
132
  };
117
- let bin_indent = rel_indent + BINARY_NEST;
133
+ let bin_indent = rel_indent + BINARY_NEST + cont_indent;
118
134
 
119
135
  // One relation: the whole row is a single segment. It keeps the lone relation
120
136
  // on the opening line; an over-width binary RHS breaks before each `+`, each
@@ -311,8 +327,14 @@ mod tests {
311
327
  node.children_with_tokens().collect()
312
328
  }
313
329
 
330
+ /// Bare alignment geometry (no extra continuation indent).
314
331
  fn lines(content: &str, width: usize) -> Vec<String> {
315
- break_free_row(&elems(content), width)
332
+ break_free_row(&elems(content), width, 0)
333
+ }
334
+
335
+ /// Layout with a continuation indent step (mirrors a non-zero `math-indent`).
336
+ fn lines_nested(content: &str, width: usize, cont_indent: usize) -> Vec<String> {
337
+ break_free_row(&elems(content), width, cont_indent)
316
338
  }
317
339
 
318
340
  fn rel_indices(content: &str) -> Vec<usize> {
@@ -423,6 +445,45 @@ mod tests {
423
445
  );
424
446
  }
425
447
 
448
+ #[test]
449
+ fn continuation_indent_nests_binary_terms_no_relation() {
450
+ // A non-zero continuation indent nests each `+ term` one step under the
451
+ // head instead of flush.
452
+ assert_eq!(
453
+ lines_nested("aaaa + bbbb + cccc + dddd", 12, 2),
454
+ vec!["aaaa", " + bbbb", " + cccc", " + dddd"],
455
+ );
456
+ }
457
+
458
+ #[test]
459
+ fn continuation_indent_nests_binary_terms_one_relation() {
460
+ // The `+ term` rows nest one step past the relation's right-hand side
461
+ // (bare `bin_indent` 4 + continuation indent 2 = 6).
462
+ assert_eq!(
463
+ lines_nested("A = aaaaaaaaaa + bbbbbbbbbb + cccccccccc", 20, 2),
464
+ vec!["A = aaaaaaaaaa", " + bbbbbbbbbb", " + cccccccccc"],
465
+ );
466
+ }
467
+
468
+ #[test]
469
+ fn continuation_indent_leaves_relation_columns_aligned() {
470
+ // Relation continuations stay aligned under the first `=`; only the
471
+ // binary terms pick up the extra continuation indent.
472
+ assert_eq!(
473
+ lines_nested(
474
+ "A = aaaaaaaaaa + bbbbbbbbbb = cccccccccc + dddddddddd",
475
+ 20,
476
+ 2
477
+ ),
478
+ vec![
479
+ "A = aaaaaaaaaa",
480
+ " + bbbbbbbbbb",
481
+ " = cccccccccc",
482
+ " + dddddddddd",
483
+ ],
484
+ );
485
+ }
486
+
426
487
  #[test]
427
488
  fn zero_relation_no_binary_stays_one_line() {
428
489
  // No relation and no top-level binary (the ops live inside the `\frac`
@@ -36,7 +36,13 @@ fn render_display(top: &[SyntaxElement], opts: &MathFormatOptions) -> String {
36
36
 
37
37
  for el in top {
38
38
  if el.kind() == SyntaxKind::MATH_ENVIRONMENT {
39
- flush_free_rows(&pending, &flat_indent, opts.line_width, &mut lines);
39
+ flush_free_rows(
40
+ &pending,
41
+ &flat_indent,
42
+ opts.line_width,
43
+ opts.math_indent,
44
+ &mut lines,
45
+ );
40
46
  pending.clear();
41
47
  if let Some(node) = el.as_node() {
42
48
  lines.extend(render_environment_lines(node, 0, opts));
@@ -45,7 +51,13 @@ fn render_display(top: &[SyntaxElement], opts: &MathFormatOptions) -> String {
45
51
  pending.push(el.clone());
46
52
  }
47
53
  }
48
- flush_free_rows(&pending, &flat_indent, opts.line_width, &mut lines);
54
+ flush_free_rows(
55
+ &pending,
56
+ &flat_indent,
57
+ opts.line_width,
58
+ opts.math_indent,
59
+ &mut lines,
60
+ );
49
61
  lines.join("\n")
50
62
  }
51
63
 
@@ -61,13 +73,17 @@ fn flush_free_rows(
61
73
  elems: &[SyntaxElement],
62
74
  indent: &str,
63
75
  line_width: usize,
76
+ cont_indent: usize,
64
77
  lines: &mut Vec<String>,
65
78
  ) {
66
79
  for row in split_logical_rows(elems) {
67
80
  if row.is_blank() {
68
81
  continue;
69
82
  }
70
- let physical = linebreak::break_free_row(&row.elems, line_width);
83
+ // Charge the flat math-indent against the budget so packed (and single)
84
+ // lines genuinely stay within `line_width` once the indent is prepended.
85
+ let budget = line_width.saturating_sub(indent.chars().count());
86
+ let physical = linebreak::break_free_row(&row.elems, budget, cont_indent);
71
87
  let last = physical.len() - 1;
72
88
  for (i, content) in physical.into_iter().enumerate() {
73
89
  // The trailing `\\` (if any) rides the final physical line.
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.17.1](https://github.com/jolars/panache/compare/panache-parser-v0.17.0...panache-parser-v0.17.1) (2026-06-15)
4
+
5
+ ### Bug Fixes
6
+ - **parser:** claim trailing caption for table-first list item ([`a09f066`](https://github.com/jolars/panache/commit/a09f066a9493f3f626b44691023c59c151caafb8))
7
+
3
8
  ## [0.17.0](https://github.com/jolars/panache/compare/panache-parser-v0.16.0...panache-parser-v0.17.0) (2026-06-13)
4
9
 
5
10
  ### Features
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "panache-parser"
3
- version = "0.17.0"
3
+ version = "0.17.1"
4
4
  edition.workspace = true
5
5
  readme = "README.md"
6
6
  include = [
@@ -601,6 +601,92 @@ impl<'a> Parser<'a> {
601
601
  Some(consumed.saturating_sub(1))
602
602
  }
603
603
 
604
+ /// When a new list item's marker line *begins a table* that is followed by a
605
+ /// trailing caption (`- | a | b |\n | - | - |\n\n : cap` or the
606
+ /// `Table:`/`table:` keyword form), parse the whole table-with-caption as the
607
+ /// item's content here, at the marker line.
608
+ ///
609
+ /// A marker-line table is normally buffered and recognized only at item
610
+ /// close via [`crate::parser::utils::list_item_buffer::ListItemBuffer`]'s
611
+ /// structural lift. That lift cannot see a *trailing* caption: the blank line
612
+ /// after the table flushes the buffer (`Table:` form), and a bare `: cap`
613
+ /// line is additionally claimed by the definition-list parser as a term/
614
+ /// definition (`: cap` form) — both before the caption ever reaches the
615
+ /// buffer. Parsing the table at the marker line instead lets the table
616
+ /// parser's own trailing-caption scan (`find_caption_after_table`) absorb the
617
+ /// caption, matching pandoc, which always treats `: cap` after a table as the
618
+ /// table's `Caption`, never a definition list.
619
+ ///
620
+ /// Gated on a caption actually being present so the *no-caption* marker-line
621
+ /// table keeps its existing buffer-lift CST untouched. Returns the number of
622
+ /// source lines consumed beyond the list-marker line.
623
+ fn maybe_open_table_with_trailing_caption_in_new_list_item(&mut self) -> Option<usize> {
624
+ if !self.config.extensions.table_captions {
625
+ return None;
626
+ }
627
+ if !(self.config.extensions.simple_tables
628
+ || self.config.extensions.multiline_tables
629
+ || self.config.extensions.grid_tables
630
+ || self.config.extensions.pipe_tables)
631
+ {
632
+ return None;
633
+ }
634
+
635
+ let Some(Container::ListItem {
636
+ content_col,
637
+ buffer,
638
+ ..
639
+ }) = self.containers.stack.last()
640
+ else {
641
+ return None;
642
+ };
643
+ // Only the marker line is buffered so far; a multi-segment buffer means
644
+ // more content already accumulated and this is not a fresh marker line.
645
+ if buffer.segment_count() != 1 {
646
+ return None;
647
+ }
648
+ // Cheap pre-filter: a table on the marker line begins with `|` or `+`.
649
+ let first = buffer.first_text()?;
650
+ if !matches!(
651
+ first.trim_start().as_bytes().first(),
652
+ Some(b'|') | Some(b'+')
653
+ ) {
654
+ return None;
655
+ }
656
+ let content_col = *content_col;
657
+
658
+ let bq_depth = self.current_blockquote_depth();
659
+ let prefix = ContainerPrefix::from_scalars(bq_depth, content_col, bq_depth > 0, 0, true);
660
+ let window = StrippedLines::new(&self.lines, self.pos, &prefix);
661
+
662
+ // A caption-led table (`- : cap` / `- Table: cap` then table) is the
663
+ // sibling function's job; never double-handle it here.
664
+ if tables::is_caption_followed_by_table(&window, self.pos) {
665
+ return None;
666
+ }
667
+
668
+ // Probe into a throwaway builder: only commit when the marker-line table
669
+ // actually pulls in a trailing caption. Otherwise leave the line buffered
670
+ // so the no-caption structural lift produces its established CST.
671
+ let mut probe = GreenNodeBuilder::new();
672
+ let _ = try_parse_any_table_kind(&window, &mut probe, self.config)?;
673
+ let probe_root = SyntaxNode::new_root(probe.finish());
674
+ let has_caption = probe_root
675
+ .children()
676
+ .any(|c| c.kind() == SyntaxKind::TABLE_CAPTION);
677
+ if !has_caption {
678
+ return None;
679
+ }
680
+
681
+ // Commit: emit the table (with its `TABLE_CAPTION`) as the list item's
682
+ // content and drop the buffered marker line so it isn't also emitted.
683
+ let consumed = try_parse_any_table_kind(&window, &mut self.builder, self.config)?;
684
+ if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
685
+ buffer.clear();
686
+ }
687
+ Some(consumed.saturating_sub(1))
688
+ }
689
+
604
690
  /// CommonMark §5.2 rule #2: when a list marker is followed by ≥ 5 columns
605
691
  /// of whitespace and non-empty content, the content begins as an indented
606
692
  /// code block on the marker line. The marker parser collapses the post-
@@ -1202,6 +1288,11 @@ impl<'a> Parser<'a> {
1202
1288
  if let Some(extras) = self.maybe_open_caption_table_in_new_list_item() {
1203
1289
  return extras;
1204
1290
  }
1291
+ if let Some(extras) =
1292
+ self.maybe_open_table_with_trailing_caption_in_new_list_item()
1293
+ {
1294
+ return extras;
1295
+ }
1205
1296
  self.maybe_open_indented_code_in_new_list_item();
1206
1297
  return self.dispatch_bq_after_list_item(finish);
1207
1298
  }
@@ -1223,6 +1314,9 @@ impl<'a> Parser<'a> {
1223
1314
  if let Some(extras) = self.maybe_open_caption_table_in_new_list_item() {
1224
1315
  return extras;
1225
1316
  }
1317
+ if let Some(extras) = self.maybe_open_table_with_trailing_caption_in_new_list_item() {
1318
+ return extras;
1319
+ }
1226
1320
  self.maybe_open_indented_code_in_new_list_item();
1227
1321
  return self.dispatch_bq_after_list_item(finish);
1228
1322
  }
@@ -1265,6 +1359,9 @@ impl<'a> Parser<'a> {
1265
1359
  if let Some(extras) = self.maybe_open_caption_table_in_new_list_item() {
1266
1360
  return extras;
1267
1361
  }
1362
+ if let Some(extras) = self.maybe_open_table_with_trailing_caption_in_new_list_item() {
1363
+ return extras;
1364
+ }
1268
1365
  self.maybe_open_indented_code_in_new_list_item();
1269
1366
  return self.dispatch_bq_after_list_item(finish);
1270
1367
  }
@@ -1313,6 +1410,9 @@ impl<'a> Parser<'a> {
1313
1410
  if let Some(extras) = self.maybe_open_caption_table_in_new_list_item() {
1314
1411
  return extras;
1315
1412
  }
1413
+ if let Some(extras) = self.maybe_open_table_with_trailing_caption_in_new_list_item() {
1414
+ return extras;
1415
+ }
1316
1416
  self.maybe_open_indented_code_in_new_list_item();
1317
1417
  self.dispatch_bq_after_list_item(finish)
1318
1418
  }
@@ -2634,6 +2734,10 @@ impl<'a> Parser<'a> {
2634
2734
  extras
2635
2735
  } else if let Some(extras) = self.maybe_open_caption_table_in_new_list_item() {
2636
2736
  extras
2737
+ } else if let Some(extras) =
2738
+ self.maybe_open_table_with_trailing_caption_in_new_list_item()
2739
+ {
2740
+ extras
2637
2741
  } else {
2638
2742
  self.maybe_open_indented_code_in_new_list_item();
2639
2743
  self.dispatch_bq_after_list_item(finish)
@@ -3656,6 +3760,32 @@ impl<'a> Parser<'a> {
3656
3760
  /// Pandoc parses `Term\n: # Heading\n Some text` as DefinitionList where the
3657
3761
  /// definition contains [Header, Plain]; the `# Heading` line is a real Header
3658
3762
  /// inside the definition, not text that happens to start with `#`.
3763
+ /// Try each enabled table kind in turn (Grid → Multiline → Pipe → Simple),
3764
+ /// emitting the first match into `builder` and returning the lines consumed.
3765
+ /// Every kind validates before its first `start_node`, so on a full miss the
3766
+ /// builder is left untouched and `None` is returned. Mirrors the dispatcher's
3767
+ /// `first_kind_at` cascade for the list-item marker-line table paths.
3768
+ fn try_parse_any_table_kind(
3769
+ window: &StrippedLines,
3770
+ builder: &mut GreenNodeBuilder<'static>,
3771
+ config: &ParserOptions,
3772
+ ) -> Option<usize> {
3773
+ let mut consumed = None;
3774
+ if config.extensions.grid_tables {
3775
+ consumed = tables::try_parse_grid_table(window, builder, config);
3776
+ }
3777
+ if consumed.is_none() && config.extensions.multiline_tables {
3778
+ consumed = tables::try_parse_multiline_table(window, builder, config);
3779
+ }
3780
+ if consumed.is_none() && config.extensions.pipe_tables {
3781
+ consumed = tables::try_parse_pipe_table(window, builder, config);
3782
+ }
3783
+ if consumed.is_none() && config.extensions.simple_tables {
3784
+ consumed = tables::try_parse_simple_table(window, builder, config);
3785
+ }
3786
+ consumed
3787
+ }
3788
+
3659
3789
  fn emit_definition_plain_or_heading(
3660
3790
  builder: &mut GreenNodeBuilder<'static>,
3661
3791
  text: &str,