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#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
193#[serde(rename_all = "snake_case")]
194pub enum DeclarationNormalizationMode {
195 Auto,
196 ByCategory(Vec<DeclarationNormalizationCategory>),
197 ByName,
198 #[default]
199 None,
200}
201
202fn default_true() -> bool {
203 true
204}
205
206#[derive(Debug, Clone, Deserialize, Serialize)]
207pub struct ExternalFormatterConfig {
208 #[serde(default = "default_true")]
209 pub adjust_indent: bool,
210 pub commandline: Vec<String>,
211}
212
213pub fn format_ast(
214 mut ast: impl Formattable,
215 config: &FormatConfig,
216 whitespaces: BTreeMap<HashLineColumn, Vec<Whitespace>>,
217) -> Result<FormatRes, loga::Error> {
218 normalize_declarations::validate_declaration_normalization(config)?;
219 let mut whitespaces: BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)> =
220 whitespaces.into_iter().map(|(k, v)| (k, (1, v))).collect();
221 ast.normalize_imports(config, &mut whitespaces);
222 ast.normalize_declarations(config, &mut whitespaces);
223
224 for (_, (count, _)) in whitespaces.iter_mut() {
228 if *count == 0 {
229 *count = 1;
230 }
231 }
232
233 let mut out = MakeSegsState {
235 nodes: vec![],
236 segs: vec![],
237 whitespaces,
238 config: config.clone(),
239 macro_depth: Default::default(),
240 warnings: vec![],
241 };
242 let base_indent = Alignment(Rc::new(RefCell::new(Alignment_ {
243 parent: None,
244 active: false,
245 })));
246 let root = ast.make_segs(&mut out, &base_indent);
247 if out.whitespaces.contains_key(&HashLineColumn(LineColumn {
248 line: 0,
249 column: 1,
250 })) {
251 let mut sg = new_sg(&mut out);
252 append_whitespace(&mut out, &base_indent, &mut sg, LineColumn {
253 line: 0,
254 column: 1,
255 });
256 sg.build(&mut out);
257 }
258 let mut lines = Lines {
259 head: None,
260 owned_lines: vec![],
261 };
262 {
263 let line_i = LineIdx(lines.owned_lines.len());
264 lines.owned_lines.push(Line {
265 next: None,
266 segs: out.segs.iter().enumerate().map(|(i, _)| SegmentIdx(i)).collect(),
267 });
268 lines.head = Some(line_i);
269 for (j, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
270 out.segs.get_mut(seg_i.0).unwrap().line = Some(SegmentLine {
271 line: line_i,
272 seg_index: j,
273 });
274 }
275 }
276
277 {
285 let synth_seg_node = new_sg(&mut out).build(&mut out);
286 let mut cur = lines.head;
287 let mut skip_first = false;
288 let mut prev_comment = None;
289 while let Some(line_i) = cur {
290 let mut res = None;
291 {
292 'segs : loop {
293 for (i, seg_i) in lines.owned_lines.get(line_i.0).unwrap().segs.iter().enumerate() {
294 if i == 0 && skip_first {
295 skip_first = false;
296 continue;
297 }
298 let seg = out.segs.get(seg_i.0).unwrap();
299 let node = out.nodes.get(seg.node.0).unwrap();
300 match (&seg.content, match (&seg.mode, node.split) {
301 (SegmentMode::All, true) => true,
302 (SegmentMode::All, false) => true,
303 (SegmentMode::Unsplit, true) => false,
304 (SegmentMode::Unsplit, false) => true,
305 (SegmentMode::Split, true) => true,
306 (SegmentMode::Split, false) => false,
307 }) {
308 (SegmentContent::Break(_, _), true) => {
309 res = Some((line_i, i, None));
310 prev_comment = None;
311 break 'segs;
312 },
313 (SegmentContent::Whitespace(c), _) => {
314 res = Some((line_i, i, None));
315 prev_comment = Some(c.0.clone());
316 break 'segs;
317 },
318 (_, _) => {
319 if let Some(a) = prev_comment.take() {
320 let seg_i = SegmentIdx(out.segs.len());
321 out.segs.push(Segment {
322 node: synth_seg_node,
323 line: None,
324 mode: SegmentMode::All,
325 content: SegmentContent::Break(a, true),
326 });
327 res = Some((line_i, i, Some(seg_i)));
328 break 'segs;
329 }
330 },
331 };
332 }
333 prev_comment = None;
334 break;
335 }
336 }
337 if let Some((line_i, at, insert_start)) = res {
338 split_line_at(&mut out, &mut lines, line_i, at, insert_start);
339 skip_first = true;
340 }
341 cur = lines.owned_lines.get(line_i.0).unwrap().next;
342 }
343 }
344
345 fn recurse(out: &mut MakeSegsState, lines: &mut Lines, config: &FormatConfig, sg_i: SplitGroupIdx) -> bool {
347 let mut split = false;
348 for seg_i in &out.nodes.get(sg_i.0).unwrap().segments {
349 let seg = out.segs.get(seg_i.0).unwrap();
350 let len = line_length(out, lines, seg.line.as_ref().unwrap().line);
351 if len > config.max_width {
352 split = true;
353 break;
354 }
355 }
356 if split {
357 split_group(out, lines, sg_i);
358 }
359 let mut split_from_child = false;
360 for child_sg_i in &out.nodes.get(sg_i.0).unwrap().children.clone() {
361 let new_split_from_child = recurse(out, lines, config, *child_sg_i);
362 split_from_child = split_from_child || new_split_from_child;
363 }
364 if !split && split_from_child {
365 split_group(out, lines, sg_i);
366 }
367 config.root_splits && (split || split_from_child)
368 }
369
370 recurse(&mut out, &mut lines, config, root);
371
372 struct Rendered {
373 col: usize,
374 text: String,
375 }
376
377 impl Rendered {
378 fn push(&mut self, c: char) {
379 self.text.push(c);
380 if c == '\n' {
381 self.col = 0;
382 } else {
383 self.col += 1;
384 }
385 }
386
387 fn push_str(&mut self, s: &str) {
388 self.text.push_str(s);
389 if let Some(nl) = s.rfind('\n') {
390 self.col = s[nl + 1..].chars().count();
391 } else {
392 self.col += s.chars().count();
393 }
394 }
395 }
396
397 let mut rendered = Rendered {
399 text: String::new(),
400 col: 0,
401 };
402 let mut warnings = vec![];
403 let lines = lines;
404 let mut cur = lines.head;
405 let mut line_pos = 0usize;
406 while let Some(line_i) = cur {
407 'continue_lineloop : loop {
408 let segs = lines.owned_lines.get(line_i.0).unwrap().segs.iter().filter_map(|seg_i| {
409 let res = {
410 let seg = out.segs.get(seg_i.0).unwrap();
411 let node = out.nodes.get(seg.node.0).unwrap();
412 match (&seg.mode, node.split) {
413 (SegmentMode::All, _) => true,
414 (SegmentMode::Unsplit, true) => false,
415 (SegmentMode::Unsplit, false) => true,
416 (SegmentMode::Split, true) => true,
417 (SegmentMode::Split, false) => false,
418 }
419 };
420 if res {
421 Some(*seg_i)
422 } else {
423 None
424 }
425 }).collect::<Vec<SegmentIdx>>();
426 if segs.is_empty() {
427 break 'continue_lineloop;
428 }
429 for (seg_i, seg_mem_i) in segs.iter().enumerate() {
430 let seg = out.segs.get(seg_mem_i.0).unwrap();
431 match &seg.content {
432 SegmentContent::Text(t) => {
433 let t = if seg_i == 1 && line_pos > 0 {
434 t.trim_start()
437 } else {
438 t
439 };
440 rendered.push_str(t);
441 },
442 SegmentContent::RawTextAdjustIndent(t) => {
443 let prefix_len = t.find('"').map(|i| i + 1).unwrap_or(0);
446 let indent_col = rendered.col + prefix_len;
447 let mut text_lines = t.split('\n');
448 if let Some(first_line) = text_lines.next() {
449 rendered.push_str(first_line);
450 }
451 for line in text_lines {
452 rendered.push('\n');
453 let indent_str = " ".repeat(indent_col);
454 rendered.push_str(&indent_str);
455 rendered.push_str(line);
456 }
457 },
458 SegmentContent::Break(b, activate) => {
459 let next_line_first_seg_comment =
460 lines
461 .owned_lines
462 .get(line_i.0)
463 .unwrap()
464 .next
465 .map(|i| lines.owned_lines.get(i.0).unwrap())
466 .and_then(|l| l.segs.first())
467 .map(|seg_i| {
468 let seg = out.segs.get(seg_i.0).unwrap();
469 matches!(&seg.content, SegmentContent::Whitespace(_))
470 })
471 .unwrap_or(false);
472
473 if segs.len() == 1 && next_line_first_seg_comment {
476 break 'continue_lineloop;
477 }
478 if *activate {
479 b.activate();
480 }
481 if segs.len() > 1 {
482 rendered.push_str(&render_indent(config, b.get()));
484 }
485 },
486 SegmentContent::Whitespace((b, whitespaces)) => {
487 for (comment_i, whitespace) in whitespaces.iter().enumerate() {
488 match &whitespace.mode {
489 WhitespaceMode::BlankLines(count) => {
490 if *count > 0 {
491 for _ in 0 .. *count {
492 rendered.push('\n');
493 }
494 continue;
495 }
496 },
497 WhitespaceMode::Comment(comment) => {
498 if comment_i > 0 {
499 rendered.push('\n');
500 }
501 let prefix = format!(
502 "{}//{} ",
504 render_indent(config, b.get()),
505 match comment.mode {
506 CommentMode::Normal => "",
507 CommentMode::ExplicitNormal => "?",
508 CommentMode::DocInner => "!",
509 CommentMode::DocOuter => "/",
510 CommentMode::Verbatim => ".",
511 CommentMode::Directive => "#",
512 }
513 );
514 let verbatim;
515 match comment.mode {
516 CommentMode::Directive => {
517 for (i, line) in comment.lines.lines().enumerate() {
518 if i > 0 {
519 rendered.push('\n');
520 }
521 if let Some((k, v)) = line.split_once(":") {
522 rendered.push_str(
523 &format!("{}{}: {}", prefix, k.trim(), v.trim()),
524 );
525 } else {
526 rendered.push_str(&format!("{}{}", prefix, line.trim()));
527 }
528 }
529 continue;
530 },
531 CommentMode::Verbatim => {
532 verbatim = true;
533 },
534 CommentMode::Normal if config.explicit_markdown_comments => {
535 verbatim = true;
536 },
537 _ => {
538 match format_md(&mut rendered.text, config, &prefix, &comment.lines) {
539 Err(e) => {
540 let err =
541 loga::err_with(
542 "Error formatting comments",
543 ea!(
544 line = whitespace.loc.line,
545 column = whitespace.loc.column,
546 comments = comment.lines
547 ),
548 );
549 if config.comment_errors_fatal {
550 return Err(err);
551 } else {
552 warnings.push(e);
553 }
554 verbatim = true;
555 },
556 Ok(_) => {
557 let text = &rendered.text;
558 rendered.col = if let Some(nl_pos) = text.rfind('\n') {
559 text[nl_pos + 1..].chars().count()
560 } else {
561 text.chars().count()
562 };
563 verbatim = false;
564 },
565 }
566 },
567 };
568 if verbatim {
569 for (i, line) in comment.lines.lines().enumerate() {
570 if i > 0 {
571 rendered.push('\n');
572 }
573 let line = line.strip_prefix(' ').unwrap_or(line);
574 rendered.push_str(&format!("{}{}", prefix, line.trim_end()));
575 }
576 }
577 },
578 }
579 }
580 },
581 }
582 }
583 rendered.push('\n');
584 break;
585 }
586 cur = lines.owned_lines.get(line_i.0).unwrap().next;
587 line_pos += 1;
588 }
589 let mk_warnings = std::mem::take(&mut out.warnings);
590 Ok(FormatRes {
591 rendered: rendered.text,
592 lost_comments: out.whitespaces.into_iter().map(|(k, (_, v))| (k, v)).collect(),
593 warnings: {
594 let mut all_warnings = mk_warnings;
595 all_warnings.extend(warnings);
596 all_warnings
597 },
598 })
599}
600
601pub fn format_str(source: &str, config: &FormatConfig) -> Result<FormatRes, loga::Error> {
602 let shebang;
603 let shebang_line_off;
604 let source1;
605 if source.starts_with("#!/") {
606 let shebang_end = match source.find("\n") {
607 Some(o) => o + 1,
608 None => source.len(),
609 };
610 shebang = Some(&source[..shebang_end]);
611 source1 = &source[shebang_end..];
612 shebang_line_off = 1;
613 } else {
614 shebang = None;
615 source1 = source;
616 shebang_line_off = 0;
617 }
618 let stripped_source = source1;
619 let (whitespaces, tokens) = extract_whitespaces(config.keep_max_blank_lines, stripped_source)?;
620 for ws_list in whitespaces.values() {
621 for ws in ws_list {
622 if let WhitespaceMode::Comment(comment) = &ws.mode {
623 if comment.mode == CommentMode::Directive {
624 for line in comment.lines.lines() {
625 if line.trim() == "genemichaels-file-skip" {
626 return Ok(FormatRes {
627 rendered: source.to_string(),
628 lost_comments: BTreeMap::new(),
629 warnings: vec![],
630 });
631 }
632 }
633 }
634 }
635 }
636 }
637 let out =
638 format_ast(
639 syn::parse2::<File>(
640 tokens,
641 ).map_err(
642 |e| loga::err_with(
643 "Syn error parsing Rust code",
644 ea!(line = e.span().start().line + shebang_line_off, column = e.span().start().column, err = e),
645 ),
646 )?,
647 config,
648 whitespaces,
649 )?;
650 if let Some(shebang) = shebang {
651 return Ok(FormatRes {
652 rendered: format!("{}{}", shebang, out.rendered),
653 lost_comments: out.lost_comments,
654 warnings: out.warnings,
655 });
656 } else {
657 return Ok(out);
658 }
659}
660
661#[derive(Debug, Clone, Deserialize, Serialize)]
662#[serde(default)]
663pub struct FormatConfig {
664 pub comment_errors_fatal: bool,
665 pub comment_width: Option<usize>,
666 pub declaration_normalization: DeclarationNormalizationMode,
667 pub explicit_markdown_comments: bool,
668 pub external_formatters: BTreeMap<String, ExternalFormatterConfig>,
669 pub import_normalization: ImportNormalizationMode,
670 pub indent_spaces: usize,
671 pub indent_unit: IndentUnit,
673 pub keep_max_blank_lines: usize,
674 pub max_width: usize,
675 pub root_splits: bool,
676 pub split_attributes: bool,
677 pub split_brace_threshold: Option<usize>,
678 pub split_where: bool,
679}
680
681impl Default for FormatConfig {
682 fn default() -> Self {
683 Self {
684 max_width: 120,
685 root_splits: false,
686 split_brace_threshold: Some(1usize),
687 split_attributes: true,
688 split_where: true,
689 comment_width: Some(80usize),
690 comment_errors_fatal: false,
691 keep_max_blank_lines: 0,
692 indent_spaces: 4,
693 indent_unit: IndentUnit::Spaces,
694 explicit_markdown_comments: false,
695 import_normalization: ImportNormalizationMode::None,
696 declaration_normalization: DeclarationNormalizationMode::None,
697 external_formatters: BTreeMap::new(),
698 }
699 }
700}
701
702pub struct FormatRes {
703 pub lost_comments: BTreeMap<HashLineColumn, Vec<Whitespace>>,
704 pub rendered: String,
705 pub warnings: Vec<Error>,
706}
707
708#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Default)]
709#[serde(rename_all = "snake_case")]
710pub enum ImportNormalizationMode {
711 Combine,
712 #[default]
713 None,
714 Split,
715}
716
717pub struct IncMacroDepth(Rc<Cell<usize>>);
718
719impl IncMacroDepth {
720 fn new(s: &MakeSegsState) -> Self {
721 s.macro_depth.update(|x| x + 1);
722 return Self(s.macro_depth.clone());
723 }
724}
725
726impl Drop for IncMacroDepth {
727 fn drop(&mut self) {
728 self.0.update(|x| x - 1);
729 }
730}
731
732struct IndentLevel(usize);
733
734#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
735#[serde(rename_all = "snake_case")]
736pub enum IndentUnit {
737 Spaces,
738 Tabs,
739}
740
741pub(crate) fn insert_line_after(out: &mut MakeSegsState, lines: &mut Lines, after: LineIdx, segs: Vec<SegmentIdx>) {
742 let line_i = LineIdx(lines.owned_lines.len());
743 let next = lines.owned_lines.get(after.0).unwrap().next;
744 lines.owned_lines.push(Line {
745 next,
746 segs,
747 });
748 lines.owned_lines.get_mut(after.0).unwrap().next = Some(line_i);
749 let line_segs_len = lines.owned_lines.get(line_i.0).unwrap().segs.len();
750 for i in 0 .. line_segs_len {
751 let seg_i = lines.owned_lines.get(line_i.0).unwrap().segs[i];
752 let seg = out.segs.get_mut(seg_i.0).unwrap();
753 match seg.line.as_mut() {
754 Some(l) => {
755 l.line = line_i;
756 l.seg_index = i;
757 },
758 None => {
759 seg.line = Some(SegmentLine {
760 line: line_i,
761 seg_index: i,
762 });
763 },
764 };
765 }
766}
767
768pub(crate) struct Line {
769 next: Option<LineIdx>,
770 segs: Vec<SegmentIdx>,
771}
772
773pub(crate) fn line_length(out: &MakeSegsState, lines: &Lines, line_i: LineIdx) -> usize {
774 let mut max_len = 0;
775 let mut len = 0;
776 for seg_i in &lines.owned_lines.get(line_i.0).unwrap().segs {
777 let seg = out.segs.get(seg_i.0).unwrap();
778 match &seg.content {
779 SegmentContent::Text(t) => {
780 let mut first = true;
781 for line in t.split('\n') {
782 if !first {
783 max_len = max_len.max(len);
784 len = line.chars().count();
785 } else {
786 len += line.chars().count();
787 first = false;
788 }
789 }
790 },
791 SegmentContent::Break(b, _) => {
792 if out.nodes.get(seg.node.0).unwrap().split {
793 len += out.config.indent_spaces * b.get().0;
794 }
795 },
796 SegmentContent::Whitespace(_) => { },
797 SegmentContent::RawTextAdjustIndent(t) => {
798 let prefix_len = t.find('"').map(|i| i + 1).unwrap_or(0);
799 let start_len = len;
800 let mut first = true;
801 let mut local_max = 0;
802 for line in t.split('\n') {
803 if !first {
804 len = start_len + prefix_len + line.chars().count();
805 local_max = local_max.max(len);
806 } else {
807 len += line.chars().count();
808 local_max = local_max.max(len);
809 first = false;
810 }
811 }
812 len = local_max;
813 },
814 };
815 }
816 max_len.max(len)
817}
818
819#[derive(Clone, Copy)]
820pub(crate) struct LineIdx(usize);
821
822struct Lines {
823 head: Option<LineIdx>,
824 owned_lines: Vec<Line>,
825}
826
827pub struct MakeSegsState {
828 config: FormatConfig,
829 macro_depth: Rc<Cell<usize>>,
834 nodes: Vec<SplitGroup>,
835 segs: Vec<Segment>,
836 pub(crate) warnings: Vec<Error>,
837 whitespaces: BTreeMap<HashLineColumn, (usize, Vec<Whitespace>)>,
838}
839
840#[derive(PartialEq, Debug)]
841pub(crate) enum MarginGroup {
842 Attr,
843 BlockDef,
844 Import,
845 None,
846}
847
848pub(crate) fn new_sg(out: &mut MakeSegsState) -> SplitGroupBuilder {
849 let idx = SplitGroupIdx(out.nodes.len());
850 out.nodes.push(SplitGroup {
851 split: false,
852 segments: vec![],
853 children: vec![],
854 });
855 SplitGroupBuilder {
856 node: idx,
857 segs: vec![],
858 children: vec![],
859 initial_split: false,
860 reverse_children: false,
861 }
862}
863
864pub(crate) fn new_sg_lit(
865 out: &mut MakeSegsState,
866 start: Option<(&Alignment, Vec<LineColumn>)>,
867 text: impl ToString,
868) -> SplitGroupIdx {
869 let mut sg = new_sg(out);
870 if let Some((base_indent, starts)) = start {
871 for loc in starts {
872 append_whitespace(out, base_indent, &mut sg, loc);
873 }
874 }
875 sg.seg(out, text.to_string());
876 sg.build(out)
877}
878
879fn render_indent(config: &FormatConfig, current_indent: IndentLevel) -> String {
880 match config.indent_unit {
881 IndentUnit::Spaces => return " ".repeat(config.indent_spaces * current_indent.0),
882 IndentUnit::Tabs => return "\t".repeat(current_indent.0),
883 }
884}
885
886pub(crate) fn run_external_formatter(command: &[String], content: &str) -> Result<String, loga::Error> {
887 let mut tmp_file: Option<NamedTempFile> = None;
888 let args = command[1..].iter().map(|a| -> Result<String, loga::Error> {
889 if a == "{}" {
890 let f = match tmp_file {
891 Some(ref f) => f,
892 None => {
893 let mut f = NamedTempFile::new().context("Failed to create external formatter temp file")?;
894 f.write_all(content.as_bytes()).context("Failed to write external formatter temp file")?;
895 f.flush().context("Failed to flush external formatter temp file")?;
896 tmp_file.insert(f)
897 },
898 };
899 return Ok(f.path().to_string_lossy().to_string());
900 } else {
901 return Ok(a.clone());
902 }
903 }).collect::<Result<Vec<_>, _>>()?;
904 if let Some(ref tmp_file) = tmp_file {
905 let status =
906 Command::new(&command[0])
907 .args(&args)
908 .status()
909 .context_with("Failed to run external formatter", ea!(cmd = command[0].as_str()))?;
910 if !status.success() {
911 return Err(loga::err_with("External formatter exited with error", ea!(cmd = &command[0])));
912 }
913 return fs::read_to_string(tmp_file.path()).context("Failed to read external formatter output");
914 } else {
915 let mut child =
916 Command::new(&command[0])
917 .args(&args)
918 .stdin(Stdio::piped())
919 .stdout(Stdio::piped())
920 .spawn()
921 .context_with("Failed to spawn external formatter", ea!(cmd = command[0].as_str()))?;
922 child
923 .stdin
924 .take()
925 .unwrap()
926 .write_all(content.as_bytes())
927 .context("Failed to write to external formatter stdin")?;
928 let output = child.wait_with_output().context("Failed to get external formatter output")?;
929 if !output.status.success() {
930 return Err(loga::err_with("External formatter exited with error", ea!(cmd = &command[0])));
931 }
932 return String::from_utf8(output.stdout)
933 .map_err(loga::err)
934 .context("External formatter output is not valid UTF-8");
935 }
936}
937
938pub(crate) struct Segment {
939 pub(crate) content: SegmentContent,
940 pub(crate) line: Option<SegmentLine>,
941 pub(crate) mode: SegmentMode,
942 pub(crate) node: SplitGroupIdx,
943}
944
945#[derive(Debug)]
946pub(crate) enum SegmentContent {
947 Break(Alignment, bool),
948 RawTextAdjustIndent(String),
949 Text(String),
950 Whitespace((Alignment, Vec<Whitespace>)),
951}
952
953#[derive(Clone, Copy)]
954pub struct SegmentIdx(usize);
955
956pub(crate) struct SegmentLine {
957 pub(crate) line: LineIdx,
958 pub(crate) seg_index: usize,
959}
960
961#[derive(Debug, Clone, Copy)]
962pub(crate) enum SegmentMode {
963 All,
964 Split,
965 Unsplit,
966}
967
968pub(crate) fn split_group(out: &mut MakeSegsState, lines: &mut Lines, sg_i: SplitGroupIdx) {
969 let sg = out.nodes.get_mut(sg_i.0).unwrap();
970 sg.split = true;
971 for seg_i in &sg.segments.clone() {
972 let res = {
973 let seg = out.segs.get(seg_i.0).unwrap();
974 match (&seg.mode, &seg.content) {
975 (SegmentMode::Split, SegmentContent::Break(_, _)) => {
976 let seg_line = seg.line.as_ref().unwrap();
977 Some((seg_line.line, seg_line.seg_index))
978 },
979 _ => None,
980 }
981 };
982 if let Some((line_i, off)) = res {
983 split_line_at(out, lines, line_i, off, None);
984 };
985 }
986}
987
988pub(crate) fn split_line_at(
989 out: &mut MakeSegsState,
990 lines: &mut Lines,
991 line_idx: LineIdx,
992 off: usize,
993 inject_start: Option<SegmentIdx>,
994) {
995 let line = lines.owned_lines.get_mut(line_idx.0).unwrap();
996 let mut new_segs = vec![];
997 if let Some(s) = inject_start {
998 new_segs.push(s);
999 }
1000 new_segs.extend(line.segs.split_off(off));
1001 {
1002 let seg_i = new_segs.get(0).unwrap();
1003 let seg = out.segs.get(seg_i.0).unwrap();
1004 match &seg.content {
1005 SegmentContent::Break(a, activate) => {
1006 if *activate {
1007 a.activate();
1008 }
1009 },
1010 SegmentContent::Whitespace((a, _)) => {
1011 a.activate();
1012 },
1013 _ => { },
1014 };
1015 }
1016 insert_line_after(out, lines, line_idx, new_segs);
1017}
1018
1019pub struct SplitGroup {
1020 pub(crate) children: Vec<SplitGroupIdx>,
1021 pub(crate) segments: Vec<SegmentIdx>,
1022 pub(crate) split: bool,
1023}
1024
1025pub(crate) struct SplitGroupBuilder {
1026 children: Vec<SplitGroupIdx>,
1027 initial_split: bool,
1028 pub(crate) node: SplitGroupIdx,
1029 reverse_children: bool,
1030 segs: Vec<SegmentIdx>,
1031}
1032
1033impl SplitGroupBuilder {
1034 pub(crate) fn add(&mut self, out: &mut MakeSegsState, seg: Segment) {
1035 let idx = SegmentIdx(out.segs.len());
1036 out.segs.push(seg);
1037 self.segs.push(idx);
1038 }
1039
1040 pub(crate) fn build(self, out: &mut MakeSegsState) -> SplitGroupIdx {
1041 let sg = out.nodes.get_mut(self.node.0).unwrap();
1042 sg.split = self.initial_split;
1043 sg.children = self.children;
1044 if self.reverse_children {
1045 sg.children.reverse();
1046 }
1047 sg.segments = self.segs;
1048 for seg in &sg.segments {
1049 out.segs.get_mut(seg.0).unwrap().node = self.node;
1050 }
1051 self.node
1052 }
1053
1054 pub(crate) fn child(&mut self, child: SplitGroupIdx) {
1055 self.children.push(child);
1056 }
1057
1058 pub(crate) fn initial_split(&mut self) {
1059 self.initial_split = true;
1060 }
1061
1062 pub(crate) fn reverse_children(&mut self) {
1063 self.reverse_children = true;
1064 }
1065
1066 pub(crate) fn seg(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1067 self.add(out, Segment {
1068 node: self.node,
1069 line: None,
1070 mode: SegmentMode::All,
1071 content: SegmentContent::Text(text.to_string()),
1072 });
1073 }
1074
1075 pub(crate) fn seg_split(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1076 self.add(out, Segment {
1077 node: self.node,
1078 line: None,
1079 mode: SegmentMode::Split,
1080 content: SegmentContent::Text(text.to_string()),
1081 });
1082 }
1083
1084 pub(crate) fn seg_unsplit(&mut self, out: &mut MakeSegsState, text: impl ToString) {
1085 self.add(out, Segment {
1086 node: self.node,
1087 line: None,
1088 mode: SegmentMode::Unsplit,
1089 content: SegmentContent::Text(text.to_string()),
1090 });
1091 }
1092
1093 pub(crate) fn split(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
1094 self.add(out, Segment {
1095 node: self.node,
1096 line: None,
1097 mode: SegmentMode::Split,
1098 content: SegmentContent::Break(alignment, activate),
1099 });
1100 }
1101
1102 pub(crate) fn split_always(&mut self, out: &mut MakeSegsState, alignment: Alignment, activate: bool) {
1103 self.add(out, Segment {
1104 node: self.node,
1105 line: None,
1106 mode: SegmentMode::All,
1107 content: SegmentContent::Break(alignment, activate),
1108 });
1109 }
1110
1111 pub(crate) fn split_if(&mut self, out: &mut MakeSegsState, alignment: Alignment, always: bool, activate: bool) {
1112 self.add(out, Segment {
1113 node: self.node,
1114 line: None,
1115 mode: if always {
1116 SegmentMode::All
1117 } else {
1118 SegmentMode::Split
1119 },
1120 content: SegmentContent::Break(alignment, activate),
1121 });
1122 }
1123}
1124
1125#[derive(Clone, Copy)]
1126pub struct SplitGroupIdx(usize);
1127
1128#[derive(Debug, Clone)]
1129pub struct Whitespace {
1130 pub loc: LineColumn,
1133 pub mode: WhitespaceMode,
1134}
1135
1136#[derive(Debug, Clone)]
1137pub enum WhitespaceMode {
1138 BlankLines(usize),
1139 Comment(Comment),
1140}