1use {
2 crate::{
3 Comment,
4 CommentMode,
5 FormatConfig,
6 Whitespace,
7 WhitespaceMode,
8 },
9 loga::{
10 ResultContext,
11 ea,
12 },
13 markdown::mdast::Node,
14 proc_macro2::{
15 Group,
16 LineColumn,
17 TokenStream,
18 },
19 regex::Regex,
20 std::{
21 cell::RefCell,
22 collections::BTreeMap,
23 hash::Hash,
24 rc::Rc,
25 str::FromStr,
26 },
27};
28
29pub fn extract_whitespaces(
33 keep_max_blank_lines: usize,
34 source: &str,
35) -> Result<(BTreeMap<HashLineColumn, Vec<Whitespace>>, TokenStream), loga::Error> {
36 let mut line_lookup = vec![];
37 {
38 let mut offset = 0usize;
39 loop {
40 line_lookup.push(offset);
41 offset += match source[offset..].find('\n') {
42 Some(r) => r,
43 None => {
44 break;
45 },
46 } + 1;
47 }
48 }
49
50 struct State<'a> {
51 block_event_re: Option<Regex>,
52 keep_max_blank_lines: usize,
53 last_offset: usize,
54 line_lookup: Vec<usize>,
56 line_start: Option<LineColumn>,
59 source: &'a str,
60 start_re: Option<Regex>,
61 whitespaces: BTreeMap<HashLineColumn, Vec<Whitespace>>,
62 }
63
64 impl<'a> State<'a> {
65 fn add_comments(&mut self, end: LineColumn, abs_start: usize, between_ast_nodes: &str) {
66 let start_re = &self.start_re.get_or_insert_with(|| Regex::new(
67 r#"(?:(//)(/|!|\.|\?|#)?)|(/\*\*/)|(?:(/\*)(\*|!)?)"#,
69 ).unwrap());
70 let block_event_re =
71 &self.block_event_re.get_or_insert_with(|| Regex::new(r#"((?:/\*)|(?:\*/))"#).unwrap());
72
73 struct CommentBuffer {
74 blank_lines: usize,
75 keep_max_blank_lines: usize,
76 lines: Vec<String>,
77 loc: LineColumn,
78 mode: CommentMode,
79 orig_start_offset: Option<usize>,
80 out: Vec<Whitespace>,
81 }
82
83 impl CommentBuffer {
84 fn add(&mut self, mode: CommentMode, line: &str, orig_start_offset: usize) {
85 if self.mode != mode && !self.lines.is_empty() {
86 self.flush();
87 }
88 self.mode = mode;
89 self.lines.push(line.to_string());
90 self.orig_start_offset.get_or_insert(orig_start_offset);
91 }
92
93 fn add_blank_lines(&mut self, text: &str) {
94 let blank_lines = text.as_bytes().iter().filter(|x| **x == b'\n').count();
95 if blank_lines > 1 && self.keep_max_blank_lines > 0 {
96 self.flush();
97 self.out.push(Whitespace {
98 loc: self.loc,
99 mode: crate::WhitespaceMode::BlankLines(
100 (blank_lines - 1).min(self.keep_max_blank_lines),
101 ),
102 });
103 }
104 }
105
106 fn flush(&mut self) {
107 if self.lines.is_empty() {
108 return;
109 }
110 self.out.push(Whitespace {
111 loc: self.loc,
112 mode: crate::WhitespaceMode::Comment(Comment {
113 mode: self.mode,
114 lines: self.lines.split_off(0).join("\n"),
115 orig_start_offset: self.orig_start_offset.unwrap(),
116 }),
117 });
118 self.blank_lines = 0;
119 self.orig_start_offset = None;
120 }
121 }
122
123 let mut buffer = CommentBuffer {
124 keep_max_blank_lines: self.keep_max_blank_lines,
125 blank_lines: 0,
126 out: vec![],
127 mode: CommentMode::Normal,
128 lines: vec![],
129 loc: end,
130 orig_start_offset: None,
131 };
132 let mut text = (abs_start, between_ast_nodes);
133 'comment_loop : loop {
134 match start_re.captures(text.1) {
135 Some(found_start) => {
136 let orig_start_offset = abs_start + found_start.get(0).unwrap().start();
137 let start_prefix_match =
138 found_start
139 .get(1)
140 .or_else(|| found_start.get(3))
141 .or_else(|| found_start.get(4))
142 .unwrap();
143 if buffer.out.is_empty() && buffer.lines.is_empty() {
144 buffer.add_blank_lines(&text.1[..start_prefix_match.start()]);
145 }
146 match start_prefix_match.as_str() {
147 "//" => {
148 let mode = {
149 let start_suffix_match = found_start.get(2);
150 let (mut mode, mut match_end) = match start_suffix_match {
151 Some(start_suffix_match) => (match start_suffix_match.as_str() {
152 "/" => CommentMode::DocOuter,
153 "!" => CommentMode::DocInner,
154 "." => CommentMode::Verbatim,
155 "#" => CommentMode::Directive,
156 "?" => CommentMode::ExplicitNormal,
157 _ => unreachable!(),
158 }, start_suffix_match.end()),
159 None => (CommentMode::Normal, start_prefix_match.end()),
160 };
161 if mode == CommentMode::DocOuter && text.1[match_end..].starts_with("/") {
162 mode = CommentMode::Normal;
164 match_end = start_prefix_match.end();
165 }
166 text = (text.0 + match_end, &text.1[match_end..]);
167 mode
168 };
169 let (line, next_start) = match text.1.find('\n') {
170 Some(line_end) => (&text.1[..line_end], line_end + 1),
171 None => (text.1, text.1.len()),
172 };
173 buffer.add(mode, line, orig_start_offset);
174 text = (text.0 + next_start, &text.1[next_start..]);
175 },
176 "/**/" => {
177 buffer.add(CommentMode::Normal, "".into(), orig_start_offset);
178 text = (text.0 + start_prefix_match.end(), &text.1[start_prefix_match.end()..]);
179 },
180 "/*" => {
181 let mode = {
182 let start_suffix_match = found_start.get(5);
183 let (mode, match_end) = match start_suffix_match {
184 Some(start_suffix_match) => (match start_suffix_match.as_str() {
185 "*" => CommentMode::DocOuter,
186 "!" => CommentMode::DocInner,
187 _ => unreachable!(),
188 }, start_suffix_match.end()),
189 None => (CommentMode::Normal, start_prefix_match.end()),
190 };
191 text = (text.0 + match_end, &text.1[match_end..]);
192 mode
193 };
194 let mut nesting = 1;
195 let mut search_end_at = 0usize;
196 let (lines, next_start) = loop {
197 let found_event =
198 block_event_re.captures(&text.1[search_end_at..]).unwrap().get(1).unwrap();
199 let event_start = search_end_at + found_event.start();
200 search_end_at += found_event.end();
201 match found_event.as_str() {
202 "/*" => {
203 nesting += 1;
204 },
205 "*/" => {
206 nesting -= 1;
207 if nesting == 0 {
208 break (&text.1[..event_start], search_end_at);
209 }
210 },
211 _ => unreachable!(),
212 }
213 };
214 for line in lines.lines() {
215 let mut line = line.trim();
216 line = line.strip_prefix("* ").unwrap_or(line);
217 buffer.add(mode, line, orig_start_offset);
218 }
219 text = (text.0 + next_start, &text.1[next_start..]);
220 },
221 _ => unreachable!(),
222 }
223 },
224 None => {
225 if buffer.out.is_empty() && buffer.lines.is_empty() {
226 buffer.add_blank_lines(text.1);
227 }
228 break 'comment_loop;
229 },
230 }
231 }
232 buffer.flush();
233 if !buffer.out.is_empty() {
234 let whitespaces = self.whitespaces.entry(HashLineColumn(end)).or_insert(vec![]);
235
236 'merge : loop {
239 let Some(previous_whitespace) = whitespaces.last_mut() else {
240 break;
241 };
242 let WhitespaceMode::Comment(previous_comment) = &mut previous_whitespace.mode else {
243 break;
244 };
245 let start = buffer.out.remove(0);
246 loop {
247 let WhitespaceMode::Comment(start_comment) = &start.mode else {
248 break;
249 };
250 if previous_comment.mode != start_comment.mode {
251 break;
252 }
253 previous_comment.lines.push_str("\n");
254 previous_comment.lines.push_str(&start_comment.lines);
255 break 'merge;
256 }
257 buffer.out.insert(0, start);
258 break;
259 }
260
261 whitespaces.extend(buffer.out);
263 }
264 }
265
266 fn extract(&mut self, mut start: usize, end: LineColumn) {
267 if loop {
269 let previous_start = match &self.line_start {
270 None => break true,
272 Some(s) => s,
273 };
274 if end.line <= previous_start.line {
275 break false;
277 }
278 let eol = match self.source[start..].find('\n') {
279 Some(n) => start + n,
280 None => self.source.len(),
281 };
282 let text = &self.source[start .. eol];
283 if text.trim_start().starts_with("//") {
284 self.add_comments(*previous_start, start, text);
285 }
286 start = eol;
287 break true;
288 } {
289 self.line_start = Some(end);
290 }
291
292 let end_offset = self.to_offset(end);
294 if end_offset < start {
295 return;
296 }
297 let whole_text = &self.source[start .. end_offset];
298 self.add_comments(end, start, whole_text);
299 }
300
301 fn to_offset(&self, loc: LineColumn) -> usize {
302 if loc.line == 0 {
303 return 0usize;
304 }
305 let line_start_offset = *self.line_lookup.get(loc.line - 1).unwrap();
306 line_start_offset +
307 self.source[line_start_offset..].chars().take(loc.column).map(char::len_utf8).sum::<usize>()
308 }
309 }
310
311 let mut state = State {
313 source: source,
314 keep_max_blank_lines: keep_max_blank_lines,
315 line_lookup: line_lookup,
316 whitespaces: BTreeMap::new(),
317 last_offset: 0usize,
318 line_start: None,
319 start_re: None,
320 block_event_re: None,
321 };
322
323 fn recurse(state: &mut State, ts: TokenStream) -> TokenStream {
324 let mut out = vec![];
325 let mut ts = ts.into_iter().peekable();
326 while let Some(t) = ts.next() {
327 match t {
328 proc_macro2::TokenTree::Group(g) => {
329 state.extract(state.last_offset, g.span_open().start());
330 state.last_offset = state.to_offset(g.span_open().end());
331 let subtokens = recurse(state, g.stream());
332 state.extract(state.last_offset, g.span_close().start());
333 state.last_offset = state.to_offset(g.span_close().end());
334 let mut new_g = Group::new(g.delimiter(), subtokens);
335 new_g.set_span(g.span());
336 out.push(proc_macro2::TokenTree::Group(new_g));
337 },
338 proc_macro2::TokenTree::Ident(g) => {
339 state.extract(state.last_offset, g.span().start());
340 state.last_offset = state.to_offset(g.span().end());
341 out.push(proc_macro2::TokenTree::Ident(g));
342 },
343 proc_macro2::TokenTree::Punct(g) => {
344 let offset = state.to_offset(g.span().start());
345 if g.as_char() == '#' && &state.source[offset .. offset + 1] == "/" {
346 loop {
350 let in_comment = ts.peek().map(|n| n.span().start() < g.span().end()).unwrap_or(false);
351 if !in_comment {
352 break;
353 }
354 ts.next();
355 }
356 } else {
357 state.extract(state.last_offset, g.span().start());
358 state.last_offset = state.to_offset(g.span().end());
359 out.push(proc_macro2::TokenTree::Punct(g));
360 }
361 },
362 proc_macro2::TokenTree::Literal(g) => {
363 state.extract(state.last_offset, g.span().start());
364 state.last_offset = state.to_offset(g.span().end());
365 out.push(proc_macro2::TokenTree::Literal(g));
366 },
367 }
368 }
369 TokenStream::from_iter(out)
370 }
371
372 let tokens =
373 recurse(
374 &mut state,
375 TokenStream::from_str(
376 source,
377 ).map_err(
378 |e| loga::err_with(
379 "Error undoing syn parse transformations",
380 ea!(
381 line = e.span().start().line,
382 column = e.span().start().column,
383 error = e.to_string(),
384 source = source.lines().skip(e.span().start().line - 1).next().unwrap()
385 ),
386 ),
387 )?,
388 );
389 state.add_comments(LineColumn {
390 line: 0,
391 column: 1,
392 }, state.last_offset, &source[state.last_offset..]);
393 Ok((state.whitespaces, tokens))
394}
395
396pub fn format_md(
397 true_out: &mut String,
398 config: &FormatConfig,
399 prefix: &str,
400 source: &str,
401) -> Result<(), loga::Error> {
402 match || -> Result<String, loga::Error> {
406 let mut out = String::new();
407 let mut state = State {
408 line_buffer: String::new(),
409 need_nl: false,
410 config: config.clone(),
411 };
412 let ast = markdown::to_mdast(source, &markdown::ParseOptions {
413 constructs: markdown::Constructs { ..Default::default() },
414 ..Default::default()
415 }).map_err(loga::err).context("Error parsing markdown")?;
416 recurse_write(
417 &mut state,
418 &mut out,
419 LineState::new(
420 unicode_len(&prefix),
421 None,
422 prefix.to_string(),
423 VisualLen(config.max_width),
424 config.comment_width.map(VisualLen),
425 ),
426 &ast,
427 false,
428 );
429 Ok(out)
430 }() {
431 Ok(o) => {
432 true_out.push_str(&o);
433 Ok(())
434 },
435 Err(e) => {
436 Err(e)
437 },
438 }
439}
440
441fn get_splits(text: &str) -> Vec<usize> {
443 text.char_indices().filter(|i| i.1 == ' ').map(|i| i.0 + 1).collect()
447}
448
449#[derive(PartialEq, Eq, Debug, Clone, Copy)]
450pub struct HashLineColumn(pub LineColumn);
451
452impl Hash for HashLineColumn {
453 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
454 (self.0.line, self.0.column).hash(state);
455 }
456}
457
458impl Ord for HashLineColumn {
459 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
460 return self.0.line.cmp(&other.0.line).then(self.0.column.cmp(&other.0.column));
461 }
462}
463
464impl PartialOrd for HashLineColumn {
465 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
466 return Some(self.0.cmp(&other.0));
467 }
468}
469
470struct LineState(Rc<RefCell<LineState_>>);
471
472impl LineState {
473 fn clone_indent(&self, first_prefix: Option<String>, prefix: String) -> LineState {
474 let mut s = self.0.as_ref().borrow_mut();
475 LineState(Rc::new(RefCell::new(LineState_ {
476 base_prefix_len: s.base_prefix_len,
477 first_prefix: match (s.first_prefix.take(), first_prefix) {
478 (None, None) => None,
479 (None, Some(p)) => Some(format!("{}{}", s.prefix, p)),
480 (Some(p), None) => Some(p),
481 (Some(p1), Some(p2)) => Some(format!("{}{}", p1, p2)),
482 },
483 prefix: format!("{}{}", s.prefix, prefix),
484 max_width: s.max_width,
485 rel_max_width: s.rel_max_width,
486 backward_break: None,
487 unbreakable: false,
488 })))
489 }
490
491 fn clone_inline(&self) -> LineState {
492 LineState(self.0.clone())
493 }
494
495 fn clone_unbreakable(&self, first_prefix: Option<String>) -> LineState {
496 let mut s = self.0.as_ref().borrow_mut();
497 LineState(Rc::new(RefCell::new(LineState_ {
498 base_prefix_len: s.base_prefix_len,
499 first_prefix: match (s.first_prefix.take(), first_prefix) {
500 (None, None) => None,
501 (None, Some(p)) => Some(format!("{}{}", s.prefix, p)),
502 (Some(p), None) => Some(p),
503 (Some(p1), Some(p2)) => Some(format!("{}{}", p1, p2)),
504 },
505 prefix: s.prefix.clone(),
506 max_width: s.max_width,
507 rel_max_width: s.rel_max_width,
508 backward_break: None,
509 unbreakable: true,
510 })))
511 }
512
513 fn clone_zero_indent(&self) -> LineState {
514 let mut s = self.0.as_ref().borrow_mut();
515 LineState(Rc::new(RefCell::new(LineState_ {
516 base_prefix_len: s.base_prefix_len,
517 first_prefix: s.first_prefix.take(),
518 prefix: s.prefix.clone(),
519 max_width: s.max_width,
520 rel_max_width: s.rel_max_width,
521 backward_break: None,
522 unbreakable: false,
523 })))
524 }
525
526 fn flush_always(&self, state: &mut State, out: &mut String) {
527 self.0.as_ref().borrow_mut().flush_always(state, out);
528 }
529
530 fn new(
531 base_prefix_len: VisualLen,
532 first_prefix: Option<String>,
533 prefix: String,
534 max_width: VisualLen,
535 rel_max_width: Option<VisualLen>,
536 ) -> LineState {
537 LineState(Rc::new(RefCell::new(LineState_ {
538 base_prefix_len: base_prefix_len,
539 first_prefix,
540 prefix,
541 max_width,
542 rel_max_width,
543 backward_break: None,
544 unbreakable: false,
545 })))
546 }
547
548 fn write(&self, state: &mut State, out: &mut String, text: &str, breaks: &[usize]) {
549 let mut s = self.0.as_ref().borrow_mut();
550 if s.unbreakable {
551 state.line_buffer.push_str(text);
552 return;
553 }
554 let max_len = s.calc_max_width();
555
556 struct FoundWritableLen<'a> {
557 next_break: Option<(usize, &'a [usize])>,
560 previous_break: Option<(usize, &'a [usize])>,
562 writable: usize,
565 }
566
567 fn find_writable_len<
568 'a,
569 >(
570 width: VisualLen,
571 max_len: VisualLen,
572 text: &str,
573 breaks_offset: usize,
574 breaks: &'a [usize],
575 ) -> FoundWritableLen<'a> {
576 let mut previous_break = None;
577 let mut writable = 0;
578 for (i, b) in breaks.iter().enumerate() {
579 let b = *b - breaks_offset;
580 let next_break = Some((b, &breaks[i + 1..]));
581 if width + unicode_len(&text[..b]) > max_len {
582 return FoundWritableLen {
583 writable: writable,
584 previous_break: previous_break,
585 next_break: next_break,
586 };
587 }
588 previous_break = next_break;
589 writable = b;
590 }
591 return FoundWritableLen {
592 writable: if width + unicode_len(&text) > max_len {
593 writable
594 } else {
595 text.len()
596 },
597 previous_break: previous_break,
598 next_break: None,
599 };
600 }
601
602 fn write_forward(state: &mut State, s: &mut LineState_, text: &str, b: Option<usize>) {
604 if let Some(b) = b {
605 s.backward_break = Some(state.line_buffer.len() + b);
606 }
607 state.line_buffer.push_str(&text);
608 }
609
610 fn write_forward_breaks(
611 state: &mut State,
612 s: &mut LineState_,
613 out: &mut String,
614 max_len: VisualLen,
615 mut first: bool,
616 mut text: String,
617 mut breaks_offset: usize,
618 breaks: &[usize],
619 ) {
620 let mut breaks = breaks;
621 while !text.is_empty() {
622 if first {
623 first = false;
624 } else {
625 s.flush(state, out);
626 }
627 let found = find_writable_len(s.calc_current_len(state), max_len, &text, breaks_offset, breaks);
628 if found.writable > 0 {
629 write_forward(state, s, &text[..found.writable], found.previous_break.map(|b| b.0));
630 breaks = found.previous_break.map(|b| b.1).unwrap_or(breaks);
631 text = text.split_off(found.writable);
632 breaks_offset += found.writable;
633 } else if let Some((b, breaks0)) = found.next_break {
634 write_forward(state, s, &text[..b], Some(b));
635 breaks = breaks0;
636 text = text.split_off(b);
637 breaks_offset += b;
638 } else {
639 state.line_buffer.push_str(&text);
640 return;
641 }
642 }
643 }
644
645 let found = find_writable_len(s.calc_current_len(state), max_len, text, 0, breaks);
646 if found.writable > 0 {
647 write_forward(state, &mut s, &text[..found.writable], found.previous_break.map(|b| b.0));
648 write_forward_breaks(
649 state,
650 &mut s,
651 out,
652 max_len,
653 false,
654 (&text[found.writable..]).to_string(),
655 found.writable,
656 found.previous_break.map(|b| b.1).unwrap_or(breaks),
657 );
658 } else if let Some(at) = s.backward_break.take() {
659 let prefix = state.line_buffer.split_off(at);
662 s.flush(state, out);
663 state.line_buffer.push_str(&prefix);
664 write_forward_breaks(state, &mut s, out, max_len, true, text.to_string(), 0, breaks);
665 } else if let Some((b, breaks)) = found.next_break {
666 write_forward(state, &mut s, &text[..b], Some(b));
669 write_forward_breaks(state, &mut s, out, max_len, false, (&text[b..]).to_string(), b, breaks);
670 } else {
671 state.line_buffer.push_str(text);
672 }
673 }
674
675 fn write_breakable(&self, state: &mut State, out: &mut String, text: &str) {
676 self.write(state, out, text, &get_splits(text));
677 }
678
679 fn write_newline(&self, state: &mut State, out: &mut String) {
680 let mut s = self.0.as_ref().borrow_mut();
681 if !state.line_buffer.is_empty() {
682 panic!();
683 }
684 s.flush_always(state, out);
685 }
686
687 fn write_unbreakable(&self, state: &mut State, out: &mut String, text: &str) {
688 self.write(state, out, text, &[]);
689 }
690}
691
692#[derive(Debug)]
693struct LineState_ {
694 backward_break: Option<usize>,
695 base_prefix_len: VisualLen,
696 first_prefix: Option<String>,
697 max_width: VisualLen,
698 prefix: String,
699 rel_max_width: Option<VisualLen>,
700 unbreakable: bool,
701}
702
703impl LineState_ {
704 fn calc_current_len(&self, state: &State) -> VisualLen {
705 self.base_prefix_len + unicode_len(&state.line_buffer)
706 }
707
708 fn calc_max_width(&self) -> VisualLen {
709 match self.rel_max_width {
710 Some(w) => unicode_len(&self.prefix) + w,
711 None => self.max_width,
712 }
713 }
714
715 fn flush(&mut self, state: &mut State, out: &mut String) {
716 if !state.line_buffer.trim().is_empty() {
717 self.flush_always(state, out);
718 }
719 }
720
721 fn flush_always(&mut self, state: &mut State, out: &mut String) {
722 out.push_str(format!(
723 "{}{}{}",
725 if state.need_nl {
726 "\n"
727 } else {
728 ""
729 },
730 match &self.first_prefix.take() {
731 Some(t) => t,
732 None => &*self.prefix,
733 },
734 &state.line_buffer,
735 ).trim_end());
736 state.line_buffer.clear();
737 state.need_nl = true;
738 self.backward_break = None;
739 }
740}
741
742fn recurse_write(state: &mut State, out: &mut String, line: LineState, node: &Node, inline: bool) {
743 fn join_lines(text: &str) -> String {
744 let lines = Regex::new("\r?\n").unwrap().split(text).collect::<Vec<&str>>();
745 let mut joined = String::new();
746 for (i, line) in lines.iter().enumerate() {
747 let mut line = *line;
748 if i > 0 {
749 line = line.trim_start();
750 joined.push(' ');
751 }
752 if i < lines.len() - 1 {
753 line = line.trim_end();
754 }
755 joined.push_str(line);
756 }
757 joined
758 }
759
760 match node {
761 Node::Root(x) => {
763 for (i, child) in x.children.iter().enumerate() {
764 if i > 0 {
765 line.write_newline(state, out);
766 }
767 recurse_write(state, out, line.clone_zero_indent(), child, false);
768 }
769 },
770 Node::Blockquote(x) => {
771 let line = line.clone_indent(None, "> ".into());
772 for (i, child) in x.children.iter().enumerate() {
773 if i > 0 {
774 line.write_newline(state, out);
775 }
776 recurse_write(state, out, line.clone_inline(), child, false);
777 }
778 },
779 Node::List(x) => {
780 match &x.start {
781 Some(i) => {
782 for (j, child) in x.children.iter().enumerate() {
785 if j > 0 {
786 line.write_newline(state, out);
787 }
788 recurse_write(
789 state,
790 out,
791 line.clone_indent(Some(format!("{}. ", *i as usize + j)), " ".into()),
792 child,
793 false,
794 );
795 }
796 },
797 None => {
798 for (i, child) in x.children.iter().enumerate() {
799 if i > 0 {
800 line.write_newline(state, out);
801 }
802 recurse_write(state, out, line.clone_indent(Some("* ".into()), " ".into()), child, false);
803 }
804 },
805 };
806 },
807 Node::ListItem(x) => {
808 for (i, child) in x.children.iter().enumerate() {
809 if i > 0 {
810 line.write_newline(state, out);
811 }
812 recurse_write(state, out, line.clone_zero_indent(), child, false);
813 }
814 },
815 Node::Code(x) => {
817 let mut content = None;
818 if let Some(lang) = &x.lang {
819 if lang == "rust" {
820 let current_prefix_len = line.0.borrow().prefix.chars().count();
821 let base_prefix_len = line.0.borrow().base_prefix_len.0;
822 let rel_max_width = line.0.borrow().rel_max_width.map(|v| v.0);
823 let overall_max_width = line.0.borrow().max_width.0;
824 let nested_max_width = if let Some(rel) = rel_max_width {
825 let markdown_indent = current_prefix_len - base_prefix_len;
826 rel.saturating_sub(markdown_indent)
827 } else {
828 overall_max_width.saturating_sub(current_prefix_len)
829 };
830
831 let nested_max_width = std::cmp::max(nested_max_width, 40);
833 let mut nested_config = state.config.clone();
834 nested_config.max_width = nested_max_width;
835 match crate::format_str(&x.value, &nested_config) {
836 Ok(res) => {
837 content = Some(res.rendered);
838 },
839 Err(_) => {
840 },
842 }
843 } else if let Some(fmt_config) = state.config.external_formatters.get(lang.as_str()).cloned() {
844 match crate::run_external_formatter(&fmt_config.commandline, &x.value) {
845 Ok(formatted) => {
846 content = Some(formatted);
847 },
848 Err(_) => {
849 },
851 }
852 }
853 }
854 line.write_unbreakable(state, out, &format!("```{}", match &x.lang {
855 None => "",
856 Some(x) => x,
857 }));
858 line.flush_always(state, out);
859 let content = content.unwrap_or_else(|| x.value.clone());
860 for l in content.as_str().lines() {
861 line.write_unbreakable(state, out, l);
862 line.flush_always(state, out);
863 }
864 line.write_unbreakable(state, out, "```");
865 line.flush_always(state, out);
866 },
867 Node::Heading(x) => {
868 let line = line.clone_unbreakable(Some(format!("{} ", "#".repeat(x.depth as usize))));
869 for child in &x.children {
870 recurse_write(state, out, line.clone_inline(), child, true);
871 }
872 line.flush_always(state, out);
873 },
874 Node::FootnoteDefinition(x) => {
875 let line = line.clone_indent(Some(format!("[^{}]: ", x.identifier)), " ".into());
876 for child in &x.children {
877 recurse_write(state, out, line.clone_inline(), child, true);
878 }
879 line.flush_always(state, out);
880 },
881 Node::ThematicBreak(_) => {
882 line.write_unbreakable(state, out, "---");
883 line.flush_always(state, out);
884 },
885 Node::Definition(x) => {
886 line.write_unbreakable(state, out, &format!("[{}]: {}", x.identifier.trim(), x.url));
887 if let Some(title) = &x.title {
888 line.write_unbreakable(state, out, " \"");
889 line.write_breakable(state, out, title);
890 line.write_unbreakable(state, out, "\"");
891 }
892 line.flush_always(state, out);
893 },
894 Node::Paragraph(x) => {
895 for child in &x.children {
896 recurse_write(state, out, line.clone_inline(), child, true);
897 }
898 line.flush_always(state, out);
899 },
900 Node::Html(x) if !inline => {
901 line.write_unbreakable(state, out, &format!("`{}`", join_lines(&x.value)));
902 line.flush_always(state, out);
903 },
904 Node::Text(x) => {
906 line.write_breakable(state, out, &join_lines(&x.value));
907 },
908 Node::InlineCode(x) => {
909 line.write_unbreakable(state, out, &format!("`{}`", join_lines(&x.value)));
910 },
911 Node::Strong(x) => {
912 line.write_unbreakable(state, out, "**");
913 for child in &x.children {
914 recurse_write(state, out, line.clone_inline(), child, true);
915 }
916 line.write_unbreakable(state, out, "**");
917 },
918 Node::Delete(x) => {
919 line.write_unbreakable(state, out, "~~");
920 for child in &x.children {
921 recurse_write(state, out, line.clone_inline(), child, true);
922 }
923 line.write_unbreakable(state, out, "~~");
924 },
925 Node::Emphasis(x) => {
926 line.write_unbreakable(state, out, "_");
927 for child in &x.children {
928 recurse_write(state, out, line.clone_inline(), child, true);
929 }
930 line.write_unbreakable(state, out, "_");
931 },
932 Node::FootnoteReference(x) => {
933 line.write_unbreakable(state, out, &format!("[^{}]", x.identifier));
934 },
935 Node::Html(x) => {
936 line.write_unbreakable(state, out, &format!("`{}`", join_lines(&x.value)));
937 },
938 Node::Image(x) => {
939 let alt = join_lines(&x.alt);
940 match (get_splits(&join_lines(&alt)).first().is_some(), &x.title) {
941 (false, None) => {
942 line.write_unbreakable(state, out, &format!("", alt, x.url));
943 },
944 (false, Some(t)) => {
945 line.write_unbreakable(state, out, &format!(");
946 line.write_unbreakable(state, out, " \"");
947 line.write_breakable(state, out, &join_lines(t));
948 line.write_unbreakable(state, out, "\")");
949 },
950 (true, None) => {
951 line.write_unbreakable(state, out, "", x.url));
954 },
955 (true, Some(t)) => {
956 line.write_unbreakable(state, out, ");
959 line.write_unbreakable(state, out, " \"");
960 line.write_breakable(state, out, &join_lines(t));
961 line.write_unbreakable(state, out, "\")");
962 },
963 }
964 },
965 Node::ImageReference(x) => {
966 line.write_unbreakable(state, out, &format!("![][{}]", x.identifier));
967 },
968 Node::Link(x) => {
969 let simple_text = if x.children.len() != 1 {
970 None
971 } else {
972 x.children.get(0)
973 }.and_then(|c| match c {
974 Node::Text(t) => {
975 let t = join_lines(&t.value);
976 if get_splits(&t).first().is_some() {
977 None
978 } else {
979 Some(t)
980 }
981 },
982 Node::InlineCode(t) => {
983 let t = join_lines(&t.value);
984 if get_splits(&t).first().is_some() {
985 None
986 } else {
987 Some(format!("`{}`", t))
988 }
989 },
990 _ => None,
991 });
992 match (simple_text, &x.title) {
993 (Some(unbroken_content), None) => {
994 if unbroken_content.as_str() == x.url.as_str() {
995 line.write_unbreakable(state, out, &format!("<{}>", x.url));
996 } else {
997 line.write_unbreakable(state, out, &format!("[{}]({})", unbroken_content, x.url));
998 }
999 },
1000 (Some(c), Some(title)) => {
1001 line.write_unbreakable(state, out, &format!("[{}]({}", c, x.url));
1002 line.write_unbreakable(state, out, " \"");
1003 line.write_breakable(state, out, title);
1004 line.write_unbreakable(state, out, "\")");
1005 },
1006 (None, None) => {
1007 line.write_unbreakable(state, out, "[");
1008 for child in &x.children {
1009 recurse_write(state, out, line.clone_inline(), child, true);
1010 }
1011 line.write_unbreakable(state, out, &format!("]({})", x.url));
1012 },
1013 (None, Some(title)) => {
1014 line.write_unbreakable(state, out, "[");
1015 for child in &x.children {
1016 recurse_write(state, out, line.clone_inline(), child, true);
1017 }
1018 line.write_unbreakable(state, out, &format!("]({}", x.url));
1019 line.write_unbreakable(state, out, " \"");
1020 line.write_breakable(state, out, title);
1021 line.write_unbreakable(state, out, "\")");
1022 },
1023 }
1024 },
1025 Node::LinkReference(x) => {
1026 let simple_text = if x.children.len() != 1 {
1027 None
1028 } else {
1029 x.children.get(0)
1030 }.and_then(|c| match c {
1031 Node::Text(t) => if get_splits(&t.value).first().is_some() {
1032 None
1033 } else {
1034 Some(t.value.clone())
1035 },
1036 Node::InlineCode(t) => if get_splits(&t.value).first().is_some() {
1037 None
1038 } else {
1039 Some(format!("`{}`", t.value))
1040 },
1041 _ => {
1042 None
1043 },
1044 });
1045 match simple_text {
1046 Some(t) if t == x.identifier => {
1047 line.write_unbreakable(state, out, &format!("[{}]", t));
1048 },
1049 _ => {
1050 line.write_unbreakable(state, out, "[");
1051 for child in &x.children {
1052 recurse_write(state, out, line.clone_inline(), child, true);
1053 }
1054 line.write_unbreakable(state, out, &format!("][{}]", x.identifier));
1055 },
1056 }
1057 },
1058 Node::Break(_) => {
1059 },
1061 Node::Math(_) => unreachable!(),
1062 Node::Table(_) => unreachable!(),
1063 Node::TableRow(_) => unreachable!(),
1064 Node::TableCell(_) => unreachable!(),
1065 Node::MdxJsxTextElement(_) => unreachable!(),
1066 Node::MdxFlowExpression(_) => unreachable!(),
1067 Node::MdxJsxFlowElement(_) => unreachable!(),
1068 Node::MdxjsEsm(_) => unreachable!(),
1069 Node::Toml(_) => unreachable!(),
1070 Node::Yaml(_) => unreachable!(),
1071 Node::InlineMath(_) => unreachable!(),
1072 Node::MdxTextExpression(_) => unreachable!(),
1073 }
1074}
1075
1076struct State {
1077 config: FormatConfig,
1078 line_buffer: String,
1079 need_nl: bool,
1080}
1081
1082fn unicode_len(text: &str) -> VisualLen {
1083 VisualLen(text.chars().count())
1084}
1085
1086#[derive(Debug, derive_more::Add, PartialEq, Eq, PartialOrd, Ord, derive_more::Sub, Clone, Copy)]
1087struct VisualLen(usize);