1use std::{
2 borrow::Cow,
3 collections::{HashMap, HashSet},
4 fs,
5 io::{self, BufRead, Read, Write},
6 ops,
7 path::{Path, PathBuf},
8 process::Stdio,
9 sync::{
10 Arc,
11 atomic::{AtomicBool, Ordering::Relaxed},
12 },
13 task::Poll,
14 time::{Duration, SystemTime},
15};
16
17use clap::*;
18use once_cell::sync::Lazy;
19use parking_lot::Mutex;
20use rayon::prelude::*;
21use regex::Regex;
22use sha2::Digest;
23
24use crate::util;
25
26const FMT_VERSION: &str = "1";
28
29#[derive(Args, Debug, Default)]
30pub struct FmtArgs {
31 #[arg(long, action)]
33 check: bool,
34
35 #[arg(long)]
37 manifest_path: Option<String>,
38
39 #[arg(short, long)]
41 package: Option<String>,
42
43 #[arg(short, long)]
45 files: Option<String>,
46
47 #[arg(short, long, action)]
49 stdin: bool,
50
51 #[arg(long, default_value = "2024")]
53 edition: String,
54
55 #[arg(long, action)]
57 full: bool,
58
59 #[arg(long, action, hide = true)]
61 rustfmt_errors: bool,
62}
63
64pub fn run(mut args: FmtArgs) {
65 if args.rustfmt_errors {
66 SHOW_RUSTFMT_ERRORS.store(true, Relaxed);
67 }
68
69 if args.stdin {
70 if args.manifest_path.is_some() || args.package.is_some() || args.files.is_some() {
71 fatal!("stdin can only be used standalone or with --check");
72 }
73
74 let mut code = String::new();
75 if let Err(e) = std::io::stdin().read_to_string(&mut code) {
76 fatal!("stdin read error, {e}");
77 }
78
79 if code.is_empty() {
80 return;
81 }
82
83 if let Some(code) = rustfmt_stdin(&code, &args.edition) {
84 let stream = code.parse().unwrap_or_else(|e| fatal!("cannot parse stdin, {e}"));
85
86 let fmt_server = FmtFragServer::spawn(args.edition.clone());
87 let mut formatted = Box::pin(try_fmt_child_macros(&code, stream, &fmt_server));
88 let formatted = loop {
89 std::thread::sleep(Duration::from_millis(50));
90 match formatted
91 .as_mut()
92 .poll(&mut std::task::Context::from_waker(std::task::Waker::noop()))
93 {
94 Poll::Ready(r) => break r,
95 Poll::Pending => {}
96 }
97 };
98
99 if let Err(e) = std::io::stdout().write_all(formatted.as_bytes()) {
100 fatal!("stdout write error, {e}");
101 }
102 }
103
104 return;
105 }
106
107 let mut file_patterns = vec![];
108 if let Some(glob) = &args.files {
109 file_patterns.push(PathBuf::from(glob));
110 }
111 if let Some(pkg) = &args.package {
112 if args.manifest_path.is_some() {
113 fatal!("expected only one of --package, --manifest-path");
114 }
115 match util::manifest_path_from_package(pkg) {
116 Some(m) => args.manifest_path = Some(m),
117 None => fatal!("package `{pkg}` not found in workspace"),
118 }
119 }
120 let manifest_paths = if let Some(path) = &args.manifest_path {
121 vec![PathBuf::from(path)]
122 } else if args.files.is_none() {
123 let workspace_root = workspace_root().unwrap_or_else(|e| fatal!("cannot find workspace root, {e}"));
124 file_patterns.push(workspace_root.join("README.md"));
125 file_patterns.push(workspace_root.join("docs/**/*.md"));
126 util::workspace_manifest_paths()
127 } else {
128 vec![]
129 };
130 for path in manifest_paths {
131 let r = (|path: &Path| -> Result<(), Box<dyn std::error::Error>> {
132 let path = path.parent().ok_or("root dir")?;
133 for entry in fs::read_dir(path)? {
134 let entry = entry?;
135 let ft = entry.file_type()?;
136 if ft.is_dir() {
137 if entry.file_name() != "target" {
138 file_patterns.push(entry.path().join("**/*.rs"));
139 file_patterns.push(entry.path().join("**/*.md"));
140 }
141 } else if ft.is_file() {
142 file_patterns.push(entry.path());
143 }
144 }
145
146 Ok(())
147 })(&path);
148 if let Err(e) = r {
149 error!("failed to select files for {}, {e}", path.display())
150 }
151 }
152
153 let mut history = match FmtHistory::load() {
155 Ok(h) => h,
156 Err(e) => {
157 warn!("cannot load fmt history, {e}");
158 FmtHistory::default()
159 }
160 };
161 let cutout_time = history.insert(&args);
162 let files: HashSet<PathBuf> = file_patterns
163 .into_par_iter()
164 .flat_map(|pattern| {
165 let files = match glob::glob(&pattern.display().to_string().replace('\\', "/")) {
166 Ok(f) => f.flat_map(|e| e.ok()).collect(),
167 Err(_) => vec![],
168 };
169 files
170 .into_par_iter()
171 .filter(|f| matches!(f.extension(), Some(ext) if ext == "rs" || ext == "md"))
172 })
173 .collect();
174
175 let mut files: Vec<_> = files
176 .into_par_iter()
177 .filter_map(|p| {
178 if let Ok(meta) = std::fs::metadata(&p)
179 && let Ok(modified) = meta.modified()
180 {
181 let modified = FmtHistory::time(modified);
182 if modified > cutout_time {
183 return Some((p, modified));
184 };
185 }
186 None
187 })
188 .collect();
189
190 files.sort_by(|a, b| b.1.cmp(&a.1));
192
193 let files: Vec<_> = files.into_iter().map(|(p, _)| p).collect();
194
195 let fmt_server = FmtFragServer::spawn(args.edition.clone());
196
197 files.par_chunks(64).for_each(|c| {
198 rustfmt_files(c, &args.edition, args.check);
200 });
201
202 let check = args.check;
204 let fmt_server2 = fmt_server.clone();
205 let mut futs: Vec<_> = files
206 .par_iter()
207 .map(move |file| {
208 let fmt_server = fmt_server2.clone();
209 Some(Box::pin(async move {
210 let is_rs = file.extension().unwrap() == "rs";
211 let r = if is_rs {
212 custom_fmt_rs(file.clone(), check, fmt_server).await
213 } else {
214 debug_assert!(file.extension().unwrap() == "md");
215 custom_fmt_md(file.clone(), check, fmt_server).await.map(|_| None)
216 };
217 match r {
218 Ok(r) => r,
219 Err(e) => {
220 error!("{e}");
221 None
222 }
223 }
224 }))
225 })
226 .collect();
227
228 let reformat = Mutex::new(vec![]);
229 loop {
230 std::thread::sleep(Duration::from_millis(25));
231 futs.par_iter_mut().for_each(|f| {
232 match f
233 .as_mut()
234 .unwrap()
235 .as_mut()
236 .poll(&mut std::task::Context::from_waker(std::task::Waker::noop()))
237 {
238 Poll::Ready(changed) => {
239 if let Some(p) = changed {
240 reformat.lock().push(p);
241 }
242 *f = None
243 }
244 Poll::Pending => {}
245 }
246 });
247 futs.retain(|t| t.is_some());
248 if futs.is_empty() {
249 break;
250 }
251 }
252
253 let reformat = reformat.into_inner();
254 if !reformat.is_empty() {
255 reformat.par_chunks(64).for_each(|c| {
256 rustfmt_files(c, &args.edition, args.check);
258 });
259 }
260
261 if let Err(e) = history.save() {
262 warn!("cannot save fmt history, {e}")
263 }
264}
265
266async fn custom_fmt_rs(rs_file: PathBuf, check: bool, fmt: FmtFragServer) -> io::Result<Option<PathBuf>> {
272 let file = fs::read_to_string(&rs_file)?;
273
274 let file_code = file.strip_prefix('\u{feff}').unwrap_or(file.as_str());
276 let file_code = if file_code.starts_with("#!") && !file_code.starts_with("#![") {
278 &file_code[file_code.find('\n').unwrap_or(file_code.len())..]
279 } else {
280 file_code
281 };
282
283 let mut formatted_code = file[..file.len() - file_code.len()].to_owned();
284 formatted_code.reserve(file.len());
285
286 let file_stream = match file_code.parse() {
287 Ok(s) => s,
288 Err(e) => {
289 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
290 error!("cannot parse `{}`, {e}", rs_file.display());
291 }
292 return Ok(None);
293 }
294 };
295 formatted_code.push_str(&try_fmt_child_macros(file_code, file_stream, &fmt).await);
296
297 let formatted_code = custom_fmt_docs(&formatted_code, &fmt, &rs_file).await;
298
299 if formatted_code != file {
300 if check {
301 let formatted_code = fmt.format(formatted_code.clone()).await.unwrap_or(formatted_code);
303 if formatted_code != file {
304 let diff = similar::TextDiff::from_lines(&file, &formatted_code);
305 fatal!("Diff in {}:\n{}", rs_file.display(), diff.unified_diff().context_radius(2));
306 }
307 return Ok(None);
308 }
309 fs::write(&rs_file, formatted_code)?;
310 Ok(Some(rs_file))
311 } else {
312 Ok(None)
313 }
314}
315async fn custom_fmt_docs(code: &str, fmt: &FmtFragServer, rs_file: &Path) -> String {
316 let mut formatted_code = String::new();
317 let mut lines = code.lines().peekable();
318 while let Some(mut line) = lines.next() {
319 let maybe = line.trim_start();
320 if maybe.starts_with("//!") || maybe.starts_with("///") {
321 let prefix = &line[..line.find("//").unwrap() + 3];
323 loop {
324 formatted_code.push_str(line);
326 formatted_code.push('\n');
327
328 let doc_line = line.strip_prefix(prefix).unwrap();
329 match doc_line.trim().strip_prefix("```") {
330 Some("" | "rust" | "should_panic" | "no_run" | "edition2015" | "edition2018" | "edition2021" | "edition2024") => {
331 let mut code = String::new();
334 let mut close_line = "";
335 while let Some(l) = lines.next_if(|l| l.starts_with(prefix)) {
336 let doc_line = l.strip_prefix(prefix).unwrap();
337 if doc_line.trim_start().starts_with("```") {
338 close_line = l;
339 break;
340 }
341 code.push_str(doc_line);
342 code.push('\n');
343 }
344
345 static HIDDEN_LINES_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^ *#(?: +(.*)$|$)").unwrap());
346 if !close_line.is_empty() && !code.trim().is_empty() && let Some(mut code) = {
349 let escaped = format!("fn __zng_fmt() {{\n{}\n}}", HIDDEN_LINES_RGX.replace_all(&code, "// __# $1"));
350 fmt.format(escaped).await
351 } {
352 let stream = code
355 .parse::<proc_macro2::TokenStream>()
356 .map(pm2_send::TokenStream::from)
357 .map_err(|e| e.to_string());
358 match stream {
359 Ok(s) => code = try_fmt_child_macros(&code, s, fmt).await,
360 Err(e) => {
361 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
362 error!("cannot parse doctest block in `{}`, {e}", rs_file.display());
363 }
364 }
365 };
366
367 let code = code
368 .strip_prefix("fn __zng_fmt() {")
369 .unwrap()
370 .trim_end()
371 .strip_suffix('}')
372 .unwrap()
373 .replace("// __# ", "# ")
374 .replace("// __#", "#");
375 let mut fmt_code = String::new();
376 let mut wrapper_tabs = String::new();
377 for line in code.lines() {
378 if line.trim().is_empty() {
379 fmt_code.push('\n');
380 } else {
381 if wrapper_tabs.is_empty() {
382 for _ in 0..(line.len() - line.trim_start().len()) {
383 wrapper_tabs.push(' ');
384 }
385 }
386 fmt_code.push_str(line.strip_prefix(&wrapper_tabs).unwrap_or(line));
387 fmt_code.push('\n');
388 }
389 }
390 for line in fmt_code.trim().lines() {
391 formatted_code.push_str(prefix);
392 if !line.trim().is_empty() {
393 formatted_code.push(' ');
394 formatted_code.push_str(line);
395 }
396 formatted_code.push('\n');
397 }
398 } else {
399 for line in code.lines() {
401 formatted_code.push_str(prefix);
402 formatted_code.push_str(line);
403 formatted_code.push('\n');
404 }
405 }
406 if !close_line.is_empty() {
407 formatted_code.push_str(close_line);
408 formatted_code.push('\n');
409 }
410 }
411 Some(_) => {
412 while let Some(l) = lines.next_if(|l| l.starts_with(prefix)) {
414 formatted_code.push_str(l);
415 formatted_code.push('\n');
416 let doc_line = l.strip_prefix(prefix).unwrap();
417 if doc_line.trim_start().starts_with("```") {
418 break;
419 }
420 }
421 }
422 None => {}
423 }
424
425 match lines.next_if(|l| l.starts_with(prefix)) {
426 Some(l) => line = l, None => break, }
429 }
430 } else {
431 formatted_code.push_str(line);
433 formatted_code.push('\n');
434 }
435 }
436 formatted_code
437}
438async fn custom_fmt_md(md_file: PathBuf, check: bool, fmt: FmtFragServer) -> io::Result<()> {
440 let file = fs::read_to_string(&md_file)?;
441
442 let mut formatted = String::new();
443
444 let mut lines = file.lines();
445 while let Some(line) = lines.next() {
446 if line.trim_start().starts_with("```rust") {
447 formatted.push_str(line);
448 formatted.push('\n');
449
450 let mut code = String::new();
451 let mut close_line = "";
452 for line in lines.by_ref() {
453 if line.trim_start().starts_with("```") {
454 close_line = line;
455 break;
456 } else {
457 code.push_str(line);
458 code.push('\n');
459 }
460 }
461
462 if close_line.is_empty() {
463 formatted.push_str(&code);
464 continue;
465 }
466
467 if !code.trim().is_empty()
469 && let Some(mut code) = fmt.format(format!("fn __zng_fmt() {{\n{code}\n}}")).await
470 {
471 let stream = code
474 .parse::<proc_macro2::TokenStream>()
475 .map(pm2_send::TokenStream::from)
476 .map_err(|e| e.to_string());
477 match stream {
478 Ok(s) => code = try_fmt_child_macros(&code, s, &fmt).await,
479 Err(e) => {
480 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
481 error!("cannot parse code block in `{}`, {e}", md_file.display());
482 }
483 }
484 };
485 let code = code.strip_prefix("fn __zng_fmt() {").unwrap().trim_end().strip_suffix('}').unwrap();
486 let mut fmt_code = String::new();
487 let mut wrapper_tabs = String::new();
488 for line in code.lines() {
489 if line.trim().is_empty() {
490 fmt_code.push('\n');
491 } else {
492 if wrapper_tabs.is_empty() {
493 for _ in 0..(line.len() - line.trim_start().len()) {
494 wrapper_tabs.push(' ');
495 }
496 }
497 fmt_code.push_str(line.strip_prefix(&wrapper_tabs).unwrap_or(line));
498 fmt_code.push('\n');
499 }
500 }
501 formatted.push_str(fmt_code.trim());
502 formatted.push('\n');
503 } else {
504 formatted.push_str(&code);
505 }
506 formatted.push_str(close_line);
507 formatted.push('\n');
508 } else {
509 formatted.push_str(line);
510 formatted.push('\n')
511 }
512 }
513
514 if formatted != file {
515 if check {
516 let diff = similar::TextDiff::from_lines(&file, &formatted);
517 fatal!("Diff in {}:\n{}", md_file.display(), diff.unified_diff().context_radius(2));
518 }
519 fs::write(&md_file, formatted)?;
520 }
521
522 Ok(())
523}
524
525async fn try_fmt_child_macros(code: &str, stream: pm2_send::TokenStream, fmt: &FmtFragServer) -> String {
527 let mut formatted_code = String::new();
528 let mut last_already_fmt_start = 0;
529
530 let mut stream_stack = vec![stream.into_iter()];
531 let next = |stack: &mut Vec<std::vec::IntoIter<pm2_send::TokenTree>>| {
532 while !stack.is_empty() {
533 let tt = stack.last_mut().unwrap().next();
534 if let Some(tt) = tt {
535 return Some(tt);
536 }
537 stack.pop();
538 }
539 None
540 };
541 let mut tail2: Vec<pm2_send::TokenTree> = Vec::with_capacity(2);
542
543 let mut skip_next_group = false;
544 while let Some(tt) = next(&mut stream_stack) {
545 match tt {
546 pm2_send::TokenTree::Group(g) => {
547 if tail2.len() == 2
548 && matches!(g.delimiter(), pm2_send::Delimiter::Brace)
549 && matches!(&tail2[0], pm2_send::TokenTree::Punct(p) if p.as_char() == '!')
550 && matches!(&tail2[1], pm2_send::TokenTree::Ident(_))
551 {
552 if std::mem::take(&mut skip_next_group) {
554 continue;
555 }
556 if let pm2_send::TokenTree::Ident(i) = &tail2[1] {
557 if i == &"__P_" || i == &"quote" || i == &"quote_spanned" || i == &"parse_quote" || i == &"parse_quote_spanned" {
558 continue;
559 }
560 } else {
561 unreachable!()
562 }
563
564 let bang = tail2[0].span().byte_range().start;
565 let line_start = code[..bang].rfind('\n').unwrap_or(0);
566 let base_indent = code[line_start..bang]
567 .chars()
568 .skip_while(|&c| c != ' ')
569 .take_while(|&c| c == ' ')
570 .count();
571
572 let group_bytes = g.span().byte_range();
573 let group_code = &code[group_bytes.clone()];
574
575 if let Some(formatted) = try_fmt_macro(base_indent, group_code, fmt).await
576 && formatted != group_code
577 {
578 if let Some(stable) = try_fmt_macro(base_indent, &formatted, fmt).await {
580 if formatted == stable {
581 let already_fmt = &code[last_already_fmt_start..group_bytes.start];
583 formatted_code.push_str(already_fmt);
584 formatted_code.push_str(&formatted);
585 last_already_fmt_start = group_bytes.end;
586 } else if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
587 error!("unstable format skipped");
588 }
589 }
590 }
591 } else if !tail2.is_empty()
592 && matches!(g.delimiter(), pm2_send::Delimiter::Bracket)
593 && matches!(&tail2[0], pm2_send::TokenTree::Punct(p) if p.as_char() == '#')
594 {
595 let mut attr = g.stream().into_iter();
597 let attr = [attr.next(), attr.next(), attr.next(), attr.next(), attr.next()];
598 if let [
599 Some(pm2_send::TokenTree::Ident(i0)),
600 Some(pm2_send::TokenTree::Punct(p0)),
601 Some(pm2_send::TokenTree::Punct(p1)),
602 Some(pm2_send::TokenTree::Ident(i1)),
603 None,
604 ] = attr
605 && i0 == "rustfmt"
606 && p0.as_char() == ':'
607 && p1.as_char() == ':'
608 && i1 == "skip"
609 {
610 skip_next_group = true;
612 }
613 } else if !std::mem::take(&mut skip_next_group) {
614 stream_stack.push(g.stream().into_iter());
615 }
616 tail2.clear();
617 }
618 ref tt1 @ pm2_send::TokenTree::Ident(ref i) if i == &"macro_rules" => {
619 if let Some(tt2) = next(&mut stream_stack) {
620 if matches!(tt2, pm2_send::TokenTree::Punct(ref p) if p.as_char() == '!') {
621 next(&mut stream_stack); next(&mut stream_stack); } else {
625 tail2.clear();
626 tail2.push(tt2);
627 tail2.push(tt1.clone());
628 }
629 } else {
630 }
632 }
633 tt => {
634 if tail2.len() == 2 {
635 tail2.pop();
636 }
637 tail2.insert(0, tt);
638 }
639 }
640 }
641
642 formatted_code.push_str(&code[last_already_fmt_start..]);
643 formatted_code
644}
645
646async fn try_fmt_macro(base_indent: usize, group_code: &str, fmt: &FmtFragServer) -> Option<String> {
647 let mut replaced_code = Cow::Borrowed(group_code);
649
650 let mut is_lazy_static = false;
651 if matches!(&replaced_code, Cow::Borrowed(_)) {
652 replaced_code = replace_static_ref(group_code, false);
653 is_lazy_static = matches!(&replaced_code, Cow::Owned(_));
654 }
655
656 let mut is_bitflags = false;
657 if matches!(&replaced_code, Cow::Borrowed(_)) {
658 replaced_code = replace_bitflags(group_code, false);
659 is_bitflags = matches!(&replaced_code, Cow::Owned(_));
660 }
661
662 let mut is_event_args = false;
663 if matches!(&replaced_code, Cow::Borrowed(_)) {
664 replaced_code = replace_event_args(group_code, false);
665 is_event_args = matches!(&replaced_code, Cow::Owned(_));
666 }
667
668 let mut is_event = false;
669 if matches!(&replaced_code, Cow::Borrowed(_)) {
670 replaced_code = replace_event(group_code, false);
671 is_event = matches!(&replaced_code, Cow::Owned(_));
672 }
673
674 let mut is_command = false;
675 if matches!(&replaced_code, Cow::Borrowed(_)) {
676 replaced_code = replace_command(group_code, false);
677 is_command = matches!(&replaced_code, Cow::Owned(_));
678 }
679
680 let mut is_widget_impl = false;
681 if matches!(&replaced_code, Cow::Borrowed(_)) {
682 replaced_code = replace_widget_impl(group_code, false);
683 is_widget_impl = matches!(&replaced_code, Cow::Owned(_));
684 }
685
686 let mut is_widget = false;
687 if matches!(&replaced_code, Cow::Borrowed(_)) {
688 replaced_code = replace_widget(group_code, false);
689 is_widget = matches!(&replaced_code, Cow::Owned(_));
690 }
691
692 let mut is_expr_var = false;
693 if matches!(&replaced_code, Cow::Borrowed(_)) {
694 replaced_code = replace_expr_var(group_code, false);
695 is_expr_var = matches!(&replaced_code, Cow::Owned(_));
696 }
697
698 let mut is_struct_like = false;
699 if matches!(&replaced_code, Cow::Borrowed(_)) {
700 replaced_code = replace_struct_like(group_code, false);
701 is_struct_like = matches!(&replaced_code, Cow::Owned(_));
702 }
703
704 let mut is_simple_list = false;
705 if matches!(&replaced_code, Cow::Borrowed(_)) {
706 replaced_code = replace_simple_ident_list(group_code, false);
707 is_simple_list = matches!(&replaced_code, Cow::Owned(_));
708 }
709
710 let mut is_when_var = false;
711 if matches!(&replaced_code, Cow::Borrowed(_)) {
712 replaced_code = replace_when_var(group_code, false);
713 is_when_var = matches!(&replaced_code, Cow::Owned(_));
714 }
715
716 let replaced_code = replaced_code.into_owned();
717 let code_stream: pm2_send::TokenStream = match replaced_code.parse() {
719 Ok(t) => t,
720 Err(e) => {
721 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
722 error!("internal error: {e}");
723 eprintln!("CODE:\n{replaced_code}");
724 }
725 return None;
726 }
727 };
728 let mut inner_group = None;
729 for tt in code_stream {
730 if let pm2_send::TokenTree::Group(g) = tt {
731 inner_group = Some(g);
733 break;
734 }
735 }
736 let code_stream = match inner_group {
737 Some(g) => g.stream(),
738 None => {
739 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
740 error!("internal error, invalid replacement");
741 eprintln!("CODE:\n{replaced_code}");
742 }
743 return None;
744 }
745 };
746 let code = Box::pin(try_fmt_child_macros(&replaced_code, code_stream, fmt)).await;
747
748 let code = fmt.format(code).await?;
750
751 let code = if is_event_args {
753 replace_event_args(&code, true)
754 } else if is_widget {
755 replace_widget(&code, true)
756 } else if is_expr_var {
757 replace_expr_var(&code, true)
758 } else if is_lazy_static {
759 replace_static_ref(&code, true)
760 } else if is_event {
761 replace_event(&code, true)
762 } else if is_command {
763 replace_command(&code, true)
764 } else if is_struct_like {
765 replace_struct_like(&code, true)
766 } else if is_bitflags {
767 replace_bitflags(&code, true)
768 } else if is_simple_list {
769 replace_simple_ident_list(&code, true)
770 } else if is_widget_impl {
771 replace_widget_impl(&code, true)
772 } else if is_when_var {
773 replace_when_var(&code, true)
774 } else {
775 Cow::Owned(code)
776 };
777
778 let mut out = String::new();
780 let mut lb_indent = String::with_capacity(base_indent + 1);
781 for line in code.lines() {
782 if line.is_empty() {
783 if !lb_indent.is_empty() {
784 out.push('\n');
785 }
786 } else {
787 out.push_str(&lb_indent);
788 }
789 out.push_str(line);
790 if lb_indent.is_empty() {
792 lb_indent.push('\n');
793 for _ in 0..base_indent {
794 lb_indent.push(' ');
795 }
796 }
797 }
798 Some(out)
799}
800fn replace_event_args(code: &str, reverse: bool) -> Cow<'_, str> {
809 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*(\.\.)\s*$").unwrap());
810 static MARKER: &str = "// cargo-zng::fmt::dot_dot\n}\nimpl CargoZngFmt {\n";
811 static RGX_REV: Lazy<Regex> =
812 Lazy::new(|| Regex::new(r"(?m)^(\s+)// cargo-zng::fmt::dot_dot\n\s*}\n\s*impl CargoZngFmt\s*\{\n").unwrap());
813
814 if !reverse {
815 RGX.replace_all(code, |caps: ®ex::Captures| {
816 format!(
817 "{}{MARKER}{}",
818 &caps[0][..caps.get(1).unwrap().start() - caps.get(0).unwrap().start()],
819 &caps[0][caps.get(1).unwrap().end() - caps.get(0).unwrap().start()..]
820 )
821 })
822 } else {
823 RGX_REV.replace_all(code, "\n$1..\n\n")
824 }
825}
826fn replace_event(code: &str, reverse: bool) -> Cow<'_, str> {
828 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+):\s+([\w:]+)\s+\{").unwrap());
829 static RGX_REV: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+): __fmt__::([\w:]+) = \{").unwrap());
830 if !reverse {
831 RGX.replace_all(code, "${1}static $2: __fmt__::$3 = {")
832 } else {
833 RGX_REV.replace_all(code, "${1}static $2: $3 {")
834 }
835}
836fn replace_command(code: &str, reverse: bool) -> Cow<'_, str> {
840 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+) ? ?\{(\s+\w+!?:)").unwrap());
841 static RGX_DEFAULTS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+) ?;").unwrap());
842 if !reverse {
843 let cmd = RGX_DEFAULTS.replace_all(code, "${1}static $2: __fmt__ = T;");
844 let mut cmd2 = RGX.replace_all(&cmd, "${1}static $2: __fmt__ = __A_ {$3");
845 if let Cow::Owned(cmd) = &mut cmd2 {
846 *cmd = cmd.replace("l10n!:", "l10n__fmt:");
847 }
848 match cmd2 {
849 Cow::Borrowed(_) => cmd,
850 Cow::Owned(s) => Cow::Owned(s),
851 }
852 } else {
853 Cow::Owned(
854 code.replace(": __fmt__ = T;", ";")
855 .replace(": __fmt__ = __A_ {", " {")
856 .replace("l10n__fmt:", "l10n!:"),
857 )
858 }
859}
860fn replace_widget(code: &str, reverse: bool) -> Cow<'_, str> {
862 static IGNORE_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m): +\w+\s+=\s+\{").unwrap());
863 static PROPERTY_NAME_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*([\w:<>]+)\s+=\s+").unwrap());
864
865 #[derive(Debug)]
866 enum Item<'s> {
867 Property { name: &'s str, value: &'s str },
868 PropertyShorthand(&'s str),
869 When { expr: &'s str, items: Vec<Item<'s>> },
870 Text(&'s str),
871 WidgetSetSelfExpr(&'s str),
872 }
873 #[allow(unused)]
874 struct Error<'s> {
875 partial: Vec<Item<'s>>,
876 error: &'static str,
877 }
878 impl<'s> Error<'s> {
879 fn new(partial: Vec<Item<'s>>, error: &'static str) -> Self {
880 Self { partial, error }
881 }
882 }
883 fn parse<'s>(code: &'s str, stream: proc_macro2::TokenStream, code_span: ops::Range<usize>) -> Result<Vec<Item<'s>>, Error<'s>> {
884 use proc_macro2::{Delimiter, TokenTree as Tt};
885
886 let mut items = vec![];
887 let mut stream = stream.into_iter().peekable();
888 let mut text_start = code_span.start;
889
890 if code_span.start == 1 {
891 if let Some(Tt::Group(g)) = stream.next()
892 && g.delimiter() == Delimiter::Brace
893 {
894 stream = g.stream().into_iter().peekable();
895 } else {
896 return Err(Error::new(items, "expected macro block at root"));
897 }
898
899 if let Some(Tt::Punct(p)) = stream.peek()
900 && p.as_char() == '&'
901 {
902 let amp = stream.next().unwrap();
904 if let Some(Tt::Ident(m)) = stream.next()
905 && m == "mut"
906 {
907 let start = amp.span().byte_range().start;
908 for tt in stream.by_ref() {
909 if let Tt::Punct(p) = tt
910 && p.as_char() == ';'
911 {
912 let end = p.span().byte_range().end;
913 items.push(Item::Text(&code[text_start..start]));
914 items.push(Item::WidgetSetSelfExpr(&code[start..end]));
915 text_start = end;
916 break;
917 }
918 }
919 }
920 if text_start == code_span.start {
921 return Err(Error::new(items, "expected &mut <self>"));
922 }
923 }
924 }
925 'outer: while let Some(tt_attr_or_name) = stream.next() {
926 if let Tt::Punct(p) = &tt_attr_or_name
928 && p.as_char() == '#'
929 {
930 if let Some(Tt::Group(g)) = stream.next()
931 && g.delimiter() == proc_macro2::Delimiter::Bracket
932 {
933 continue 'outer;
934 } else {
935 return Err(Error::new(items, "expected attribute"));
936 }
937 }
938
939 if let Tt::Ident(ident) = &tt_attr_or_name {
941 if ident == "when" {
942 items.push(Item::Text(&code[text_start..ident.span().byte_range().start]));
943
944 let expr_start = ident.span().byte_range().end;
947 if stream.next().is_some()
948 && let Some(mut tt_block) = stream.next()
949 {
950 loop {
952 if let Tt::Group(g) = &tt_block
953 && g.delimiter() == Delimiter::Brace
954 && stream
955 .peek()
956 .map(|tt| matches!(tt, Tt::Ident(_)) || matches!(tt, Tt::Punct(p) if p.as_char() == '#'))
957 .unwrap_or(true)
958 {
959 let block_span = g.span().byte_range();
961 let expr = &code[expr_start..block_span.start];
962 items.push(Item::When {
963 expr,
964 items: parse(code, g.stream(), g.span_open().byte_range().end..g.span_close().byte_range().start)?,
965 });
966 text_start = block_span.end;
967 continue 'outer;
968 }
969 if let Some(tt) = stream.next() {
971 tt_block = tt;
972 } else {
973 break;
974 }
975 }
976 } else {
977 return Err(Error::new(items, "expected when expression and block"));
978 }
979 } else {
980 let name_start = tt_attr_or_name.span().byte_range().start;
982 let mut tt_name_end = tt_attr_or_name;
983 while let Some(tt) = stream
984 .next_if(|tt| matches!(tt, Tt::Ident(_)) || matches!(tt, Tt::Punct(p) if [':', '<', '>'].contains(&p.as_char())))
985 {
986 tt_name_end = tt;
987 }
988
989 items.push(Item::Text(&code[text_start..name_start]));
990
991 let name_end = tt_name_end.span().byte_range().end;
992 let name = &code[name_start..name_end];
993 if name.is_empty() {
994 return Err(Error::new(items, "expected property name"));
995 }
996
997 if let Some(tt_punct) = stream.next() {
998 if let Tt::Punct(p) = tt_punct {
999 if p.as_char() == ';' {
1000 items.push(Item::PropertyShorthand(name));
1001 text_start = p.span().byte_range().end;
1002 continue 'outer;
1003 } else if p.as_char() == '=' {
1004 let value_start = p.span().byte_range().end;
1006 if let Some(mut tt_value_end) = stream.next() {
1007 while let Some(tt) = stream.next_if(|tt| !matches!(tt, Tt::Punct(p) if p.as_char() == ';')) {
1008 tt_value_end = tt;
1009 }
1010 text_start = tt_value_end.span().byte_range().end;
1011 items.push(Item::Property {
1012 name,
1013 value: &code[value_start..text_start],
1014 });
1015 if let Some(tt_semi) = stream.next() {
1016 debug_assert!(matches!(&tt_semi, Tt::Punct(p) if p.as_char() == ';'));
1017 text_start = tt_semi.span().byte_range().end;
1018 }
1019 continue 'outer;
1020 } else {
1021 return Err(Error::new(items, "expected value"));
1022 }
1023 }
1024 } else {
1025 return Err(Error::new(items, "expected = or ;"));
1026 }
1027 } else {
1028 items.push(Item::PropertyShorthand(name));
1030 text_start = name_end;
1031 continue 'outer;
1032 }
1033 }
1034 }
1035
1036 return Err(Error::new(items, "expected attribute or property name"));
1037 }
1038
1039 items.push(Item::Text(&code[text_start..code_span.end]));
1040
1041 Ok(items)
1042 }
1043
1044 if !reverse {
1045 if !PROPERTY_NAME_RGX.is_match(&code[1..code.len() - 1]) || IGNORE_RGX.is_match(code) {
1046 return Cow::Borrowed(code);
1048 }
1049 let items = match code.parse() {
1050 Ok(t) => match parse(code, t, 1..code.len() - 1) {
1051 Ok(its) => its,
1052 Err(e) => {
1053 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1054 warn!("cannot parse widget, {}", e.error);
1056 }
1057 return Cow::Borrowed(code);
1058 }
1059 },
1060 Err(_) => return Cow::Borrowed(code),
1061 };
1062
1063 fn escape(items: &[Item], r: &mut String) {
1064 for item in items {
1065 match item {
1066 Item::Property { name, value } => {
1070 r.push_str(name); r.push_str(" =");
1072
1073 static NAMED_FIELDS_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*\{\s*\w+:\s+").unwrap());
1074 if NAMED_FIELDS_RGX.is_match(value) {
1075 r.push_str(" __ZngFmt");
1076 r.push_str(value);
1077 r.push(';');
1078 } else if value.trim() == "unset!" {
1079 r.push_str("__unset!();");
1080 } else {
1081 r.push_str(" __fmt(");
1082 r.push_str(value);
1083 r.push_str("); /*__fmt*/");
1084 }
1085 }
1086 Item::PropertyShorthand(name) => {
1087 r.push_str(name);
1088 r.push(';');
1089 }
1090 Item::When { expr, items } => {
1091 r.push_str("/*__fmt_w*/ if ");
1092 let expr = replace_expr_var(expr, false);
1094 static PROPERTY_REF_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)#([\w:]+)").unwrap());
1096 r.push_str(&PROPERTY_REF_RGX.replace_all(&expr, "__P_($1)"));
1097 r.push_str(" { /*__fmt*/");
1098 escape(items, r);
1099 r.push('}');
1100 }
1101 Item::Text(txt) => {
1102 r.push_str(txt);
1103 }
1104 Item::WidgetSetSelfExpr(expr) => {
1105 r.push_str("let __fmt_self = ");
1106 r.push_str(expr);
1107 r.push_str("; /*__zng-fmt*/");
1108 }
1109 }
1110 }
1111 }
1112 let mut escaped = "{".to_owned();
1113 escape(&items, &mut escaped);
1114 escaped.push('}');
1115 Cow::Owned(escaped)
1116 } else {
1117 let code = code
1118 .replace("= __ZngFmt {", "= {")
1119 .replace("); /*__fmt*/", ";")
1120 .replace("__unset!()", "unset!")
1121 .replace("let __fmt_self = ", "")
1122 .replace("; /*__zng-fmt*/", ";");
1123
1124 static WHEN_PREFIX_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"/\*__fmt_w\*/\s+if").unwrap());
1125 static WHEN_SUFFIX_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r" \{\s+/\*__fmt\*/").unwrap());
1126 let code = WHEN_PREFIX_REV_RGX.replace_all(&code, "when");
1127 let code = WHEN_SUFFIX_REV_RGX.replace_all(&code, " {");
1128 let code = match replace_expr_var(&code, true) {
1129 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1130 Cow::Owned(o) => Cow::Owned(o),
1131 };
1132
1133 static UNNAMED_VALUE_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)=\s+__fmt\(([^\r\n]?)").unwrap());
1136 let replaced = UNNAMED_VALUE_REV_RGX.replace_all(&code, |caps: ®ex::Captures| {
1137 let next_char = &caps[1];
1138 if next_char.is_empty() {
1139 "=".to_owned()
1140 } else {
1141 format!("= {next_char}")
1142 }
1143 });
1144 let code = match replaced {
1145 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1146 Cow::Owned(o) => Cow::Owned(o),
1147 };
1148
1149 static PROPERTY_REF_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)__P_\(([\w:]+)\)").unwrap());
1150 match PROPERTY_REF_REV_RGX.replace_all(&code, "#$1") {
1151 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1152 Cow::Owned(o) => Cow::Owned(o),
1153 }
1154 }
1155}
1156
1157fn replace_expr_var(code: &str, reverse: bool) -> Cow<'_, str> {
1159 static POUND_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(#)[\w\{]").unwrap());
1160 static POUND_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__P_!\s?").unwrap());
1161 if !reverse {
1162 POUND_RGX.replace_all(code, |caps: ®ex::Captures| {
1163 let c = &caps[0][caps.get(1).unwrap().end() - caps.get(0).unwrap().start()..];
1164 if c == "{" {
1165 Cow::Borrowed("__P_!{")
1166 } else {
1167 Cow::Owned(caps[0].to_owned())
1168 }
1169 })
1170 } else {
1171 POUND_REV_RGX.replace_all(code, "#")
1172 }
1173}
1174fn replace_when_var(code: &str, reverse: bool) -> Cow<'_, str> {
1176 if !reverse {
1177 let stream: proc_macro2::TokenStream = match code[1..code.len() - 1].parse() {
1178 Ok(s) => s,
1179 Err(_) => return Cow::Borrowed(code),
1180 };
1181 let mut arrow_at_root = false;
1182 let mut stream = stream.into_iter();
1183 while let Some(tt) = stream.next() {
1184 if let proc_macro2::TokenTree::Punct(p) = tt
1185 && p.as_char() == '='
1186 && let Some(proc_macro2::TokenTree::Punct(p2)) = stream.next()
1187 && p2.as_char() == '>'
1188 {
1189 arrow_at_root = true;
1190 break;
1191 }
1192 }
1193 if arrow_at_root {
1194 Cow::Owned(format!("static __zng_fmt__: T = match 0 {code}; /*__zng-fmt*/"))
1195 } else {
1196 Cow::Borrowed(code)
1197 }
1198 } else {
1199 Cow::Owned(
1200 code.replace("static __zng_fmt__: T = match 0 {", "{")
1201 .replace("}; /*__zng-fmt*/", "}"),
1202 )
1203 }
1204}
1205fn replace_static_ref(code: &str, reverse: bool) -> Cow<'_, str> {
1207 if !reverse {
1208 if code.contains("static ref ") {
1209 Cow::Owned(code.replace("static ref ", "static __fmt_ref__"))
1210 } else {
1211 Cow::Borrowed(code)
1212 }
1213 } else {
1214 Cow::Owned(code.replace("static __fmt_ref__", "static ref "))
1215 }
1216}
1217
1218fn replace_struct_like(code: &str, reverse: bool) -> Cow<'_, str> {
1221 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*\{\s+(\w+):([^:])").unwrap());
1222 static RGX_GENERICS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m):\s*\w+<\w").unwrap());
1223 if !reverse {
1224 if RGX.is_match(code) {
1225 if RGX_GENERICS.is_match(code) {
1226 RGX.replace_all(code, "struct __ZngFmt__ {\n$1:$2")
1228 } else {
1229 let mut r = RGX.replace_all(code, "static __fmt__: T = __A_ {\n$1:$2").into_owned();
1231
1232 const OPEN: &str = ": T = __A_ {";
1233 const CLOSE_MARKER: &str = "; /*__zng-fmt*/";
1234 let mut start = 0;
1235 while let Some(i) = r[start..].find(OPEN) {
1236 let i = start + i + OPEN.len();
1237 let mut count = 1;
1238 let mut close_i = i;
1239 for (ci, c) in r[i..].char_indices() {
1240 match c {
1241 '{' => count += 1,
1242 '}' => {
1243 count -= 1;
1244 if count == 0 {
1245 close_i = i + ci + 1;
1246 break;
1247 }
1248 }
1249 _ => {}
1250 }
1251 }
1252 r.insert_str(close_i, CLOSE_MARKER);
1253 start = close_i + CLOSE_MARKER.len();
1254 }
1255 Cow::Owned(r)
1256 }
1257 } else {
1258 Cow::Borrowed(code)
1259 }
1260 } else {
1261 Cow::Owned(
1262 code.replace("static __fmt__: T = __A_ {", "{")
1263 .replace("}; /*__zng-fmt*/", "}")
1264 .replace("struct __ZngFmt__ {", "{"),
1265 )
1266 }
1267}
1268
1269fn replace_bitflags(code: &str, reverse: bool) -> Cow<'_, str> {
1272 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)struct +(\w+): +(\w+) +\{").unwrap());
1273 static RGX_CONST: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*const +(\w+) +=").unwrap());
1274 static RGX_REV: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)static __fmt_vis__: T = T;\s+impl __fmt_(\w+)__C_(\w+) \{").unwrap());
1275 if !reverse {
1276 let mut r = RGX.replace_all(code, "static __fmt_vis__: T = T;\nimpl __fmt_${1}__C_$2 {");
1277 if let Cow::Owned(r) = &mut r
1278 && let Cow::Owned(rr) = RGX_CONST.replace_all(r, "const $1: __A_ =")
1279 {
1280 *r = rr;
1281 }
1282 r
1283 } else {
1284 let code = RGX_REV.replace_all(code, "struct ${1}: $2 {");
1285 Cow::Owned(code.replace(": __A_ =", " ="))
1286 }
1287}
1288
1289fn replace_simple_ident_list(code: &str, reverse: bool) -> Cow<'_, str> {
1291 static WORD_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\w+$").unwrap());
1292 if !reverse {
1293 assert!(code.starts_with('{') && code.ends_with('}'));
1294 let inner = &code[1..code.len() - 1];
1295 if inner.contains(',') {
1296 let mut output = "static __fmt: T = [".to_owned();
1297 for ident in inner.split(',') {
1298 let ident = ident.trim();
1299 if WORD_RGX.is_match(ident) {
1300 output.push_str(ident);
1301 output.push_str(", ");
1302 } else if ident.is_empty() {
1303 continue;
1304 } else {
1305 return Cow::Borrowed(code);
1306 }
1307 }
1308 output.push_str("];");
1309 Cow::Owned(output)
1310 } else {
1311 let mut output = "static __fmt_s: T = [".to_owned();
1312 let mut any = false;
1313 for ident in inner.split(' ') {
1314 let ident = ident.trim();
1315 if WORD_RGX.is_match(ident) {
1316 any = true;
1317 output.push_str(ident);
1318 output.push_str(", ");
1319 } else if ident.is_empty() {
1320 continue;
1321 } else {
1322 return Cow::Borrowed(code);
1323 }
1324 }
1325 if any {
1326 output.push_str("];");
1327 Cow::Owned(output)
1328 } else {
1329 Cow::Borrowed(code)
1330 }
1331 }
1332 } else {
1333 let code = if code.trim_end().contains('\n') {
1334 if code.contains("static __fmt: T = [") {
1335 code.replace("static __fmt: T = [", "{").replace("];", "}")
1336 } else {
1337 assert!(code.contains("static __fmt_s: T = ["));
1338 code.replace("static __fmt_s: T = [", "{").replace("];", "}").replace(',', "")
1339 }
1340 } else if code.contains("static __fmt: T = [") {
1341 code.replace("static __fmt: T = [", "{ ").replace("];", " }")
1342 } else {
1343 assert!(code.contains("static __fmt_s: T = ["));
1344 code.replace("static __fmt_s: T = [", "{ ").replace("];", " }").replace(',', "")
1345 };
1346 Cow::Owned(code)
1347 }
1348}
1349
1350fn replace_widget_impl(code: &str, reverse: bool) -> Cow<'_, str> {
1352 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([:\w]+\((?:\w+: .+)\));").unwrap());
1353 if !reverse {
1354 RGX.replace_all(code, |caps: ®ex::Captures| {
1355 let (a, b) = caps[1].split_once('(').unwrap();
1357 format!("fn __fmt__{}({b};", a.replace("::", "__C__"))
1358 })
1359 } else {
1360 Cow::Owned(code.replace("fn __fmt__", "").replace("__C__", "::"))
1361 }
1362}
1363
1364#[derive(Clone)]
1371struct FmtFragServer {
1372 data: Arc<Mutex<FmtFragServerData>>,
1373 edition: String,
1374}
1375struct FmtFragServerData {
1376 requests: HashMap<String, FmtFragRequest>,
1378}
1379struct FmtFragRequest {
1380 pending: bool,
1381 response: Arc<Mutex<String>>,
1382}
1383
1384impl FmtFragRequest {
1385 fn new() -> Self {
1386 Self {
1387 pending: true,
1388 response: Arc::new(Mutex::new(String::new())),
1389 }
1390 }
1391}
1392impl FmtFragServer {
1393 pub fn spawn(edition: String) -> Self {
1394 let s = Self {
1395 data: Arc::new(Mutex::new(FmtFragServerData { requests: HashMap::new() })),
1396 edition,
1397 };
1398 let s_read = s.clone();
1399 std::thread::Builder::new()
1400 .name("rustfmt-frag-server".to_owned())
1401 .spawn(move || {
1402 loop {
1403 s_read.poll();
1404 }
1405 })
1406 .unwrap();
1407 s
1408 }
1409
1410 #[track_caller]
1411 pub fn format(&self, code: String) -> impl Future<Output = Option<String>> {
1412 let res = self
1413 .data
1414 .lock()
1415 .requests
1416 .entry(code)
1417 .or_insert_with(FmtFragRequest::new)
1418 .response
1419 .clone();
1420 std::future::poll_fn(move |_cx| {
1421 let res = res.lock();
1422 match res.as_str() {
1423 "" => Poll::Pending,
1424 "#rustfmt-error#" => Poll::Ready(None),
1425 _ => Poll::Ready(Some(res.clone())),
1426 }
1427 })
1428 }
1429
1430 fn poll(&self) {
1431 let requests: Vec<_> = self
1432 .data
1433 .lock()
1434 .requests
1435 .iter_mut()
1436 .filter_map(|(k, v)| {
1437 if v.pending {
1438 v.pending = false;
1439 Some((k.clone(), v.response.clone()))
1440 } else {
1441 None
1442 }
1443 })
1444 .collect();
1445 if requests.is_empty() {
1446 std::thread::sleep(Duration::from_millis(100));
1447 return;
1448 }
1449
1450 let edition = self.edition.clone();
1451 blocking::unblock(move || {
1452 match rustfmt_stdin(&Self::wrap_batch_for_fmt(requests.iter().map(|(k, _)| k.as_str())), &edition) {
1454 Some(r) => {
1455 let r = Self::unwrap_batch_for_fmt(r, requests.len());
1456 for ((_, response), r) in requests.into_iter().zip(r) {
1457 *response.lock() = r;
1458 }
1459 }
1460 None => {
1461 if requests.len() == 1 {
1462 *requests[0].1.lock() = "#rustfmt-error#".to_owned();
1463 } else {
1464 for (request, response) in requests {
1465 let r = match rustfmt_stdin(&Self::wrap_batch_for_fmt([request].iter().map(|r| r.as_str())), &edition) {
1466 Some(f) => Self::unwrap_batch_for_fmt(f, 1).remove(0),
1467 None => "#rustfmt-error#".to_owned(),
1468 };
1469 *response.lock() = r;
1470 }
1471 }
1472 }
1473 }
1474 })
1475 .detach();
1476 }
1477
1478 const PREFIX: &str = "fn __frag__() ";
1479
1480 fn wrap_batch_for_fmt<'a>(requests: impl Iterator<Item = &'a str>) -> String {
1481 let mut s = String::new();
1482 for code in requests {
1483 s.push_str("mod __batch__ {\n#![__zng_fmt_batch_tabs]\n");
1484 if code.starts_with("{") {
1485 s.push_str(Self::PREFIX);
1486 }
1487 s.push_str(code);
1488 s.push_str("\n}");
1489 }
1490 s
1491 }
1492 fn unwrap_batch_for_fmt(fmt: String, count: usize) -> Vec<String> {
1493 let mut item = String::new();
1494 let mut r = vec![];
1495 let mut lines = fmt.lines();
1496 let mut strip_tabs = String::new();
1497 while let Some(line) = lines.next() {
1498 if line.starts_with("mod __batch__") {
1499 if !item.is_empty() {
1500 let it = item.trim();
1501 if let Some(it) = it.strip_prefix(Self::PREFIX) {
1502 r.push(it.to_owned());
1503 } else {
1504 r.push(it.to_owned());
1505 }
1506 item.clear();
1507 }
1508
1509 let tabs_line = lines.next().unwrap();
1510 assert!(tabs_line.contains("#![__zng_fmt_batch_tabs]"));
1511 let count = tabs_line.len() - tabs_line.trim_start().len();
1512 strip_tabs.clear();
1513 for _ in 0..count {
1514 strip_tabs.push(' ');
1515 }
1516 } else if line.is_empty() {
1517 item.push('\n');
1518 } else if let Some(line) = line.strip_prefix(&strip_tabs) {
1519 item.push_str(line);
1520 item.push('\n');
1521 } else if line != "}" {
1522 item.push_str(line);
1523 item.push('\n');
1524 }
1525 }
1526 if !item.is_empty() {
1527 let it = item.trim();
1528 if let Some(it) = it.strip_prefix(Self::PREFIX) {
1529 r.push(it.to_owned());
1530 } else {
1531 r.push(it.to_owned());
1532 }
1533 }
1534 assert_eq!(r.len(), count);
1535 r
1536 }
1537}
1538
1539static SHOW_RUSTFMT_ERRORS: AtomicBool = AtomicBool::new(false);
1540fn rustfmt_stdin(code: &str, edition: &str) -> Option<String> {
1541 let mut rustfmt = std::process::Command::new("rustfmt");
1542 if !SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1543 rustfmt.stderr(Stdio::null());
1544 }
1545 let mut s = rustfmt
1546 .arg("--edition")
1547 .arg(edition)
1548 .stdin(Stdio::piped())
1549 .stdout(Stdio::piped())
1550 .spawn()
1551 .ok()?;
1552 s.stdin.take().unwrap().write_all(code.as_bytes()).ok()?;
1553 let s = s.wait_with_output().ok()?;
1554
1555 if s.status.success() {
1556 let code = String::from_utf8(s.stdout).ok()?;
1557 Some(code)
1558 } else {
1559 None
1560 }
1561}
1562
1563fn rustfmt_files(files: &[PathBuf], edition: &str, check: bool) {
1564 let mut rustfmt = std::process::Command::new("rustfmt");
1565 if !SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1566 rustfmt.stderr(Stdio::null());
1567 }
1568 rustfmt.args(["--config", "skip_children=true"]);
1569 rustfmt.arg("--edition").arg(edition);
1570 if check {
1571 rustfmt.arg("--check");
1572 }
1573 let mut any = false;
1574 for file in files {
1575 if let Some(ext) = file.extension()
1576 && ext == "rs"
1577 {
1578 rustfmt.arg(file);
1579 any = true;
1580 }
1581 }
1582 if !any {
1583 return;
1584 }
1585
1586 match rustfmt.status() {
1587 Ok(s) => {
1588 if !s.success() && SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1589 error!("rustfmt error {s}");
1590 }
1591 }
1592 Err(e) => error!("{e}"),
1593 }
1594}
1595
1596#[derive(Default)]
1597struct FmtHistory {
1598 entries: Vec<(String, u128)>,
1600}
1601impl FmtHistory {
1602 const TIMESTAMP_ON_SAVE: u128 = u128::MAX;
1605
1606 const MAX_ENTRIES: usize = 30;
1607
1608 pub fn load() -> io::Result<Self> {
1609 let now = Self::time(SystemTime::now());
1610
1611 match std::fs::File::open(Self::path()?) {
1612 Ok(file) => {
1613 let reader = std::io::BufReader::new(file);
1614 let mut out = Self { entries: vec![] };
1615 for line in reader.lines().take(Self::MAX_ENTRIES) {
1616 if let Some((key, ts)) = line?.split_once(' ') {
1617 let t: u128 = ts.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
1618 if t > now {
1619 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid timestamp"));
1620 }
1621 out.entries.push((key.to_owned(), t));
1622 }
1623 }
1624 Ok(out)
1625 }
1626 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Self { entries: vec![] }),
1627 Err(e) => Err(e),
1628 }
1629 }
1630
1631 pub fn insert(&mut self, args: &FmtArgs) -> u128 {
1633 let mut args_key = sha2::Sha256::new();
1634 if let Some(f) = &args.files {
1635 args_key.update(f.as_bytes());
1636 }
1637 if let Some(f) = &args.manifest_path {
1638 args_key.update(f.as_bytes());
1639 }
1640 args_key.update(args.edition.as_bytes());
1641 let rustfmt_version = std::process::Command::new("rustfmt")
1642 .arg("--version")
1643 .output()
1644 .unwrap_or_else(|e| fatal!("{e}"));
1645 if !rustfmt_version.status.success() {
1646 fatal!("rustfmt error {}", rustfmt_version.status);
1647 }
1648 let rustfmt_version = String::from_utf8_lossy(&rustfmt_version.stdout);
1649 args_key.update(rustfmt_version.as_bytes());
1650 let args_key = format!("{FMT_VERSION}:{:x}", args_key.finalize());
1651
1652 for (key, t) in self.entries.iter_mut() {
1653 if key == &args_key {
1654 let prev_t = *t;
1655 assert_ne!(prev_t, Self::TIMESTAMP_ON_SAVE, "inserted called twice");
1656 *t = Self::TIMESTAMP_ON_SAVE;
1657 return if args.full { 0 } else { prev_t };
1658 }
1659 }
1660 self.entries.push((args_key, Self::TIMESTAMP_ON_SAVE));
1661 if self.entries.len() > Self::MAX_ENTRIES {
1662 self.entries.remove(0);
1663 }
1664 0
1665 }
1666
1667 pub fn save(&mut self) -> io::Result<()> {
1668 let now = Self::time(SystemTime::now());
1669 for (_, t) in self.entries.iter_mut() {
1670 if *t == Self::TIMESTAMP_ON_SAVE {
1671 *t = now;
1672 }
1673 }
1674
1675 let mut file = std::fs::File::create(Self::path()?)?;
1676 for (key, t) in self.entries.iter() {
1677 writeln!(&mut file, "{key} {t}")?;
1678 }
1679
1680 Ok(())
1681 }
1682
1683 pub fn time(time: SystemTime) -> u128 {
1685 time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_micros()
1686 }
1687
1688 fn path() -> io::Result<PathBuf> {
1689 let root_dir = workspace_root()?;
1690 let target_dir = root_dir.join("target");
1691 let _ = std::fs::create_dir(&target_dir);
1692 Ok(target_dir.join(".cargo-zng-fmt-history"))
1693 }
1694}
1695fn workspace_root() -> io::Result<PathBuf> {
1696 let output = std::process::Command::new("cargo")
1697 .arg("locate-project")
1698 .arg("--workspace")
1699 .arg("--message-format=plain")
1700 .stderr(Stdio::inherit())
1701 .output()?;
1702 if !output.status.success() {
1703 return Err(io::Error::new(io::ErrorKind::NotFound, "workspace root not found"));
1704 }
1705 let root_dir = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
1706 Ok(root_dir.to_owned())
1707}
1708
1709mod pm2_send {
1713 use std::{ops, str::FromStr};
1714
1715 pub use proc_macro2::Delimiter;
1716
1717 #[derive(Clone, Debug)]
1718 pub struct TokenStream(Vec<TokenTree>);
1719 impl From<proc_macro2::TokenStream> for TokenStream {
1720 fn from(value: proc_macro2::TokenStream) -> Self {
1721 Self(value.into_iter().map(Into::into).collect())
1722 }
1723 }
1724 impl FromStr for TokenStream {
1725 type Err = <proc_macro2::TokenStream as FromStr>::Err;
1726
1727 fn from_str(s: &str) -> Result<Self, Self::Err> {
1728 proc_macro2::TokenStream::from_str(s).map(Into::into)
1729 }
1730 }
1731 impl IntoIterator for TokenStream {
1732 type Item = TokenTree;
1733
1734 type IntoIter = std::vec::IntoIter<Self::Item>;
1735
1736 fn into_iter(self) -> Self::IntoIter {
1737 self.0.into_iter()
1738 }
1739 }
1740
1741 #[derive(Clone, Debug)]
1742 pub enum TokenTree {
1743 Group(Group),
1744 Ident(Ident),
1745 Punct(Punct),
1746 Other(Span),
1747 }
1748 impl From<proc_macro2::TokenTree> for TokenTree {
1749 fn from(value: proc_macro2::TokenTree) -> Self {
1750 match value {
1751 proc_macro2::TokenTree::Group(group) => Self::Group(group.into()),
1752 proc_macro2::TokenTree::Ident(ident) => Self::Ident(ident.into()),
1753 proc_macro2::TokenTree::Punct(punct) => Self::Punct(punct.into()),
1754 proc_macro2::TokenTree::Literal(literal) => Self::Other(literal.span().into()),
1755 }
1756 }
1757 }
1758 impl TokenTree {
1759 pub fn span(&self) -> Span {
1760 match self {
1761 TokenTree::Group(group) => group.span.clone(),
1762 TokenTree::Ident(ident) => ident.span.clone(),
1763 TokenTree::Punct(punct) => punct.span.clone(),
1764 TokenTree::Other(span) => span.clone(),
1765 }
1766 }
1767 }
1768
1769 #[derive(Clone, Debug)]
1770 pub struct Group {
1771 delimiter: Delimiter,
1772 span: Span,
1773 stream: TokenStream,
1774 }
1775 impl From<proc_macro2::Group> for Group {
1776 fn from(value: proc_macro2::Group) -> Self {
1777 Self {
1778 delimiter: value.delimiter(),
1779 span: value.span().into(),
1780 stream: value.stream().into(),
1781 }
1782 }
1783 }
1784 impl Group {
1785 pub fn delimiter(&self) -> Delimiter {
1786 self.delimiter
1787 }
1788
1789 pub fn span(&self) -> Span {
1790 self.span.clone()
1791 }
1792
1793 pub fn stream(&self) -> TokenStream {
1794 self.stream.clone()
1795 }
1796 }
1797
1798 #[derive(Clone, Debug)]
1799 pub struct Ident {
1800 span: Span,
1801 s: String,
1802 }
1803 impl From<proc_macro2::Ident> for Ident {
1804 fn from(value: proc_macro2::Ident) -> Self {
1805 Self {
1806 span: value.span().into(),
1807 s: value.to_string(),
1808 }
1809 }
1810 }
1811 impl<'a> PartialEq<&'a str> for Ident {
1812 fn eq(&self, other: &&'a str) -> bool {
1813 self.s == *other
1814 }
1815 }
1816
1817 #[derive(Clone, Debug)]
1818 pub struct Punct {
1819 span: Span,
1820 c: char,
1821 }
1822 impl From<proc_macro2::Punct> for Punct {
1823 fn from(value: proc_macro2::Punct) -> Self {
1824 Self {
1825 span: value.span().into(),
1826 c: value.as_char(),
1827 }
1828 }
1829 }
1830 impl Punct {
1831 pub fn as_char(&self) -> char {
1832 self.c
1833 }
1834 }
1835
1836 #[derive(Clone, Debug)]
1837 pub struct Span {
1838 byte_range: ops::Range<usize>,
1839 }
1840 impl From<proc_macro2::Span> for Span {
1841 fn from(value: proc_macro2::Span) -> Self {
1842 Self {
1843 byte_range: value.byte_range(),
1844 }
1845 }
1846 }
1847 impl Span {
1848 pub fn byte_range(&self) -> ops::Range<usize> {
1849 self.byte_range.clone()
1850 }
1851 }
1852}