Skip to main content

genemichaels_lib/
lib.rs

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    /// The offset in the original source where the comment started (location of // or
54    /// /* that initiated this specific comment mode)
55    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    // The loc of the AST node this whitespace is associated with. Special loc (0, 1)
67    // == end of file.
68    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    // MakeSegsState should be cloned at each call level with flags overwritable, but
129    // it's a huge amount of work and only applies to this field for now. So instead
130    // manage externally (for now).
131    /// Positive if processing within a macro, used to control macro tweaks.
132    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    /// Indent with spaces or tabs.
515    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    // Build text
594    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    // Do initial splits
651    //
652    // * initially split nodes
653    //
654    // * always split break segments
655    //
656    // * comments segments
657    {
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    // Do width based splitting, other splitting
720    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    // Render
747    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                            // Work around comments splitting lines at weird places (seg_i_i 0 == break,
788                            // except on first line)
789                            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                        // since comments are always new lines we end up with duped newlines sometimes if
809                        // there's a (break), (comment) on consec lines. skip the break
810                        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                            // if empty line (=just break), don't write indent
818                            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                                        //. .
838                                        "{}//{} ",
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}