1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
use arena_tree::Node; use nodes::{make_block, AstNode, NodeValue, TableAlignment}; use parser::Parser; use scanners; use std::cell::RefCell; use std::cmp::min; use strings::trim; pub fn try_opening_block<'a, 'o, 'c>( parser: &mut Parser<'a, 'o, 'c>, container: &'a AstNode<'a>, line: &[u8], ) -> Option<(&'a AstNode<'a>, bool)> { let aligns = match container.data.borrow().value { NodeValue::Paragraph => None, NodeValue::Table(ref aligns) => Some(aligns.clone()), _ => return None, }; match aligns { None => try_opening_header(parser, container, line), Some(ref aligns) => try_opening_row(parser, container, aligns, line), } } fn try_opening_header<'a, 'o, 'c>( parser: &mut Parser<'a, 'o, 'c>, container: &'a AstNode<'a>, line: &[u8], ) -> Option<(&'a AstNode<'a>, bool)> { if scanners::table_start(&line[parser.first_nonspace..]).is_none() { return Some((container, false)); } let header_row = match row(&container.data.borrow().content) { Some(header_row) => header_row, None => return Some((container, false)), }; let marker_row = row(&line[parser.first_nonspace..]).unwrap(); if header_row.len() != marker_row.len() { return Some((container, false)); } let mut alignments = vec![]; for cell in marker_row { let left = !cell.is_empty() && cell[0] == b':'; let right = !cell.is_empty() && cell[cell.len() - 1] == b':'; alignments.push(if left && right { TableAlignment::Center } else if left { TableAlignment::Left } else if right { TableAlignment::Right } else { TableAlignment::None }); } let child = make_block(NodeValue::Table(alignments), parser.line_number); let table = parser.arena.alloc(Node::new(RefCell::new(child))); container.append(table); let header = parser.add_child(table, NodeValue::TableRow(true)); for header_str in header_row { let header_cell = parser.add_child(header, NodeValue::TableCell); header_cell.data.borrow_mut().content = header_str; } let offset = line.len() - 1 - parser.offset; parser.advance_offset(line, offset, false); Some((table, true)) } fn try_opening_row<'a, 'o, 'c>( parser: &mut Parser<'a, 'o, 'c>, container: &'a AstNode<'a>, alignments: &[TableAlignment], line: &[u8], ) -> Option<(&'a AstNode<'a>, bool)> { if parser.blank { return None; } let this_row = row(&line[parser.first_nonspace..]).unwrap(); let new_row = parser.add_child(container, NodeValue::TableRow(false)); let mut i = 0; while i < min(alignments.len(), this_row.len()) { let cell = parser.add_child(new_row, NodeValue::TableCell); cell.data.borrow_mut().content = this_row[i].clone(); i += 1; } while i < alignments.len() { parser.add_child(new_row, NodeValue::TableCell); i += 1; } let offset = line.len() - 1 - parser.offset; parser.advance_offset(line, offset, false); Some((new_row, false)) } fn row(string: &[u8]) -> Option<Vec<Vec<u8>>> { let len = string.len(); let mut v = vec![]; let mut offset = 0; if len > 0 && string[0] == b'|' { offset += 1; } loop { let cell_matched = scanners::table_cell(&string[offset..]).unwrap_or(0); let mut pipe_matched = scanners::table_cell_end(&string[offset + cell_matched..]).unwrap_or(0); if cell_matched > 0 || pipe_matched > 0 { let mut cell = unescape_pipes(&string[offset..offset + cell_matched]); trim(&mut cell); v.push(cell); } offset += cell_matched + pipe_matched; if pipe_matched == 0 { pipe_matched = scanners::table_row_end(&string[offset..]).unwrap_or(0); offset += pipe_matched; } if !((cell_matched > 0 || pipe_matched > 0) && offset < len) { break; } } if offset != len || v.is_empty() { None } else { Some(v) } } fn unescape_pipes(string: &[u8]) -> Vec<u8> { let len = string.len(); let mut v = Vec::with_capacity(len); for (i, &c) in string.iter().enumerate() { if c == b'\\' && i + 1 < len && string[i + 1] == b'|' { continue; } else { v.push(c); } } v } pub fn matches(line: &[u8]) -> bool { row(line).is_some() }