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,428 +0,0 @@
|
|
|
1
|
-
//! Handler for link elements (a, anchor).
|
|
2
|
-
//!
|
|
3
|
-
//! Converts HTML anchor tags to Markdown links with support for:
|
|
4
|
-
//! - Standard Markdown link syntax `[label](href "title")`
|
|
5
|
-
//! - Autolinks for simple URLs like `<https://example.com>`
|
|
6
|
-
//! - Link label escaping for special Markdown characters
|
|
7
|
-
//! - Heading-in-link special handling (wraps link around heading)
|
|
8
|
-
//! - Visitor callbacks for custom link processing
|
|
9
|
-
//! - Metadata collection for links (links, URLs, titles, rel attributes)
|
|
10
|
-
//! - Block-level content within links (via inline context)
|
|
11
|
-
|
|
12
|
-
#[cfg(feature = "visitor")]
|
|
13
|
-
use crate::converter::utility::content::collect_tag_attributes;
|
|
14
|
-
use crate::converter::utility::content::{collect_link_label_text, escape_link_label, normalize_link_label};
|
|
15
|
-
use crate::converter::utility::preprocessing::sanitize_markdown_url;
|
|
16
|
-
use crate::options::ConversionOptions;
|
|
17
|
-
#[cfg(any(feature = "metadata", feature = "visitor"))]
|
|
18
|
-
use std::collections::BTreeMap;
|
|
19
|
-
use tl::{NodeHandle, Parser};
|
|
20
|
-
|
|
21
|
-
// Type aliases for Context and DomContext to avoid circular imports
|
|
22
|
-
// These are imported from converter.rs and should be made accessible
|
|
23
|
-
type Context = crate::converter::Context;
|
|
24
|
-
type DomContext = crate::converter::DomContext;
|
|
25
|
-
|
|
26
|
-
/// Handler for anchor/link elements: `<a>`.
|
|
27
|
-
///
|
|
28
|
-
/// Processes anchor tags to generate Markdown links:
|
|
29
|
-
/// - Detects autolinks (link text matches href)
|
|
30
|
-
/// - Extracts and normalizes link labels
|
|
31
|
-
/// - Handles nested headings within links
|
|
32
|
-
/// - Escapes special characters in labels
|
|
33
|
-
/// - Collects metadata when feature is enabled
|
|
34
|
-
/// - Supports visitor callbacks for custom processing
|
|
35
|
-
///
|
|
36
|
-
/// # Link Label Extraction
|
|
37
|
-
/// For links with block-level content, extracts text separately.
|
|
38
|
-
/// Collapses newlines and normalizes whitespace per Markdown spec.
|
|
39
|
-
///
|
|
40
|
-
/// # Autolinks
|
|
41
|
-
/// When `autolinks` option is enabled, detects links where the text equals
|
|
42
|
-
/// the href (e.g., `<a href="https://example.com">https://example.com</a>`)
|
|
43
|
-
/// and outputs as `<https://example.com>` instead.
|
|
44
|
-
///
|
|
45
|
-
/// # Note
|
|
46
|
-
/// This function references helper functions from converter.rs
|
|
47
|
-
/// which must be accessible (pub(crate)) for this module to work correctly.
|
|
48
|
-
pub fn handle(
|
|
49
|
-
node_handle: &NodeHandle,
|
|
50
|
-
parser: &Parser,
|
|
51
|
-
output: &mut String,
|
|
52
|
-
options: &ConversionOptions,
|
|
53
|
-
ctx: &Context,
|
|
54
|
-
depth: usize,
|
|
55
|
-
dom_ctx: &DomContext,
|
|
56
|
-
) {
|
|
57
|
-
// Import helper functions from parent converter module
|
|
58
|
-
use crate::converter::block::heading::{heading_allows_inline_images, push_heading};
|
|
59
|
-
use crate::converter::utility::content::normalized_tag_name;
|
|
60
|
-
#[allow(unused_imports)]
|
|
61
|
-
use crate::converter::utility::serialization::serialize_node;
|
|
62
|
-
use crate::converter::{find_single_heading_child, get_text_content, walk_node};
|
|
63
|
-
|
|
64
|
-
let Some(node) = node_handle.get(parser) else {
|
|
65
|
-
return;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
let tl::Node::Tag(tag) = node else {
|
|
69
|
-
return;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Extract href and title attributes
|
|
73
|
-
let href_attr = tag.attributes().get("href").flatten().map(|v| {
|
|
74
|
-
let decoded = crate::text::decode_html_entities(&v.as_utf8_str());
|
|
75
|
-
sanitize_markdown_url(&decoded).into_owned()
|
|
76
|
-
});
|
|
77
|
-
let title = tag
|
|
78
|
-
.attributes()
|
|
79
|
-
.get("title")
|
|
80
|
-
.flatten()
|
|
81
|
-
.map(|v| v.as_utf8_str().to_string());
|
|
82
|
-
|
|
83
|
-
if let Some(href) = href_attr {
|
|
84
|
-
let raw_text = crate::text::normalize_whitespace(&get_text_content(node_handle, parser, dom_ctx))
|
|
85
|
-
.trim()
|
|
86
|
-
.to_string();
|
|
87
|
-
|
|
88
|
-
// If we're already inside a link, just render the text content, don't create a nested link
|
|
89
|
-
if ctx.in_link {
|
|
90
|
-
let children = tag.children();
|
|
91
|
-
for child_handle in children.top().iter() {
|
|
92
|
-
walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check if this should be rendered as an autolink
|
|
98
|
-
let is_autolink = options.autolinks
|
|
99
|
-
&& !options.default_title
|
|
100
|
-
&& !href.is_empty()
|
|
101
|
-
&& (raw_text == href || (href.starts_with("mailto:") && raw_text == href[7..]));
|
|
102
|
-
|
|
103
|
-
if is_autolink {
|
|
104
|
-
output.push('<');
|
|
105
|
-
if href.starts_with("mailto:") && raw_text == href[7..] {
|
|
106
|
-
output.push_str(&raw_text);
|
|
107
|
-
} else {
|
|
108
|
-
output.push_str(&href);
|
|
109
|
-
}
|
|
110
|
-
output.push('>');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Check if link contains a single heading child element
|
|
115
|
-
if let Some((heading_level, heading_handle)) = find_single_heading_child(*node_handle, parser) {
|
|
116
|
-
if let Some(heading_node) = heading_handle.get(parser) {
|
|
117
|
-
if let tl::Node::Tag(heading_tag) = heading_node {
|
|
118
|
-
let heading_name = normalized_tag_name(heading_tag.name().as_utf8_str()).into_owned();
|
|
119
|
-
let mut heading_text = String::new();
|
|
120
|
-
let heading_ctx = Context {
|
|
121
|
-
in_heading: true,
|
|
122
|
-
convert_as_inline: true,
|
|
123
|
-
heading_allow_inline_images: heading_allows_inline_images(
|
|
124
|
-
&heading_name,
|
|
125
|
-
&ctx.keep_inline_images_in,
|
|
126
|
-
),
|
|
127
|
-
..ctx.clone()
|
|
128
|
-
};
|
|
129
|
-
walk_node(
|
|
130
|
-
&heading_handle,
|
|
131
|
-
parser,
|
|
132
|
-
&mut heading_text,
|
|
133
|
-
options,
|
|
134
|
-
&heading_ctx,
|
|
135
|
-
depth + 1,
|
|
136
|
-
dom_ctx,
|
|
137
|
-
);
|
|
138
|
-
let trimmed_heading = heading_text.trim();
|
|
139
|
-
if !trimmed_heading.is_empty() {
|
|
140
|
-
let escaped_label = escape_link_label(trimmed_heading);
|
|
141
|
-
let mut link_buffer = String::new();
|
|
142
|
-
append_markdown_link(
|
|
143
|
-
&mut link_buffer,
|
|
144
|
-
&escaped_label,
|
|
145
|
-
href.as_str(),
|
|
146
|
-
title.as_deref(),
|
|
147
|
-
raw_text.as_str(),
|
|
148
|
-
options,
|
|
149
|
-
ctx.reference_collector.as_ref(),
|
|
150
|
-
);
|
|
151
|
-
push_heading(output, ctx, options, heading_level, link_buffer.as_str());
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Collect link label from children
|
|
159
|
-
let children: Vec<_> = tag.children().top().iter().copied().collect();
|
|
160
|
-
let (inline_label, _block_nodes, saw_block) = collect_link_label_text(&children, parser, dom_ctx);
|
|
161
|
-
let mut label = if saw_block {
|
|
162
|
-
let mut content = String::new();
|
|
163
|
-
let link_ctx = Context {
|
|
164
|
-
inline_depth: ctx.inline_depth + 1,
|
|
165
|
-
convert_as_inline: true,
|
|
166
|
-
in_link: true,
|
|
167
|
-
..ctx.clone()
|
|
168
|
-
};
|
|
169
|
-
for child_handle in &children {
|
|
170
|
-
let mut child_buf = String::new();
|
|
171
|
-
walk_node(
|
|
172
|
-
child_handle,
|
|
173
|
-
parser,
|
|
174
|
-
&mut child_buf,
|
|
175
|
-
options,
|
|
176
|
-
&link_ctx,
|
|
177
|
-
depth + 1,
|
|
178
|
-
dom_ctx,
|
|
179
|
-
);
|
|
180
|
-
if !child_buf.trim().is_empty()
|
|
181
|
-
&& !content.is_empty()
|
|
182
|
-
&& !content.chars().last().is_none_or(char::is_whitespace)
|
|
183
|
-
&& !child_buf.chars().next().is_none_or(char::is_whitespace)
|
|
184
|
-
{
|
|
185
|
-
content.push(' ');
|
|
186
|
-
}
|
|
187
|
-
content.push_str(&child_buf);
|
|
188
|
-
}
|
|
189
|
-
if content.trim().is_empty() {
|
|
190
|
-
normalize_link_label(&inline_label)
|
|
191
|
-
} else {
|
|
192
|
-
normalize_link_label(&content)
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
let mut content = String::new();
|
|
196
|
-
let link_ctx = Context {
|
|
197
|
-
inline_depth: ctx.inline_depth + 1,
|
|
198
|
-
in_link: true,
|
|
199
|
-
..ctx.clone()
|
|
200
|
-
};
|
|
201
|
-
for child_handle in &children {
|
|
202
|
-
walk_node(
|
|
203
|
-
child_handle,
|
|
204
|
-
parser,
|
|
205
|
-
&mut content,
|
|
206
|
-
options,
|
|
207
|
-
&link_ctx,
|
|
208
|
-
depth + 1,
|
|
209
|
-
dom_ctx,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
normalize_link_label(&content)
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// Apply fallback label strategies
|
|
216
|
-
if label.is_empty() && saw_block {
|
|
217
|
-
let fallback = crate::text::normalize_whitespace(&get_text_content(node_handle, parser, dom_ctx));
|
|
218
|
-
label = normalize_link_label(&fallback);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if label.is_empty() && !raw_text.is_empty() {
|
|
222
|
-
label = normalize_link_label(&raw_text);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if label.is_empty() && !href.is_empty() && !children.is_empty() {
|
|
226
|
-
label = href.clone();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Truncate label if it exceeds maximum length
|
|
230
|
-
let escaped_label = escape_link_label(&label);
|
|
231
|
-
|
|
232
|
-
// Handle visitor callbacks if feature is enabled
|
|
233
|
-
#[cfg(feature = "visitor")]
|
|
234
|
-
let link_output = if let Some(ref visitor_handle) = ctx.visitor {
|
|
235
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
236
|
-
|
|
237
|
-
let attributes: BTreeMap<String, String> = collect_tag_attributes(tag);
|
|
238
|
-
|
|
239
|
-
let node_id = node_handle.get_inner();
|
|
240
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
241
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
242
|
-
|
|
243
|
-
let node_ctx = NodeContext {
|
|
244
|
-
node_type: NodeType::Link,
|
|
245
|
-
tag_name: "a".to_string(),
|
|
246
|
-
attributes,
|
|
247
|
-
depth,
|
|
248
|
-
index_in_parent,
|
|
249
|
-
parent_tag,
|
|
250
|
-
is_inline: true,
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
let visit_result = {
|
|
254
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
255
|
-
visitor.visit_link(&node_ctx, &href, &label, title.as_deref())
|
|
256
|
-
};
|
|
257
|
-
match visit_result {
|
|
258
|
-
VisitResult::Continue => {
|
|
259
|
-
let mut buf = String::new();
|
|
260
|
-
append_markdown_link(
|
|
261
|
-
&mut buf,
|
|
262
|
-
&escaped_label,
|
|
263
|
-
href.as_str(),
|
|
264
|
-
title.as_deref(),
|
|
265
|
-
label.as_str(),
|
|
266
|
-
options,
|
|
267
|
-
ctx.reference_collector.as_ref(),
|
|
268
|
-
);
|
|
269
|
-
Some(buf)
|
|
270
|
-
}
|
|
271
|
-
VisitResult::Custom(custom) => Some(custom),
|
|
272
|
-
VisitResult::Skip => None,
|
|
273
|
-
VisitResult::Error(err) => {
|
|
274
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
275
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
276
|
-
}
|
|
277
|
-
None
|
|
278
|
-
}
|
|
279
|
-
VisitResult::PreserveHtml => Some(serialize_node(node_handle, parser)),
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
let mut buf = String::new();
|
|
283
|
-
append_markdown_link(
|
|
284
|
-
&mut buf,
|
|
285
|
-
&escaped_label,
|
|
286
|
-
href.as_str(),
|
|
287
|
-
title.as_deref(),
|
|
288
|
-
label.as_str(),
|
|
289
|
-
options,
|
|
290
|
-
ctx.reference_collector.as_ref(),
|
|
291
|
-
);
|
|
292
|
-
Some(buf)
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
#[cfg(not(feature = "visitor"))]
|
|
296
|
-
let link_output = {
|
|
297
|
-
let mut buf = String::new();
|
|
298
|
-
append_markdown_link(
|
|
299
|
-
&mut buf,
|
|
300
|
-
&escaped_label,
|
|
301
|
-
href.as_str(),
|
|
302
|
-
title.as_deref(),
|
|
303
|
-
label.as_str(),
|
|
304
|
-
options,
|
|
305
|
-
ctx.reference_collector.as_ref(),
|
|
306
|
-
);
|
|
307
|
-
Some(buf)
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
if let Some(link_text) = link_output {
|
|
311
|
-
output.push_str(&link_text);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Collect metadata if feature is enabled
|
|
315
|
-
#[cfg(feature = "metadata")]
|
|
316
|
-
if ctx.metadata_wants_links {
|
|
317
|
-
if let Some(ref collector) = ctx.metadata_collector {
|
|
318
|
-
let rel_attr = tag
|
|
319
|
-
.attributes()
|
|
320
|
-
.get("rel")
|
|
321
|
-
.flatten()
|
|
322
|
-
.map(|v| v.as_utf8_str().to_string());
|
|
323
|
-
let mut attributes_map = BTreeMap::new();
|
|
324
|
-
for (key, value_opt) in tag.attributes().iter() {
|
|
325
|
-
let key_str = key.to_string();
|
|
326
|
-
if key_str == "href" {
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let value = value_opt.map(|v| v.to_string()).unwrap_or_default();
|
|
331
|
-
attributes_map.insert(key_str, value);
|
|
332
|
-
}
|
|
333
|
-
collector
|
|
334
|
-
.borrow_mut()
|
|
335
|
-
.add_link(href.clone(), label, title.clone(), rel_attr, attributes_map);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
// No href: just process children as inline content
|
|
340
|
-
let children = tag.children();
|
|
341
|
-
for child_handle in children.top().iter() {
|
|
342
|
-
walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/// Format and append a Markdown link to the output string.
|
|
348
|
-
///
|
|
349
|
-
/// Generates the link syntax: `[label](href "title")`
|
|
350
|
-
/// Handles special cases:
|
|
351
|
-
/// - Empty href renders as `[label]()`
|
|
352
|
-
/// - Hrefs with spaces/newlines get wrapped in angle brackets: `[label](<URL with spaces>)`
|
|
353
|
-
/// - Unbalanced parentheses in href get escaped: `[label](url\(example\))`
|
|
354
|
-
/// - Titles are wrapped in quotes and quotes inside are escaped
|
|
355
|
-
/// - When `default_title` option is true and raw_text equals href, adds href as title
|
|
356
|
-
///
|
|
357
|
-
/// # Arguments
|
|
358
|
-
/// * `output` - Output buffer to append the link to
|
|
359
|
-
/// * `label` - The link text (already escaped)
|
|
360
|
-
/// * `href` - The URL/destination
|
|
361
|
-
/// * `title` - Optional link title attribute
|
|
362
|
-
/// * `raw_text` - Original unprocessed text (for default_title option)
|
|
363
|
-
/// * `options` - Conversion options
|
|
364
|
-
pub fn append_markdown_link(
|
|
365
|
-
output: &mut String,
|
|
366
|
-
label: &str,
|
|
367
|
-
href: &str,
|
|
368
|
-
title: Option<&str>,
|
|
369
|
-
raw_text: &str,
|
|
370
|
-
options: &ConversionOptions,
|
|
371
|
-
reference_collector: Option<&crate::converter::reference_collector::ReferenceCollectorHandle>,
|
|
372
|
-
) {
|
|
373
|
-
if options.link_style == crate::options::validation::LinkStyle::Reference && !href.is_empty() {
|
|
374
|
-
if let Some(collector) = reference_collector {
|
|
375
|
-
let ref_num = collector.borrow_mut().get_or_insert(href, title);
|
|
376
|
-
output.push('[');
|
|
377
|
-
output.push_str(label);
|
|
378
|
-
output.push_str("][");
|
|
379
|
-
output.push_str(&ref_num.to_string());
|
|
380
|
-
output.push(']');
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
output.push('[');
|
|
386
|
-
output.push_str(label);
|
|
387
|
-
output.push_str("](");
|
|
388
|
-
|
|
389
|
-
if href.is_empty() {
|
|
390
|
-
output.push_str("<>");
|
|
391
|
-
} else if href.contains(' ') || href.contains('\n') {
|
|
392
|
-
output.push('<');
|
|
393
|
-
output.push_str(href);
|
|
394
|
-
output.push('>');
|
|
395
|
-
} else {
|
|
396
|
-
let open_count = href.chars().filter(|&c| c == '(').count();
|
|
397
|
-
let close_count = href.chars().filter(|&c| c == ')').count();
|
|
398
|
-
|
|
399
|
-
if open_count == close_count {
|
|
400
|
-
output.push_str(href);
|
|
401
|
-
} else {
|
|
402
|
-
let escaped_href = href.replace('(', "\\(").replace(')', "\\)");
|
|
403
|
-
output.push_str(&escaped_href);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if let Some(title_text) = title {
|
|
408
|
-
output.push_str(" \"");
|
|
409
|
-
if title_text.contains('"') {
|
|
410
|
-
let escaped_title = title_text.replace('"', "\\\"");
|
|
411
|
-
output.push_str(&escaped_title);
|
|
412
|
-
} else {
|
|
413
|
-
output.push_str(title_text);
|
|
414
|
-
}
|
|
415
|
-
output.push('"');
|
|
416
|
-
} else if options.default_title && raw_text == href {
|
|
417
|
-
output.push_str(" \"");
|
|
418
|
-
if href.contains('"') {
|
|
419
|
-
let escaped_href = href.replace('"', "\\\"");
|
|
420
|
-
output.push_str(&escaped_href);
|
|
421
|
-
} else {
|
|
422
|
-
output.push_str(href);
|
|
423
|
-
}
|
|
424
|
-
output.push('"');
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
output.push(')');
|
|
428
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
//! Inline element handlers for HTML to Markdown conversion.
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides specialized handlers for inline HTML elements:
|
|
4
|
-
//! - Emphasis elements (strong, b, em, i)
|
|
5
|
-
//! - Links (a)
|
|
6
|
-
//! - Code elements (code, kbd, samp)
|
|
7
|
-
//! - Semantic elements (mark, del, s, ins, u, small, sub, sup, var, dfn, abbr)
|
|
8
|
-
//! - Ruby annotation elements (ruby, rb, rt, rp, rtc)
|
|
9
|
-
//!
|
|
10
|
-
//! These handlers are designed to be extracted from the main `converter.rs`
|
|
11
|
-
//! file and integrated through the dispatcher function.
|
|
12
|
-
//!
|
|
13
|
-
//! **Integration Pattern:**
|
|
14
|
-
//! Each handler function takes the same signature:
|
|
15
|
-
//! - `tag_name: &str` - The HTML tag being processed
|
|
16
|
-
//! - `node_handle: &NodeHandle` - The DOM node handle
|
|
17
|
-
//! - `parser: &Parser` - The HTML parser reference
|
|
18
|
-
//! - `output: &mut String` - The output buffer to write to
|
|
19
|
-
//! - `options: &ConversionOptions` - Conversion configuration
|
|
20
|
-
//! - `ctx: &Context` - Processing context (state tracking)
|
|
21
|
-
//! - `depth: usize` - Current DOM tree depth
|
|
22
|
-
//! - `dom_ctx: &DomContext` - DOM context for tree relationships
|
|
23
|
-
//!
|
|
24
|
-
//! The main dispatcher function `dispatch_inline_handler` routes tags to
|
|
25
|
-
//! their appropriate handlers and returns a boolean indicating success.
|
|
26
|
-
|
|
27
|
-
pub mod code;
|
|
28
|
-
pub mod emphasis;
|
|
29
|
-
pub mod link;
|
|
30
|
-
pub mod ruby;
|
|
31
|
-
pub mod semantic;
|
|
32
|
-
|
|
33
|
-
// Re-export types from parent module for submodule access
|
|
34
|
-
|
|
35
|
-
// Re-export handler functions for internal use by dispatcher (crate-private)
|
|
36
|
-
// pub(crate) use ruby::handle as handle_ruby;
|
|
37
|
-
|
|
38
|
-
/// Dispatches inline element handling to the appropriate handler.
|
|
39
|
-
///
|
|
40
|
-
/// This function routes inline HTML elements to their specialized handlers
|
|
41
|
-
/// based on tag name. It is designed to be called from the main `walk_node`
|
|
42
|
-
/// function in `converter.rs`.
|
|
43
|
-
///
|
|
44
|
-
/// # Routing Table
|
|
45
|
-
///
|
|
46
|
-
/// The following tag routes are supported:
|
|
47
|
-
///
|
|
48
|
-
/// | Tag(s) | Handler | Description |
|
|
49
|
-
/// |--------|---------|-------------|
|
|
50
|
-
/// | `strong`, `b` | emphasis | Bold/strong text formatting |
|
|
51
|
-
/// | `em`, `i` | emphasis | Italic/emphasis text formatting |
|
|
52
|
-
/// | `a` | link | Hyperlinks and anchors |
|
|
53
|
-
/// | `code`, `kbd`, `samp` | code | Inline code and keyboard input |
|
|
54
|
-
/// | `mark`, `del`, `s`, `ins`, `u`, `small`, `sub`, `sup`, `var`, `dfn`, `abbr`, `span` | semantic | Semantic formatting |
|
|
55
|
-
/// | `ruby`, `rb`, `rt`, `rp`, `rtc` | ruby | Ruby annotations (East Asian typography) |
|
|
56
|
-
///
|
|
57
|
-
/// # Return Value
|
|
58
|
-
///
|
|
59
|
-
/// Returns `true` if the tag was recognized and handled, `false` otherwise.
|
|
60
|
-
/// This allows the caller to distinguish between:
|
|
61
|
-
/// - Handled inline elements (return `true`)
|
|
62
|
-
/// - Unhandled elements (return `false`) that should be processed as text or passed through
|
|
63
|
-
///
|
|
64
|
-
/// # Usage in converter.rs
|
|
65
|
-
///
|
|
66
|
-
/// ```text
|
|
67
|
-
/// if crate::converter::inline::dispatch_inline_handler(
|
|
68
|
-
/// &tag_name,
|
|
69
|
-
/// &node_handle,
|
|
70
|
-
/// parser,
|
|
71
|
-
/// output,
|
|
72
|
-
/// options,
|
|
73
|
-
/// ctx,
|
|
74
|
-
/// depth,
|
|
75
|
-
/// dom_ctx,
|
|
76
|
-
/// ) {
|
|
77
|
-
/// return; // Element was handled, move to next sibling
|
|
78
|
-
/// }
|
|
79
|
-
/// // Element was not handled, process as default inline element
|
|
80
|
-
/// ```
|
|
81
|
-
///
|
|
82
|
-
/// # Parameters
|
|
83
|
-
///
|
|
84
|
-
/// * `tag_name` - The normalized HTML tag name (lowercase)
|
|
85
|
-
/// * `node_handle` - The DOM node handle from the parser
|
|
86
|
-
/// * `parser` - Reference to the tl HTML parser
|
|
87
|
-
/// * `output` - Output buffer to write converted content to
|
|
88
|
-
/// * `options` - Conversion configuration options
|
|
89
|
-
/// * `ctx` - Processing context with state tracking
|
|
90
|
-
/// * `depth` - Current DOM tree depth for recursion tracking
|
|
91
|
-
/// * `dom_ctx` - DOM context for accessing tree structure
|
|
92
|
-
///
|
|
93
|
-
/// # Example
|
|
94
|
-
///
|
|
95
|
-
/// For `<strong>Bold text</strong>`, the dispatcher:
|
|
96
|
-
/// 1. Recognizes "strong" tag
|
|
97
|
-
/// 2. Routes to emphasis handler
|
|
98
|
-
/// 3. Returns `true`
|
|
99
|
-
/// 4. Emphasis handler outputs `**Bold text**` to output buffer
|
|
100
|
-
///
|
|
101
|
-
/// For `<span>Normal text</span>`, the dispatcher:
|
|
102
|
-
/// 1. Fails to recognize "span" tag
|
|
103
|
-
/// 2. Returns `false`
|
|
104
|
-
/// 3. Caller processes as default inline content
|
|
105
|
-
pub fn dispatch_inline_handler(
|
|
106
|
-
tag_name: &str,
|
|
107
|
-
node_handle: &tl::NodeHandle,
|
|
108
|
-
parser: &tl::Parser,
|
|
109
|
-
output: &mut String,
|
|
110
|
-
options: &crate::options::ConversionOptions,
|
|
111
|
-
ctx: &crate::converter::Context,
|
|
112
|
-
depth: usize,
|
|
113
|
-
dom_ctx: &crate::converter::DomContext,
|
|
114
|
-
) -> bool {
|
|
115
|
-
match tag_name {
|
|
116
|
-
// Emphasis elements: strong, b (bold) and em, i (italic)
|
|
117
|
-
"strong" | "b" | "em" | "i" => {
|
|
118
|
-
emphasis::handle(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
119
|
-
true
|
|
120
|
-
}
|
|
121
|
-
// Link elements: a (anchor)
|
|
122
|
-
"a" => {
|
|
123
|
-
link::handle(node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
124
|
-
true
|
|
125
|
-
}
|
|
126
|
-
// Code elements: code, kbd (keyboard input), samp (sample output)
|
|
127
|
-
"code" | "kbd" | "samp" => {
|
|
128
|
-
code::handle(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
129
|
-
true
|
|
130
|
-
}
|
|
131
|
-
// Semantic elements: mark, del, s, ins, u, small, sub, sup, var, dfn, abbr, span
|
|
132
|
-
"mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr" | "span" => {
|
|
133
|
-
semantic::handle(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
134
|
-
true
|
|
135
|
-
}
|
|
136
|
-
// Ruby annotation elements: ruby, rb, rt, rp, rtc
|
|
137
|
-
"ruby" | "rb" | "rt" | "rp" | "rtc" => {
|
|
138
|
-
ruby::handle(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
139
|
-
true
|
|
140
|
-
}
|
|
141
|
-
// Unknown element - not handled by inline dispatcher
|
|
142
|
-
_ => false,
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
#[cfg(test)]
|
|
147
|
-
mod tests {
|
|
148
|
-
/// Test that all expected tags are properly dispatched
|
|
149
|
-
#[test]
|
|
150
|
-
fn test_dispatcher_routes_emphasis_tags() {
|
|
151
|
-
assert!(matches!(
|
|
152
|
-
("strong", "strong"),
|
|
153
|
-
(tag, _) if matches!(tag, "strong" | "b" | "em" | "i")
|
|
154
|
-
));
|
|
155
|
-
assert!(matches!(
|
|
156
|
-
("em", "em"),
|
|
157
|
-
(tag, _) if matches!(tag, "strong" | "b" | "em" | "i")
|
|
158
|
-
));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
#[test]
|
|
162
|
-
fn test_dispatcher_routes_code_tags() {
|
|
163
|
-
assert!(matches!(
|
|
164
|
-
("code", "code"),
|
|
165
|
-
(tag, _) if matches!(tag, "code" | "kbd" | "samp")
|
|
166
|
-
));
|
|
167
|
-
assert!(matches!(
|
|
168
|
-
("kbd", "kbd"),
|
|
169
|
-
(tag, _) if matches!(tag, "code" | "kbd" | "samp")
|
|
170
|
-
));
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
#[test]
|
|
174
|
-
fn test_dispatcher_routes_semantic_tags() {
|
|
175
|
-
assert!(matches!(
|
|
176
|
-
("mark", "mark"),
|
|
177
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
178
|
-
));
|
|
179
|
-
assert!(matches!(
|
|
180
|
-
("del", "del"),
|
|
181
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
182
|
-
));
|
|
183
|
-
assert!(matches!(
|
|
184
|
-
("sub", "sub"),
|
|
185
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
186
|
-
));
|
|
187
|
-
assert!(matches!(
|
|
188
|
-
("var", "var"),
|
|
189
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
190
|
-
));
|
|
191
|
-
assert!(matches!(
|
|
192
|
-
("dfn", "dfn"),
|
|
193
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
194
|
-
));
|
|
195
|
-
assert!(matches!(
|
|
196
|
-
("abbr", "abbr"),
|
|
197
|
-
(tag, _) if matches!(tag, "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr")
|
|
198
|
-
));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
#[test]
|
|
202
|
-
fn test_dispatcher_recognizes_link_tag() {
|
|
203
|
-
assert!(matches!(
|
|
204
|
-
("a", "a"),
|
|
205
|
-
(tag, _) if tag == "a"
|
|
206
|
-
));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
#[test]
|
|
210
|
-
fn test_dispatcher_routes_ruby_tags() {
|
|
211
|
-
assert!(matches!(
|
|
212
|
-
("ruby", "ruby"),
|
|
213
|
-
(tag, _) if matches!(tag, "ruby" | "rb" | "rt" | "rp" | "rtc")
|
|
214
|
-
));
|
|
215
|
-
assert!(matches!(
|
|
216
|
-
("rt", "rt"),
|
|
217
|
-
(tag, _) if matches!(tag, "ruby" | "rb" | "rt" | "rp" | "rtc")
|
|
218
|
-
));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
#[test]
|
|
222
|
-
fn test_unknown_tags_not_routed() {
|
|
223
|
-
// These should fall through to default handling
|
|
224
|
-
let unknown_tags = vec!["div", "p", "section", "article", "table"];
|
|
225
|
-
for tag in unknown_tags {
|
|
226
|
-
assert!(matches!(
|
|
227
|
-
(tag, tag),
|
|
228
|
-
(tag, _) if !matches!(
|
|
229
|
-
tag,
|
|
230
|
-
"strong" | "b" | "em" | "i" | "a" | "code" | "kbd" | "samp"
|
|
231
|
-
| "mark" | "del" | "s" | "ins" | "u" | "small" | "sub" | "sup" | "var" | "dfn" | "abbr"
|
|
232
|
-
| "ruby" | "rb" | "rt" | "rp" | "rtc" | "span"
|
|
233
|
-
)
|
|
234
|
-
));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|