1use {
2 loga::{
3 ea,
4 Error,
5 },
6 proc_macro2::{
7 Ident,
8 LineColumn,
9 },
10 quote::ToTokens,
11 serde::{
12 Serialize,
13 Deserialize,
14 },
15 sg_general::append_whitespace,
16 std::{
17 collections::BTreeMap,
18 cell::{
19 Cell,
20 RefCell,
21 },
22 rc::Rc,
23 },
24 syn::File,
25};
26pub use whitespace::{
27 format_md,
28 HashLineColumn,
29};
30
31pub(crate) mod whitespace;
32pub(crate) mod sg_expr;
33pub(crate) mod sg_general;
34pub(crate) mod sg_pat;
35pub(crate) mod sg_statement;
36pub(crate) mod sg_type;
37pub(crate) mod sg_root;
38pub(crate) mod sg_general_lists;
39
40#[derive(PartialEq, Clone, Copy, Debug)]
41pub enum CommentMode {
42 Normal,
43 ExplicitNormal,
44 DocInner,
45 DocOuter,
46 Verbatim,
47}
48
49#[derive(Debug)]
50pub struct Comment {
51 pub mode: CommentMode,
52 pub lines: String,
53 pub orig_start_offset: usize,
56}
57
58#[derive(Debug)]
59pub enum WhitespaceMode {
60 BlankLines(usize),
61 Comment(Comment),
62}
63
64#[derive(Debug)]
65pub struct Whitespace {
66 pub loc: LineColumn,
69 pub mode: WhitespaceMode,
70}
71
72#[derive(Clone, Copy)]
73pub struct SplitGroupIdx(usize);
74
75#[derive(Clone, Copy)]
76pub struct SegmentIdx(usize);
77
78#[derive(Clone, Copy)]
79pub(crate) struct LineIdx(usize);
80
81pub struct SplitGroup {
82 pub(crate) children: Vec<SplitGroupIdx>,
83 pub(crate) split: bool,
84 pub(crate) segments: Vec<SegmentIdx>,
85}
86
87#[derive(Debug, Clone, Copy)]
88pub(crate) enum SegmentMode {
89 All,
90 Unsplit,
91 Split,
92}
93
94pub(crate) struct SegmentLine {
95 pub(crate) line: LineIdx,
96 pub(crate) seg_index: usize,
97}
98
99#[derive(Debug)]
100pub(crate) enum SegmentContent {
101 Text(String),
102 Whitespace((Alignment, Vec<Whitespace>)),
103 Break(Alignment, bool),
104}
105
106pub(crate) struct Segment {
107 pub(crate) node: SplitGroupIdx,
108 pub(crate) line: Option<SegmentLine>,
109 pub(crate) mode: SegmentMode,
110 pub(crate) content: SegmentContent,
111}
112
113pub(crate) struct Line {
114 index: usize,
115 segs: Vec<SegmentIdx>,
116}
117
118struct Lines {
119 owned_lines: Vec<Line>,
120 lines: Vec<LineIdx>,
121}
122
123pub struct MakeSegsState {
124 nodes: Vec<SplitGroup>,
125 segs: Vec<Segment>,
126 whitespaces: BTreeMap<HashLineColumn, Vec<Whitespace>>,
127 config: FormatConfig,
128 macro_depth: Rc<Cell<usize>>,
133}
134
135pub struct IncMacroDepth(Rc<Cell<usize>>);
136
137impl IncMacroDepth {
138 fn new(s: &MakeSegsState) -> Self {
139 s.macro_depth.update(|x| x + 1);
140 return Self(s.macro_depth.clone());
141 }
142}
143
144impl Drop for IncMacroDepth {
145 fn drop(&mut self) {
146 self.0.update(|x| x - 1);
147 }
148}
149
150pub(crate) fn check_split_brace_threshold(out: &MakeSegsState, count: usize) -> bool {
151 out.config.split_brace_threshold.map(|t| count >= t).unwrap_or(false)
152}
153
154pub(crate) fn line_length(out: &MakeSegsState, lines: &Lines, line_i: LineIdx) -> usize {
155 let mut len = 0;
156 for seg_i in &lines.owned_lines.get(line_i.0).unwrap().segs {
157 let seg = out.segs.get(seg_i.0).unwrap();
158 match &seg.content {
159 SegmentContent::Text(t) => len += t.chars().count(),
160 SegmentContent::Break(b, _) => {
161 if out.nodes.get(seg.node.0).unwrap().split {
162 len += out.config.indent_spaces * b.get().0;
163 }
164 },
165 SegmentContent::Whitespace(_) => { },
166 };
167 }
168 len
169}
170
171pub(crate) fn split_group(out: &mut MakeSegsState, lines: &mut Lines, sg_i: SplitGroupIdx) {
172 let sg = out.nodes.get_mut(sg_i.0).unwrap();
173 sg.split = true;
174 for seg_i in &sg.segments.clone() {
175 let res = {
176 let seg = out.segs.get(seg_i.0).unwrap();
177 match (&seg.mode, &seg.content) {
178 (SegmentMode::Split, SegmentContent::Break(_, _)) => {
179 let seg_line = seg.line.as_ref().unwrap();
180 Some((seg_line.line, seg_line.seg_index))
181 },
182 _ => None,
183 }
184 };
185 if let Some((line_i, off)) = res {
186 split_line_at(out, lines, line_i, off, None);
187 };
188 }
189}
190
191pub(crate) fn split_line_at(
192 out: &mut MakeSegsState,
193 lines: &mut Lines,
194 line_idx: LineIdx,
195 off: usize,
196 inject_start: Option<SegmentIdx>,
197) {
198 let line = lines.owned_lines.get_mut(line_idx.0).unwrap();
199 let mut new_segs = vec![];
200 if let Some(s) = inject_start {
201 new_segs.push(s);
202 }
203 new_segs.extend(line.segs.split_off(off));
204 {
205 let seg_i = new_segs.get(0).unwrap();
206 let seg = out.segs.get(seg_i.0).unwrap();
207 match &seg.content {
208 SegmentContent::Break(a, activate) => {
209 if *activate {
210 a.activate();
211 }
212 },
213 SegmentContent::Whitespace((a, _)) => {
214 a.activate();
215 },
216 _ => { },
217 };
218 }
219 let insert_at = line.index + 1;
220 insert_line(out, lines, insert_at, new_segs);
221}
222
223pub(crate) fn insert_line(out: &mut MakeSegsState, lines: &mut Lines, at: usize, segs: Vec<SegmentIdx>) {
224 let line_i = LineIdx(lines.owned_lines.len());
225 lines.owned_lines.push(Line {
226 index: at,
227 segs,
228 });
229 for (i, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
230 let seg = out.segs.get_mut(seg_i.0).unwrap();
231 match seg.line.as_mut() {
232 Some(l) => {
233 l.line = line_i;
234 l.seg_index = i;
235 },
236 None => {
237 seg.line = Some(SegmentLine {
238 line: line_i,
239 seg_index: i,
240 });
241 },
242 };
243 }
244 lines.lines.insert(at, line_i);
245 for (i, line_i) in lines.lines.iter().enumerate().skip(at + 1) {
246 lines.owned_lines.get_mut(line_i.0).unwrap().index = i;
247 }
248 for (i, line_i) in lines.lines.iter().enumerate() {
249 let line = lines.owned_lines.get(line_i.0).unwrap();
250 assert_eq!(line.index, i, "line index wrong; after insert at line {}", at);
251 for (j, seg_i) in line.segs.iter().enumerate() {
252 assert_eq!(
253 out.segs.get(seg_i.0).unwrap().line.as_ref().unwrap().seg_index,
254 j,
255 "seg index wrong; on line {}, after insert at line {}",
256 i,
257 at
258 );
259 }
260 }
261}
262
263pub(crate) struct Alignment_ {
264 pub(crate) parent: Option<Alignment>,
265 pub(crate) active: bool,
266}
267
268struct IndentLevel(usize);
269
270#[derive(Clone)]
271pub struct Alignment(Rc<RefCell<Alignment_>>);
272
273impl std::fmt::Debug for Alignment {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 "(align)".fmt(f)
276 }
277}
278
279impl Alignment {
280 pub(crate) fn indent(&self) -> Alignment {
281 Alignment(Rc::new(RefCell::new(Alignment_ {
282 parent: Some(self.clone()),
283 active: false,
284 })))
285 }
286
287 pub(crate) fn activate(&self) {
288 self.0.borrow_mut().active = true;
289 }
290
291 pub(crate) fn get(&self) -> IndentLevel {
292 let parent = match &self.0.as_ref().borrow().parent {
293 Some(p) => p.get(),
294 None => {
295 return IndentLevel(0usize);
296 },
297 };
298 if self.0.as_ref().borrow_mut().active {
299 IndentLevel(parent.0 + 1)
300 } else {
301 parent
302 }
303 }
304}
305
306pub(crate) struct SplitGroupBuilder {
307 node: SplitGroupIdx,
308 initial_split: bool,
309 reverse_children: bool,
310 segs: Vec<SegmentIdx>,
311 children: Vec<SplitGroupIdx>,
312}
313
314impl SplitGroupBuilder {
315 pub(crate) fn add(&mut self, out: &mut MakeSegsState, seg: Segment) {
316 let idx = SegmentIdx(out.segs.len());
317 out.segs.push(seg);
318 self.segs.push(idx);
319 }
320
321 pub(crate) fn initial_split(&mut self) {
322 self.initial_split = true;
323 }
324
325 pub(crate) fn reverse_children(&mut self) {
326 self.reverse_children = true;
327 }
328
329 pub(crate) fn child(&mut self, child: SplitGroupIdx) {
330 self.children.push(child);
331 }
332
333 pub(crate) fn build(self, out: &mut MakeSegsState) -> SplitGroupIdx {
334 let sg = out.nodes.get_mut(self.node.0).unwrap();
335 sg.split = self.initial_split;
336 sg.children = self.children;
337 if self.reverse_children {
338 sg.children.reverse();
339 }
340 sg.segments = self.segs;
341 for seg in &sg.segments {
342 out.segs.get_mut(seg.0).unwrap().node = self.node;
343 }
344 self.node
345 }
346
347 pub(crate) fn seg(&mut self, out: &mut MakeSegsState, text: impl ToString) {
348 self.add(out, Segment {
349 node: self.node,
350 line: None,
351 mode: SegmentMode::All,
352 content: SegmentContent::Text(text.to_string()),
353 });
354 }
355
356 pub(crate) fn seg_split(&mut self, out: &mut MakeSegsState, text: impl ToString) {
357 self.add(out, Segment {
358 node: self.node,
359 line: None,
360 mode: SegmentMode::Split,
361 content: SegmentContent::Text(text.to_string()),
362 });
363 }
364
365 pub(crate) fn seg_unsplit(&mut self, out: &mut MakeSegsState, text: impl ToString) {
366 self.add(out, Segment {
367 node: self.node,
368 line: None,
369 mode: SegmentMode::Unsplit,
370 content: SegmentContent::Text(text.to_string()),
371 });
372 }
373
374 pub(crate) fn split_if(&mut self, out: &mut MakeSegsState, alignment: Alignment, always: bool, activate: bool) {
375 self.add(out, Segment {
376 node: self.node,
377 line: None,
378 mode: if always {
379 SegmentMode::All
380 } else {
381 SegmentMode::Split
382 },
383 content: SegmentContent::Break(alignment, activate),
384 });
385 }
386
387 pub(crate) fn split(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
388 self.add(out, Segment {
389 node: self.node,
390 line: None,
391 mode: SegmentMode::Split,
392 content: SegmentContent::Break(alignment, activate),
393 });
394 }
395
396 pub(crate) fn split_always(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
397 self.add(out, Segment {
398 node: self.node,
399 line: None,
400 mode: SegmentMode::All,
401 content: SegmentContent::Break(alignment, activate),
402 });
403 }
404}
405
406pub(crate) fn new_sg(out: &mut MakeSegsState) -> SplitGroupBuilder {
407 let idx = SplitGroupIdx(out.nodes.len());
408 out.nodes.push(SplitGroup {
409 split: false,
410 segments: vec![],
411 children: vec![],
412 });
413 SplitGroupBuilder {
414 node: idx,
415 segs: vec![],
416 children: vec![],
417 initial_split: false,
418 reverse_children: false,
419 }
420}
421
422pub(crate) fn new_sg_lit(
423 out: &mut MakeSegsState,
424 start: Option<(&Alignment, Vec<LineColumn>)>,
425 text: impl ToString,
426) -> SplitGroupIdx {
427 let mut sg = new_sg(out);
428 if let Some((base_indent, starts)) = start {
429 for loc in starts {
430 append_whitespace(out, base_indent, &mut sg, loc);
431 }
432 }
433 sg.seg(out, text.to_string());
434 sg.build(out)
435}
436
437#[derive(PartialEq, Debug)]
438pub(crate) enum MarginGroup {
439 Attr,
440 BlockDef,
441 Import,
442 None,
443}
444
445pub(crate) trait FormattablePunct {
446 fn span_start(&self) -> LineColumn;
447}
448
449pub(crate) trait FormattableStmt: ToTokens + Formattable {
450 fn want_margin(&self) -> (MarginGroup, bool);
451}
452
453pub trait Formattable {
454 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx;
455 fn has_attrs(&self) -> bool;
456}
457
458impl<F: Fn(&mut MakeSegsState, &Alignment) -> SplitGroupIdx> Formattable for F {
459 fn make_segs(&self, line: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
460 self(line, base_indent)
461 }
462
463 fn has_attrs(&self) -> bool {
464 false
465 }
466}
467
468impl Formattable for Ident {
469 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
470 new_sg_lit(out, Some((base_indent, vec![self.span().start()])), self)
471 }
472
473 fn has_attrs(&self) -> bool {
474 false
475 }
476}
477
478impl Formattable for &Ident {
479 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
480 (*self).make_segs(out, base_indent)
481 }
482
483 fn has_attrs(&self) -> bool {
484 false
485 }
486}
487
488#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
489#[serde(rename_all = "snake_case")]
490pub enum IndentUnit {
491 Spaces,
492 Tabs,
493}
494
495fn render_indent(config: &FormatConfig, current_indent: IndentLevel) -> String {
496 match config.indent_unit {
497 IndentUnit::Spaces => return " ".repeat(config.indent_spaces * current_indent.0),
498 IndentUnit::Tabs => return "\t".repeat(current_indent.0),
499 }
500}
501
502#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
503#[serde(default)]
504pub struct FormatConfig {
505 pub max_width: usize,
506 pub root_splits: bool,
507 pub split_brace_threshold: Option<usize>,
508 pub split_attributes: bool,
509 pub split_where: bool,
510 pub comment_width: Option<usize>,
511 pub comment_errors_fatal: bool,
512 pub keep_max_blank_lines: usize,
513 pub indent_spaces: usize,
514 pub indent_unit: IndentUnit,
516 pub explicit_markdown_comments: bool,
517}
518
519impl Default for FormatConfig {
520 fn default() -> Self {
521 Self {
522 max_width: 120,
523 root_splits: false,
524 split_brace_threshold: Some(1usize),
525 split_attributes: true,
526 split_where: true,
527 comment_width: Some(80usize),
528 comment_errors_fatal: false,
529 keep_max_blank_lines: 0,
530 indent_spaces: 4,
531 indent_unit: IndentUnit::Spaces,
532 explicit_markdown_comments: false,
533 }
534 }
535}
536
537pub struct FormatRes {
538 pub rendered: String,
539 pub lost_comments: BTreeMap<HashLineColumn, Vec<Whitespace>>,
540 pub warnings: Vec<Error>,
541}
542
543pub use whitespace::extract_whitespaces;
544
545pub fn format_str(source: &str, config: &FormatConfig) -> Result<FormatRes, loga::Error> {
546 let shebang;
547 let shebang_line_off;
548 let source1;
549 if source.starts_with("#!/") {
550 let shebang_end = match source.find("\n") {
551 Some(o) => o + 1,
552 None => source.len(),
553 };
554 shebang = Some(&source[..shebang_end]);
555 source1 = &source[shebang_end..];
556 shebang_line_off = 1;
557 } else {
558 shebang = None;
559 source1 = source;
560 shebang_line_off = 0;
561 }
562 let source = source1;
563 let (whitespaces, tokens) = extract_whitespaces(config.keep_max_blank_lines, source)?;
564 let out =
565 format_ast(
566 syn::parse2::<File>(
567 tokens,
568 ).map_err(
569 |e| loga::err_with(
570 "Syn error parsing Rust code",
571 ea!(line = e.span().start().line + shebang_line_off, column = e.span().start().column, err = e),
572 ),
573 )?,
574 config,
575 whitespaces,
576 )?;
577 if let Some(shebang) = shebang {
578 return Ok(FormatRes {
579 rendered: format!("{}{}", shebang, out.rendered),
580 lost_comments: out.lost_comments,
581 warnings: out.warnings,
582 });
583 } else {
584 return Ok(out);
585 }
586}
587
588pub fn format_ast(
589 ast: impl Formattable,
590 config: &FormatConfig,
591 whitespaces: BTreeMap<HashLineColumn, Vec<Whitespace>>,
592) -> Result<FormatRes, loga::Error> {
593 let mut out = MakeSegsState {
595 nodes: vec![],
596 segs: vec![],
597 whitespaces,
598 config: config.clone(),
599 macro_depth: Default::default(),
600 };
601 let base_indent = Alignment(Rc::new(RefCell::new(Alignment_ {
602 parent: None,
603 active: false,
604 })));
605 let root = ast.make_segs(&mut out, &base_indent);
606 if out.whitespaces.contains_key(&HashLineColumn(LineColumn {
607 line: 0,
608 column: 1,
609 })) {
610 let mut sg = new_sg(&mut out);
611 append_whitespace(&mut out, &base_indent, &mut sg, LineColumn {
612 line: 0,
613 column: 1,
614 });
615 sg.build(&mut out);
616 }
617 let mut lines = Lines {
618 lines: vec![],
619 owned_lines: vec![],
620 };
621 {
622 let line_i = LineIdx(lines.owned_lines.len());
623 lines.owned_lines.push(Line {
624 index: 0,
625 segs: out.segs.iter().enumerate().map(|(i, _)| SegmentIdx(i)).collect(),
626 });
627 lines.lines.push(line_i);
628 for line_i in &lines.lines {
629 for (j, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
630 out.segs.get_mut(seg_i.0).unwrap().line = Some(SegmentLine {
631 line: *line_i,
632 seg_index: j,
633 });
634 }
635 }
636 }
637 for (i, line_i) in lines.lines.iter().enumerate() {
638 let line = lines.owned_lines.get(line_i.0).unwrap();
639 assert_eq!(line.index, i, "line index wrong; initial");
640 for (j, seg_i) in line.segs.iter().enumerate() {
641 assert_eq!(
642 out.segs.get(seg_i.0).unwrap().line.as_ref().unwrap().seg_index,
643 j,
644 "seg index wrong; on line {}, initial",
645 i
646 );
647 }
648 }
649
650 {
658 let synth_seg_node = new_sg(&mut out).build(&mut out);
659 let mut i = 0usize;
660 let mut skip_first = false;
661 let mut prev_comment = None;
662 while i < lines.lines.len() {
663 let mut res = None;
664 {
665 let line_i = lines.lines.get(i).unwrap();
666 'segs : loop {
667 for (i, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
668 if i == 0 && skip_first {
669 skip_first = false;
670 continue;
671 }
672 let seg = out.segs.get(seg_i.0).unwrap();
673 let node = out.nodes.get(seg.node.0).unwrap();
674 match (&seg.content, match (&seg.mode, node.split) {
675 (SegmentMode::All, true) => true,
676 (SegmentMode::All, false) => true,
677 (SegmentMode::Unsplit, true) => false,
678 (SegmentMode::Unsplit, false) => true,
679 (SegmentMode::Split, true) => true,
680 (SegmentMode::Split, false) => false,
681 }) {
682 (SegmentContent::Break(_, _), true) => {
683 res = Some((*line_i, i, None));
684 prev_comment = None;
685 break 'segs;
686 },
687 (SegmentContent::Whitespace(c), _) => {
688 res = Some((*line_i, i, None));
689 prev_comment = Some(c.0.clone());
690 break 'segs;
691 },
692 (_, _) => {
693 if let Some(a) = prev_comment.take() {
694 let seg_i = SegmentIdx(out.segs.len());
695 out.segs.push(Segment {
696 node: synth_seg_node,
697 line: None,
698 mode: SegmentMode::All,
699 content: SegmentContent::Break(a, true),
700 });
701 res = Some((*line_i, i, Some(seg_i)));
702 break 'segs;
703 }
704 },
705 };
706 }
707 prev_comment = None;
708 break;
709 }
710 }
711 if let Some((line_i, at, insert_start)) = res {
712 split_line_at(&mut out, &mut lines, line_i, at, insert_start);
713 skip_first = true;
714 }
715 i += 1;
716 }
717 }
718
719 fn recurse(out: &mut MakeSegsState, lines: &mut Lines, config: &FormatConfig, sg_i: SplitGroupIdx) -> bool {
721 let mut split = false;
722 for seg_i in &out.nodes.get(sg_i.0).unwrap().segments {
723 let seg = out.segs.get(seg_i.0).unwrap();
724 let len = line_length(out, lines, seg.line.as_ref().unwrap().line);
725 if len > config.max_width {
726 split = true;
727 break;
728 }
729 }
730 if split {
731 split_group(out, lines, sg_i);
732 }
733 let mut split_from_child = false;
734 for child_sg_i in &out.nodes.get(sg_i.0).unwrap().children.clone() {
735 let new_split_from_child = recurse(out, lines, config, *child_sg_i);
736 split_from_child = split_from_child || new_split_from_child;
737 }
738 if !split && split_from_child {
739 split_group(out, lines, sg_i);
740 }
741 config.root_splits && (split || split_from_child)
742 }
743
744 recurse(&mut out, &mut lines, config, root);
745
746 let mut rendered = String::new();
748
749 macro_rules! push{
750 ($text: expr) => {
751 rendered.push_str($text);
752 };
753 }
754
755 let mut warnings = vec![];
756 let lines = lines;
757 let mut line_i = 0usize;
758 while line_i < lines.lines.len() {
759 'continue_lineloop : loop {
760 let segs =
761 lines.owned_lines.get(lines.lines.get(line_i).unwrap().0).unwrap().segs.iter().filter_map(|seg_i| {
762 let res = {
763 let seg = out.segs.get(seg_i.0).unwrap();
764 let node = out.nodes.get(seg.node.0).unwrap();
765 match (&seg.mode, node.split) {
766 (SegmentMode::All, _) => true,
767 (SegmentMode::Unsplit, true) => false,
768 (SegmentMode::Unsplit, false) => true,
769 (SegmentMode::Split, true) => true,
770 (SegmentMode::Split, false) => false,
771 }
772 };
773 if res {
774 Some(*seg_i)
775 } else {
776 None
777 }
778 }).collect::<Vec<SegmentIdx>>();
779 if segs.is_empty() {
780 break 'continue_lineloop;
781 }
782 for (seg_i, seg_mem_i) in segs.iter().enumerate() {
783 let seg = out.segs.get(seg_mem_i.0).unwrap();
784 match &seg.content {
785 SegmentContent::Text(t) => {
786 let t = if seg_i == 1 && line_i > 0 {
787 t.trim_start()
790 } else {
791 t
792 };
793 push!(t);
794 },
795 SegmentContent::Break(b, activate) => {
796 let next_line_first_seg_comment =
797 lines
798 .lines
799 .get(line_i + 1)
800 .map(|i| lines.owned_lines.get(i.0).unwrap())
801 .and_then(|l| l.segs.first())
802 .map(|seg_i| {
803 let seg = out.segs.get(seg_i.0).unwrap();
804 matches!(&seg.content, SegmentContent::Whitespace(_))
805 })
806 .unwrap_or(false);
807
808 if segs.len() == 1 && next_line_first_seg_comment {
811 break 'continue_lineloop;
812 }
813 if *activate {
814 b.activate();
815 }
816 if segs.len() > 1 {
817 push!(&render_indent(config, b.get()));
819 }
820 },
821 SegmentContent::Whitespace((b, whitespaces)) => {
822 for (comment_i, whitespace) in whitespaces.iter().enumerate() {
823 match &whitespace.mode {
824 WhitespaceMode::BlankLines(count) => {
825 if *count > 0 {
826 for _ in 0 .. *count {
827 push!("\n");
828 }
829 continue;
830 }
831 },
832 WhitespaceMode::Comment(comment) => {
833 if comment_i > 0 {
834 push!("\n");
835 }
836 let prefix = format!(
837 "{}//{} ",
839 render_indent(config, b.get()),
840 match comment.mode {
841 CommentMode::Normal => "",
842 CommentMode::ExplicitNormal => "?",
843 CommentMode::DocInner => "!",
844 CommentMode::DocOuter => "/",
845 CommentMode::Verbatim => ".",
846 }
847 );
848 let verbatim = match comment.mode {
849 CommentMode::Verbatim => {
850 true
851 },
852 CommentMode::Normal if config.explicit_markdown_comments => {
853 true
854 },
855 _ => {
856 match format_md(
857 &mut rendered,
858 config.max_width,
859 config.comment_width,
860 &prefix,
861 &comment.lines,
862 ) {
863 Err(e) => {
864 let err =
865 loga::err_with(
866 "Error formatting comments",
867 ea!(
868 line = whitespace.loc.line,
869 column = whitespace.loc.column,
870 comments = comment.lines
871 ),
872 );
873 if config.comment_errors_fatal {
874 return Err(err);
875 } else {
876 warnings.push(e);
877 }
878 true
879 },
880 Ok(_) => {
881 false
882 },
883 }
884 },
885 };
886 if verbatim {
887 for (i, line) in comment.lines.lines().enumerate() {
888 if i > 0 {
889 push!("\n");
890 }
891 let line = line.strip_prefix(' ').unwrap_or(line);
892 push!(&format!("{}{}", prefix, line.trim_end()));
893 }
894 }
895 },
896 }
897 }
898 },
899 }
900 }
901 push!("\n");
902 break;
903 }
904 line_i += 1;
905 }
906 Ok(FormatRes {
907 rendered: rendered,
908 lost_comments: out.whitespaces,
909 warnings: warnings,
910 })
911}