html-to-markdown 3.4.0 → 3.6.0.pre.rc.24

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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +347 -0
  4. data/Steepfile +10 -2
  5. data/ext/html_to_markdown_rb/Cargo.toml +3 -2
  6. data/ext/html_to_markdown_rb/extconf.rb +5 -5
  7. data/ext/html_to_markdown_rb/native/Cargo.lock +962 -0
  8. data/ext/html_to_markdown_rb/native/Cargo.toml +6 -11
  9. data/ext/html_to_markdown_rb/native/extconf.rb +14 -0
  10. data/ext/html_to_markdown_rb/src/lib.rs +1715 -646
  11. data/lib/html_to_markdown/native.rb +913 -37
  12. data/lib/html_to_markdown/version.rb +3 -3
  13. data/lib/html_to_markdown.rb +9 -4
  14. data/lib/html_to_markdown_rb.so +0 -0
  15. data/sig/types.rbs +59 -292
  16. metadata +32 -179
  17. data/ext/html_to_markdown_rb/Makefile +0 -592
  18. data/lib/bin/html-to-markdown +0 -0
  19. data/vendor/Cargo.toml +0 -33
  20. data/vendor/html-to-markdown-rs/Cargo.toml +0 -54
  21. data/vendor/html-to-markdown-rs/README.md +0 -278
  22. data/vendor/html-to-markdown-rs/examples/basic.rs +0 -24
  23. data/vendor/html-to-markdown-rs/examples/table.rs +0 -25
  24. data/vendor/html-to-markdown-rs/examples/test_deser.rs +0 -12
  25. data/vendor/html-to-markdown-rs/examples/test_escape.rs +0 -58
  26. data/vendor/html-to-markdown-rs/examples/test_inline_formatting.rs +0 -113
  27. data/vendor/html-to-markdown-rs/examples/test_lists.rs +0 -39
  28. data/vendor/html-to-markdown-rs/examples/test_semantic_tags.rs +0 -89
  29. data/vendor/html-to-markdown-rs/examples/test_tables.rs +0 -100
  30. data/vendor/html-to-markdown-rs/examples/test_task_lists.rs +0 -61
  31. data/vendor/html-to-markdown-rs/examples/test_whitespace.rs +0 -34
  32. data/vendor/html-to-markdown-rs/src/convert_api.rs +0 -349
  33. data/vendor/html-to-markdown-rs/src/converter/block/blockquote.rs +0 -178
  34. data/vendor/html-to-markdown-rs/src/converter/block/container.rs +0 -114
  35. data/vendor/html-to-markdown-rs/src/converter/block/div.rs +0 -149
  36. data/vendor/html-to-markdown-rs/src/converter/block/heading.rs +0 -428
  37. data/vendor/html-to-markdown-rs/src/converter/block/horizontal_rule.rs +0 -103
  38. data/vendor/html-to-markdown-rs/src/converter/block/line_break.rs +0 -89
  39. data/vendor/html-to-markdown-rs/src/converter/block/mod.rs +0 -10
  40. data/vendor/html-to-markdown-rs/src/converter/block/paragraph.rs +0 -140
  41. data/vendor/html-to-markdown-rs/src/converter/block/preformatted.rs +0 -298
  42. data/vendor/html-to-markdown-rs/src/converter/block/table/builder.rs +0 -453
  43. data/vendor/html-to-markdown-rs/src/converter/block/table/caption.rs +0 -44
  44. data/vendor/html-to-markdown-rs/src/converter/block/table/cell.rs +0 -276
  45. data/vendor/html-to-markdown-rs/src/converter/block/table/cells.rs +0 -336
  46. data/vendor/html-to-markdown-rs/src/converter/block/table/layout.rs +0 -58
  47. data/vendor/html-to-markdown-rs/src/converter/block/table/mod.rs +0 -266
  48. data/vendor/html-to-markdown-rs/src/converter/block/table/scanner.rs +0 -146
  49. data/vendor/html-to-markdown-rs/src/converter/block/table/utils.rs +0 -34
  50. data/vendor/html-to-markdown-rs/src/converter/block/unknown.rs +0 -138
  51. data/vendor/html-to-markdown-rs/src/converter/context.rs +0 -208
  52. data/vendor/html-to-markdown-rs/src/converter/dom_context.rs +0 -337
  53. data/vendor/html-to-markdown-rs/src/converter/form/elements.rs +0 -770
  54. data/vendor/html-to-markdown-rs/src/converter/form/mod.rs +0 -82
  55. data/vendor/html-to-markdown-rs/src/converter/format/djot.rs +0 -64
  56. data/vendor/html-to-markdown-rs/src/converter/format/markdown.rs +0 -59
  57. data/vendor/html-to-markdown-rs/src/converter/format/mod.rs +0 -43
  58. data/vendor/html-to-markdown-rs/src/converter/handlers/blockquote.rs +0 -173
  59. data/vendor/html-to-markdown-rs/src/converter/handlers/code_block.rs +0 -434
  60. data/vendor/html-to-markdown-rs/src/converter/handlers/graphic.rs +0 -234
  61. data/vendor/html-to-markdown-rs/src/converter/handlers/image.rs +0 -282
  62. data/vendor/html-to-markdown-rs/src/converter/handlers/link.rs +0 -316
  63. data/vendor/html-to-markdown-rs/src/converter/handlers/mod.rs +0 -26
  64. data/vendor/html-to-markdown-rs/src/converter/inline/code.rs +0 -306
  65. data/vendor/html-to-markdown-rs/src/converter/inline/emphasis.rs +0 -345
  66. data/vendor/html-to-markdown-rs/src/converter/inline/link.rs +0 -428
  67. data/vendor/html-to-markdown-rs/src/converter/inline/mod.rs +0 -237
  68. data/vendor/html-to-markdown-rs/src/converter/inline/ruby.rs +0 -337
  69. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/marks.rs +0 -566
  70. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/mod.rs +0 -86
  71. data/vendor/html-to-markdown-rs/src/converter/inline/semantic/typography.rs +0 -558
  72. data/vendor/html-to-markdown-rs/src/converter/list/definition.rs +0 -232
  73. data/vendor/html-to-markdown-rs/src/converter/list/item.rs +0 -332
  74. data/vendor/html-to-markdown-rs/src/converter/list/mod.rs +0 -70
  75. data/vendor/html-to-markdown-rs/src/converter/list/ordered.rs +0 -201
  76. data/vendor/html-to-markdown-rs/src/converter/list/unordered.rs +0 -195
  77. data/vendor/html-to-markdown-rs/src/converter/list/utils.rs +0 -314
  78. data/vendor/html-to-markdown-rs/src/converter/main.rs +0 -710
  79. data/vendor/html-to-markdown-rs/src/converter/main_helpers.rs +0 -452
  80. data/vendor/html-to-markdown-rs/src/converter/media/embedded.rs +0 -393
  81. data/vendor/html-to-markdown-rs/src/converter/media/graphic.rs +0 -4
  82. data/vendor/html-to-markdown-rs/src/converter/media/image.rs +0 -183
  83. data/vendor/html-to-markdown-rs/src/converter/media/mod.rs +0 -87
  84. data/vendor/html-to-markdown-rs/src/converter/media/svg.rs +0 -280
  85. data/vendor/html-to-markdown-rs/src/converter/metadata.rs +0 -220
  86. data/vendor/html-to-markdown-rs/src/converter/mod.rs +0 -156
  87. data/vendor/html-to-markdown-rs/src/converter/plain_text.rs +0 -516
  88. data/vendor/html-to-markdown-rs/src/converter/preprocessing_helpers.rs +0 -201
  89. data/vendor/html-to-markdown-rs/src/converter/reference_collector.rs +0 -69
  90. data/vendor/html-to-markdown-rs/src/converter/semantic/attributes.rs +0 -269
  91. data/vendor/html-to-markdown-rs/src/converter/semantic/definition_list.rs +0 -266
  92. data/vendor/html-to-markdown-rs/src/converter/semantic/figure.rs +0 -391
  93. data/vendor/html-to-markdown-rs/src/converter/semantic/mod.rs +0 -112
  94. data/vendor/html-to-markdown-rs/src/converter/semantic/sectioning.rs +0 -85
  95. data/vendor/html-to-markdown-rs/src/converter/semantic/summary.rs +0 -324
  96. data/vendor/html-to-markdown-rs/src/converter/text/mod.rs +0 -8
  97. data/vendor/html-to-markdown-rs/src/converter/text/processing.rs +0 -56
  98. data/vendor/html-to-markdown-rs/src/converter/text_node.rs +0 -269
  99. data/vendor/html-to-markdown-rs/src/converter/utility/attributes.rs +0 -151
  100. data/vendor/html-to-markdown-rs/src/converter/utility/caching.rs +0 -74
  101. data/vendor/html-to-markdown-rs/src/converter/utility/content.rs +0 -271
  102. data/vendor/html-to-markdown-rs/src/converter/utility/mod.rs +0 -17
  103. data/vendor/html-to-markdown-rs/src/converter/utility/preprocessing.rs +0 -1002
  104. data/vendor/html-to-markdown-rs/src/converter/utility/serialization.rs +0 -126
  105. data/vendor/html-to-markdown-rs/src/converter/utility/siblings.rs +0 -97
  106. data/vendor/html-to-markdown-rs/src/converter/visitor_hooks.rs +0 -189
  107. data/vendor/html-to-markdown-rs/src/error.rs +0 -43
  108. data/vendor/html-to-markdown-rs/src/exports.rs +0 -24
  109. data/vendor/html-to-markdown-rs/src/inline_images.rs +0 -336
  110. data/vendor/html-to-markdown-rs/src/lib.rs +0 -139
  111. data/vendor/html-to-markdown-rs/src/metadata/collector.rs +0 -457
  112. data/vendor/html-to-markdown-rs/src/metadata/config.rs +0 -394
  113. data/vendor/html-to-markdown-rs/src/metadata/extraction.rs +0 -398
  114. data/vendor/html-to-markdown-rs/src/metadata/mod.rs +0 -288
  115. data/vendor/html-to-markdown-rs/src/metadata/types.rs +0 -477
  116. data/vendor/html-to-markdown-rs/src/options/conversion.rs +0 -559
  117. data/vendor/html-to-markdown-rs/src/options/inline_image.rs +0 -111
  118. data/vendor/html-to-markdown-rs/src/options/mod.rs +0 -20
  119. data/vendor/html-to-markdown-rs/src/options/preprocessing.rs +0 -201
  120. data/vendor/html-to-markdown-rs/src/options/validation.rs +0 -416
  121. data/vendor/html-to-markdown-rs/src/prelude.rs +0 -1
  122. data/vendor/html-to-markdown-rs/src/rcdom.rs +0 -487
  123. data/vendor/html-to-markdown-rs/src/text.rs +0 -358
  124. data/vendor/html-to-markdown-rs/src/types/document.rs +0 -191
  125. data/vendor/html-to-markdown-rs/src/types/mod.rs +0 -17
  126. data/vendor/html-to-markdown-rs/src/types/result.rs +0 -54
  127. data/vendor/html-to-markdown-rs/src/types/structure_builder.rs +0 -791
  128. data/vendor/html-to-markdown-rs/src/types/structure_collector.rs +0 -483
  129. data/vendor/html-to-markdown-rs/src/types/tables.rs +0 -52
  130. data/vendor/html-to-markdown-rs/src/types/warnings.rs +0 -33
  131. data/vendor/html-to-markdown-rs/src/validation.rs +0 -158
  132. data/vendor/html-to-markdown-rs/src/visitor/default_impl.rs +0 -63
  133. data/vendor/html-to-markdown-rs/src/visitor/mod.rs +0 -41
  134. data/vendor/html-to-markdown-rs/src/visitor/traits.rs +0 -370
  135. data/vendor/html-to-markdown-rs/src/visitor/types.rs +0 -319
  136. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/callbacks/mod.rs +0 -1
  137. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/content.rs +0 -126
  138. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/mod.rs +0 -27
  139. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/state.rs +0 -110
  140. data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/traversal.rs +0 -250
  141. data/vendor/html-to-markdown-rs/src/visitor_helpers.rs +0 -597
  142. data/vendor/html-to-markdown-rs/src/wrapper/sync.rs +0 -413
  143. data/vendor/html-to-markdown-rs/src/wrapper/utils.rs +0 -290
  144. data/vendor/html-to-markdown-rs/src/wrapper.rs +0 -9
  145. data/vendor/html-to-markdown-rs/tests/br_in_inline_test.rs +0 -87
  146. data/vendor/html-to-markdown-rs/tests/commonmark_compliance_test.rs +0 -297
  147. data/vendor/html-to-markdown-rs/tests/djot_output_test.rs +0 -153
  148. data/vendor/html-to-markdown-rs/tests/exclude_selectors_test.rs +0 -132
  149. data/vendor/html-to-markdown-rs/tests/integration_test.rs +0 -631
  150. data/vendor/html-to-markdown-rs/tests/issue_121_regressions.rs +0 -49
  151. data/vendor/html-to-markdown-rs/tests/issue_127_regressions.rs +0 -58
  152. data/vendor/html-to-markdown-rs/tests/issue_128_regressions.rs +0 -17
  153. data/vendor/html-to-markdown-rs/tests/issue_131_regressions.rs +0 -41
  154. data/vendor/html-to-markdown-rs/tests/issue_134_regressions.rs +0 -40
  155. data/vendor/html-to-markdown-rs/tests/issue_139_regressions.rs +0 -26
  156. data/vendor/html-to-markdown-rs/tests/issue_140_regressions.rs +0 -185
  157. data/vendor/html-to-markdown-rs/tests/issue_143_regressions.rs +0 -100
  158. data/vendor/html-to-markdown-rs/tests/issue_145_regressions.rs +0 -133
  159. data/vendor/html-to-markdown-rs/tests/issue_146_regressions.rs +0 -144
  160. data/vendor/html-to-markdown-rs/tests/issue_176_regressions.rs +0 -62
  161. data/vendor/html-to-markdown-rs/tests/issue_190_regressions.rs +0 -128
  162. data/vendor/html-to-markdown-rs/tests/issue_199_regressions.rs +0 -20
  163. data/vendor/html-to-markdown-rs/tests/issue_200_regressions.rs +0 -62
  164. data/vendor/html-to-markdown-rs/tests/issue_212_regressions.rs +0 -68
  165. data/vendor/html-to-markdown-rs/tests/issue_216_217_regressions.rs +0 -87
  166. data/vendor/html-to-markdown-rs/tests/issue_336_regressions.rs +0 -74
  167. data/vendor/html-to-markdown-rs/tests/issue_339_regressions.rs +0 -92
  168. data/vendor/html-to-markdown-rs/tests/issue_347_regressions.rs +0 -154
  169. data/vendor/html-to-markdown-rs/tests/issue_348_visitor_plain.rs +0 -93
  170. data/vendor/html-to-markdown-rs/tests/json_ld_script_extraction.rs +0 -44
  171. data/vendor/html-to-markdown-rs/tests/lists_test.rs +0 -199
  172. data/vendor/html-to-markdown-rs/tests/plain_output_test.rs +0 -273
  173. data/vendor/html-to-markdown-rs/tests/preprocessing_tests.rs +0 -61
  174. data/vendor/html-to-markdown-rs/tests/reference_links_test.rs +0 -169
  175. data/vendor/html-to-markdown-rs/tests/sectioning_elements_test.rs +0 -137
  176. data/vendor/html-to-markdown-rs/tests/skip_images_test.rs +0 -522
  177. data/vendor/html-to-markdown-rs/tests/tables_test.rs +0 -743
  178. data/vendor/html-to-markdown-rs/tests/test_custom_elements.rs +0 -41
  179. data/vendor/html-to-markdown-rs/tests/test_issue_187.rs +0 -204
  180. data/vendor/html-to-markdown-rs/tests/test_issue_218.rs +0 -68
  181. data/vendor/html-to-markdown-rs/tests/test_issue_277.rs +0 -77
  182. data/vendor/html-to-markdown-rs/tests/test_max_depth.rs +0 -82
  183. data/vendor/html-to-markdown-rs/tests/test_nested_simple.rs +0 -45
  184. data/vendor/html-to-markdown-rs/tests/test_script_style_stripping.rs +0 -396
  185. data/vendor/html-to-markdown-rs/tests/test_spa_bisect.rs +0 -34
  186. data/vendor/html-to-markdown-rs/tests/visitor_code_integration_test.rs +0 -121
  187. data/vendor/html-to-markdown-rs/tests/visitor_integration_test.rs +0 -1190
  188. data/vendor/html-to-markdown-rs/tests/xml_tables_test.rs +0 -372
