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.
- checksums.yaml +4 -4
- data/LICENSE +21 -0
- data/README.md +347 -0
- data/Steepfile +10 -2
- data/ext/html_to_markdown_rb/Cargo.toml +3 -2
- data/ext/html_to_markdown_rb/extconf.rb +5 -5
- data/ext/html_to_markdown_rb/native/Cargo.lock +962 -0
- data/ext/html_to_markdown_rb/native/Cargo.toml +6 -11
- data/ext/html_to_markdown_rb/native/extconf.rb +14 -0
- data/ext/html_to_markdown_rb/src/lib.rs +1715 -646
- data/lib/html_to_markdown/native.rb +913 -37
- data/lib/html_to_markdown/version.rb +3 -3
- data/lib/html_to_markdown.rb +9 -4
- data/lib/html_to_markdown_rb.so +0 -0
- data/sig/types.rbs +59 -292
- metadata +32 -179
- data/ext/html_to_markdown_rb/Makefile +0 -592
- data/lib/bin/html-to-markdown +0 -0
- data/vendor/Cargo.toml +0 -33
- data/vendor/html-to-markdown-rs/Cargo.toml +0 -54
- data/vendor/html-to-markdown-rs/README.md +0 -278
- data/vendor/html-to-markdown-rs/examples/basic.rs +0 -24
- data/vendor/html-to-markdown-rs/examples/table.rs +0 -25
- data/vendor/html-to-markdown-rs/examples/test_deser.rs +0 -12
- data/vendor/html-to-markdown-rs/examples/test_escape.rs +0 -58
- data/vendor/html-to-markdown-rs/examples/test_inline_formatting.rs +0 -113
- data/vendor/html-to-markdown-rs/examples/test_lists.rs +0 -39
- data/vendor/html-to-markdown-rs/examples/test_semantic_tags.rs +0 -89
- data/vendor/html-to-markdown-rs/examples/test_tables.rs +0 -100
- data/vendor/html-to-markdown-rs/examples/test_task_lists.rs +0 -61
- data/vendor/html-to-markdown-rs/examples/test_whitespace.rs +0 -34
- data/vendor/html-to-markdown-rs/src/convert_api.rs +0 -349
- data/vendor/html-to-markdown-rs/src/converter/block/blockquote.rs +0 -178
- data/vendor/html-to-markdown-rs/src/converter/block/container.rs +0 -114
- data/vendor/html-to-markdown-rs/src/converter/block/div.rs +0 -149
- data/vendor/html-to-markdown-rs/src/converter/block/heading.rs +0 -428
- data/vendor/html-to-markdown-rs/src/converter/block/horizontal_rule.rs +0 -103
- data/vendor/html-to-markdown-rs/src/converter/block/line_break.rs +0 -89
- data/vendor/html-to-markdown-rs/src/converter/block/mod.rs +0 -10
- data/vendor/html-to-markdown-rs/src/converter/block/paragraph.rs +0 -140
- data/vendor/html-to-markdown-rs/src/converter/block/preformatted.rs +0 -298
- data/vendor/html-to-markdown-rs/src/converter/block/table/builder.rs +0 -453
- data/vendor/html-to-markdown-rs/src/converter/block/table/caption.rs +0 -44
- data/vendor/html-to-markdown-rs/src/converter/block/table/cell.rs +0 -276
- data/vendor/html-to-markdown-rs/src/converter/block/table/cells.rs +0 -336
- data/vendor/html-to-markdown-rs/src/converter/block/table/layout.rs +0 -58
- data/vendor/html-to-markdown-rs/src/converter/block/table/mod.rs +0 -266
- data/vendor/html-to-markdown-rs/src/converter/block/table/scanner.rs +0 -146
- data/vendor/html-to-markdown-rs/src/converter/block/table/utils.rs +0 -34
- data/vendor/html-to-markdown-rs/src/converter/block/unknown.rs +0 -138
- data/vendor/html-to-markdown-rs/src/converter/context.rs +0 -208
- data/vendor/html-to-markdown-rs/src/converter/dom_context.rs +0 -337
- data/vendor/html-to-markdown-rs/src/converter/form/elements.rs +0 -770
- data/vendor/html-to-markdown-rs/src/converter/form/mod.rs +0 -82
- data/vendor/html-to-markdown-rs/src/converter/format/djot.rs +0 -64
- data/vendor/html-to-markdown-rs/src/converter/format/markdown.rs +0 -59
- data/vendor/html-to-markdown-rs/src/converter/format/mod.rs +0 -43
- data/vendor/html-to-markdown-rs/src/converter/handlers/blockquote.rs +0 -173
- data/vendor/html-to-markdown-rs/src/converter/handlers/code_block.rs +0 -434
- data/vendor/html-to-markdown-rs/src/converter/handlers/graphic.rs +0 -234
- data/vendor/html-to-markdown-rs/src/converter/handlers/image.rs +0 -282
- data/vendor/html-to-markdown-rs/src/converter/handlers/link.rs +0 -316
- data/vendor/html-to-markdown-rs/src/converter/handlers/mod.rs +0 -26
- data/vendor/html-to-markdown-rs/src/converter/inline/code.rs +0 -306
- data/vendor/html-to-markdown-rs/src/converter/inline/emphasis.rs +0 -345
- data/vendor/html-to-markdown-rs/src/converter/inline/link.rs +0 -428
- data/vendor/html-to-markdown-rs/src/converter/inline/mod.rs +0 -237
- data/vendor/html-to-markdown-rs/src/converter/inline/ruby.rs +0 -337
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/marks.rs +0 -566
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/mod.rs +0 -86
- data/vendor/html-to-markdown-rs/src/converter/inline/semantic/typography.rs +0 -558
- data/vendor/html-to-markdown-rs/src/converter/list/definition.rs +0 -232
- data/vendor/html-to-markdown-rs/src/converter/list/item.rs +0 -332
- data/vendor/html-to-markdown-rs/src/converter/list/mod.rs +0 -70
- data/vendor/html-to-markdown-rs/src/converter/list/ordered.rs +0 -201
- data/vendor/html-to-markdown-rs/src/converter/list/unordered.rs +0 -195
- data/vendor/html-to-markdown-rs/src/converter/list/utils.rs +0 -314
- data/vendor/html-to-markdown-rs/src/converter/main.rs +0 -710
- data/vendor/html-to-markdown-rs/src/converter/main_helpers.rs +0 -452
- data/vendor/html-to-markdown-rs/src/converter/media/embedded.rs +0 -393
- data/vendor/html-to-markdown-rs/src/converter/media/graphic.rs +0 -4
- data/vendor/html-to-markdown-rs/src/converter/media/image.rs +0 -183
- data/vendor/html-to-markdown-rs/src/converter/media/mod.rs +0 -87
- data/vendor/html-to-markdown-rs/src/converter/media/svg.rs +0 -280
- data/vendor/html-to-markdown-rs/src/converter/metadata.rs +0 -220
- data/vendor/html-to-markdown-rs/src/converter/mod.rs +0 -156
- data/vendor/html-to-markdown-rs/src/converter/plain_text.rs +0 -516
- data/vendor/html-to-markdown-rs/src/converter/preprocessing_helpers.rs +0 -201
- data/vendor/html-to-markdown-rs/src/converter/reference_collector.rs +0 -69
- data/vendor/html-to-markdown-rs/src/converter/semantic/attributes.rs +0 -269
- data/vendor/html-to-markdown-rs/src/converter/semantic/definition_list.rs +0 -266
- data/vendor/html-to-markdown-rs/src/converter/semantic/figure.rs +0 -391
- data/vendor/html-to-markdown-rs/src/converter/semantic/mod.rs +0 -112
- data/vendor/html-to-markdown-rs/src/converter/semantic/sectioning.rs +0 -85
- data/vendor/html-to-markdown-rs/src/converter/semantic/summary.rs +0 -324
- data/vendor/html-to-markdown-rs/src/converter/text/mod.rs +0 -8
- data/vendor/html-to-markdown-rs/src/converter/text/processing.rs +0 -56
- data/vendor/html-to-markdown-rs/src/converter/text_node.rs +0 -269
- data/vendor/html-to-markdown-rs/src/converter/utility/attributes.rs +0 -151
- data/vendor/html-to-markdown-rs/src/converter/utility/caching.rs +0 -74
- data/vendor/html-to-markdown-rs/src/converter/utility/content.rs +0 -271
- data/vendor/html-to-markdown-rs/src/converter/utility/mod.rs +0 -17
- data/vendor/html-to-markdown-rs/src/converter/utility/preprocessing.rs +0 -1002
- data/vendor/html-to-markdown-rs/src/converter/utility/serialization.rs +0 -126
- data/vendor/html-to-markdown-rs/src/converter/utility/siblings.rs +0 -97
- data/vendor/html-to-markdown-rs/src/converter/visitor_hooks.rs +0 -189
- data/vendor/html-to-markdown-rs/src/error.rs +0 -43
- data/vendor/html-to-markdown-rs/src/exports.rs +0 -24
- data/vendor/html-to-markdown-rs/src/inline_images.rs +0 -336
- data/vendor/html-to-markdown-rs/src/lib.rs +0 -139
- data/vendor/html-to-markdown-rs/src/metadata/collector.rs +0 -457
- data/vendor/html-to-markdown-rs/src/metadata/config.rs +0 -394
- data/vendor/html-to-markdown-rs/src/metadata/extraction.rs +0 -398
- data/vendor/html-to-markdown-rs/src/metadata/mod.rs +0 -288
- data/vendor/html-to-markdown-rs/src/metadata/types.rs +0 -477
- data/vendor/html-to-markdown-rs/src/options/conversion.rs +0 -559
- data/vendor/html-to-markdown-rs/src/options/inline_image.rs +0 -111
- data/vendor/html-to-markdown-rs/src/options/mod.rs +0 -20
- data/vendor/html-to-markdown-rs/src/options/preprocessing.rs +0 -201
- data/vendor/html-to-markdown-rs/src/options/validation.rs +0 -416
- data/vendor/html-to-markdown-rs/src/prelude.rs +0 -1
- data/vendor/html-to-markdown-rs/src/rcdom.rs +0 -487
- data/vendor/html-to-markdown-rs/src/text.rs +0 -358
- data/vendor/html-to-markdown-rs/src/types/document.rs +0 -191
- data/vendor/html-to-markdown-rs/src/types/mod.rs +0 -17
- data/vendor/html-to-markdown-rs/src/types/result.rs +0 -54
- data/vendor/html-to-markdown-rs/src/types/structure_builder.rs +0 -791
- data/vendor/html-to-markdown-rs/src/types/structure_collector.rs +0 -483
- data/vendor/html-to-markdown-rs/src/types/tables.rs +0 -52
- data/vendor/html-to-markdown-rs/src/types/warnings.rs +0 -33
- data/vendor/html-to-markdown-rs/src/validation.rs +0 -158
- data/vendor/html-to-markdown-rs/src/visitor/default_impl.rs +0 -63
- data/vendor/html-to-markdown-rs/src/visitor/mod.rs +0 -41
- data/vendor/html-to-markdown-rs/src/visitor/traits.rs +0 -370
- data/vendor/html-to-markdown-rs/src/visitor/types.rs +0 -319
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/callbacks/mod.rs +0 -1
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/content.rs +0 -126
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/mod.rs +0 -27
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/state.rs +0 -110
- data/vendor/html-to-markdown-rs/src/visitor_helpers/helpers/traversal.rs +0 -250
- data/vendor/html-to-markdown-rs/src/visitor_helpers.rs +0 -597
- data/vendor/html-to-markdown-rs/src/wrapper/sync.rs +0 -413
- data/vendor/html-to-markdown-rs/src/wrapper/utils.rs +0 -290
- data/vendor/html-to-markdown-rs/src/wrapper.rs +0 -9
- data/vendor/html-to-markdown-rs/tests/br_in_inline_test.rs +0 -87
- data/vendor/html-to-markdown-rs/tests/commonmark_compliance_test.rs +0 -297
- data/vendor/html-to-markdown-rs/tests/djot_output_test.rs +0 -153
- data/vendor/html-to-markdown-rs/tests/exclude_selectors_test.rs +0 -132
- data/vendor/html-to-markdown-rs/tests/integration_test.rs +0 -631
- data/vendor/html-to-markdown-rs/tests/issue_121_regressions.rs +0 -49
- data/vendor/html-to-markdown-rs/tests/issue_127_regressions.rs +0 -58
- data/vendor/html-to-markdown-rs/tests/issue_128_regressions.rs +0 -17
- data/vendor/html-to-markdown-rs/tests/issue_131_regressions.rs +0 -41
- data/vendor/html-to-markdown-rs/tests/issue_134_regressions.rs +0 -40
- data/vendor/html-to-markdown-rs/tests/issue_139_regressions.rs +0 -26
- data/vendor/html-to-markdown-rs/tests/issue_140_regressions.rs +0 -185
- data/vendor/html-to-markdown-rs/tests/issue_143_regressions.rs +0 -100
- data/vendor/html-to-markdown-rs/tests/issue_145_regressions.rs +0 -133
- data/vendor/html-to-markdown-rs/tests/issue_146_regressions.rs +0 -144
- data/vendor/html-to-markdown-rs/tests/issue_176_regressions.rs +0 -62
- data/vendor/html-to-markdown-rs/tests/issue_190_regressions.rs +0 -128
- data/vendor/html-to-markdown-rs/tests/issue_199_regressions.rs +0 -20
- data/vendor/html-to-markdown-rs/tests/issue_200_regressions.rs +0 -62
- data/vendor/html-to-markdown-rs/tests/issue_212_regressions.rs +0 -68
- data/vendor/html-to-markdown-rs/tests/issue_216_217_regressions.rs +0 -87
- data/vendor/html-to-markdown-rs/tests/issue_336_regressions.rs +0 -74
- data/vendor/html-to-markdown-rs/tests/issue_339_regressions.rs +0 -92
- data/vendor/html-to-markdown-rs/tests/issue_347_regressions.rs +0 -154
- data/vendor/html-to-markdown-rs/tests/issue_348_visitor_plain.rs +0 -93
- data/vendor/html-to-markdown-rs/tests/json_ld_script_extraction.rs +0 -44
- data/vendor/html-to-markdown-rs/tests/lists_test.rs +0 -199
- data/vendor/html-to-markdown-rs/tests/plain_output_test.rs +0 -273
- data/vendor/html-to-markdown-rs/tests/preprocessing_tests.rs +0 -61
- data/vendor/html-to-markdown-rs/tests/reference_links_test.rs +0 -169
- data/vendor/html-to-markdown-rs/tests/sectioning_elements_test.rs +0 -137
- data/vendor/html-to-markdown-rs/tests/skip_images_test.rs +0 -522
- data/vendor/html-to-markdown-rs/tests/tables_test.rs +0 -743
- data/vendor/html-to-markdown-rs/tests/test_custom_elements.rs +0 -41
- data/vendor/html-to-markdown-rs/tests/test_issue_187.rs +0 -204
- data/vendor/html-to-markdown-rs/tests/test_issue_218.rs +0 -68
- data/vendor/html-to-markdown-rs/tests/test_issue_277.rs +0 -77
- data/vendor/html-to-markdown-rs/tests/test_max_depth.rs +0 -82
- data/vendor/html-to-markdown-rs/tests/test_nested_simple.rs +0 -45
- data/vendor/html-to-markdown-rs/tests/test_script_style_stripping.rs +0 -396
- data/vendor/html-to-markdown-rs/tests/test_spa_bisect.rs +0 -34
- data/vendor/html-to-markdown-rs/tests/visitor_code_integration_test.rs +0 -121
- data/vendor/html-to-markdown-rs/tests/visitor_integration_test.rs +0 -1190
- data/vendor/html-to-markdown-rs/tests/xml_tables_test.rs +0 -372
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
//! Semantic HTML5 element handlers for HTML to Markdown conversion.
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides specialized handlers for semantic HTML5 elements:
|
|
4
|
-
//! - Sectioning elements (article, section, nav, aside, header, footer, main)
|
|
5
|
-
//! - Figure elements (figure, figcaption)
|
|
6
|
-
//! - Interactive elements (details, summary, dialog)
|
|
7
|
-
//! - Semantic inline attributes (cite, q, abbr, dfn, time, data)
|
|
8
|
-
//!
|
|
9
|
-
//! These handlers are designed to be extracted from the main `converter.rs`
|
|
10
|
-
//! file and integrated through the dispatcher function.
|
|
11
|
-
//!
|
|
12
|
-
//! **Integration Pattern:**
|
|
13
|
-
//! Each handler function takes the same signature:
|
|
14
|
-
//! - `tag_name: &str` - The HTML tag being processed
|
|
15
|
-
//! - `node_handle: &NodeHandle` - The DOM node handle
|
|
16
|
-
//! - `parser: &Parser` - The HTML parser reference
|
|
17
|
-
//! - `output: &mut String` - The output buffer to write to
|
|
18
|
-
//! - `options: &ConversionOptions` - Conversion configuration
|
|
19
|
-
//! - `ctx: &Context` - Processing context (state tracking)
|
|
20
|
-
//! - `depth: usize` - Current DOM tree depth
|
|
21
|
-
//! - `dom_ctx: &DomContext` - DOM context for tree relationships
|
|
22
|
-
//!
|
|
23
|
-
//! The main dispatcher function `dispatch_semantic_handler` routes tags to
|
|
24
|
-
//! their appropriate handlers and returns a boolean indicating success.
|
|
25
|
-
|
|
26
|
-
pub mod attributes;
|
|
27
|
-
pub mod definition_list;
|
|
28
|
-
pub mod figure;
|
|
29
|
-
pub mod sectioning;
|
|
30
|
-
pub mod summary;
|
|
31
|
-
|
|
32
|
-
// Re-export types from parent module for submodule access
|
|
33
|
-
pub use super::walk_node;
|
|
34
|
-
pub use super::{Context, DomContext};
|
|
35
|
-
|
|
36
|
-
// Re-export handler functions for direct use
|
|
37
|
-
pub use attributes::handle as handle_attributes;
|
|
38
|
-
pub use definition_list::handle as handle_definition_list;
|
|
39
|
-
pub use figure::handle as handle_figure;
|
|
40
|
-
pub use sectioning::handle as handle_sectioning;
|
|
41
|
-
pub use summary::handle as handle_summary;
|
|
42
|
-
|
|
43
|
-
// Re-exports are done via the dispatch function parameter types
|
|
44
|
-
|
|
45
|
-
/// Dispatches semantic element handling to the appropriate handler.
|
|
46
|
-
///
|
|
47
|
-
/// This function routes semantic HTML5 elements to their specialized handlers
|
|
48
|
-
/// based on tag name. It is designed to be called from the main `walk_node`
|
|
49
|
-
/// function in `converter.rs`.
|
|
50
|
-
///
|
|
51
|
-
/// # Routing Table
|
|
52
|
-
///
|
|
53
|
-
/// The following tag routes are supported:
|
|
54
|
-
/// - **Sectioning**: article, section, nav, aside, header, footer, main
|
|
55
|
-
/// - **Figure**: figure, figcaption
|
|
56
|
-
/// - **Summary**: details, summary, dialog
|
|
57
|
-
/// - **Definition List**: hgroup, dl, dt, dd, menu
|
|
58
|
-
/// - **Attributes**: cite, q, abbr, dfn, time, data
|
|
59
|
-
///
|
|
60
|
-
/// # Returns
|
|
61
|
-
///
|
|
62
|
-
/// Returns `true` if the tag was successfully handled by a semantic handler,
|
|
63
|
-
/// `false` if the tag is not a semantic element and requires other handling.
|
|
64
|
-
///
|
|
65
|
-
/// # Example
|
|
66
|
-
///
|
|
67
|
-
/// ```text
|
|
68
|
-
/// if dispatch_semantic_handler(tag_name, &node_handle, &parser, output, options, ctx, depth, dom_ctx) {
|
|
69
|
-
/// // Tag was handled
|
|
70
|
-
/// } else {
|
|
71
|
-
/// // Continue with other handlers
|
|
72
|
-
/// }
|
|
73
|
-
/// ```
|
|
74
|
-
pub fn dispatch_semantic_handler(
|
|
75
|
-
tag_name: &str,
|
|
76
|
-
node_handle: &tl::NodeHandle,
|
|
77
|
-
parser: &tl::Parser,
|
|
78
|
-
output: &mut String,
|
|
79
|
-
options: &crate::options::ConversionOptions,
|
|
80
|
-
ctx: &super::Context,
|
|
81
|
-
depth: usize,
|
|
82
|
-
dom_ctx: &super::DomContext,
|
|
83
|
-
) -> bool {
|
|
84
|
-
match tag_name {
|
|
85
|
-
// Sectioning elements
|
|
86
|
-
"article" | "section" | "nav" | "aside" | "header" | "footer" | "main" => {
|
|
87
|
-
handle_sectioning(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
88
|
-
true
|
|
89
|
-
}
|
|
90
|
-
// Figure elements
|
|
91
|
-
"figure" | "figcaption" => {
|
|
92
|
-
handle_figure(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
93
|
-
true
|
|
94
|
-
}
|
|
95
|
-
// Summary and interactive elements
|
|
96
|
-
"details" | "summary" | "dialog" => {
|
|
97
|
-
handle_summary(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
98
|
-
true
|
|
99
|
-
}
|
|
100
|
-
// Definition list and related elements
|
|
101
|
-
"hgroup" | "dl" | "dt" | "dd" | "menu" => {
|
|
102
|
-
handle_definition_list(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
103
|
-
true
|
|
104
|
-
}
|
|
105
|
-
// Semantic inline attributes
|
|
106
|
-
"cite" | "q" | "abbr" | "dfn" | "time" | "data" => {
|
|
107
|
-
handle_attributes(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
108
|
-
true
|
|
109
|
-
}
|
|
110
|
-
_ => false,
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
//! Handlers for HTML5 sectioning elements.
|
|
2
|
-
//!
|
|
3
|
-
//! Processes semantic sectioning elements:
|
|
4
|
-
//! - `<article>` - Independent, self-contained content
|
|
5
|
-
//! - `<section>` - Generic grouping of thematic content
|
|
6
|
-
//! - `<nav>` - Navigation links (typically rendered inline or in sidebars)
|
|
7
|
-
//! - `<aside>` - Peripheral content (sidebars, callouts)
|
|
8
|
-
//! - `<header>` - Introductory content (page headers)
|
|
9
|
-
//! - `<footer>` - End content (page footers)
|
|
10
|
-
//! - `<main>` - Primary content area
|
|
11
|
-
//!
|
|
12
|
-
//! All these elements are treated as block-level containers.
|
|
13
|
-
//! Their content is extracted and formatted with proper spacing.
|
|
14
|
-
|
|
15
|
-
// Note: Context and DomContext are defined in converter.rs
|
|
16
|
-
// walk_node is also defined there and must be called via the parent module
|
|
17
|
-
|
|
18
|
-
/// Handles sectioning elements (article, section, nav, aside, header, footer, main).
|
|
19
|
-
///
|
|
20
|
-
/// Sectioning elements are rendered as block-level containers. When in inline
|
|
21
|
-
/// conversion mode, their content is rendered inline without block spacing.
|
|
22
|
-
/// Otherwise, content is wrapped with proper blank lines to separate from other blocks.
|
|
23
|
-
///
|
|
24
|
-
/// # Behavior
|
|
25
|
-
///
|
|
26
|
-
/// - **Inline mode**: Children are processed inline; block spacing is skipped
|
|
27
|
-
/// - **Block mode**: Content is collected, trimmed, and formatted with blank lines
|
|
28
|
-
/// - **Empty content**: Empty sections are skipped entirely
|
|
29
|
-
///
|
|
30
|
-
/// # Implementation Note
|
|
31
|
-
///
|
|
32
|
-
/// Sectioning elements act as transparent containers—their presence doesn't
|
|
33
|
-
/// add any special formatting beyond structural grouping.
|
|
34
|
-
pub fn handle(
|
|
35
|
-
_tag_name: &str,
|
|
36
|
-
node_handle: &tl::NodeHandle,
|
|
37
|
-
parser: &tl::Parser,
|
|
38
|
-
output: &mut String,
|
|
39
|
-
options: &crate::options::ConversionOptions,
|
|
40
|
-
ctx: &super::Context,
|
|
41
|
-
depth: usize,
|
|
42
|
-
dom_ctx: &super::DomContext,
|
|
43
|
-
) {
|
|
44
|
-
if let Some(tl::Node::Tag(tag)) = node_handle.get(parser) {
|
|
45
|
-
// In inline context, just process children inline
|
|
46
|
-
if ctx.convert_as_inline {
|
|
47
|
-
let children = tag.children();
|
|
48
|
-
{
|
|
49
|
-
for child_handle in children.top().iter() {
|
|
50
|
-
super::walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Collect content in a separate buffer
|
|
57
|
-
let mut content = String::with_capacity(256);
|
|
58
|
-
let children = tag.children();
|
|
59
|
-
{
|
|
60
|
-
for child_handle in children.top().iter() {
|
|
61
|
-
super::walk_node(child_handle, parser, &mut content, options, ctx, depth + 1, dom_ctx);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Skip if content is empty
|
|
66
|
-
if content.trim().is_empty() {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Add spacing before the content
|
|
71
|
-
if !output.is_empty() && !output.ends_with("\n\n") {
|
|
72
|
-
output.push_str("\n\n");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Append the content
|
|
76
|
-
output.push_str(&content);
|
|
77
|
-
|
|
78
|
-
// Ensure proper spacing after the content
|
|
79
|
-
if content.ends_with('\n') && !content.ends_with("\n\n") {
|
|
80
|
-
output.push('\n');
|
|
81
|
-
} else if !content.ends_with('\n') {
|
|
82
|
-
output.push_str("\n\n");
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
//! Handlers for HTML5 interactive elements.
|
|
2
|
-
//!
|
|
3
|
-
//! Processes interactive disclosure and dialog semantic elements:
|
|
4
|
-
//! - `<details>` - Expandable/collapsible disclosure widget
|
|
5
|
-
//! - `<summary>` - Summary or caption for a details element
|
|
6
|
-
//! - `<dialog>` - Dialog box overlay widget
|
|
7
|
-
//!
|
|
8
|
-
//! These elements are treated as block-level content containers
|
|
9
|
-
//! with special formatting for the summary element.
|
|
10
|
-
|
|
11
|
-
// Note: Context and DomContext are defined in converter.rs
|
|
12
|
-
// walk_node is also defined there and must be called via the parent module
|
|
13
|
-
use super::walk_node;
|
|
14
|
-
|
|
15
|
-
/// Handles the `<details>` element.
|
|
16
|
-
///
|
|
17
|
-
/// A details element represents a disclosure widget that can be toggled
|
|
18
|
-
/// to show/hide additional content. In Markdown, it's rendered as a block
|
|
19
|
-
/// with all content visible.
|
|
20
|
-
///
|
|
21
|
-
/// # Behavior
|
|
22
|
-
///
|
|
23
|
-
/// - **Inline mode**: Children are processed inline without block spacing
|
|
24
|
-
/// - **Block mode**: Content is collected and wrapped with proper blank-line spacing
|
|
25
|
-
/// - **Empty content**: Skipped entirely
|
|
26
|
-
#[cfg_attr(not(feature = "visitor"), allow(unused_variables))]
|
|
27
|
-
pub fn handle_details(
|
|
28
|
-
_tag_name: &str,
|
|
29
|
-
node_handle: &tl::NodeHandle,
|
|
30
|
-
parser: &tl::Parser,
|
|
31
|
-
output: &mut String,
|
|
32
|
-
options: &crate::options::ConversionOptions,
|
|
33
|
-
ctx: &super::Context,
|
|
34
|
-
depth: usize,
|
|
35
|
-
dom_ctx: &super::DomContext,
|
|
36
|
-
) {
|
|
37
|
-
if let Some(tl::Node::Tag(tag)) = node_handle.get(parser) {
|
|
38
|
-
#[cfg(feature = "visitor")]
|
|
39
|
-
if let Some(ref visitor_handle) = ctx.visitor {
|
|
40
|
-
use crate::converter::utility::content::collect_tag_attributes;
|
|
41
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
42
|
-
|
|
43
|
-
let attributes = collect_tag_attributes(tag);
|
|
44
|
-
let node_id = node_handle.get_inner();
|
|
45
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
46
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
47
|
-
let open = tag.attributes().get("open").is_some();
|
|
48
|
-
let node_ctx = NodeContext {
|
|
49
|
-
node_type: NodeType::Details,
|
|
50
|
-
tag_name: "details".to_string(),
|
|
51
|
-
attributes,
|
|
52
|
-
depth,
|
|
53
|
-
index_in_parent,
|
|
54
|
-
parent_tag,
|
|
55
|
-
is_inline: false,
|
|
56
|
-
};
|
|
57
|
-
let visit_result = {
|
|
58
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
59
|
-
visitor.visit_details(&node_ctx, open)
|
|
60
|
-
};
|
|
61
|
-
match visit_result {
|
|
62
|
-
VisitResult::Continue => {}
|
|
63
|
-
VisitResult::Skip => return,
|
|
64
|
-
VisitResult::Custom(custom) => {
|
|
65
|
-
if !output.is_empty() && !output.ends_with("\n\n") {
|
|
66
|
-
output.push_str("\n\n");
|
|
67
|
-
}
|
|
68
|
-
output.push_str(&custom);
|
|
69
|
-
output.push_str("\n\n");
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
VisitResult::PreserveHtml => {
|
|
73
|
-
use crate::converter::utility::serialization::serialize_node;
|
|
74
|
-
output.push_str(&serialize_node(node_handle, parser));
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
VisitResult::Error(err) => {
|
|
78
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
79
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
80
|
-
}
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// In inline context, just process children inline
|
|
87
|
-
if ctx.convert_as_inline {
|
|
88
|
-
let children = tag.children();
|
|
89
|
-
{
|
|
90
|
-
for child_handle in children.top().iter() {
|
|
91
|
-
super::walk_node(child_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Collect content
|
|
98
|
-
let mut content = String::with_capacity(256);
|
|
99
|
-
let children = tag.children();
|
|
100
|
-
{
|
|
101
|
-
for child_handle in children.top().iter() {
|
|
102
|
-
walk_node(child_handle, parser, &mut content, options, ctx, depth, dom_ctx);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let trimmed = content.trim();
|
|
107
|
-
if !trimmed.is_empty() {
|
|
108
|
-
// Add spacing before if needed
|
|
109
|
-
if !output.is_empty() && !output.ends_with("\n\n") {
|
|
110
|
-
output.push_str("\n\n");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Output content
|
|
114
|
-
output.push_str(trimmed);
|
|
115
|
-
output.push_str("\n\n");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/// Handles the `<summary>` element.
|
|
121
|
-
///
|
|
122
|
-
/// A summary element contains a caption for a details element.
|
|
123
|
-
/// It is rendered as strong (bold) text to distinguish it from regular content.
|
|
124
|
-
///
|
|
125
|
-
/// # Behavior
|
|
126
|
-
///
|
|
127
|
-
/// - **Inline mode**: Content is rendered inline without emphasis
|
|
128
|
-
/// - **Block mode**: Content is wrapped in strong markers (e.g., `**text**`)
|
|
129
|
-
/// - Uses the configured strong/emphasis symbol from ConversionOptions
|
|
130
|
-
///
|
|
131
|
-
/// # Implementation Details
|
|
132
|
-
///
|
|
133
|
-
/// The handler:
|
|
134
|
-
/// 1. Creates a context with `in_strong: true` for nested formatting
|
|
135
|
-
/// 2. Collects content from all children
|
|
136
|
-
/// 3. Wraps non-empty content in strong markers (repeated twice per Markdown spec)
|
|
137
|
-
#[cfg_attr(not(feature = "visitor"), allow(unused_variables))]
|
|
138
|
-
pub fn handle_summary(
|
|
139
|
-
_tag_name: &str,
|
|
140
|
-
node_handle: &tl::NodeHandle,
|
|
141
|
-
parser: &tl::Parser,
|
|
142
|
-
output: &mut String,
|
|
143
|
-
options: &crate::options::ConversionOptions,
|
|
144
|
-
ctx: &super::Context,
|
|
145
|
-
depth: usize,
|
|
146
|
-
dom_ctx: &super::DomContext,
|
|
147
|
-
) {
|
|
148
|
-
if let Some(tl::Node::Tag(tag)) = node_handle.get(parser) {
|
|
149
|
-
let mut content = String::with_capacity(64);
|
|
150
|
-
|
|
151
|
-
// Set strong context for nested content
|
|
152
|
-
let mut summary_ctx = ctx.clone();
|
|
153
|
-
if !ctx.convert_as_inline {
|
|
154
|
-
summary_ctx.in_strong = true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
let children = tag.children();
|
|
158
|
-
{
|
|
159
|
-
for child_handle in children.top().iter() {
|
|
160
|
-
super::walk_node(
|
|
161
|
-
child_handle,
|
|
162
|
-
parser,
|
|
163
|
-
&mut content,
|
|
164
|
-
options,
|
|
165
|
-
&summary_ctx,
|
|
166
|
-
depth + 1,
|
|
167
|
-
dom_ctx,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
let trimmed = content.trim().to_owned();
|
|
173
|
-
if trimmed.is_empty() {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
#[cfg(feature = "visitor")]
|
|
178
|
-
if let Some(ref visitor_handle) = ctx.visitor {
|
|
179
|
-
use crate::converter::utility::content::collect_tag_attributes;
|
|
180
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
181
|
-
|
|
182
|
-
let attributes = collect_tag_attributes(tag);
|
|
183
|
-
let node_id = node_handle.get_inner();
|
|
184
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
185
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
186
|
-
let node_ctx = NodeContext {
|
|
187
|
-
node_type: NodeType::Summary,
|
|
188
|
-
tag_name: "summary".to_string(),
|
|
189
|
-
attributes,
|
|
190
|
-
depth,
|
|
191
|
-
index_in_parent,
|
|
192
|
-
parent_tag,
|
|
193
|
-
is_inline: false,
|
|
194
|
-
};
|
|
195
|
-
let visit_result = {
|
|
196
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
197
|
-
visitor.visit_summary(&node_ctx, &trimmed)
|
|
198
|
-
};
|
|
199
|
-
match visit_result {
|
|
200
|
-
VisitResult::Continue => {}
|
|
201
|
-
VisitResult::Skip => return,
|
|
202
|
-
VisitResult::Custom(custom) => {
|
|
203
|
-
if ctx.convert_as_inline {
|
|
204
|
-
output.push_str(&custom);
|
|
205
|
-
} else {
|
|
206
|
-
output.push_str(&custom);
|
|
207
|
-
if !custom.ends_with('\n') {
|
|
208
|
-
output.push_str("\n\n");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
VisitResult::PreserveHtml => {
|
|
214
|
-
use crate::converter::utility::serialization::serialize_node;
|
|
215
|
-
output.push_str(&serialize_node(node_handle, parser));
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
VisitResult::Error(err) => {
|
|
219
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
220
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
221
|
-
}
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if ctx.convert_as_inline {
|
|
228
|
-
// Inline mode: output without formatting
|
|
229
|
-
output.push_str(&trimmed);
|
|
230
|
-
} else {
|
|
231
|
-
// Block mode: output with strong markers
|
|
232
|
-
let mut symbol = String::with_capacity(2);
|
|
233
|
-
symbol.push(options.strong_em_symbol);
|
|
234
|
-
symbol.push(options.strong_em_symbol);
|
|
235
|
-
output.push_str(&symbol);
|
|
236
|
-
output.push_str(&trimmed);
|
|
237
|
-
output.push_str(&symbol);
|
|
238
|
-
output.push_str("\n\n");
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/// Handles the `<dialog>` element.
|
|
244
|
-
///
|
|
245
|
-
/// A dialog element represents a modal dialog box. In Markdown, it's rendered
|
|
246
|
-
/// as a block container with content visible.
|
|
247
|
-
///
|
|
248
|
-
/// # Behavior
|
|
249
|
-
///
|
|
250
|
-
/// - **Inline mode**: Children are processed inline without block spacing
|
|
251
|
-
/// - **Block mode**: Content is processed and wrapped with proper blank lines
|
|
252
|
-
/// - Trailing whitespace is removed from collected content
|
|
253
|
-
///
|
|
254
|
-
/// # Implementation Details
|
|
255
|
-
///
|
|
256
|
-
/// The handler:
|
|
257
|
-
/// 1. Marks the position in output before processing children
|
|
258
|
-
/// 2. Processes all children in the normal context
|
|
259
|
-
/// 3. Removes trailing spaces and tabs from the output
|
|
260
|
-
/// 4. Ensures proper blank-line spacing after the dialog
|
|
261
|
-
pub fn handle_dialog(
|
|
262
|
-
_tag_name: &str,
|
|
263
|
-
node_handle: &tl::NodeHandle,
|
|
264
|
-
parser: &tl::Parser,
|
|
265
|
-
output: &mut String,
|
|
266
|
-
options: &crate::options::ConversionOptions,
|
|
267
|
-
ctx: &super::Context,
|
|
268
|
-
depth: usize,
|
|
269
|
-
dom_ctx: &super::DomContext,
|
|
270
|
-
) {
|
|
271
|
-
if let Some(tl::Node::Tag(tag)) = node_handle.get(parser) {
|
|
272
|
-
// In inline context, just process children inline
|
|
273
|
-
if ctx.convert_as_inline {
|
|
274
|
-
let children = tag.children();
|
|
275
|
-
{
|
|
276
|
-
for child_handle in children.top().iter() {
|
|
277
|
-
super::walk_node(child_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Mark position before processing children
|
|
284
|
-
let content_start = output.len();
|
|
285
|
-
|
|
286
|
-
let children = tag.children();
|
|
287
|
-
{
|
|
288
|
-
for child_handle in children.top().iter() {
|
|
289
|
-
super::walk_node(child_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Remove trailing whitespace from dialog content
|
|
294
|
-
while output.len() > content_start && (output.ends_with(' ') || output.ends_with('\t')) {
|
|
295
|
-
output.pop();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Ensure proper spacing after dialog
|
|
299
|
-
if output.len() > content_start && !output.ends_with("\n\n") {
|
|
300
|
-
output.push_str("\n\n");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/// Dispatcher for interactive elements.
|
|
306
|
-
///
|
|
307
|
-
/// Routes `<details>`, `<summary>`, and `<dialog>` elements to their respective handlers.
|
|
308
|
-
pub fn handle(
|
|
309
|
-
tag_name: &str,
|
|
310
|
-
node_handle: &tl::NodeHandle,
|
|
311
|
-
parser: &tl::Parser,
|
|
312
|
-
output: &mut String,
|
|
313
|
-
options: &crate::options::ConversionOptions,
|
|
314
|
-
ctx: &super::Context,
|
|
315
|
-
depth: usize,
|
|
316
|
-
dom_ctx: &super::DomContext,
|
|
317
|
-
) {
|
|
318
|
-
match tag_name {
|
|
319
|
-
"details" => handle_details(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx),
|
|
320
|
-
"summary" => handle_summary(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx),
|
|
321
|
-
"dialog" => handle_dialog(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx),
|
|
322
|
-
_ => {}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
//! Text processing module for HTML to Markdown conversion.
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides utilities for normalizing, escaping, and processing text content
|
|
4
|
-
//! extracted from HTML documents during the conversion to Markdown format.
|
|
5
|
-
|
|
6
|
-
mod processing;
|
|
7
|
-
|
|
8
|
-
pub use processing::dedent_code_block;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
//! Text processing utilities for HTML to Markdown conversion.
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides functions for processing text content, including
|
|
4
|
-
//! code block dedentation and special character handling.
|
|
5
|
-
|
|
6
|
-
/// Remove common leading whitespace from all lines in a code block.
|
|
7
|
-
///
|
|
8
|
-
/// This is useful when HTML authors indent `<pre>` content for readability,
|
|
9
|
-
/// so we can strip the shared indentation without touching meaningful spacing.
|
|
10
|
-
///
|
|
11
|
-
/// # Examples
|
|
12
|
-
///
|
|
13
|
-
/// ```text
|
|
14
|
-
/// " line1\n line2" → "line1\nline2"
|
|
15
|
-
/// " indent1\n indent2" → "indent1\n indent2" (removes 2 chars, minimum indent)
|
|
16
|
-
/// " \n code" → "\ncode"
|
|
17
|
-
/// ```
|
|
18
|
-
pub fn dedent_code_block(content: &str) -> String {
|
|
19
|
-
let lines: Vec<&str> = content.lines().collect();
|
|
20
|
-
if lines.is_empty() {
|
|
21
|
-
return String::new();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let min_indent = lines
|
|
25
|
-
.iter()
|
|
26
|
-
.filter(|line| !line.trim().is_empty())
|
|
27
|
-
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
|
|
28
|
-
.min()
|
|
29
|
-
.unwrap_or(0);
|
|
30
|
-
|
|
31
|
-
lines.iter().fold(String::new(), |mut acc, line| {
|
|
32
|
-
if !acc.is_empty() {
|
|
33
|
-
acc.push('\n');
|
|
34
|
-
}
|
|
35
|
-
let processed = if line.trim().is_empty() {
|
|
36
|
-
*line
|
|
37
|
-
} else {
|
|
38
|
-
let mut remaining = min_indent;
|
|
39
|
-
let mut cut = 0;
|
|
40
|
-
for (idx, ch) in line.char_indices() {
|
|
41
|
-
if remaining == 0 {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
if ch.is_whitespace() {
|
|
45
|
-
remaining -= 1;
|
|
46
|
-
cut = idx + ch.len_utf8();
|
|
47
|
-
} else {
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
&line[cut..]
|
|
52
|
-
};
|
|
53
|
-
acc.push_str(processed);
|
|
54
|
-
acc
|
|
55
|
-
})
|
|
56
|
-
}
|