html-to-markdown 3.4.0 → 3.6.0.pre.rc.23
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,306 +0,0 @@
|
|
|
1
|
-
//! Handler for code-related inline elements (code, kbd, samp).
|
|
2
|
-
//!
|
|
3
|
-
//! Converts HTML code elements to Markdown inline code formatting with support for:
|
|
4
|
-
//! - Inline code blocks with backtick delimiters
|
|
5
|
-
//! - Keyboard input (<kbd>) rendered as code
|
|
6
|
-
//! - Sample output (<samp>) rendered as code
|
|
7
|
-
//! - Smart backtick escaping for nested backticks
|
|
8
|
-
//! - Delimiter spacing to prevent ambiguous parsing
|
|
9
|
-
//! - Nested code context tracking (suppress formatting in <code> within <code>)
|
|
10
|
-
//! - Visitor callbacks for custom code processing
|
|
11
|
-
//! - Whitespace normalization for kbd/samp elements
|
|
12
|
-
|
|
13
|
-
#[cfg(feature = "visitor")]
|
|
14
|
-
use crate::converter::utility::content::collect_tag_attributes;
|
|
15
|
-
use crate::options::ConversionOptions;
|
|
16
|
-
use crate::text;
|
|
17
|
-
#[allow(unused_imports)]
|
|
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 code-related inline elements: code, kbd (keyboard), and samp (sample output).
|
|
27
|
-
///
|
|
28
|
-
/// Processes code content based on context:
|
|
29
|
-
/// - For <code> within <code>: passes content through without wrapping backticks (nested code detection)
|
|
30
|
-
/// - For <kbd> and <samp>: normalizes whitespace and wraps with backticks
|
|
31
|
-
/// - For standalone <code>: applies smart backtick escaping and delimiter spacing
|
|
32
|
-
/// - Handles visitor callbacks for custom behavior when feature is enabled
|
|
33
|
-
/// - Properly escapes backticks in content that contains them
|
|
34
|
-
///
|
|
35
|
-
/// # Note
|
|
36
|
-
/// This function references helper functions and `walk_node` from converter.rs
|
|
37
|
-
/// which must be accessible (pub(crate)) for this module to work correctly.
|
|
38
|
-
pub fn handle(
|
|
39
|
-
tag_name: &str,
|
|
40
|
-
node_handle: &NodeHandle,
|
|
41
|
-
parser: &Parser,
|
|
42
|
-
output: &mut String,
|
|
43
|
-
options: &ConversionOptions,
|
|
44
|
-
ctx: &Context,
|
|
45
|
-
depth: usize,
|
|
46
|
-
dom_ctx: &DomContext,
|
|
47
|
-
) {
|
|
48
|
-
// Import helper functions from parent converter module
|
|
49
|
-
|
|
50
|
-
match tag_name {
|
|
51
|
-
"code" => {
|
|
52
|
-
handle_code(node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
53
|
-
}
|
|
54
|
-
"kbd" | "samp" => {
|
|
55
|
-
handle_kbd_samp(tag_name, node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
56
|
-
}
|
|
57
|
-
_ => {}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/// Handle inline code element (<code> tag).
|
|
62
|
-
///
|
|
63
|
-
/// Smart handling of nested code:
|
|
64
|
-
/// - If already in code context (ctx.in_code), just process children without wrapping
|
|
65
|
-
/// - Otherwise, wraps content with backticks with smart escaping logic
|
|
66
|
-
///
|
|
67
|
-
/// Smart backtick escaping:
|
|
68
|
-
/// - Detects consecutive backticks in content
|
|
69
|
-
/// - Adds extra backticks if needed to avoid ambiguous parsing
|
|
70
|
-
/// - Adds space delimiters if content starts/ends with backticks or spaces
|
|
71
|
-
fn handle_code(
|
|
72
|
-
node_handle: &NodeHandle,
|
|
73
|
-
parser: &Parser,
|
|
74
|
-
output: &mut String,
|
|
75
|
-
options: &ConversionOptions,
|
|
76
|
-
ctx: &Context,
|
|
77
|
-
depth: usize,
|
|
78
|
-
dom_ctx: &DomContext,
|
|
79
|
-
) {
|
|
80
|
-
#[allow(unused_imports)]
|
|
81
|
-
use crate::converter::{serialize_node, walk_node};
|
|
82
|
-
|
|
83
|
-
let Some(node) = node_handle.get(parser) else { return };
|
|
84
|
-
|
|
85
|
-
let tag = match node {
|
|
86
|
-
tl::Node::Tag(tag) => tag,
|
|
87
|
-
_ => return,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
let code_ctx = Context {
|
|
91
|
-
in_code: true,
|
|
92
|
-
..ctx.clone()
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// Nested code detection: if already in code, just process children
|
|
96
|
-
if ctx.in_code {
|
|
97
|
-
let children = tag.children();
|
|
98
|
-
for child_handle in children.top().iter() {
|
|
99
|
-
walk_node(child_handle, parser, output, options, &code_ctx, depth + 1, dom_ctx);
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
let mut content = String::with_capacity(32);
|
|
103
|
-
let children = tag.children();
|
|
104
|
-
{
|
|
105
|
-
for child_handle in children.top().iter() {
|
|
106
|
-
walk_node(
|
|
107
|
-
child_handle,
|
|
108
|
-
parser,
|
|
109
|
-
&mut content,
|
|
110
|
-
options,
|
|
111
|
-
&code_ctx,
|
|
112
|
-
depth + 1,
|
|
113
|
-
dom_ctx,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
let trimmed = &content;
|
|
119
|
-
|
|
120
|
-
if !content.trim().is_empty() {
|
|
121
|
-
#[cfg(feature = "visitor")]
|
|
122
|
-
let code_output = if let Some(ref visitor_handle) = ctx.visitor {
|
|
123
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
124
|
-
|
|
125
|
-
let attributes: BTreeMap<String, String> = collect_tag_attributes(tag);
|
|
126
|
-
|
|
127
|
-
let node_id = node_handle.get_inner();
|
|
128
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
129
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
130
|
-
|
|
131
|
-
let node_ctx = NodeContext {
|
|
132
|
-
node_type: NodeType::Code,
|
|
133
|
-
tag_name: tag.name().as_utf8_str().to_string(),
|
|
134
|
-
attributes,
|
|
135
|
-
depth,
|
|
136
|
-
index_in_parent,
|
|
137
|
-
parent_tag,
|
|
138
|
-
is_inline: true,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
let visit_result = {
|
|
142
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
143
|
-
visitor.visit_code_inline(&node_ctx, trimmed)
|
|
144
|
-
};
|
|
145
|
-
match visit_result {
|
|
146
|
-
VisitResult::Continue => None,
|
|
147
|
-
VisitResult::Custom(custom) => Some(custom),
|
|
148
|
-
VisitResult::Skip => Some(String::new()),
|
|
149
|
-
VisitResult::PreserveHtml => Some(serialize_node(node_handle, parser)),
|
|
150
|
-
VisitResult::Error(err) => {
|
|
151
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
152
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
153
|
-
}
|
|
154
|
-
None
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
None
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
#[cfg(feature = "visitor")]
|
|
162
|
-
if let Some(custom_output) = code_output {
|
|
163
|
-
output.push_str(&custom_output);
|
|
164
|
-
} else {
|
|
165
|
-
render_code_with_escaping(trimmed, output);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
#[cfg(not(feature = "visitor"))]
|
|
169
|
-
{
|
|
170
|
-
render_code_with_escaping(trimmed, output);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/// Handle keyboard and sample output elements (<kbd> and <samp> tags).
|
|
177
|
-
///
|
|
178
|
-
/// These elements are rendered as inline code with:
|
|
179
|
-
/// - Whitespace normalization (via text::normalize_whitespace)
|
|
180
|
-
/// - Chomp inline handling for prefix/suffix spacing
|
|
181
|
-
/// - Simple single backtick wrapping (no smart escaping for keyboard/sample)
|
|
182
|
-
fn handle_kbd_samp(
|
|
183
|
-
_tag_name: &str,
|
|
184
|
-
node_handle: &NodeHandle,
|
|
185
|
-
parser: &Parser,
|
|
186
|
-
output: &mut String,
|
|
187
|
-
options: &ConversionOptions,
|
|
188
|
-
ctx: &Context,
|
|
189
|
-
depth: usize,
|
|
190
|
-
dom_ctx: &DomContext,
|
|
191
|
-
) {
|
|
192
|
-
use crate::converter::{append_inline_suffix, chomp_inline, walk_node};
|
|
193
|
-
|
|
194
|
-
let Some(node) = node_handle.get(parser) else { return };
|
|
195
|
-
|
|
196
|
-
let _tag = match node {
|
|
197
|
-
tl::Node::Tag(tag) => tag,
|
|
198
|
-
_ => return,
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
let code_ctx = Context {
|
|
202
|
-
in_code: true,
|
|
203
|
-
..ctx.clone()
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
let mut content = String::with_capacity(32);
|
|
207
|
-
let children = _tag.children();
|
|
208
|
-
{
|
|
209
|
-
for child_handle in children.top().iter() {
|
|
210
|
-
walk_node(
|
|
211
|
-
child_handle,
|
|
212
|
-
parser,
|
|
213
|
-
&mut content,
|
|
214
|
-
options,
|
|
215
|
-
&code_ctx,
|
|
216
|
-
depth + 1,
|
|
217
|
-
dom_ctx,
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Normalize whitespace for kbd/samp (unlike code, which preserves it)
|
|
223
|
-
let normalized = text::normalize_whitespace(&content);
|
|
224
|
-
let (prefix, suffix, trimmed) = chomp_inline(&normalized);
|
|
225
|
-
|
|
226
|
-
if !content.trim().is_empty() {
|
|
227
|
-
output.push_str(prefix);
|
|
228
|
-
output.push('`');
|
|
229
|
-
output.push_str(trimmed);
|
|
230
|
-
output.push('`');
|
|
231
|
-
append_inline_suffix(output, suffix, !trimmed.is_empty(), node_handle, parser, dom_ctx);
|
|
232
|
-
} else if !content.is_empty() {
|
|
233
|
-
output.push_str(prefix);
|
|
234
|
-
append_inline_suffix(output, suffix, false, node_handle, parser, dom_ctx);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/// Render inline code with smart backtick escaping.
|
|
239
|
-
///
|
|
240
|
-
/// Handles the logic for:
|
|
241
|
-
/// 1. Detecting backticks in content
|
|
242
|
-
/// 2. Calculating the required number of backticks
|
|
243
|
-
/// 3. Adding delimiter spaces when needed
|
|
244
|
-
///
|
|
245
|
-
/// # Backtick Escaping Logic
|
|
246
|
-
///
|
|
247
|
-
/// - If content contains no backticks: use single backtick delimiters
|
|
248
|
-
/// - If content has single backticks: use double backtick delimiters
|
|
249
|
-
/// - If content has consecutive backticks: use single backtick but rely on spacing
|
|
250
|
-
///
|
|
251
|
-
/// # Delimiter Space Rules
|
|
252
|
-
///
|
|
253
|
-
/// Add space delimiters if:
|
|
254
|
-
/// - Content is all spaces
|
|
255
|
-
/// - Content starts/ends with backtick
|
|
256
|
-
/// - Content starts and ends with spaces AND contains backticks
|
|
257
|
-
fn render_code_with_escaping(trimmed: &str, output: &mut String) {
|
|
258
|
-
let contains_backtick = trimmed.contains('`');
|
|
259
|
-
|
|
260
|
-
let needs_delimiter_spaces = {
|
|
261
|
-
let first_char = trimmed.chars().next();
|
|
262
|
-
let last_char = trimmed.chars().last();
|
|
263
|
-
let starts_with_space = first_char == Some(' ');
|
|
264
|
-
let ends_with_space = last_char == Some(' ');
|
|
265
|
-
let starts_with_backtick = first_char == Some('`');
|
|
266
|
-
let ends_with_backtick = last_char == Some('`');
|
|
267
|
-
let all_spaces = trimmed.chars().all(|c| c == ' ');
|
|
268
|
-
|
|
269
|
-
all_spaces
|
|
270
|
-
|| starts_with_backtick
|
|
271
|
-
|| ends_with_backtick
|
|
272
|
-
|| (starts_with_space && ends_with_space && contains_backtick)
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
let (num_backticks, needs_spaces) = if contains_backtick {
|
|
276
|
-
let max_consecutive = trimmed
|
|
277
|
-
.chars()
|
|
278
|
-
.fold((0, 0), |(max, current), c| {
|
|
279
|
-
if c == '`' {
|
|
280
|
-
let new_current = current + 1;
|
|
281
|
-
(max.max(new_current), new_current)
|
|
282
|
-
} else {
|
|
283
|
-
(max, 0)
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
.0;
|
|
287
|
-
let num = if max_consecutive == 1 { 2 } else { 1 };
|
|
288
|
-
(num, needs_delimiter_spaces)
|
|
289
|
-
} else {
|
|
290
|
-
(1, needs_delimiter_spaces)
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
for _ in 0..num_backticks {
|
|
294
|
-
output.push('`');
|
|
295
|
-
}
|
|
296
|
-
if needs_spaces {
|
|
297
|
-
output.push(' ');
|
|
298
|
-
}
|
|
299
|
-
output.push_str(trimmed);
|
|
300
|
-
if needs_spaces {
|
|
301
|
-
output.push(' ');
|
|
302
|
-
}
|
|
303
|
-
for _ in 0..num_backticks {
|
|
304
|
-
output.push('`');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
//! Handler for emphasis elements (strong, b, em, i).
|
|
2
|
-
//!
|
|
3
|
-
//! Converts HTML emphasis tags to Markdown formatting with support for:
|
|
4
|
-
//! - Bold/strong formatting using configurable symbols (** or __)
|
|
5
|
-
//! - Italic/emphasis formatting using configurable symbols (* or _)
|
|
6
|
-
//! - Nested emphasis context tracking
|
|
7
|
-
//! - Code context handling (suppress formatting in <code>)
|
|
8
|
-
//! - Visitor callbacks for custom emphasis processing
|
|
9
|
-
//! - Bootstrap caret detection (.caret class)
|
|
10
|
-
|
|
11
|
-
#[cfg(feature = "visitor")]
|
|
12
|
-
use crate::converter::utility::content::collect_tag_attributes;
|
|
13
|
-
use crate::options::{ConversionOptions, OutputFormat};
|
|
14
|
-
#[allow(unused_imports)]
|
|
15
|
-
use std::collections::BTreeMap;
|
|
16
|
-
use tl::{NodeHandle, Parser};
|
|
17
|
-
|
|
18
|
-
// Type aliases for Context and DomContext to avoid circular imports
|
|
19
|
-
// These are imported from converter.rs and should be made accessible
|
|
20
|
-
type Context = crate::converter::Context;
|
|
21
|
-
type DomContext = crate::converter::DomContext;
|
|
22
|
-
|
|
23
|
-
/// Handler for emphasis elements: strong, b (bold) and em, i (italic).
|
|
24
|
-
///
|
|
25
|
-
/// Processes emphasis content based on context:
|
|
26
|
-
/// - Suppresses formatting when already in strong/code context
|
|
27
|
-
/// - Applies configurable emphasis symbols (* or _)
|
|
28
|
-
/// - Handles nested emphasis with proper context tracking
|
|
29
|
-
/// - Supports visitor callbacks for custom behavior
|
|
30
|
-
/// - Detects Bootstrap caret elements (.caret class)
|
|
31
|
-
///
|
|
32
|
-
/// # Note
|
|
33
|
-
/// This function references helper functions and `walk_node` from converter.rs
|
|
34
|
-
/// which must be accessible (pub(crate)) for this module to work correctly.
|
|
35
|
-
pub fn handle(
|
|
36
|
-
tag_name: &str,
|
|
37
|
-
node_handle: &NodeHandle,
|
|
38
|
-
parser: &Parser,
|
|
39
|
-
output: &mut String,
|
|
40
|
-
options: &ConversionOptions,
|
|
41
|
-
ctx: &Context,
|
|
42
|
-
depth: usize,
|
|
43
|
-
dom_ctx: &DomContext,
|
|
44
|
-
) {
|
|
45
|
-
// Import helper functions from parent converter module
|
|
46
|
-
|
|
47
|
-
match tag_name {
|
|
48
|
-
"strong" | "b" => {
|
|
49
|
-
handle_strong(node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
50
|
-
}
|
|
51
|
-
"em" | "i" => {
|
|
52
|
-
handle_emphasis(node_handle, parser, output, options, ctx, depth, dom_ctx);
|
|
53
|
-
}
|
|
54
|
-
_ => {}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/// Handle strong/bold emphasis (strong, b tags).
|
|
59
|
-
fn handle_strong(
|
|
60
|
-
node_handle: &NodeHandle,
|
|
61
|
-
parser: &Parser,
|
|
62
|
-
output: &mut String,
|
|
63
|
-
options: &ConversionOptions,
|
|
64
|
-
ctx: &Context,
|
|
65
|
-
depth: usize,
|
|
66
|
-
dom_ctx: &DomContext,
|
|
67
|
-
) {
|
|
68
|
-
#[allow(unused_imports)]
|
|
69
|
-
use crate::converter::{append_inline_suffix, chomp_inline, get_text_content, serialize_node, walk_node};
|
|
70
|
-
|
|
71
|
-
let Some(node) = node_handle.get(parser) else { return };
|
|
72
|
-
|
|
73
|
-
let tag = match node {
|
|
74
|
-
tl::Node::Tag(tag) => tag,
|
|
75
|
-
_ => return,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
if ctx.in_code {
|
|
79
|
-
// Suppress formatting in code context, just process children
|
|
80
|
-
let children = tag.children();
|
|
81
|
-
for child_handle in children.top().iter() {
|
|
82
|
-
walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
let mut content = String::with_capacity(64);
|
|
86
|
-
let children = tag.children();
|
|
87
|
-
{
|
|
88
|
-
let strong_ctx = Context {
|
|
89
|
-
inline_depth: ctx.inline_depth + 1,
|
|
90
|
-
in_strong: true,
|
|
91
|
-
..ctx.clone()
|
|
92
|
-
};
|
|
93
|
-
for child_handle in children.top().iter() {
|
|
94
|
-
walk_node(
|
|
95
|
-
child_handle,
|
|
96
|
-
parser,
|
|
97
|
-
&mut content,
|
|
98
|
-
options,
|
|
99
|
-
&strong_ctx,
|
|
100
|
-
depth + 1,
|
|
101
|
-
dom_ctx,
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
#[cfg(feature = "visitor")]
|
|
107
|
-
let strong_output = if let Some(ref visitor_handle) = ctx.visitor {
|
|
108
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
109
|
-
|
|
110
|
-
let text_content = get_text_content(node_handle, parser, dom_ctx);
|
|
111
|
-
let attributes: BTreeMap<String, String> = collect_tag_attributes(tag);
|
|
112
|
-
|
|
113
|
-
let node_id = node_handle.get_inner();
|
|
114
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
115
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
116
|
-
|
|
117
|
-
let node_ctx = NodeContext {
|
|
118
|
-
node_type: NodeType::Strong,
|
|
119
|
-
tag_name: tag.name().as_utf8_str().to_string(),
|
|
120
|
-
attributes,
|
|
121
|
-
depth,
|
|
122
|
-
index_in_parent,
|
|
123
|
-
parent_tag,
|
|
124
|
-
is_inline: true,
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
let visit_result = {
|
|
128
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
129
|
-
visitor.visit_strong(&node_ctx, &text_content)
|
|
130
|
-
};
|
|
131
|
-
match visit_result {
|
|
132
|
-
VisitResult::Continue => None,
|
|
133
|
-
VisitResult::Custom(custom) => Some(custom),
|
|
134
|
-
VisitResult::Skip => Some(String::new()),
|
|
135
|
-
VisitResult::PreserveHtml => Some(serialize_node(node_handle, parser)),
|
|
136
|
-
VisitResult::Error(err) => {
|
|
137
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
138
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
139
|
-
}
|
|
140
|
-
None
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
None
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
#[cfg(feature = "visitor")]
|
|
148
|
-
if let Some(custom_output) = strong_output {
|
|
149
|
-
output.push_str(&custom_output);
|
|
150
|
-
} else {
|
|
151
|
-
let (prefix, suffix, trimmed) = chomp_inline(&content);
|
|
152
|
-
if !content.trim().is_empty() {
|
|
153
|
-
output.push_str(prefix);
|
|
154
|
-
if ctx.in_strong {
|
|
155
|
-
output.push_str(trimmed);
|
|
156
|
-
} else if options.output_format == OutputFormat::Djot {
|
|
157
|
-
// Djot uses single asterisk for strong
|
|
158
|
-
output.push('*');
|
|
159
|
-
output.push_str(trimmed);
|
|
160
|
-
output.push('*');
|
|
161
|
-
} else {
|
|
162
|
-
output.push(options.strong_em_symbol);
|
|
163
|
-
output.push(options.strong_em_symbol);
|
|
164
|
-
output.push_str(trimmed);
|
|
165
|
-
output.push(options.strong_em_symbol);
|
|
166
|
-
output.push(options.strong_em_symbol);
|
|
167
|
-
}
|
|
168
|
-
append_inline_suffix(output, suffix, !trimmed.is_empty(), node_handle, parser, dom_ctx);
|
|
169
|
-
} else if !content.is_empty() {
|
|
170
|
-
output.push_str(prefix);
|
|
171
|
-
append_inline_suffix(output, suffix, false, node_handle, parser, dom_ctx);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
#[cfg(not(feature = "visitor"))]
|
|
176
|
-
{
|
|
177
|
-
let (prefix, suffix, trimmed) = chomp_inline(&content);
|
|
178
|
-
if !content.trim().is_empty() {
|
|
179
|
-
output.push_str(prefix);
|
|
180
|
-
if ctx.in_strong {
|
|
181
|
-
output.push_str(trimmed);
|
|
182
|
-
} else if options.output_format == OutputFormat::Djot {
|
|
183
|
-
// Djot uses single asterisk for strong
|
|
184
|
-
output.push('*');
|
|
185
|
-
output.push_str(trimmed);
|
|
186
|
-
output.push('*');
|
|
187
|
-
} else {
|
|
188
|
-
output.push(options.strong_em_symbol);
|
|
189
|
-
output.push(options.strong_em_symbol);
|
|
190
|
-
output.push_str(trimmed);
|
|
191
|
-
output.push(options.strong_em_symbol);
|
|
192
|
-
output.push(options.strong_em_symbol);
|
|
193
|
-
}
|
|
194
|
-
append_inline_suffix(output, suffix, !trimmed.is_empty(), node_handle, parser, dom_ctx);
|
|
195
|
-
} else if !content.is_empty() {
|
|
196
|
-
output.push_str(prefix);
|
|
197
|
-
append_inline_suffix(output, suffix, false, node_handle, parser, dom_ctx);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/// Handle emphasis/italic (em, i tags).
|
|
204
|
-
fn handle_emphasis(
|
|
205
|
-
node_handle: &NodeHandle,
|
|
206
|
-
parser: &Parser,
|
|
207
|
-
output: &mut String,
|
|
208
|
-
options: &ConversionOptions,
|
|
209
|
-
ctx: &Context,
|
|
210
|
-
depth: usize,
|
|
211
|
-
dom_ctx: &DomContext,
|
|
212
|
-
) {
|
|
213
|
-
#[allow(unused_imports)]
|
|
214
|
-
use crate::converter::{append_inline_suffix, chomp_inline, get_text_content, serialize_node, walk_node};
|
|
215
|
-
|
|
216
|
-
let Some(node) = node_handle.get(parser) else { return };
|
|
217
|
-
|
|
218
|
-
let tag = match node {
|
|
219
|
-
tl::Node::Tag(tag) => tag,
|
|
220
|
-
_ => return,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
if ctx.in_code {
|
|
224
|
-
// Suppress formatting in code context, just process children
|
|
225
|
-
let children = tag.children();
|
|
226
|
-
for child_handle in children.top().iter() {
|
|
227
|
-
walk_node(child_handle, parser, output, options, ctx, depth + 1, dom_ctx);
|
|
228
|
-
}
|
|
229
|
-
} else {
|
|
230
|
-
let mut content = String::with_capacity(64);
|
|
231
|
-
let children = tag.children();
|
|
232
|
-
{
|
|
233
|
-
let em_ctx = Context {
|
|
234
|
-
inline_depth: ctx.inline_depth + 1,
|
|
235
|
-
..ctx.clone()
|
|
236
|
-
};
|
|
237
|
-
for child_handle in children.top().iter() {
|
|
238
|
-
walk_node(child_handle, parser, &mut content, options, &em_ctx, depth + 1, dom_ctx);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
#[cfg(feature = "visitor")]
|
|
243
|
-
let em_output = if let Some(ref visitor_handle) = ctx.visitor {
|
|
244
|
-
use crate::visitor::{NodeContext, NodeType, VisitResult};
|
|
245
|
-
|
|
246
|
-
let text_content = get_text_content(node_handle, parser, dom_ctx);
|
|
247
|
-
let attributes: BTreeMap<String, String> = collect_tag_attributes(tag);
|
|
248
|
-
|
|
249
|
-
let node_id = node_handle.get_inner();
|
|
250
|
-
let parent_tag = dom_ctx.parent_tag_name(node_id, parser);
|
|
251
|
-
let index_in_parent = dom_ctx.get_sibling_index(node_id).unwrap_or(0);
|
|
252
|
-
|
|
253
|
-
let node_ctx = NodeContext {
|
|
254
|
-
node_type: NodeType::Em,
|
|
255
|
-
tag_name: tag.name().as_utf8_str().to_string(),
|
|
256
|
-
attributes,
|
|
257
|
-
depth,
|
|
258
|
-
index_in_parent,
|
|
259
|
-
parent_tag,
|
|
260
|
-
is_inline: true,
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
let visit_result = {
|
|
264
|
-
let mut visitor = visitor_handle.borrow_mut();
|
|
265
|
-
visitor.visit_emphasis(&node_ctx, &text_content)
|
|
266
|
-
};
|
|
267
|
-
match visit_result {
|
|
268
|
-
VisitResult::Continue => None,
|
|
269
|
-
VisitResult::Custom(custom) => Some(custom),
|
|
270
|
-
VisitResult::Skip => Some(String::new()),
|
|
271
|
-
VisitResult::PreserveHtml => Some(serialize_node(node_handle, parser)),
|
|
272
|
-
VisitResult::Error(err) => {
|
|
273
|
-
if ctx.visitor_error.borrow().is_none() {
|
|
274
|
-
*ctx.visitor_error.borrow_mut() = Some(err);
|
|
275
|
-
}
|
|
276
|
-
None
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
} else {
|
|
280
|
-
None
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
#[cfg(feature = "visitor")]
|
|
284
|
-
if let Some(custom_output) = em_output {
|
|
285
|
-
output.push_str(&custom_output);
|
|
286
|
-
} else {
|
|
287
|
-
let (prefix, suffix, trimmed) = chomp_inline(&content);
|
|
288
|
-
if !content.trim().is_empty() {
|
|
289
|
-
output.push_str(prefix);
|
|
290
|
-
if options.output_format == OutputFormat::Djot {
|
|
291
|
-
// Djot uses underscore for emphasis
|
|
292
|
-
output.push('_');
|
|
293
|
-
output.push_str(trimmed);
|
|
294
|
-
output.push('_');
|
|
295
|
-
} else {
|
|
296
|
-
output.push(options.strong_em_symbol);
|
|
297
|
-
output.push_str(trimmed);
|
|
298
|
-
output.push(options.strong_em_symbol);
|
|
299
|
-
}
|
|
300
|
-
append_inline_suffix(output, suffix, !trimmed.is_empty(), node_handle, parser, dom_ctx);
|
|
301
|
-
} else if !content.is_empty() {
|
|
302
|
-
output.push_str(prefix);
|
|
303
|
-
append_inline_suffix(output, suffix, false, node_handle, parser, dom_ctx);
|
|
304
|
-
} else if let Some(class_value) = tag
|
|
305
|
-
.attributes()
|
|
306
|
-
.get("class")
|
|
307
|
-
.and_then(|v| v.as_ref().map(|val| val.as_utf8_str().to_string()))
|
|
308
|
-
{
|
|
309
|
-
if class_value.contains("caret") && !output.ends_with(' ') {
|
|
310
|
-
output.push_str(" > ");
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
#[cfg(not(feature = "visitor"))]
|
|
316
|
-
{
|
|
317
|
-
let (prefix, suffix, trimmed) = chomp_inline(&content);
|
|
318
|
-
if !content.trim().is_empty() {
|
|
319
|
-
output.push_str(prefix);
|
|
320
|
-
if options.output_format == OutputFormat::Djot {
|
|
321
|
-
// Djot uses underscore for emphasis
|
|
322
|
-
output.push('_');
|
|
323
|
-
output.push_str(trimmed);
|
|
324
|
-
output.push('_');
|
|
325
|
-
} else {
|
|
326
|
-
output.push(options.strong_em_symbol);
|
|
327
|
-
output.push_str(trimmed);
|
|
328
|
-
output.push(options.strong_em_symbol);
|
|
329
|
-
}
|
|
330
|
-
append_inline_suffix(output, suffix, !trimmed.is_empty(), node_handle, parser, dom_ctx);
|
|
331
|
-
} else if !content.is_empty() {
|
|
332
|
-
output.push_str(prefix);
|
|
333
|
-
append_inline_suffix(output, suffix, false, node_handle, parser, dom_ctx);
|
|
334
|
-
} else if let Some(class_value) = tag
|
|
335
|
-
.attributes()
|
|
336
|
-
.get("class")
|
|
337
|
-
.and_then(|v| v.as_ref().map(|val| val.as_utf8_str().to_string()))
|
|
338
|
-
{
|
|
339
|
-
if class_value.contains("caret") && !output.ends_with(' ') {
|
|
340
|
-
output.push_str(" > ");
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|