@@ -1,82 +0,0 @@
1
- //! Form element handlers for HTML to Markdown conversion.
2
- //!
3
- //! This module provides specialized handlers for HTML form elements:
4
- //! - Container elements (form, fieldset, legend, label)
5
- //! - Input elements (input, textarea, select, option, optgroup, button)
6
- //! - Measurement elements (progress, meter, output, datalist)
7
- //!
8
- //! These handlers are designed to be extracted from the main `converter.rs`
9
- //! file and integrated through the dispatcher function.
10
- //!
11
- //! **Integration Pattern:**
12
- //! Each handler function takes the same signature:
13
- //! - `tag_name: &str` - The HTML tag being processed
14
- //! - `node_handle: &NodeHandle` - The DOM node handle
15
- //! - `parser: &Parser` - The HTML parser reference
16
- //! - `output: &mut String` - The output buffer to write to
17
- //! - `options: &ConversionOptions` - Conversion configuration
18
- //! - `ctx: &Context` - Processing context (state tracking)
19
- //! - `depth: usize` - Current DOM tree depth
20
- //! - `dom_ctx: &DomContext` - DOM context for tree relationships
21
- //!
22
- //! The main dispatcher function `dispatch_form_handler` routes tags to
23
- //! their appropriate handlers and returns a boolean indicating success.
24
-
25
- pub mod elements;
26
-
27
- // Re-export types from parent module for submodule access
28
- pub use super::walk_node;
29
- pub use super::{Context, DomContext};
30
-
31
- // Re-export handler function for direct use
32
- pub use elements::handle as handle_form_elements;
33
-
34
- // Re-exports are done via the dispatch function parameter types
35
-
36
- /// Dispatches form element handling to the appropriate handler.
37
- ///
38
- /// This function routes form-related HTML elements to their specialized handlers
39
- /// based on tag name. It is designed to be called from the main `walk_node`
40
- /// function in `converter.rs`.
41
- ///
42
- /// # Routing Table
43
- ///
44
- /// The following tag routes are supported:
45
- /// - **Containers**: form, fieldset, legend, label
46
- /// - **Inputs**: input, textarea, select, option, optgroup, button
47
- /// - **Measurements**: progress, meter, output, datalist
48
- ///
49
- /// # Returns
50
- ///
51
- /// Returns `true` if the tag was successfully handled by a form handler,
52
- /// `false` if the tag is not a form element and requires other handling.
53
- ///
54
- /// # Example
55
- ///
56
- /// ```text
57
- /// if dispatch_form_handler(tag_name, &node_handle, &parser, output, options, ctx, depth, dom_ctx) {
58
- /// // Tag was handled
59
- /// } else {
60
- /// // Continue with other handlers
61
- /// }
62
- /// ```
63
- pub fn dispatch_form_handler(
64
- tag_name: &str,
65
- node_handle: &tl::NodeHandle,
66
- parser: &tl::Parser,
67
- output: &mut String,
68
- options: &crate::options::ConversionOptions,
69
- ctx: &super::Context,
70
- depth: usize,
71
- dom_ctx: &super::DomContext,
72
- ) -> bool {
73
- match tag_name {
74
- // Form containers and metadata
75
- "form" | "fieldset" | "legend" | "label" | "input" | "textarea" | "select" | "option" | "optgroup"
76
- | "button" | "progress" | "meter" | "output" | "datalist" => {
77
- handle_form_elements(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
78
- true
79
- }
80
- _ => false,
81
- }
82
- }
@@ -1,64 +0,0 @@
1
- //! Djot format renderer.
2
-
3
- use super::FormatRenderer;
4
-
5
- /// Renderer for Djot lightweight markup output.
6
- #[derive(Debug, Clone, Copy, Default)]
7
- pub struct DjotRenderer;
8
-
9
- impl FormatRenderer for DjotRenderer {
10
- fn emphasis(&self, content: &str) -> String {
11
- format!("_{content}_")
12
- }
13
-
14
- fn strong(&self, content: &str, _symbol: char) -> String {
15
- // Djot always uses single asterisk for strong
16
- format!("*{content}*")
17
- }
18
-
19
- fn strikethrough(&self, content: &str) -> String {
20
- format!("{{-{content}-}}")
21
- }
22
-
23
- fn highlight(&self, content: &str) -> String {
24
- format!("{{={content}=}}")
25
- }
26
-
27
- fn inserted(&self, content: &str) -> String {
28
- format!("{{+{content}+}}")
29
- }
30
-
31
- fn subscript(&self, content: &str, _custom_symbol: &str) -> String {
32
- // Djot has native subscript support
33
- format!("~{content}~")
34
- }
35
-
36
- fn superscript(&self, content: &str, _custom_symbol: &str) -> String {
37
- // Djot has native superscript support
38
- format!("^{content}^")
39
- }
40
-
41
- fn span_with_attributes(&self, content: &str, classes: &[&str], id: Option<&str>) -> String {
42
- if classes.is_empty() && id.is_none() {
43
- return content.to_string();
44
- }
45
-
46
- let mut attrs = classes.iter().map(|c| format!(".{c}")).collect::<Vec<_>>();
47
- if let Some(id_val) = id {
48
- attrs.push(format!("#{id_val}"));
49
- }
50
- format!("[{content}]{{{}}}", attrs.join(" "))
51
- }
52
-
53
- fn div_with_attributes(&self, content: &str, classes: &[&str]) -> String {
54
- if classes.is_empty() {
55
- return content.to_string();
56
- }
57
- let class_str = classes.join(" ");
58
- format!("::: {class_str}\n{content}\n:::")
59
- }
60
-
61
- fn is_djot(&self) -> bool {
62
- true
63
- }
64
- }
@@ -1,59 +0,0 @@
1
- //! Markdown format renderer.
2
-
3
- use super::FormatRenderer;
4
-
5
- /// Renderer for standard Markdown output.
6
- #[derive(Debug, Clone, Copy, Default)]
7
- pub struct MarkdownRenderer;
8
-
9
- impl FormatRenderer for MarkdownRenderer {
10
- fn emphasis(&self, content: &str) -> String {
11
- format!("*{content}*")
12
- }
13
-
14
- fn strong(&self, content: &str, symbol: char) -> String {
15
- format!("{symbol}{symbol}{content}{symbol}{symbol}")
16
- }
17
-
18
- fn strikethrough(&self, content: &str) -> String {
19
- format!("~~{content}~~")
20
- }
21
-
22
- fn highlight(&self, content: &str) -> String {
23
- format!("=={content}==")
24
- }
25
-
26
- fn inserted(&self, content: &str) -> String {
27
- format!("++{content}++")
28
- }
29
-
30
- fn subscript(&self, content: &str, custom_symbol: &str) -> String {
31
- if custom_symbol.is_empty() {
32
- format!("~{content}~")
33
- } else {
34
- format!("{custom_symbol}{content}{custom_symbol}")
35
- }
36
- }
37
-
38
- fn superscript(&self, content: &str, custom_symbol: &str) -> String {
39
- if custom_symbol.is_empty() {
40
- format!("^{content}^")
41
- } else {
42
- format!("{custom_symbol}{content}{custom_symbol}")
43
- }
44
- }
45
-
46
- fn span_with_attributes(&self, content: &str, _classes: &[&str], _id: Option<&str>) -> String {
47
- // Markdown doesn't support span attributes, just return content
48
- content.to_string()
49
- }
50
-
51
- fn div_with_attributes(&self, content: &str, _classes: &[&str]) -> String {
52
- // Markdown doesn't support div attributes, just return content
53
- content.to_string()
54
- }
55
-
56
- fn is_djot(&self) -> bool {
57
- false
58
- }
59
- }
@@ -1,43 +0,0 @@
1
- //! Output format renderers for HTML to Markdown/Djot conversion.
2
- //!
3
- //! This module provides format-specific rendering through the `FormatRenderer` trait,
4
- //! allowing clean separation between Markdown and Djot output syntax.
5
-
6
- mod djot;
7
- mod markdown;
8
-
9
- /// Trait for format-specific rendering of inline elements.
10
- ///
11
- /// Implementations provide the syntax for emphasis, strong, strikethrough, etc.
12
- /// in their respective output formats.
13
- pub trait FormatRenderer: Send + Sync {
14
- /// Render emphasis (em, i elements)
15
- fn emphasis(&self, content: &str) -> String;
16
-
17
- /// Render strong emphasis (strong, b elements)
18
- fn strong(&self, content: &str, symbol: char) -> String;
19
-
20
- /// Render strikethrough (del, s elements)
21
- fn strikethrough(&self, content: &str) -> String;
22
-
23
- /// Render highlight (mark element)
24
- fn highlight(&self, content: &str) -> String;
25
-
26
- /// Render inserted text (ins element)
27
- fn inserted(&self, content: &str) -> String;
28
-
29
- /// Render subscript (sub element)
30
- fn subscript(&self, content: &str, custom_symbol: &str) -> String;
31
-
32
- /// Render superscript (sup element)
33
- fn superscript(&self, content: &str, custom_symbol: &str) -> String;
34
-
35
- /// Render span with attributes (for Djot: [text]{.class})
36
- fn span_with_attributes(&self, content: &str, classes: &[&str], id: Option<&str>) -> String;
37
-
38
- /// Render div with attributes (for Djot: ::: class)
39
- fn div_with_attributes(&self, content: &str, classes: &[&str]) -> String;
40
-
41
- /// Check if this is Djot format
42
- fn is_djot(&self) -> bool;
43
- }
@@ -1,173 +0,0 @@
1
- //! Blockquote element handler for HTML to Markdown conversion.
2
- //!
3
- //! Handles `<blockquote>` elements including:
4
- //! - Basic blockquote markdown output with `> ` prefix
5
- //! - Nested blockquotes
6
- //! - Citation URLs via `cite` attribute
7
- //! - Visitor callback integration
8
-
9
- use crate::converter::Context;
10
- use crate::converter::dom_context::DomContext;
11
- use crate::converter::main::walk_node;
12
- use crate::options::ConversionOptions;
13
-
14
- #[cfg(feature = "visitor")]
15
- #[cfg(feature = "visitor")]
16
- use crate::converter::utility::content::collect_tag_attributes;
17
- #[cfg(feature = "visitor")]
18
- use std::collections::BTreeMap;
19
-
20
- #[cfg(feature = "visitor")]
21
- use crate::converter::utility::serialization::serialize_node_to_html;
22
-
23
- /// Handle a `<blockquote>` element and convert to Markdown.
24
- ///
25
- /// This handler processes blockquote elements including:
26
- /// - Converting inline blockquotes by processing children as inline
27
- /// - Handling nested blockquotes via blockquote_depth tracking
28
- /// - Processing citation URLs from cite attribute
29
- /// - Invoking visitor callbacks when the visitor feature is enabled
30
- /// - Adding proper spacing and blockquote prefix formatting
31
- #[allow(clippy::too_many_arguments)]
32
- #[allow(clippy::too_many_lines)]
33
- #[cfg_attr(not(feature = "visitor"), allow(unused_variables))]
34
- pub fn handle_blockquote(
35
- node_handle: &tl::NodeHandle,
36
- tag: &tl::HTMLTag,
37
- parser: &tl::Parser,
38
- output: &mut String,
39
- options: &ConversionOptions,
40
- ctx: &Context,
41
- depth: usize,
42
- dom_ctx: &DomContext,
43
- ) {
44
- // If in inline conversion mode, just process children inline
45
- if ctx.convert_as_inline {
46
- let children = tag.children();
47
- {
48
- for child_handle in children.top().iter() {
49
- walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
50
- }
51
- }
52
- return;
53
- }
54
-
55
- // Extract cite attribute if present
56
- let cite = tag
57
- .attributes()
58
- .get("cite")
59
- .flatten()
60
- .map(|v| v.as_utf8_str().to_string());
61
-
62
- // Create context for nested blockquote processing
63
- let blockquote_ctx = Context {
64
- blockquote_depth: ctx.blockquote_depth + 1,
65
- ..ctx.clone()
66
- };
67
-
68
- // Process blockquote content
69
- let mut content = String::with_capacity(256);
70
- let children = tag.children();
71
- {
72
- for child_handle in children.top().iter() {
73
- walk_node(
74
- child_handle,
75
- parser,
76
- &mut content,
77
- options,
78
- &blockquote_ctx,
79
- depth + 1,
80
- dom_ctx,
81
- );
82
- }
83
- }
84
-
85
- let trimmed_content = content.trim();
86
-
87
- // Handle visitor integration
88
- #[cfg(feature = "visitor")]
89
- if let Some(ref visitor) = ctx.visitor {
90
- use crate::visitor::{NodeContext, NodeType, VisitResult};
91
-
92
- let attributes: BTreeMap<String, String> = collect_tag_attributes(tag);
93
-
94
- let node_id = node_handle.get_inner();
95
- let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
96
- let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
97
-
98
- let node_ctx = NodeContext {
99
- node_type: NodeType::Blockquote,
100
- tag_name: "blockquote".to_string(),
101
- attributes,
102
- depth,
103
- index_in_parent,
104
- parent_tag,
105
- is_inline: false,
106
- };
107
-
108
- let mut visitor_ref = visitor.borrow_mut();
109
- match visitor_ref.visit_blockquote(&node_ctx, trimmed_content, ctx.blockquote_depth) {
110
- VisitResult::Continue => {}
111
- VisitResult::Custom(custom) => {
112
- output.push_str(&custom);
113
- return;
114
- }
115
- VisitResult::Skip => return,
116
- VisitResult::PreserveHtml => {
117
- let mut html_output = String::new();
118
- serialize_node_to_html(node_handle, parser, &mut html_output);
119
- output.push_str(&html_output);
120
- return;
121
- }
122
- VisitResult::Error(err) => {
123
- if ctx.visitor_error.borrow().is_none() {
124
- *ctx.visitor_error.borrow_mut() = Some(err);
125
- }
126
- return;
127
- }
128
- }
129
- }
130
-
131
- // Output blockquote if content is not empty
132
- if !trimmed_content.is_empty() {
133
- // Add proper spacing based on blockquote depth
134
- if ctx.blockquote_depth > 0 {
135
- output.push_str("\n\n\n");
136
- } else if !output.is_empty() {
137
- if output.ends_with("\n\n") {
138
- // Paragraph already added \n\n; blockquote needs just \n
139
- output.truncate(output.len() - 1);
140
- } else if !output.ends_with('\n') {
141
- output.push_str("\n\n");
142
- } else if !output.ends_with("\n\n") {
143
- output.push('\n');
144
- }
145
- }
146
-
147
- // Add blockquote prefix to each line
148
- let prefix = "> ";
149
-
150
- for line in trimmed_content.lines() {
151
- output.push_str(prefix);
152
- output.push_str(line.trim());
153
- output.push('\n');
154
- }
155
-
156
- // Add citation if present
157
- if let Some(url) = cite {
158
- output.push('\n');
159
- output.push_str("— <");
160
- output.push_str(&url);
161
- output.push_str(">\n\n");
162
- }
163
-
164
- // Add trailing newlines only when appropriate for proper spacing
165
- // (matching paragraph conditional logic for CommonMark compliance)
166
- if !ctx.convert_as_inline && !ctx.in_table_cell && !ctx.in_list_item {
167
- while output.ends_with('\n') {
168
- output.truncate(output.len() - 1);
169
- }
170
- output.push_str("\n\n");
171
- }
172
- }
173
- }