1pub(crate) mod normalize_declarations;
2pub(crate) mod normalize_imports;
3pub(crate) mod sg_expr;
4pub(crate) mod sg_general;
5pub(crate) mod sg_general_lists;
6pub(crate) mod sg_pat;
7pub(crate) mod sg_root;
8pub(crate) mod sg_statement;
9pub(crate) mod sg_type;
10pub(crate) mod whitespace;
11
12pub use whitespace::{
13 HashLineColumn,
14 extract_whitespaces,
15 format_md,
16};
17use {
18 loga::{
19 Error,
20 ResultContext,
21 ea,
22 },
23 proc_macro2::{
24 Ident,
25 LineColumn,
26 },
27 quote::ToTokens,
28 serde::{
29 Deserialize,
30 Serialize,
31 },
32 sg_general::append_whitespace,
33 std::{
34 cell::{
35 Cell,
36 RefCell,
37 },
38 collections::BTreeMap,
39 fs,
40 io::Write,
41 process::{
42 Command,
43 Stdio,
44 },
45 rc::Rc,
46 },
47 syn::File,
48 tempfile::NamedTempFile,
49};
50
51pub trait Formattable {
52 fn has_attrs(&self) -> bool;
53 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx;
54
55 fn normalize_declarations(
56 &mut self,
57 _config: &FormatConfig,
58 _whitespaces: &mut BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)>,
59 ) {
60
61 }
62
63 fn normalize_imports(
64 &mut self,
65 _config: &FormatConfig,
66 _whitespaces: &mut BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)>,
67 ) {
68
69 }
70}
71
72impl Formattable for &Ident {
73 fn has_attrs(&self) -> bool {
74 false
75 }
76
77 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
78 (*self).make_segs(out, base_indent)
79 }
80}
81
82impl<F: Fn(&mut MakeSegsState, &Alignment) -> SplitGroupIdx> Formattable for F {
83 fn has_attrs(&self) -> bool {
84 false
85 }
86
87 fn make_segs(&self, line: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
88 self(line, base_indent)
89 }
90}
91
92impl Formattable for Ident {
93 fn has_attrs(&self) -> bool {
94 false
95 }
96
97 fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
98 new_sg_lit(out, Some((base_indent, vec![self.span().start()])), self)
99 }
100}
101
102pub(crate) trait FormattablePunct {
103 fn span_start(&self) -> LineColumn;
104}
105
106pub(crate) trait FormattableStmt: ToTokens + Formattable {
107 fn want_margin(&self) -> (MarginGroup, bool);
108}
109
110#[derive(Clone)]
111pub struct Alignment(Rc<RefCell<Alignment_>>);
112
113impl Alignment {
114 pub(crate) fn activate(&self) {
115 self.0.borrow_mut().active = true;
116 }
117
118 pub(crate) fn get(&self) -> IndentLevel {
119 let parent = match &self.0.as_ref().borrow().parent {
120 Some(p) => p.get(),
121 None => {
122 return IndentLevel(0usize);
123 },
124 };
125 if self.0.as_ref().borrow_mut().active {
126 IndentLevel(parent.0 + 1)
127 } else {
128 parent
129 }
130 }
131
132 pub(crate) fn indent(&self) -> Alignment {
133 Alignment(Rc::new(RefCell::new(Alignment_ {
134 parent: Some(self.clone()),
135 active: false,
136 })))
137 }
138}
139
140impl std::fmt::Debug for Alignment {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 "(align)".fmt(f)
143 }
144}
145
146pub(crate) struct Alignment_ {
147 pub(crate) active: bool,
148 pub(crate) parent: Option<Alignment>,
149}
150
151pub(crate) fn check_split_brace_threshold(out: &MakeSegsState, count: usize) -> bool {
152 out.config.split_brace_threshold.map(|t| count >= t).unwrap_or(false)
153}
154
155#[derive(Debug, Clone)]
156pub struct Comment {
157 pub lines: String,
158 pub mode: CommentMode,
159 pub orig_start_offset: usize,
162}
163
164#[derive(PartialEq, Clone, Copy, Debug)]
165pub enum CommentMode {
166 Directive,
167 DocInner,
168 DocOuter,
169 ExplicitNormal,
170 Normal,
171 Verbatim,
172}
173
174#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
175#[serde(rename_all = "snake_case")]
176pub enum DeclarationNormalizationCategory {
177 Concrete,
178 Const,
179 Enum,
180 Fn,
181 ForeignMod,
182 Macro,
183 MacroCall,
184 Mod,
185 Struct,
186 Trait,
187 TypeAlias,
188 Union,
189 Use,
190}
191
192
193#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
194#[serde(rename_all = "snake_case")]
195pub enum DeclarationNormalizationMode {
196 Auto,
197 ByCategory(Vec<DeclarationNormalizationCategory>),
198 ByName,
199 #[default]
200 None,
201}
202
203fn default_true() -> bool {
204 true
205}
206
207#[derive(Debug, Clone, Deserialize, Serialize)]
208pub struct ExternalFormatterConfig {
209 #[serde(default = "default_true")]
210 pub adjust_indent: bool,
211 pub commandline: Vec<String>,
212}
213
214pub fn format_ast(
215 mut ast: impl Formattable,
216 config: &FormatConfig,
217 whitespaces: BTreeMap<HashLineColumn, Vec<Whitespace>>,
218) -> Result<FormatRes, loga::Error> {
219 normalize_declarations::validate_declaration_normalization(config)?;
220 let mut whitespaces: BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)> =
221 whitespaces.into_iter().map(|(k, v)| (k, (1, v))).collect();
222 ast.normalize_imports(config, &mut whitespaces);
223 ast.normalize_declarations(config, &mut whitespaces);
224
225 for (_, (count, _)) in whitespaces.iter_mut() {
229 if *count == 0 {
230 *count = 1;
231 }
232 }
233
234 let mut out = MakeSegsState {
236 nodes: vec![],
237 segs: vec![],
238 whitespaces,
239 config: config.clone(),
240 macro_depth: Default::default(),
241 warnings: vec![],
242 };
243 let base_indent = Alignment(Rc::new(RefCell::new(Alignment_ {
244 parent: None,
245 active: false,
246 })));
247 let root = ast.make_segs(&mut out, &base_indent);
248 if out.whitespaces.contains_key(&HashLineColumn(LineColumn {
249 line: 0,
250 column: 1,
251 })) {
252 let mut sg = new_sg(&mut out);
253 append_whitespace(&mut out, &base_indent, &mut sg, LineColumn {
254 line: 0,
255 column: 1,
256 });
257 sg.build(&mut out);
258 }
259 let mut lines = Lines {
260 lines: vec![],
261 owned_lines: vec![],
262 };
263 {
264 let line_i = LineIdx(lines.owned_lines.len());
265 lines.owned_lines.push(Line {
266 index: 0,
267 segs: out.segs.iter().enumerate().map(|(i, _)| SegmentIdx(i)).collect(),
268 });
269 lines.lines.push(line_i);
270 for line_i in &lines.lines {
271 for (j, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
272 out.segs.get_mut(seg_i.0).unwrap().line = Some(SegmentLine {
273 line: *line_i,
274 seg_index: j,
275 });
276 }
277 }
278 }
279 for (i, line_i) in lines.lines.iter().enumerate() {
280 let line = lines.owned_lines.get(line_i.0).unwrap();
281 assert_eq!(line.index, i, "line index wrong; initial");
282 for (j, seg_i) in line.segs.iter().enumerate() {
283 assert_eq!(
284 out.segs.get(seg_i.0).unwrap().line.as_ref().unwrap().seg_index,
285 j,
286 "seg index wrong; on line {}, initial",
287 i
288 );
289 }
290 }
291
292 {
300 let synth_seg_node = new_sg(&mut out).build(&mut out);
301 let mut i = 0usize;
302 let mut skip_first = false;
303 let mut prev_comment = None;
304 while i < lines.lines.len() {
305 let mut res = None;
306 {
307 let line_i = lines.lines.get(i).unwrap();
308 'segs : loop {
309 for (i, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
310 if i == 0 && skip_first {
311 skip_first = false;
312 continue;
313 }
314 let seg = out.segs.get(seg_i.0).unwrap();
315 let node = out.nodes.get(seg.node.0).unwrap();
316 match (&seg.content, match (&seg.mode, node.split) {
317 (SegmentMode::All, true) => true,
318 (SegmentMode::All, false) => true,
319 (SegmentMode::Unsplit, true) => false,
320 (SegmentMode::Unsplit, false) => true,
321 (SegmentMode::Split, true) => true,
322 (SegmentMode::Split, false) => false,
323 }) {
324 (SegmentContent::Break(_, _), true) => {
325 res = Some((*line_i, i, None));
326 prev_comment = None;
327 break 'segs;
328 },
329 (SegmentContent::Whitespace(c), _) => {
330 res = Some((*line_i, i, None));
331 prev_comment = Some(c.0.clone());
332 break 'segs;
333 },
334 (_, _) => {
335 if let Some(a) = prev_comment.take() {
336 let seg_i = SegmentIdx(out.segs.len());
337 out.segs.push(Segment {
338 node: synth_seg_node,
339 line: None,
340 mode: SegmentMode::All,
341 content: SegmentContent::Break(a, true),
342 });
343 res = Some((*line_i, i, Some(seg_i)));
344 break 'segs;
345 }
346 },
347 };
348 }
349 prev_comment = None;
350 break;
351 }
352 }
353 if let Some((line_i, at, insert_start)) = res {
354 split_line_at(&mut out, &mut lines, line_i, at, insert_start);
355 skip_first = true;
356 }
357 i += 1;
358 }
359 }
360
361 fn recurse(out: &mut MakeSegsState, lines: &mut Lines, config: &FormatConfig, sg_i: SplitGroupIdx) -> bool {
363 let mut split = false;
364 for seg_i in &out.nodes.get(sg_i.0).unwrap().segments {
365 let seg = out.segs.get(seg_i.0).unwrap();
366 let len = line_length(out, lines, seg.line.as_ref().unwrap().line);
367 if len > config.max_width {
368 split = true;
369 break;
370 }
371 }
372 if split {
373 split_group(out, lines, sg_i);
374 }
375 let mut split_from_child = false;
376 for child_sg_i in &out.nodes.get(sg_i.0).unwrap().children.clone() {
377 let new_split_from_child = recurse(out, lines, config, *child_sg_i);
378 split_from_child = split_from_child || new_split_from_child;
379 }
380 if !split && split_from_child {
381 split_group(out, lines, sg_i);
382 }
383 config.root_splits && (split || split_from_child)
384 }
385
386 recurse(&mut out, &mut lines, config, root);
387
388 struct Rendered {
389 col: usize,
390 text: String,
391 }
392
393 impl Rendered {
394 fn push(&mut self, c: char) {
395 self.text.push(c);
396 if c == '\n' {
397 self.col = 0;
398 } else {
399 self.col += 1;
400 }
401 }
402
403 fn push_str(&mut self, s: &str) {
404 self.text.push_str(s);
405 if let Some(nl) = s.rfind('\n') {
406 self.col = s[nl + 1..].chars().count();
407 } else {
408 self.col += s.chars().count();
409 }
410 }
411 }
412
413 let mut rendered = Rendered {
415 text: String::new(),
416 col: 0,
417 };
418 let mut warnings = vec![];
419 let lines = lines;
420 let mut line_i = 0usize;
421 while line_i < lines.lines.len() {
422 'continue_lineloop : loop {
423 let segs =
424 lines.owned_lines.get(lines.lines.get(line_i).unwrap().0).unwrap().segs.iter().filter_map(|seg_i| {
425 let res = {
426 let seg = out.segs.get(seg_i.0).unwrap();
427 let node = out.nodes.get(seg.node.0).unwrap();
428 match (&seg.mode, node.split) {
429 (SegmentMode::All, _) => true,
430 (SegmentMode::Unsplit, true) => false,
431 (SegmentMode::Unsplit, false) => true,
432 (SegmentMode::Split, true) => true,
433 (SegmentMode::Split, false) => false,
434 }
435 };
436 if res {
437 Some(*seg_i)
438 } else {
439 None
440 }
441 }).collect::<Vec<SegmentIdx>>();
442 if segs.is_empty() {
443 break 'continue_lineloop;
444 }
445 for (seg_i, seg_mem_i) in segs.iter().enumerate() {
446 let seg = out.segs.get(seg_mem_i.0).unwrap();
447 match &seg.content {
448 SegmentContent::Text(t) => {
449 let t = if seg_i == 1 && line_i > 0 {
450 t.trim_start()
453 } else {
454 t
455 };
456 rendered.push_str(t);
457 },
458 SegmentContent::RawTextAdjustIndent(t) => {
459 let prefix_len = t.find('"').map(|i| i + 1).unwrap_or(0);
462 let indent_col = rendered.col + prefix_len;
463 let mut text_lines = t.split('\n');
464 if let Some(first_line) = text_lines.next() {
465 rendered.push_str(first_line);
466 }
467 for line in text_lines {
468 rendered.push('\n');
469 let indent_str = " ".repeat(indent_col);
470 rendered.push_str(&indent_str);
471 rendered.push_str(line);
472 }
473 },
474 SegmentContent::Break(b, activate) => {
475 let next_line_first_seg_comment =
476 lines
477 .lines
478 .get(line_i + 1)
479 .map(|i| lines.owned_lines.get(i.0).unwrap())
480 .and_then(|l| l.segs.first())
481 .map(|seg_i| {
482 let seg = out.segs.get(seg_i.0).unwrap();
483 matches!(&seg.content, SegmentContent::Whitespace(_))
484 })
485 .unwrap_or(false);
486
487 if segs.len() == 1 && next_line_first_seg_comment {
490 break 'continue_lineloop;
491 }
492 if *activate {
493 b.activate();
494 }
495 if segs.len() > 1 {
496 rendered.push_str(&render_indent(config, b.get()));
498 }
499 },
500 SegmentContent::Whitespace((b, whitespaces)) => {
501 for (comment_i, whitespace) in whitespaces.iter().enumerate() {
502 match &whitespace.mode {
503 WhitespaceMode::BlankLines(count) => {
504 if *count > 0 {
505 for _ in 0 .. *count {
506 rendered.push('\n');
507 }
508 continue;
509 }
510 },
511 WhitespaceMode::Comment(comment) => {
512 if comment_i > 0 {
513 rendered.push('\n');
514 }
515 let prefix = format!(
516 "{}//{} ",
518 render_indent(config, b.get()),
519 match comment.mode {
520 CommentMode::Normal => "",
521 CommentMode::ExplicitNormal => "?",
522 CommentMode::DocInner => "!",
523 CommentMode::DocOuter => "/",
524 CommentMode::Verbatim => ".",
525 CommentMode::Directive => "#",
526 }
527 );
528 let verbatim;
529 match comment.mode {
530 CommentMode::Directive => {
531 for (i, line) in comment.lines.lines().enumerate() {
532 if i > 0 {
533 rendered.push('\n');
534 }
535 if let Some((k, v)) = line.split_once(":") {
536 rendered.push_str(
537 &format!("{}{}: {}", prefix, k.trim(), v.trim()),
538 );
539 } else {
540 rendered.push_str(&format!("{}{}", prefix, line.trim()));
541 }
542 }
543 continue;
544 },
545 CommentMode::Verbatim => {
546 verbatim = true;
547 },
548 CommentMode::Normal if config.explicit_markdown_comments => {
549 verbatim = true;
550 },
551 _ => {
552 match format_md(&mut rendered.text, config, &prefix, &comment.lines) {
553 Err(e) => {
554 let err =
555 loga::err_with(
556 "Error formatting comments",
557 ea!(
558 line = whitespace.loc.line,
559 column = whitespace.loc.column,
560 comments = comment.lines
561 ),
562 );
563 if config.comment_errors_fatal {
564 return Err(err);
565 } else {
566 warnings.push(e);
567 }
568 verbatim = true;
569 },
570 Ok(_) => {
571 let text = &rendered.text;
572 rendered.col = if let Some(nl_pos) = text.rfind('\n') {
573 text[nl_pos + 1..].chars().count()
574 } else {
575 text.chars().count()
576 };
577 verbatim = false;
578 },
579 }
580 },
581 };
582 if verbatim {
583 for (i, line) in comment.lines.lines().enumerate() {
584 if i > 0 {
585 rendered.push('\n');
586 }
587 let line = line.strip_prefix(' ').unwrap_or(line);
588 rendered.push_str(&format!("{}{}", prefix, line.trim_end()));
589 }
590 }
591 },
592 }
593 }
594 },
595 }
596 }
597 rendered.push('\n');
598 break;
599 }
600 line_i += 1;
601 }
602 let mk_warnings = std::mem::take(&mut out.warnings);
603 Ok(FormatRes {
604 rendered: rendered.text,
605 lost_comments: out.whitespaces.into_iter().map(|(k, (_, v))| (k, v)).collect(),
606 warnings: {
607 let mut all_warnings = mk_warnings;
608 all_warnings.extend(warnings);
609 all_warnings
610 },
611 })
612}
613
614pub fn format_str(source: &str, config: &FormatConfig) -> Result<FormatRes, loga::Error> {
615 let shebang;
616 let shebang_line_off;
617 let source1;
618 if source.starts_with("#!/") {
619 let shebang_end = match source.find("\n") {
620 Some(o) => o + 1,
621 None => source.len(),
622 };
623 shebang = Some(&source[..shebang_end]);
624 source1 = &source[shebang_end..];
625 shebang_line_off = 1;
626 } else {
627 shebang = None;
628 source1 = source;
629 shebang_line_off = 0;
630 }
631 let stripped_source = source1;
632 let (whitespaces, tokens) = extract_whitespaces(config.keep_max_blank_lines, stripped_source)?;
633 for ws_list in whitespaces.values() {
634 for ws in ws_list {
635 if let WhitespaceMode::Comment(comment) = &ws.mode {
636 if comment.mode == CommentMode::Directive {
637 for line in comment.lines.lines() {
638 if line.trim() == "genemichaels-file-skip" {
639 return Ok(FormatRes {
640 rendered: source.to_string(),
641 lost_comments: BTreeMap::new(),
642 warnings: vec![],
643 });
644 }
645 }
646 }
647 }
648 }
649 }
650 let out =
651 format_ast(
652 syn::parse2::<File>(
653 tokens,
654 ).map_err(
655 |e| loga::err_with(
656 "Syn error parsing Rust code",
657 ea!(line = e.span().start().line + shebang_line_off, column = e.span().start().column, err = e),
658 ),
659 )?,
660 config,
661 whitespaces,
662 )?;
663 if let Some(shebang) = shebang {
664 return Ok(FormatRes {
665 rendered: format!("{}{}", shebang, out.rendered),
666 lost_comments: out.lost_comments,
667 warnings: out.warnings,
668 });
669 } else {
670 return Ok(out);
671 }
672}
673
674#[derive(Debug, Clone, Deserialize, Serialize)]
675#[serde(default)]
676pub struct FormatConfig {
677 pub comment_errors_fatal: bool,
678 pub comment_width: Option<usize>,
679 pub declaration_normalization: DeclarationNormalizationMode,
680 pub explicit_markdown_comments: bool,
681 pub external_formatters: BTreeMap<String, ExternalFormatterConfig>,
682 pub import_normalization: ImportNormalizationMode,
683 pub indent_spaces: usize,
684 pub indent_unit: IndentUnit,
686 pub keep_max_blank_lines: usize,
687 pub max_width: usize,
688 pub root_splits: bool,
689 pub split_attributes: bool,
690 pub split_brace_threshold: Option<usize>,
691 pub split_where: bool,
692}
693
694impl Default for FormatConfig {
695 fn default() -> Self {
696 Self {
697 max_width: 120,
698 root_splits: false,
699 split_brace_threshold: Some(1usize),
700 split_attributes: true,
701 split_where: true,
702 comment_width: Some(80usize),
703 comment_errors_fatal: false,
704 keep_max_blank_lines: 0,
705 indent_spaces: 4,
706 indent_unit: IndentUnit::Spaces,
707 explicit_markdown_comments: false,
708 import_normalization: ImportNormalizationMode::None,
709 declaration_normalization: DeclarationNormalizationMode::None,
710 external_formatters: BTreeMap::new(),
711 }
712 }
713}
714
715pub struct FormatRes {
716 pub lost_comments: BTreeMap<HashLineColumn, Vec<Whitespace>>,
717 pub rendered: String,
718 pub warnings: Vec<Error>,
719}
720
721#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Default)]
722#[serde(rename_all = "snake_case")]
723pub enum ImportNormalizationMode {
724 Combine,
725 #[default]
726 None,
727 Split,
728}
729
730pub struct IncMacroDepth(Rc<Cell<usize>>);
731
732impl IncMacroDepth {
733 fn new(s: &MakeSegsState) -> Self {
734 s.macro_depth.update(|x| x + 1);
735 return Self(s.macro_depth.clone());
736 }
737}
738
739impl Drop for IncMacroDepth {
740 fn drop(&mut self) {
741 self.0.update(|x| x - 1);
742 }
743}
744
745struct IndentLevel(usize);
746
747#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
748#[serde(rename_all = "snake_case")]
749pub enum IndentUnit {
750 Spaces,
751 Tabs,
752}
753
754pub(crate) fn insert_line(out: &mut MakeSegsState, lines: &mut Lines, at: usize, segs: Vec<SegmentIdx>) {
755 let line_i = LineIdx(lines.owned_lines.len());
756 lines.owned_lines.push(Line {
757 index: at,
758 segs,
759 });
760 for (i, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
761 let seg = out.segs.get_mut(seg_i.0).unwrap();
762 match seg.line.as_mut() {
763 Some(l) => {
764 l.line = line_i;
765 l.seg_index = i;
766 },
767 None => {
768 seg.line = Some(SegmentLine {
769 line: line_i,
770 seg_index: i,
771 });
772 },
773 };
774 }
775 lines.lines.insert(at, line_i);
776 for (i, line_i) in lines.lines.iter().enumerate().skip(at + 1) {
777 lines.owned_lines.get_mut(line_i.0).unwrap().index = i;
778 }
779 for (i, line_i) in lines.lines.iter().enumerate() {
780 let line = lines.owned_lines.get(line_i.0).unwrap();
781 assert_eq!(line.index, i, "line index wrong; after insert at line {}", at);
782 for (j, seg_i) in line.segs.iter().enumerate() {
783 assert_eq!(
784 out.segs.get(seg_i.0).unwrap().line.as_ref().unwrap().seg_index,
785 j,
786 "seg index wrong; on line {}, after insert at line {}",
787 i,
788 at
789 );
790 }
791 }
792}
793
794pub(crate) struct Line {
795 index: usize,
796 segs: Vec<SegmentIdx>,
797}
798
799pub(crate) fn line_length(out: &MakeSegsState, lines: &Lines, line_i: LineIdx) -> usize {
800 let mut max_len = 0;
801 let mut len = 0;
802 for seg_i in &lines.owned_lines.get(line_i.0).unwrap().segs {
803 let seg = out.segs.get(seg_i.0).unwrap();
804 match &seg.content {
805 SegmentContent::Text(t) => {
806 let mut first = true;
807 for line in t.split('\n') {
808 if !first {
809 max_len = max_len.max(len);
810 len = line.chars().count();
811 } else {
812 len += line.chars().count();
813 first = false;
814 }
815 }
816 },
817 SegmentContent::Break(b, _) => {
818 if out.nodes.get(seg.node.0).unwrap().split {
819 len += out.config.indent_spaces * b.get().0;
820 }
821 },
822 SegmentContent::Whitespace(_) => { },
823 SegmentContent::RawTextAdjustIndent(t) => {
824 let prefix_len = t.find('"').map(|i| i + 1).unwrap_or(0);
825 let start_len = len;
826 let mut first = true;
827 let mut local_max = 0;
828 for line in t.split('\n') {
829 if !first {
830 len = start_len + prefix_len + line.chars().count();
831 local_max = local_max.max(len);
832 } else {
833 len += line.chars().count();
834 local_max = local_max.max(len);
835 first = false;
836 }
837 }
838 len = local_max;
839 },
840 };
841 }
842 max_len.max(len)
843}
844
845#[derive(Clone, Copy)]
846pub(crate) struct LineIdx(usize);
847
848struct Lines {
849 lines: Vec<LineIdx>,
850 owned_lines: Vec<Line>,
851}
852
853pub struct MakeSegsState {
854 config: FormatConfig,
855 macro_depth: Rc<Cell<usize>>,
860 nodes: Vec<SplitGroup>,
861 segs: Vec<Segment>,
862 pub(crate) warnings: Vec<Error>,
863 whitespaces: BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)>,
864}
865
866#[derive(PartialEq, Debug)]
867pub(crate) enum MarginGroup {
868 Attr,
869 BlockDef,
870 Import,
871 None,
872}
873
874pub(crate) fn new_sg(out: &mut MakeSegsState) -> SplitGroupBuilder {
875 let idx = SplitGroupIdx(out.nodes.len());
876 out.nodes.push(SplitGroup {
877 split: false,
878 segments: vec![],
879 children: vec![],
880 });
881 SplitGroupBuilder {
882 node: idx,
883 segs: vec![],
884 children: vec![],
885 initial_split: false,
886 reverse_children: false,
887 }
888}
889
890pub(crate) fn new_sg_lit(
891 out: &mut MakeSegsState,
892 start: Option<(&Alignment, Vec<LineColumn>)>,
893 text: impl ToString,
894) -> SplitGroupIdx {
895 let mut sg = new_sg(out);
896 if let Some((base_indent, starts)) = start {
897 for loc in starts {
898 append_whitespace(out, base_indent, &mut sg, loc);
899 }
900 }
901 sg.seg(out, text.to_string());
902 sg.build(out)
903}
904
905fn render_indent(config: &FormatConfig, current_indent: IndentLevel) -> String {
906 match config.indent_unit {
907 IndentUnit::Spaces => return " ".repeat(config.indent_spaces * current_indent.0),
908 IndentUnit::Tabs => return "\t".repeat(current_indent.0),
909 }
910}
911
912pub(crate) fn run_external_formatter(command: &[String], content: &str) -> Result<String, loga::Error> {
913 let mut tmp_file: Option<NamedTempFile> = None;
914 let args = command[1..].iter().map(|a| -> Result<String, loga::Error> {
915 if a == "{}" {
916 let f = match tmp_file {
917 Some(ref f) => f,
918 None => {
919 let mut f = NamedTempFile::new().context("Failed to create external formatter temp file")?;
920 f.write_all(content.as_bytes()).context("Failed to write external formatter temp file")?;
921 f.flush().context("Failed to flush external formatter temp file")?;
922 tmp_file.insert(f)
923 },
924 };
925 return Ok(f.path().to_string_lossy().to_string());
926 } else {
927 return Ok(a.clone());
928 }
929 }).collect::<Result<Vec<_>, _>>()?;
930 if let Some(ref tmp_file) = tmp_file {
931 let status =
932 Command::new(&command[0])
933 .args(&args)
934 .status()
935 .context_with("Failed to run external formatter", ea!(cmd = command[0].as_str()))?;
936 if !status.success() {
937 return Err(loga::err_with("External formatter exited with error", ea!(cmd = &command[0])));
938 }
939 return fs::read_to_string(tmp_file.path()).context("Failed to read external formatter output");
940 } else {
941 let mut child =
942 Command::new(&command[0])
943 .args(&args)
944 .stdin(Stdio::piped())
945 .stdout(Stdio::piped())
946 .spawn()
947 .context_with("Failed to spawn external formatter", ea!(cmd = command[0].as_str()))?;
948 child
949 .stdin
950 .take()
951 .unwrap()
952 .write_all(content.as_bytes())
953 .context("Failed to write to external formatter stdin")?;
954 let output = child.wait_with_output().context("Failed to get external formatter output")?;
955 if !output.status.success() {
956 return Err(loga::err_with("External formatter exited with error", ea!(cmd = &command[0])));
957 }
958 return String::from_utf8(output.stdout)
959 .map_err(loga::err)
960 .context("External formatter output is not valid UTF-8");
961 }
962}
963
964pub(crate) struct Segment {
965 pub(crate) content: SegmentContent,
966 pub(crate) line: Option<SegmentLine>,
967 pub(crate) mode: SegmentMode,
968 pub(crate) node: SplitGroupIdx,
969}
970
971#[derive(Debug)]
972pub(crate) enum SegmentContent {
973 Break(Alignment, bool),
974 RawTextAdjustIndent(String),
975 Text(String),
976 Whitespace((Alignment, Vec<Whitespace>)),
977}
978
979#[derive(Clone, Copy)]
980pub struct SegmentIdx(usize);
981
982pub(crate) struct SegmentLine {
983 pub(crate) line: LineIdx,
984 pub(crate) seg_index: usize,
985}
986
987#[derive(Debug, Clone, Copy)]
988pub(crate) enum SegmentMode {
989 All,
990 Split,
991 Unsplit,
992}
993
994pub(crate) fn split_group(out: &mut MakeSegsState, lines: &mut Lines, sg_i: SplitGroupIdx) {
995 let sg = out.nodes.get_mut(sg_i.0).unwrap();
996 sg.split = true;
997 for seg_i in &sg.segments.clone() {
998 let res = {
999 let seg = out.segs.get(seg_i.0).unwrap();
1000 match (&seg.mode, &seg.content) {
1001 (SegmentMode::Split, SegmentContent::Break(_, _)) => {
1002 let seg_line = seg.line.as_ref().unwrap();
1003 Some((seg_line.line, seg_line.seg_index))
1004 },
1005 _ => None,
1006 }
1007 };
1008 if let Some((line_i, off)) = res {
1009 split_line_at(out, lines, line_i, off, None);
1010 };
1011 }
1012}
1013
1014pub(crate) fn split_line_at(
1015 out: &mut MakeSegsState,
1016 lines: &mut Lines,
1017 line_idx: LineIdx,
1018 off: usize,
1019 inject_start: Option<SegmentIdx>,
1020) {
1021 let line = lines.owned_lines.get_mut(line_idx.0).unwrap();
1022 let mut new_segs = vec![];
1023 if let Some(s) = inject_start {
1024 new_segs.push(s);
1025 }
1026 new_segs.extend(line.segs.split_off(off));
1027 {
1028 let seg_i = new_segs.get(0).unwrap();
1029 let seg = out.segs.get(seg_i.0).unwrap();
1030 match &seg.content {
1031 SegmentContent::Break(a, activate) => {
1032 if *activate {
1033 a.activate();
1034 }
1035 },
1036 SegmentContent::Whitespace((a, _)) => {
1037 a.activate();
1038 },
1039 _ => { },
1040 };
1041 }
1042 let insert_at = line.index + 1;
1043 insert_line(out, lines, insert_at, new_segs);
1044}
1045
1046pub struct SplitGroup {
1047 pub(crate) children: Vec<SplitGroupIdx>,
1048 pub(crate) segments: Vec<SegmentIdx>,
1049 pub(crate) split: bool,
1050}
1051
1052pub(crate) struct SplitGroupBuilder {
1053 children: Vec<SplitGroupIdx>,
1054 initial_split: bool,
1055 pub(crate) node: SplitGroupIdx,
1056 reverse_children: bool,
1057 segs: Vec<SegmentIdx>,
1058}
1059
1060impl SplitGroupBuilder {
1061 pub(crate) fn add(&mut self, out: &mut MakeSegsState, seg: Segment) {
1062 let idx = SegmentIdx(out.segs.len());
1063 out.segs.push(seg);
1064 self.segs.push(idx);
1065 }
1066
1067 pub(crate) fn build(self, out: &mut MakeSegsState) -> SplitGroupIdx {
1068 let sg = out.nodes.get_mut(self.node.0).unwrap();
1069 sg.split = self.initial_split;
1070 sg.children = self.children;
1071 if self.reverse_children {
1072 sg.children.reverse();
1073 }
1074 sg.segments = self.segs;
1075 for seg in &sg.segments {
1076 out.segs.get_mut(seg.0).unwrap().node = self.node;
1077 }
1078 self.node
1079 }
1080
1081 pub(crate) fn child(&mut self, child: SplitGroupIdx) {
1082 self.children.push(child);
1083 }
1084
1085 pub(crate) fn initial_split(&mut self) {
1086 self.initial_split = true;
1087 }
1088
1089 pub(crate) fn reverse_children(&mut self) {
1090 self.reverse_children = true;
1091 }
1092
1093 pub(crate) fn seg(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1094 self.add(out, Segment {
1095 node: self.node,
1096 line: None,
1097 mode: SegmentMode::All,
1098 content: SegmentContent::Text(text.to_string()),
1099 });
1100 }
1101
1102 pub(crate) fn seg_split(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1103 self.add(out, Segment {
1104 node: self.node,
1105 line: None,
1106 mode: SegmentMode::Split,
1107 content: SegmentContent::Text(text.to_string()),
1108 });
1109 }
1110
1111 pub(crate) fn seg_unsplit(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1112 self.add(out, Segment {
1113 node: self.node,
1114 line: None,
1115 mode: SegmentMode::Unsplit,
1116 content: SegmentContent::Text(text.to_string()),
1117 });
1118 }
1119
1120 pub(crate) fn split(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
1121 self.add(out, Segment {
1122 node: self.node,
1123 line: None,
1124 mode: SegmentMode::Split,
1125 content: SegmentContent::Break(alignment, activate),
1126 });
1127 }
1128
1129 pub(crate) fn split_always(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
1130 self.add(out, Segment {
1131 node: self.node,
1132 line: None,
1133 mode: SegmentMode::All,
1134 content: SegmentContent::Break(alignment, activate),
1135 });
1136 }
1137
1138 pub(crate) fn split_if(&mut self, out: &mut MakeSegsState, alignment: Alignment, always: bool, activate: bool) {
1139 self.add(out, Segment {
1140 node: self.node,
1141 line: None,
1142 mode: if always {
1143 SegmentMode::All
1144 } else {
1145 SegmentMode::Split
1146 },
1147 content: SegmentContent::Break(alignment, activate),
1148 });
1149 }
1150}
1151
1152#[derive(Clone, Copy)]
1153pub struct SplitGroupIdx(usize);
1154
1155#[derive(Debug, Clone)]
1156pub struct Whitespace {
1157 pub loc: LineColumn,
1160 pub mode: WhitespaceMode,
1161}
1162
1163#[derive(Debug, Clone)]
1164pub enum WhitespaceMode {
1165 BlankLines(usize),
1166 Comment(Comment),
1167}