1#[derive(Debug, Clone)]
7pub struct TableBlock {
8 pub start_line: usize,
9 pub end_line: usize,
10 pub header_line: usize,
11 pub delimiter_line: usize,
12 pub content_lines: Vec<usize>,
13}
14
15pub struct TableUtils;
17
18impl TableUtils {
19 pub fn is_potential_table_row(line: &str) -> bool {
21 let trimmed = line.trim();
22 if trimmed.is_empty() || !trimmed.contains('|') {
23 return false;
24 }
25
26 if trimmed.starts_with("- ") || trimmed.starts_with("* ") || trimmed.starts_with("+ ") {
28 return false;
29 }
30
31 if trimmed.starts_with("`") || trimmed.contains("``") {
33 return false;
34 }
35
36 let parts: Vec<&str> = trimmed.split('|').collect();
38 if parts.len() < 2 {
39 return false;
40 }
41
42 let mut valid_parts = 0;
44 let mut total_non_empty_parts = 0;
45
46 for part in &parts {
47 let part_trimmed = part.trim();
48 if part_trimmed.is_empty() {
50 continue;
51 }
52 total_non_empty_parts += 1;
53
54 if !part_trimmed.contains('\n') {
56 valid_parts += 1;
57 }
58 }
59
60 if total_non_empty_parts > 0 && valid_parts != total_non_empty_parts {
62 return false;
64 }
65
66 if total_non_empty_parts == 0 {
69 return trimmed.starts_with('|') && trimmed.ends_with('|') && parts.len() >= 3;
71 }
72
73 if trimmed.starts_with('|') && trimmed.ends_with('|') {
76 valid_parts >= 1
78 } else {
79 valid_parts >= 2
81 }
82 }
83
84 pub fn is_delimiter_row(line: &str) -> bool {
86 let trimmed = line.trim();
87 if !trimmed.contains('|') || !trimmed.contains('-') {
88 return false;
89 }
90
91 let parts: Vec<&str> = trimmed.split('|').collect();
93 let mut valid_delimiter_parts = 0;
94 let mut total_non_empty_parts = 0;
95
96 for part in &parts {
97 let part_trimmed = part.trim();
98 if part_trimmed.is_empty() {
99 continue; }
101
102 total_non_empty_parts += 1;
103
104 if part_trimmed.chars().all(|c| c == '-' || c == ':' || c.is_whitespace()) && part_trimmed.contains('-') {
106 valid_delimiter_parts += 1;
107 }
108 }
109
110 total_non_empty_parts > 0 && valid_delimiter_parts == total_non_empty_parts
112 }
113
114 pub fn find_table_blocks_with_code_info(
117 content: &str,
118 code_blocks: &[(usize, usize)],
119 code_spans: &[crate::lint_context::CodeSpan],
120 html_comment_ranges: &[crate::utils::skip_context::ByteRange],
121 ) -> Vec<TableBlock> {
122 let lines: Vec<&str> = content.lines().collect();
123 let mut tables = Vec::new();
124 let mut i = 0;
125
126 let mut line_positions = Vec::with_capacity(lines.len());
128 let mut pos = 0;
129 for line in &lines {
130 line_positions.push(pos);
131 pos += line.len() + 1; }
133
134 while i < lines.len() {
135 let line_start = line_positions[i];
137 let in_code =
138 crate::utils::code_block_utils::CodeBlockUtils::is_in_code_block_or_span(code_blocks, line_start)
139 || code_spans
140 .iter()
141 .any(|span| line_start >= span.byte_offset && line_start < span.byte_end);
142 let in_html_comment = html_comment_ranges
143 .iter()
144 .any(|range| line_start >= range.start && line_start < range.end);
145
146 if in_code || in_html_comment {
147 i += 1;
148 continue;
149 }
150
151 if Self::is_potential_table_row(lines[i]) {
153 if i + 1 < lines.len() && Self::is_delimiter_row(lines[i + 1]) {
155 let table_start = i;
157 let header_line = i;
158 let delimiter_line = i + 1;
159 let mut table_end = i + 1; let mut content_lines = Vec::new();
161
162 let mut j = i + 2;
164 while j < lines.len() {
165 let line = lines[j];
166 if line.trim().is_empty() {
167 break;
169 }
170 if Self::is_potential_table_row(line) {
171 content_lines.push(j);
172 table_end = j;
173 j += 1;
174 } else {
175 break;
177 }
178 }
179
180 tables.push(TableBlock {
181 start_line: table_start,
182 end_line: table_end,
183 header_line,
184 delimiter_line,
185 content_lines,
186 });
187 i = table_end + 1;
188 } else {
189 i += 1;
190 }
191 } else {
192 i += 1;
193 }
194 }
195
196 tables
197 }
198
199 pub fn find_table_blocks(content: &str, ctx: &crate::lint_context::LintContext) -> Vec<TableBlock> {
202 Self::find_table_blocks_with_code_info(content, &ctx.code_blocks, &ctx.code_spans(), ctx.html_comment_ranges())
203 }
204
205 pub fn count_cells(row: &str) -> usize {
208 Self::count_cells_with_flavor(row, crate::config::MarkdownFlavor::Standard)
209 }
210
211 pub fn count_cells_with_flavor(row: &str, flavor: crate::config::MarkdownFlavor) -> usize {
221 Self::split_table_row_with_flavor(row, flavor).len()
222 }
223
224 pub fn mask_pipes_in_inline_code(text: &str) -> String {
226 let mut result = String::new();
227 let chars: Vec<char> = text.chars().collect();
228 let mut i = 0;
229
230 while i < chars.len() {
231 if chars[i] == '`' {
232 let start = i;
234 let mut backtick_count = 0;
235 while i < chars.len() && chars[i] == '`' {
236 backtick_count += 1;
237 i += 1;
238 }
239
240 let mut found_closing = false;
242 let mut j = i;
243
244 while j < chars.len() {
245 if chars[j] == '`' {
246 let close_start = j;
248 let mut close_count = 0;
249 while j < chars.len() && chars[j] == '`' {
250 close_count += 1;
251 j += 1;
252 }
253
254 if close_count == backtick_count {
255 found_closing = true;
257
258 result.extend(chars[start..i].iter());
260
261 for &ch in chars.iter().take(close_start).skip(i) {
262 if ch == '|' {
263 result.push('_'); } else {
265 result.push(ch);
266 }
267 }
268
269 result.extend(chars[close_start..j].iter());
270 i = j;
271 break;
272 }
273 } else {
275 j += 1;
276 }
277 }
278
279 if !found_closing {
280 result.extend(chars[start..i].iter());
282 }
283 } else {
284 result.push(chars[i]);
285 i += 1;
286 }
287 }
288
289 result
290 }
291
292 pub fn mask_pipes_for_table_parsing(text: &str) -> String {
305 let mut result = String::new();
306 let chars: Vec<char> = text.chars().collect();
307 let mut i = 0;
308
309 while i < chars.len() {
310 if chars[i] == '\\' {
311 if i + 1 < chars.len() && chars[i + 1] == '\\' {
312 result.push('\\');
315 result.push('\\');
316 i += 2;
317 } else if i + 1 < chars.len() && chars[i + 1] == '|' {
318 result.push('\\');
320 result.push('_'); i += 2;
322 } else {
323 result.push(chars[i]);
325 i += 1;
326 }
327 } else {
328 result.push(chars[i]);
329 i += 1;
330 }
331 }
332
333 result
334 }
335
336 pub fn split_table_row_with_flavor(row: &str, flavor: crate::config::MarkdownFlavor) -> Vec<String> {
345 let trimmed = row.trim();
346
347 if !trimmed.contains('|') {
348 return Vec::new();
349 }
350
351 let masked = Self::mask_pipes_for_table_parsing(trimmed);
353
354 let final_masked = if flavor == crate::config::MarkdownFlavor::MkDocs {
356 Self::mask_pipes_in_inline_code(&masked)
357 } else {
358 masked
359 };
360
361 let has_leading = final_masked.starts_with('|');
362 let has_trailing = final_masked.ends_with('|');
363
364 let mut masked_content = final_masked.as_str();
365 let mut orig_content = trimmed;
366
367 if has_leading {
368 masked_content = &masked_content[1..];
369 orig_content = &orig_content[1..];
370 }
371
372 let stripped_trailing = has_trailing && !masked_content.is_empty();
374 if stripped_trailing {
375 masked_content = &masked_content[..masked_content.len() - 1];
376 orig_content = &orig_content[..orig_content.len() - 1];
377 }
378
379 if masked_content.is_empty() {
381 if stripped_trailing {
382 return vec![String::new()];
384 } else {
385 return Vec::new();
387 }
388 }
389
390 let masked_parts: Vec<&str> = masked_content.split('|').collect();
391 let mut cells = Vec::new();
392 let mut pos = 0;
393
394 for masked_cell in masked_parts {
395 let cell_len = masked_cell.len();
396 let orig_cell = if pos + cell_len <= orig_content.len() {
397 &orig_content[pos..pos + cell_len]
398 } else {
399 masked_cell
400 };
401 cells.push(orig_cell.to_string());
402 pos += cell_len + 1; }
404
405 cells
406 }
407
408 pub fn split_table_row(row: &str) -> Vec<String> {
410 Self::split_table_row_with_flavor(row, crate::config::MarkdownFlavor::Standard)
411 }
412
413 pub fn determine_pipe_style(line: &str) -> Option<&'static str> {
415 let trimmed = line.trim();
416 if !trimmed.contains('|') {
417 return None;
418 }
419
420 let has_leading = trimmed.starts_with('|');
421 let has_trailing = trimmed.ends_with('|');
422
423 match (has_leading, has_trailing) {
424 (true, true) => Some("leading_and_trailing"),
425 (true, false) => Some("leading_only"),
426 (false, true) => Some("trailing_only"),
427 (false, false) => Some("no_leading_or_trailing"),
428 }
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::lint_context::LintContext;
436
437 #[test]
438 fn test_is_potential_table_row() {
439 assert!(TableUtils::is_potential_table_row("| Header 1 | Header 2 |"));
441 assert!(TableUtils::is_potential_table_row("| Cell 1 | Cell 2 |"));
442 assert!(TableUtils::is_potential_table_row("Cell 1 | Cell 2"));
443 assert!(TableUtils::is_potential_table_row("| Cell |")); assert!(TableUtils::is_potential_table_row("| A | B | C | D | E |"));
447
448 assert!(TableUtils::is_potential_table_row(" | Indented | Table | "));
450 assert!(TableUtils::is_potential_table_row("| Spaces | Around |"));
451
452 assert!(!TableUtils::is_potential_table_row("- List item"));
454 assert!(!TableUtils::is_potential_table_row("* Another list"));
455 assert!(!TableUtils::is_potential_table_row("+ Plus list"));
456 assert!(!TableUtils::is_potential_table_row("Regular text"));
457 assert!(!TableUtils::is_potential_table_row(""));
458 assert!(!TableUtils::is_potential_table_row(" "));
459
460 assert!(!TableUtils::is_potential_table_row("`code with | pipe`"));
462 assert!(!TableUtils::is_potential_table_row("``multiple | backticks``"));
463
464 assert!(!TableUtils::is_potential_table_row("Just one |"));
466 assert!(!TableUtils::is_potential_table_row("| Just one"));
467
468 let long_cell = "a".repeat(150);
470 assert!(TableUtils::is_potential_table_row(&format!("| {long_cell} | b |")));
471
472 assert!(!TableUtils::is_potential_table_row("| Cell with\nnewline | Other |"));
474
475 assert!(TableUtils::is_potential_table_row("|||")); assert!(TableUtils::is_potential_table_row("||||")); assert!(TableUtils::is_potential_table_row("| | |")); }
480
481 #[test]
482 fn test_is_delimiter_row() {
483 assert!(TableUtils::is_delimiter_row("|---|---|"));
485 assert!(TableUtils::is_delimiter_row("| --- | --- |"));
486 assert!(TableUtils::is_delimiter_row("|:---|---:|"));
487 assert!(TableUtils::is_delimiter_row("|:---:|:---:|"));
488
489 assert!(TableUtils::is_delimiter_row("|-|--|"));
491 assert!(TableUtils::is_delimiter_row("|-------|----------|"));
492
493 assert!(TableUtils::is_delimiter_row("| --- | --- |"));
495 assert!(TableUtils::is_delimiter_row("| :--- | ---: |"));
496
497 assert!(TableUtils::is_delimiter_row("|---|---|---|---|"));
499
500 assert!(TableUtils::is_delimiter_row("--- | ---"));
502 assert!(TableUtils::is_delimiter_row(":--- | ---:"));
503
504 assert!(!TableUtils::is_delimiter_row("| Header | Header |"));
506 assert!(!TableUtils::is_delimiter_row("Regular text"));
507 assert!(!TableUtils::is_delimiter_row(""));
508 assert!(!TableUtils::is_delimiter_row("|||"));
509 assert!(!TableUtils::is_delimiter_row("| | |"));
510
511 assert!(!TableUtils::is_delimiter_row("| : | : |"));
513 assert!(!TableUtils::is_delimiter_row("| | |"));
514
515 assert!(!TableUtils::is_delimiter_row("| --- | text |"));
517 assert!(!TableUtils::is_delimiter_row("| abc | --- |"));
518 }
519
520 #[test]
521 fn test_count_cells() {
522 assert_eq!(TableUtils::count_cells("| Cell 1 | Cell 2 | Cell 3 |"), 3);
524 assert_eq!(TableUtils::count_cells("Cell 1 | Cell 2 | Cell 3"), 3);
525 assert_eq!(TableUtils::count_cells("| Cell 1 | Cell 2"), 2);
526 assert_eq!(TableUtils::count_cells("Cell 1 | Cell 2 |"), 2);
527
528 assert_eq!(TableUtils::count_cells("| Cell |"), 1);
530 assert_eq!(TableUtils::count_cells("Cell"), 0); assert_eq!(TableUtils::count_cells("| | | |"), 3);
534 assert_eq!(TableUtils::count_cells("| | | |"), 3);
535
536 assert_eq!(TableUtils::count_cells("| A | B | C | D | E | F |"), 6);
538
539 assert_eq!(TableUtils::count_cells("||"), 1); assert_eq!(TableUtils::count_cells("|||"), 2); assert_eq!(TableUtils::count_cells("Regular text"), 0);
545 assert_eq!(TableUtils::count_cells(""), 0);
546 assert_eq!(TableUtils::count_cells(" "), 0);
547
548 assert_eq!(TableUtils::count_cells(" | A | B | "), 2);
550 assert_eq!(TableUtils::count_cells("| A | B |"), 2);
551 }
552
553 #[test]
554 fn test_count_cells_with_escaped_pipes() {
555 assert_eq!(TableUtils::count_cells("| Challenge | Solution |"), 2);
561 assert_eq!(TableUtils::count_cells("| A | B | C |"), 3);
562 assert_eq!(TableUtils::count_cells("| One | Two |"), 2);
563
564 assert_eq!(TableUtils::count_cells(r"| Command | echo \| grep |"), 2);
566 assert_eq!(TableUtils::count_cells(r"| A | B \| C |"), 2); assert_eq!(TableUtils::count_cells(r"| Command | `echo \| grep` |"), 2);
570
571 assert_eq!(TableUtils::count_cells(r"| A | B \\| C |"), 3); assert_eq!(TableUtils::count_cells(r"| A | `B \\| C` |"), 3); assert_eq!(TableUtils::count_cells("| Command | `echo | grep` |"), 3);
578 assert_eq!(TableUtils::count_cells("| `code | one` | `code | two` |"), 4);
579 assert_eq!(TableUtils::count_cells("| `single|pipe` |"), 2);
580
581 assert_eq!(TableUtils::count_cells(r"| Hour formats | `^([0-1]?\d|2[0-3])` |"), 3);
584 assert_eq!(TableUtils::count_cells(r"| Hour formats | `^([0-1]?\d\|2[0-3])` |"), 2);
586 }
587
588 #[test]
589 fn test_determine_pipe_style() {
590 assert_eq!(
592 TableUtils::determine_pipe_style("| Cell 1 | Cell 2 |"),
593 Some("leading_and_trailing")
594 );
595 assert_eq!(
596 TableUtils::determine_pipe_style("| Cell 1 | Cell 2"),
597 Some("leading_only")
598 );
599 assert_eq!(
600 TableUtils::determine_pipe_style("Cell 1 | Cell 2 |"),
601 Some("trailing_only")
602 );
603 assert_eq!(
604 TableUtils::determine_pipe_style("Cell 1 | Cell 2"),
605 Some("no_leading_or_trailing")
606 );
607
608 assert_eq!(
610 TableUtils::determine_pipe_style(" | Cell 1 | Cell 2 | "),
611 Some("leading_and_trailing")
612 );
613 assert_eq!(
614 TableUtils::determine_pipe_style(" | Cell 1 | Cell 2 "),
615 Some("leading_only")
616 );
617
618 assert_eq!(TableUtils::determine_pipe_style("Regular text"), None);
620 assert_eq!(TableUtils::determine_pipe_style(""), None);
621 assert_eq!(TableUtils::determine_pipe_style(" "), None);
622
623 assert_eq!(TableUtils::determine_pipe_style("|"), Some("leading_and_trailing"));
625 assert_eq!(TableUtils::determine_pipe_style("| Cell"), Some("leading_only"));
626 assert_eq!(TableUtils::determine_pipe_style("Cell |"), Some("trailing_only"));
627 }
628
629 #[test]
630 fn test_find_table_blocks_simple() {
631 let content = "| Header 1 | Header 2 |
632|-----------|-----------|
633| Cell 1 | Cell 2 |
634| Cell 3 | Cell 4 |";
635
636 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
637
638 let tables = TableUtils::find_table_blocks(content, &ctx);
639 assert_eq!(tables.len(), 1);
640
641 let table = &tables[0];
642 assert_eq!(table.start_line, 0);
643 assert_eq!(table.end_line, 3);
644 assert_eq!(table.header_line, 0);
645 assert_eq!(table.delimiter_line, 1);
646 assert_eq!(table.content_lines, vec![2, 3]);
647 }
648
649 #[test]
650 fn test_find_table_blocks_multiple() {
651 let content = "Some text
652
653| Table 1 | Col A |
654|----------|-------|
655| Data 1 | Val 1 |
656
657More text
658
659| Table 2 | Col 2 |
660|----------|-------|
661| Data 2 | Data |";
662
663 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
664
665 let tables = TableUtils::find_table_blocks(content, &ctx);
666 assert_eq!(tables.len(), 2);
667
668 assert_eq!(tables[0].start_line, 2);
670 assert_eq!(tables[0].end_line, 4);
671 assert_eq!(tables[0].header_line, 2);
672 assert_eq!(tables[0].delimiter_line, 3);
673 assert_eq!(tables[0].content_lines, vec![4]);
674
675 assert_eq!(tables[1].start_line, 8);
677 assert_eq!(tables[1].end_line, 10);
678 assert_eq!(tables[1].header_line, 8);
679 assert_eq!(tables[1].delimiter_line, 9);
680 assert_eq!(tables[1].content_lines, vec![10]);
681 }
682
683 #[test]
684 fn test_find_table_blocks_no_content_rows() {
685 let content = "| Header 1 | Header 2 |
686|-----------|-----------|
687
688Next paragraph";
689
690 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
691
692 let tables = TableUtils::find_table_blocks(content, &ctx);
693 assert_eq!(tables.len(), 1);
694
695 let table = &tables[0];
696 assert_eq!(table.start_line, 0);
697 assert_eq!(table.end_line, 1); assert_eq!(table.content_lines.len(), 0);
699 }
700
701 #[test]
702 fn test_find_table_blocks_in_code_block() {
703 let content = "```
704| Not | A | Table |
705|-----|---|-------|
706| In | Code | Block |
707```
708
709| Real | Table |
710|------|-------|
711| Data | Here |";
712
713 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
714
715 let tables = TableUtils::find_table_blocks(content, &ctx);
716 assert_eq!(tables.len(), 1); let table = &tables[0];
719 assert_eq!(table.header_line, 6);
720 assert_eq!(table.delimiter_line, 7);
721 }
722
723 #[test]
724 fn test_find_table_blocks_no_tables() {
725 let content = "Just regular text
726No tables here
727- List item with | pipe
728* Another list item";
729
730 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
731
732 let tables = TableUtils::find_table_blocks(content, &ctx);
733 assert_eq!(tables.len(), 0);
734 }
735
736 #[test]
737 fn test_find_table_blocks_malformed() {
738 let content = "| Header without delimiter |
739| This looks like table |
740But no delimiter row
741
742| Proper | Table |
743|---------|-------|
744| Data | Here |";
745
746 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard);
747
748 let tables = TableUtils::find_table_blocks(content, &ctx);
749 assert_eq!(tables.len(), 1); assert_eq!(tables[0].header_line, 4);
751 }
752
753 #[test]
754 fn test_edge_cases() {
755 assert!(!TableUtils::is_potential_table_row(""));
757 assert!(!TableUtils::is_delimiter_row(""));
758 assert_eq!(TableUtils::count_cells(""), 0);
759 assert_eq!(TableUtils::determine_pipe_style(""), None);
760
761 assert!(!TableUtils::is_potential_table_row(" "));
763 assert!(!TableUtils::is_delimiter_row(" "));
764 assert_eq!(TableUtils::count_cells(" "), 0);
765 assert_eq!(TableUtils::determine_pipe_style(" "), None);
766
767 assert!(!TableUtils::is_potential_table_row("|"));
769 assert!(!TableUtils::is_delimiter_row("|"));
770 assert_eq!(TableUtils::count_cells("|"), 0); let long_single = format!("| {} |", "a".repeat(200));
775 assert!(TableUtils::is_potential_table_row(&long_single)); let long_multi = format!("| {} | {} |", "a".repeat(200), "b".repeat(200));
778 assert!(TableUtils::is_potential_table_row(&long_multi)); assert!(TableUtils::is_potential_table_row("| 你好 | 世界 |"));
782 assert!(TableUtils::is_potential_table_row("| émoji | 🎉 |"));
783 assert_eq!(TableUtils::count_cells("| 你好 | 世界 |"), 2);
784 }
785
786 #[test]
787 fn test_table_block_struct() {
788 let block = TableBlock {
789 start_line: 0,
790 end_line: 5,
791 header_line: 0,
792 delimiter_line: 1,
793 content_lines: vec![2, 3, 4, 5],
794 };
795
796 let debug_str = format!("{block:?}");
798 assert!(debug_str.contains("TableBlock"));
799 assert!(debug_str.contains("start_line: 0"));
800
801 let cloned = block.clone();
803 assert_eq!(cloned.start_line, block.start_line);
804 assert_eq!(cloned.end_line, block.end_line);
805 assert_eq!(cloned.header_line, block.header_line);
806 assert_eq!(cloned.delimiter_line, block.delimiter_line);
807 assert_eq!(cloned.content_lines, block.content_lines);
808 }
809
810 #[test]
811 fn test_split_table_row() {
812 let cells = TableUtils::split_table_row("| Cell 1 | Cell 2 | Cell 3 |");
814 assert_eq!(cells.len(), 3);
815 assert_eq!(cells[0].trim(), "Cell 1");
816 assert_eq!(cells[1].trim(), "Cell 2");
817 assert_eq!(cells[2].trim(), "Cell 3");
818
819 let cells = TableUtils::split_table_row("| Cell 1 | Cell 2");
821 assert_eq!(cells.len(), 2);
822
823 let cells = TableUtils::split_table_row("| | | |");
825 assert_eq!(cells.len(), 3);
826
827 let cells = TableUtils::split_table_row("| Cell |");
829 assert_eq!(cells.len(), 1);
830 assert_eq!(cells[0].trim(), "Cell");
831
832 let cells = TableUtils::split_table_row("No pipes here");
834 assert_eq!(cells.len(), 0);
835 }
836
837 #[test]
838 fn test_split_table_row_with_escaped_pipes() {
839 let cells = TableUtils::split_table_row(r"| A | B \| C |");
841 assert_eq!(cells.len(), 2);
842 assert!(cells[1].contains(r"\|"), "Escaped pipe should be in cell content");
843
844 let cells = TableUtils::split_table_row(r"| A | B \\| C |");
846 assert_eq!(cells.len(), 3);
847 }
848
849 #[test]
850 fn test_split_table_row_with_flavor_mkdocs() {
851 let cells =
853 TableUtils::split_table_row_with_flavor("| Type | `x | y` |", crate::config::MarkdownFlavor::MkDocs);
854 assert_eq!(cells.len(), 2);
855 assert!(
856 cells[1].contains("`x | y`"),
857 "Inline code with pipe should be single cell in MkDocs flavor"
858 );
859
860 let cells =
862 TableUtils::split_table_row_with_flavor("| Type | `a | b | c` |", crate::config::MarkdownFlavor::MkDocs);
863 assert_eq!(cells.len(), 2);
864 assert!(cells[1].contains("`a | b | c`"));
865 }
866
867 #[test]
868 fn test_split_table_row_with_flavor_standard() {
869 let cells =
871 TableUtils::split_table_row_with_flavor("| Type | `x | y` |", crate::config::MarkdownFlavor::Standard);
872 assert_eq!(cells.len(), 3);
874 }
875}