1use crate::arena_tree::Node;
2use crate::nodes;
3use crate::nodes::{Ast, AstNode, NodeTable, NodeValue, TableAlignment};
4use crate::parser::Parser;
5use crate::scanners;
6use crate::strings::trim;
7use std::cell::RefCell;
8use std::cmp::min;
9
10use super::inlines::count_newlines;
11
12const MAX_AUTOCOMPLETED_CELLS: usize = 500_000;
14
15pub fn try_opening_block<'a>(
16 parser: &mut Parser<'a, '_, '_>,
17 container: &'a AstNode<'a>,
18 line: &[u8],
19) -> Option<(&'a AstNode<'a>, bool, bool)> {
20 let aligns = match container.data.borrow().value {
21 NodeValue::Paragraph => None,
22 NodeValue::Table(NodeTable { ref alignments, .. }) => Some(alignments.clone()),
23 _ => return None,
24 };
25
26 match aligns {
27 None => try_opening_header(parser, container, line),
28 Some(ref aligns) => try_opening_row(parser, container, aligns, line),
29 }
30}
31
32fn try_opening_header<'a>(
33 parser: &mut Parser<'a, '_, '_>,
34 container: &'a AstNode<'a>,
35 line: &[u8],
36) -> Option<(&'a AstNode<'a>, bool, bool)> {
37 if container.data.borrow().table_visited {
38 return Some((container, false, false));
39 }
40
41 if scanners::table_start(&line[parser.first_nonspace..]).is_none() {
42 return Some((container, false, false));
43 }
44
45 let spoiler = parser.options.extension.spoiler;
46
47 let delimiter_row = match row(&line[parser.first_nonspace..], spoiler) {
48 Some(delimiter_row) => delimiter_row,
49 None => return Some((container, false, true)),
50 };
51
52 let header_row = match row(container.data.borrow().content.as_bytes(), spoiler) {
53 Some(header_row) => header_row,
54 None => return Some((container, false, true)),
55 };
56
57 if header_row.cells.len() != delimiter_row.cells.len() {
58 return Some((container, false, true));
59 }
60
61 if header_row.paragraph_offset > 0 {
62 try_inserting_table_header_paragraph(parser, container, header_row.paragraph_offset);
63 }
64
65 let mut alignments = vec![];
66 for cell in delimiter_row.cells {
67 let cell_content = cell.content.as_bytes();
68 let left = !cell_content.is_empty() && cell_content[0] == b':';
69 let right = !cell_content.is_empty() && cell_content[cell_content.len() - 1] == b':';
70 alignments.push(if left && right {
71 TableAlignment::Center
72 } else if left {
73 TableAlignment::Left
74 } else if right {
75 TableAlignment::Right
76 } else {
77 TableAlignment::None
78 });
79 }
80
81 let start = container.data.borrow().sourcepos.start;
82 let child = Ast::new(
83 NodeValue::Table(NodeTable {
84 alignments,
85 num_columns: header_row.cells.len(),
86 num_rows: 0,
87 num_nonempty_cells: 0,
88 }),
89 start,
90 );
91 let table = parser.arena.alloc(Node::new(RefCell::new(child)));
92 container.append(table);
93
94 let header = parser.add_child(table, NodeValue::TableRow(true), start.column);
95 {
96 let header_ast = &mut header.data.borrow_mut();
97 header_ast.sourcepos.start.line = start.line;
98 header_ast.sourcepos.end = start.column_add(
99 (container.data.borrow().content.as_bytes().len() - 2 - header_row.paragraph_offset)
100 as isize,
101 );
102 }
103
104 let mut i = 0;
105
106 while i < header_row.cells.len() {
107 let cell = &header_row.cells[i];
108 let ast_cell = parser.add_child(
109 header,
110 NodeValue::TableCell,
111 start.column + cell.start_offset - header_row.paragraph_offset,
112 );
113 let ast = &mut ast_cell.data.borrow_mut();
114 ast.sourcepos.start.line = start.line;
115 ast.sourcepos.end =
116 start.column_add((cell.end_offset - header_row.paragraph_offset) as isize);
117 ast.internal_offset = cell.internal_offset;
118 ast.content.clone_from(&cell.content);
119 ast.line_offsets.push(
120 start.column + cell.start_offset - 1 + cell.internal_offset
121 - header_row.paragraph_offset,
122 );
123
124 i += 1;
125 }
126
127 incr_table_row_count(container, i);
128
129 let offset = line.len() - 1 - parser.offset;
130 parser.advance_offset(line, offset, false);
131
132 Some((table, true, false))
133}
134
135fn try_opening_row<'a>(
136 parser: &mut Parser<'a, '_, '_>,
137 container: &'a AstNode<'a>,
138 alignments: &[TableAlignment],
139 line: &[u8],
140) -> Option<(&'a AstNode<'a>, bool, bool)> {
141 if parser.blank {
142 return None;
143 }
144
145 if get_num_autocompleted_cells(container) > MAX_AUTOCOMPLETED_CELLS {
146 return None;
147 }
148
149 let sourcepos = container.data.borrow().sourcepos;
150 let spoiler = parser.options.extension.spoiler;
151 let this_row = match row(&line[parser.first_nonspace..], spoiler) {
152 Some(this_row) => this_row,
153 None => return None,
154 };
155
156 let new_row = parser.add_child(
157 container,
158 NodeValue::TableRow(false),
159 sourcepos.start.column,
160 );
161 {
162 new_row.data.borrow_mut().sourcepos.end.column = sourcepos.end.column;
163 }
164
165 let mut i = 0;
166 let mut last_column = sourcepos.start.column;
167
168 while i < min(alignments.len(), this_row.cells.len()) {
169 let cell = &this_row.cells[i];
170 let cell_node = parser.add_child(
171 new_row,
172 NodeValue::TableCell,
173 sourcepos.start.column + cell.start_offset,
174 );
175 let cell_ast = &mut cell_node.data.borrow_mut();
176 cell_ast.internal_offset = cell.internal_offset;
177 cell_ast.sourcepos.end.column = sourcepos.start.column + cell.end_offset;
178 cell_ast.content.clone_from(&cell.content);
179 cell_ast
180 .line_offsets
181 .push(sourcepos.start.column + cell.start_offset - 1 + cell.internal_offset);
182
183 last_column = cell_ast.sourcepos.end.column;
184
185 i += 1;
186 }
187
188 incr_table_row_count(container, i);
189
190 while i < alignments.len() {
191 parser.add_child(new_row, NodeValue::TableCell, last_column);
192 i += 1;
193 }
194
195 let offset = line.len() - 1 - parser.offset;
196 parser.advance_offset(line, offset, false);
197
198 Some((new_row, false, false))
199}
200
201struct Row {
202 paragraph_offset: usize,
203 cells: Vec<Cell>,
204}
205
206struct Cell {
207 start_offset: usize,
208 end_offset: usize,
209 internal_offset: usize,
210 content: String,
211}
212
213fn row(string: &[u8], spoiler: bool) -> Option<Row> {
214 let len = string.len();
215 let mut cells: Vec<Cell> = vec![];
216
217 let mut offset = scanners::table_cell_end(string).unwrap_or(0);
218
219 let mut paragraph_offset = 0;
220 let mut expect_more_cells = true;
221 let mut max_columns_abort = false;
222
223 while offset < len && expect_more_cells {
224 let cell_matched = scanners::table_cell(&string[offset..], spoiler).unwrap_or(0);
225 let pipe_matched = scanners::table_cell_end(&string[offset + cell_matched..]).unwrap_or(0);
226
227 if cell_matched > 0 || pipe_matched > 0 {
228 let mut cell = unescape_pipes(&string[offset..offset + cell_matched]);
229 trim(&mut cell);
230
231 let mut start_offset = offset;
232 let mut internal_offset = 0;
233
234 while start_offset > paragraph_offset && string[start_offset - 1] != b'|' {
235 start_offset -= 1;
236 internal_offset += 1;
237 }
238
239 if cells.len() == <u16 as Into<usize>>::into(u16::MAX) {
241 max_columns_abort = true;
242 break;
243 }
244
245 cells.push(Cell {
246 start_offset,
247 end_offset: offset + cell_matched - 1,
248 internal_offset,
249 content: String::from_utf8(cell).unwrap(),
250 });
251 }
252
253 offset += cell_matched + pipe_matched;
254
255 if pipe_matched > 0 {
256 expect_more_cells = true;
257 } else {
258 let row_end_offset = scanners::table_row_end(&string[offset..]).unwrap_or(0);
259 offset += row_end_offset;
260
261 if row_end_offset > 0 && offset != len {
262 paragraph_offset = offset;
263 cells.clear();
264 offset += scanners::table_cell_end(&string[offset..]).unwrap_or(0);
265 expect_more_cells = true;
266 } else {
267 expect_more_cells = false;
268 }
269 }
270 }
271
272 if offset != len || cells.is_empty() || max_columns_abort {
273 None
274 } else {
275 Some(Row {
276 paragraph_offset,
277 cells,
278 })
279 }
280}
281
282fn try_inserting_table_header_paragraph<'a>(
283 parser: &mut Parser<'a, '_, '_>,
284 container: &'a AstNode<'a>,
285 paragraph_offset: usize,
286) {
287 let container_ast = &mut container.data.borrow_mut();
288
289 let preface = &container_ast.content.as_bytes()[..paragraph_offset];
290 let mut paragraph_content = unescape_pipes(preface);
291 let (newlines, _since_newline) = count_newlines(¶graph_content);
292 trim(&mut paragraph_content);
293
294 if container.parent().is_none()
295 || !nodes::can_contain_type(container.parent().unwrap(), &NodeValue::Paragraph)
296 {
297 return;
298 }
299
300 let start = container_ast.sourcepos.start;
301
302 let mut paragraph = Ast::new(NodeValue::Paragraph, start);
303 paragraph.sourcepos.end.line = start.line + newlines - 1;
304
305 for n in 0..newlines {
307 paragraph.line_offsets.push(container_ast.line_offsets[n]);
308 }
309
310 let last_line_offset = *paragraph.line_offsets.last().unwrap_or(&0);
311 paragraph.sourcepos.end.column = last_line_offset
312 + preface
313 .iter()
314 .rev()
315 .skip(1)
316 .take_while(|&&c| c != b'\n')
317 .count();
318
319 container_ast.sourcepos.start.line += newlines;
320
321 paragraph.content = String::from_utf8(paragraph_content).unwrap();
322 let node = parser.arena.alloc(Node::new(RefCell::new(paragraph)));
323 container.insert_before(node);
324}
325
326fn unescape_pipes(string: &[u8]) -> Vec<u8> {
327 let len = string.len();
328 let mut v = Vec::with_capacity(len);
329
330 for (i, &c) in string.iter().enumerate() {
331 if c == b'\\' && i + 1 < len && string[i + 1] == b'|' {
332 continue;
333 } else {
334 v.push(c);
335 }
336 }
337
338 v
339}
340
341fn incr_table_row_count<'a>(container: &'a AstNode<'a>, i: usize) -> bool {
349 return match container.data.borrow_mut().value {
350 NodeValue::Table(ref mut node_table) => {
351 node_table.num_rows += 1;
352 node_table.num_nonempty_cells += i;
353 true
354 }
355 _ => false,
356 };
357}
358
359fn get_num_autocompleted_cells<'a>(container: &'a AstNode<'a>) -> usize {
361 return match container.data.borrow().value {
362 NodeValue::Table(ref node_table) => {
363 let num_cells = node_table.num_columns * node_table.num_rows;
364
365 if num_cells < node_table.num_nonempty_cells {
366 0
367 } else {
368 (node_table.num_columns * node_table.num_rows) - node_table.num_nonempty_cells
369 }
370 }
371 _ => 0,
372 };
373}
374
375pub fn matches(line: &[u8], spoiler: bool) -> bool {
376 row(line, spoiler).is_some()
377}