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 }
590 }
591 }
592 } else if !tail2.is_empty()
593 && matches!(g.delimiter(), pm2_send::Delimiter::Bracket)
594 && matches!(&tail2[0], pm2_send::TokenTree::Punct(p) if p.as_char() == '#')
595 {
596 let mut attr = g.stream().into_iter();
598 let attr = [attr.next(), attr.next(), attr.next(), attr.next(), attr.next()];
599 if let [
600 Some(pm2_send::TokenTree::Ident(i0)),
601 Some(pm2_send::TokenTree::Punct(p0)),
602 Some(pm2_send::TokenTree::Punct(p1)),
603 Some(pm2_send::TokenTree::Ident(i1)),
604 None,
605 ] = attr
606 && i0 == "rustfmt"
607 && p0.as_char() == ':'
608 && p1.as_char() == ':'
609 && i1 == "skip"
610 {
611 skip_next_group = true;
613 }
614 } else if !std::mem::take(&mut skip_next_group) {
615 stream_stack.push(g.stream().into_iter());
616 }
617 tail2.clear();
618 }
619 ref tt1 @ pm2_send::TokenTree::Ident(ref i) if i == &"macro_rules" => {
620 if let Some(tt2) = next(&mut stream_stack) {
621 if matches!(tt2, pm2_send::TokenTree::Punct(ref p) if p.as_char() == '!') {
622 next(&mut stream_stack); next(&mut stream_stack); } else {
626 tail2.clear();
627 tail2.push(tt2);
628 tail2.push(tt1.clone());
629 }
630 } else {
631 }
633 }
634 tt => {
635 if tail2.len() == 2 {
636 tail2.pop();
637 }
638 tail2.insert(0, tt);
639 }
640 }
641 }
642
643 formatted_code.push_str(&code[last_already_fmt_start..]);
644 formatted_code
645}
646
647async fn try_fmt_macro(base_indent: usize, group_code: &str, fmt: &FmtFragServer) -> Option<String> {
648 let mut replaced_code = Cow::Borrowed(group_code);
650
651 let mut is_lazy_static = false;
652 if matches!(&replaced_code, Cow::Borrowed(_)) {
653 replaced_code = replace_static_ref(group_code, false);
654 is_lazy_static = matches!(&replaced_code, Cow::Owned(_));
655 }
656
657 let mut is_bitflags = false;
658 if matches!(&replaced_code, Cow::Borrowed(_)) {
659 replaced_code = replace_bitflags(group_code, false);
660 is_bitflags = matches!(&replaced_code, Cow::Owned(_));
661 }
662
663 let mut is_event_args = false;
664 if matches!(&replaced_code, Cow::Borrowed(_)) {
665 replaced_code = replace_event_args(group_code, false);
666 is_event_args = matches!(&replaced_code, Cow::Owned(_));
667 }
668
669 let mut is_command = false;
670 if matches!(&replaced_code, Cow::Borrowed(_)) {
671 replaced_code = replace_command(group_code, false);
672 is_command = matches!(&replaced_code, Cow::Owned(_));
673 }
674
675 let mut is_event_property = false;
676 if matches!(&replaced_code, Cow::Borrowed(_)) {
677 replaced_code = replace_event_property(group_code, false);
678 is_event_property = matches!(&replaced_code, Cow::Owned(_));
679 }
680
681 let mut is_widget_impl = false;
682 if matches!(&replaced_code, Cow::Borrowed(_)) {
683 replaced_code = replace_widget_impl(group_code, false);
684 is_widget_impl = matches!(&replaced_code, Cow::Owned(_));
685 }
686
687 let mut is_widget = false;
688 if matches!(&replaced_code, Cow::Borrowed(_)) {
689 replaced_code = replace_widget(group_code, false);
690 is_widget = matches!(&replaced_code, Cow::Owned(_));
691 }
692
693 let mut is_expr_var = false;
694 if matches!(&replaced_code, Cow::Borrowed(_)) {
695 replaced_code = replace_expr_var(group_code, false);
696 is_expr_var = matches!(&replaced_code, Cow::Owned(_));
697 }
698
699 let mut is_struct_like = false;
700 if matches!(&replaced_code, Cow::Borrowed(_)) {
701 replaced_code = replace_struct_like(group_code, false);
702 is_struct_like = matches!(&replaced_code, Cow::Owned(_));
703 }
704
705 let mut is_simple_list = false;
706 if matches!(&replaced_code, Cow::Borrowed(_)) {
707 replaced_code = replace_simple_ident_list(group_code, false);
708 is_simple_list = matches!(&replaced_code, Cow::Owned(_));
709 }
710
711 let mut is_when_var = false;
712 if matches!(&replaced_code, Cow::Borrowed(_)) {
713 replaced_code = replace_when_var(group_code, false);
714 is_when_var = matches!(&replaced_code, Cow::Owned(_));
715 }
716
717 let replaced_code = replaced_code.into_owned();
718 let code_stream: pm2_send::TokenStream = match replaced_code.parse() {
720 Ok(t) => t,
721 Err(e) => {
722 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
723 error!("internal error: {e}");
724 eprintln!("CODE:\n{replaced_code}");
725 }
726 return None;
727 }
728 };
729 let mut inner_group = None;
730 for tt in code_stream {
731 if let pm2_send::TokenTree::Group(g) = tt {
732 inner_group = Some(g);
734 break;
735 }
736 }
737 let code_stream = match inner_group {
738 Some(g) => g.stream(),
739 None => {
740 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
741 error!("internal error, invalid replacement");
742 eprintln!("CODE:\n{replaced_code}");
743 }
744 return None;
745 }
746 };
747 let code = Box::pin(try_fmt_child_macros(&replaced_code, code_stream, fmt)).await;
748
749 let code = fmt.format(code).await?;
751
752 let code = if is_event_args {
754 replace_event_args(&code, true)
755 } else if is_widget {
756 replace_widget(&code, true)
757 } else if is_expr_var {
758 replace_expr_var(&code, true)
759 } else if is_lazy_static {
760 replace_static_ref(&code, true)
761 } else if is_command {
762 replace_command(&code, true)
763 } else if is_event_property {
764 replace_event_property(&code, true)
765 } else if is_struct_like {
766 replace_struct_like(&code, true)
767 } else if is_bitflags {
768 replace_bitflags(&code, true)
769 } else if is_simple_list {
770 replace_simple_ident_list(&code, true)
771 } else if is_widget_impl {
772 replace_widget_impl(&code, true)
773 } else if is_when_var {
774 replace_when_var(&code, true)
775 } else {
776 Cow::Owned(code)
777 };
778
779 let mut out = String::new();
781 let mut lb_indent = String::with_capacity(base_indent + 1);
782 for line in code.lines() {
783 if line.is_empty() {
784 if !lb_indent.is_empty() {
785 out.push('\n');
786 }
787 } else {
788 out.push_str(&lb_indent);
789 }
790 out.push_str(line);
791 if lb_indent.is_empty() {
793 lb_indent.push('\n');
794 for _ in 0..base_indent {
795 lb_indent.push(' ');
796 }
797 }
798 }
799 Some(out)
800}
801fn replace_event_args(code: &str, reverse: bool) -> Cow<'_, str> {
810 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*(\.\.)\s*$").unwrap());
811 static MARKER: &str = "// cargo-zng::fmt::dot_dot\n}\nimpl CargoZngFmt {\n";
812 static RGX_REV: Lazy<Regex> =
813 Lazy::new(|| Regex::new(r"(?m)^(\s+)// cargo-zng::fmt::dot_dot\n\s*}\n\s*impl CargoZngFmt\s*\{\n").unwrap());
814
815 if !reverse {
816 RGX.replace_all(code, |caps: ®ex::Captures| {
817 format!(
818 "{}{MARKER}{}",
819 &caps[0][..caps.get(1).unwrap().start() - caps.get(0).unwrap().start()],
820 &caps[0][caps.get(1).unwrap().end() - caps.get(0).unwrap().start()..]
821 )
822 })
823 } else {
824 RGX_REV.replace_all(code, "\n$1..\n\n")
825 }
826}
827fn replace_command(code: &str, reverse: bool) -> Cow<'_, str> {
831 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+) ?= ?\{").unwrap());
832 static RGX_DEFAULTS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([^'])static +(\w+) ?;").unwrap());
833 if !reverse {
834 let cmd = RGX_DEFAULTS.replace_all(code, "${1}static $2: __fmt__ = T;");
835 let mut cmd2 = RGX.replace_all(&cmd, "${1}static $2: __fmt__ = __A_ {");
836 if let Cow::Owned(cmd) = &mut cmd2 {
837 *cmd = cmd.replace("l10n!:", "l10n__fmt:");
838 }
839 match cmd2 {
840 Cow::Borrowed(_) => cmd,
841 Cow::Owned(s) => Cow::Owned(s),
842 }
843 } else {
844 Cow::Owned(
845 code.replace(": __fmt__ = T;", ";")
846 .replace(": __fmt__ = __A_ {", " = {")
847 .replace("l10n__fmt:", "l10n!:"),
848 )
849 }
850}
851fn replace_event_property(code: &str, reverse: bool) -> Cow<'_, str> {
853 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m) fn +(\w+) +\{").unwrap());
854 if !reverse {
855 let mut r = RGX.replace_all(code, " static __fmt_fn__$1: T = __A_ {");
856 if let Cow::Owned(r) = &mut r {
857 const OPEN: &str = ": T = __A_ {";
858 const CLOSE_MARKER: &str = "; /*__fmt*/";
859 let mut start = 0;
860 while let Some(i) = r[start..].find(OPEN) {
861 let i = start + i + OPEN.len();
862 let mut count = 1;
863 let mut close_i = i;
864 for (ci, c) in r[i..].char_indices() {
865 match c {
866 '{' => count += 1,
867 '}' => {
868 count -= 1;
869 if count == 0 {
870 close_i = i + ci + 1;
871 break;
872 }
873 }
874 _ => {}
875 }
876 }
877 r.insert_str(close_i, CLOSE_MARKER);
878 start = close_i + CLOSE_MARKER.len();
879 }
880 }
881 r
882 } else {
883 Cow::Owned(
884 code.replace(" static __fmt_fn__", " fn ")
885 .replace(": T = __A_ {", " {")
886 .replace("}; /*__fmt*/", "}"),
887 )
888 }
889}
890fn replace_widget(code: &str, reverse: bool) -> Cow<'_, str> {
892 static IGNORE_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m): +\w+\s+=\s+\{").unwrap());
893 static PROPERTY_NAME_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*([\w:<>]+)\s+=\s+").unwrap());
894
895 #[derive(Debug)]
896 enum Item<'s> {
897 Property { name: &'s str, value: &'s str },
898 PropertyShorthand(&'s str),
899 When { expr: &'s str, items: Vec<Item<'s>> },
900 Text(&'s str),
901 WidgetSetSelfExpr(&'s str),
902 }
903 #[allow(unused)]
904 struct Error<'s> {
905 partial: Vec<Item<'s>>,
906 error: &'static str,
907 }
908 impl<'s> Error<'s> {
909 fn new(partial: Vec<Item<'s>>, error: &'static str) -> Self {
910 Self { partial, error }
911 }
912 }
913 fn parse<'s>(code: &'s str, stream: proc_macro2::TokenStream, code_span: ops::Range<usize>) -> Result<Vec<Item<'s>>, Error<'s>> {
914 use proc_macro2::{Delimiter, TokenTree as Tt};
915
916 let mut items = vec![];
917 let mut stream = stream.into_iter().peekable();
918 let mut text_start = code_span.start;
919
920 if code_span.start == 1 {
921 if let Some(Tt::Group(g)) = stream.next()
922 && g.delimiter() == Delimiter::Brace
923 {
924 stream = g.stream().into_iter().peekable();
925 } else {
926 return Err(Error::new(items, "expected macro block at root"));
927 }
928
929 if let Some(Tt::Punct(p)) = stream.peek()
930 && p.as_char() == '&'
931 {
932 let amp = stream.next().unwrap();
934 if let Some(Tt::Ident(m)) = stream.next()
935 && m == "mut"
936 {
937 let start = amp.span().byte_range().start;
938 for tt in stream.by_ref() {
939 if let Tt::Punct(p) = tt
940 && p.as_char() == ';'
941 {
942 let end = p.span().byte_range().end;
943 items.push(Item::Text(&code[text_start..start]));
944 items.push(Item::WidgetSetSelfExpr(&code[start..end]));
945 text_start = end;
946 break;
947 }
948 }
949 }
950 if text_start == code_span.start {
951 return Err(Error::new(items, "expected &mut <self>"));
952 }
953 }
954 }
955 'outer: while let Some(tt_attr_or_name) = stream.next() {
956 if let Tt::Punct(p) = &tt_attr_or_name
958 && p.as_char() == '#'
959 {
960 if let Some(Tt::Group(g)) = stream.next()
961 && g.delimiter() == proc_macro2::Delimiter::Bracket
962 {
963 continue 'outer;
964 } else {
965 return Err(Error::new(items, "expected attribute"));
966 }
967 }
968
969 if let Tt::Ident(ident) = &tt_attr_or_name {
971 if ident == "when" {
972 items.push(Item::Text(&code[text_start..ident.span().byte_range().start]));
973
974 let expr_start = ident.span().byte_range().end;
977 if stream.next().is_some()
978 && let Some(mut tt_block) = stream.next()
979 {
980 loop {
982 if let Tt::Group(g) = &tt_block
983 && g.delimiter() == Delimiter::Brace
984 && stream
985 .peek()
986 .map(|tt| matches!(tt, Tt::Ident(_)) || matches!(tt, Tt::Punct(p) if p.as_char() == '#'))
987 .unwrap_or(true)
988 {
989 let block_span = g.span().byte_range();
991 let expr = &code[expr_start..block_span.start];
992 items.push(Item::When {
993 expr,
994 items: parse(code, g.stream(), g.span_open().byte_range().end..g.span_close().byte_range().start)?,
995 });
996 text_start = block_span.end;
997 continue 'outer;
998 }
999 if let Some(tt) = stream.next() {
1001 tt_block = tt;
1002 } else {
1003 break;
1004 }
1005 }
1006 } else {
1007 return Err(Error::new(items, "expected when expression and block"));
1008 }
1009 } else {
1010 let name_start = tt_attr_or_name.span().byte_range().start;
1012 let mut tt_name_end = tt_attr_or_name;
1013 while let Some(tt) = stream
1014 .next_if(|tt| matches!(tt, Tt::Ident(_)) || matches!(tt, Tt::Punct(p) if [':', '<', '>'].contains(&p.as_char())))
1015 {
1016 tt_name_end = tt;
1017 }
1018
1019 items.push(Item::Text(&code[text_start..name_start]));
1020
1021 let name_end = tt_name_end.span().byte_range().end;
1022 let name = &code[name_start..name_end];
1023 if name.is_empty() {
1024 return Err(Error::new(items, "expected property name"));
1025 }
1026
1027 if let Some(tt_punct) = stream.next() {
1028 if let Tt::Punct(p) = tt_punct {
1029 if p.as_char() == ';' {
1030 items.push(Item::PropertyShorthand(name));
1031 text_start = p.span().byte_range().end;
1032 continue 'outer;
1033 } else if p.as_char() == '=' {
1034 let value_start = p.span().byte_range().end;
1036 if let Some(mut tt_value_end) = stream.next() {
1037 while let Some(tt) = stream.next_if(|tt| !matches!(tt, Tt::Punct(p) if p.as_char() == ';')) {
1038 tt_value_end = tt;
1039 }
1040 text_start = tt_value_end.span().byte_range().end;
1041 items.push(Item::Property {
1042 name,
1043 value: &code[value_start..text_start],
1044 });
1045 if let Some(tt_semi) = stream.next() {
1046 debug_assert!(matches!(&tt_semi, Tt::Punct(p) if p.as_char() == ';'));
1047 text_start = tt_semi.span().byte_range().end;
1048 }
1049 continue 'outer;
1050 } else {
1051 return Err(Error::new(items, "expected value"));
1052 }
1053 }
1054 } else {
1055 return Err(Error::new(items, "expected = or ;"));
1056 }
1057 } else {
1058 items.push(Item::PropertyShorthand(name));
1060 text_start = name_end;
1061 continue 'outer;
1062 }
1063 }
1064 }
1065
1066 return Err(Error::new(items, "expected attribute or property name"));
1067 }
1068
1069 items.push(Item::Text(&code[text_start..code_span.end]));
1070
1071 Ok(items)
1072 }
1073
1074 if !reverse {
1075 if !PROPERTY_NAME_RGX.is_match(&code[1..code.len() - 1]) || IGNORE_RGX.is_match(code) {
1076 return Cow::Borrowed(code);
1078 }
1079 let items = match code.parse() {
1080 Ok(t) => match parse(code, t, 1..code.len() - 1) {
1081 Ok(its) => its,
1082 Err(e) => {
1083 if SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1084 warn!("cannot parse widget, {}", e.error);
1086 }
1087 return Cow::Borrowed(code);
1088 }
1089 },
1090 Err(_) => return Cow::Borrowed(code),
1091 };
1092
1093 fn escape(items: &[Item], r: &mut String) {
1094 for item in items {
1095 match item {
1096 Item::Property { name, value } => {
1100 r.push_str(name); r.push_str(" =");
1102
1103 static NAMED_FIELDS_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*\{\s*\w+:\s+").unwrap());
1104 if NAMED_FIELDS_RGX.is_match(value) {
1105 r.push_str(" __ZngFmt");
1106 r.push_str(value);
1107 r.push(';');
1108 } else if value.trim() == "unset!" {
1109 r.push_str("__unset!();");
1110 } else {
1111 r.push_str(" __fmt(");
1112 r.push_str(value);
1113 r.push_str("); /*__fmt*/");
1114 }
1115 }
1116 Item::PropertyShorthand(name) => {
1117 r.push_str(name);
1118 r.push(';');
1119 }
1120 Item::When { expr, items } => {
1121 r.push_str("/*__fmt_w*/ if ");
1122 let expr = replace_expr_var(expr, false);
1124 static PROPERTY_REF_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)#([\w:]+)").unwrap());
1126 r.push_str(&PROPERTY_REF_RGX.replace_all(&expr, "__P_($1)"));
1127 r.push_str(" { /*__fmt*/");
1128 escape(items, r);
1129 r.push('}');
1130 }
1131 Item::Text(txt) => {
1132 r.push_str(txt);
1133 }
1134 Item::WidgetSetSelfExpr(expr) => {
1135 r.push_str("let __fmt_self = ");
1136 r.push_str(expr);
1137 r.push_str("; /*__zng-fmt*/");
1138 }
1139 }
1140 }
1141 }
1142 let mut escaped = "{".to_owned();
1143 escape(&items, &mut escaped);
1144 escaped.push('}');
1145 Cow::Owned(escaped)
1146 } else {
1147 let code = code
1148 .replace("= __ZngFmt {", "= {")
1149 .replace("); /*__fmt*/", ";")
1150 .replace("__unset!()", "unset!")
1151 .replace("let __fmt_self = ", "")
1152 .replace("; /*__zng-fmt*/", ";");
1153
1154 static WHEN_PREFIX_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"/\*__fmt_w\*/\s+if").unwrap());
1155 static WHEN_SUFFIX_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r" \{\s+/\*__fmt\*/").unwrap());
1156 let code = WHEN_PREFIX_REV_RGX.replace_all(&code, "when");
1157 let code = WHEN_SUFFIX_REV_RGX.replace_all(&code, " {");
1158 let code = match replace_expr_var(&code, true) {
1159 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1160 Cow::Owned(o) => Cow::Owned(o),
1161 };
1162
1163 static UNNAMED_VALUE_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)=\s+__fmt\(([^\r\n]?)").unwrap());
1166 let replaced = UNNAMED_VALUE_REV_RGX.replace_all(&code, |caps: ®ex::Captures| {
1167 let next_char = &caps[1];
1168 if next_char.is_empty() {
1169 "=".to_owned()
1170 } else {
1171 format!("= {next_char}")
1172 }
1173 });
1174 let code = match replaced {
1175 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1176 Cow::Owned(o) => Cow::Owned(o),
1177 };
1178
1179 static PROPERTY_REF_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)__P_\(([\w:]+)\)").unwrap());
1180 match PROPERTY_REF_REV_RGX.replace_all(&code, "#$1") {
1181 Cow::Borrowed(_) => Cow::Owned(code.into_owned()),
1182 Cow::Owned(o) => Cow::Owned(o),
1183 }
1184 }
1185}
1186
1187fn replace_expr_var(code: &str, reverse: bool) -> Cow<'_, str> {
1189 static POUND_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(#)[\w\{]").unwrap());
1190 static POUND_REV_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__P_!\s?").unwrap());
1191 if !reverse {
1192 POUND_RGX.replace_all(code, |caps: ®ex::Captures| {
1193 let c = &caps[0][caps.get(1).unwrap().end() - caps.get(0).unwrap().start()..];
1194 if c == "{" {
1195 Cow::Borrowed("__P_!{")
1196 } else {
1197 Cow::Owned(caps[0].to_owned())
1198 }
1199 })
1200 } else {
1201 POUND_REV_RGX.replace_all(code, "#")
1202 }
1203}
1204fn replace_when_var(code: &str, reverse: bool) -> Cow<'_, str> {
1206 if !reverse {
1207 let stream: proc_macro2::TokenStream = match code[1..code.len() - 1].parse() {
1208 Ok(s) => s,
1209 Err(_) => return Cow::Borrowed(code),
1210 };
1211 let mut arrow_at_root = false;
1212 let mut stream = stream.into_iter();
1213 while let Some(tt) = stream.next() {
1214 if let proc_macro2::TokenTree::Punct(p) = tt
1215 && p.as_char() == '='
1216 && let Some(proc_macro2::TokenTree::Punct(p2)) = stream.next()
1217 && p2.as_char() == '>'
1218 {
1219 arrow_at_root = true;
1220 break;
1221 }
1222 }
1223 if arrow_at_root {
1224 Cow::Owned(format!("static __zng_fmt__: T = match 0 {code}; /*__zng-fmt*/"))
1225 } else {
1226 Cow::Borrowed(code)
1227 }
1228 } else {
1229 Cow::Owned(
1230 code.replace("static __zng_fmt__: T = match 0 {", "{")
1231 .replace("}; /*__zng-fmt*/", "}"),
1232 )
1233 }
1234}
1235fn replace_static_ref(code: &str, reverse: bool) -> Cow<'_, str> {
1237 if !reverse {
1238 if code.contains("static ref ") {
1239 Cow::Owned(code.replace("static ref ", "static __fmt_ref__"))
1240 } else {
1241 Cow::Borrowed(code)
1242 }
1243 } else {
1244 Cow::Owned(code.replace("static __fmt_ref__", "static ref "))
1245 }
1246}
1247
1248fn replace_struct_like(code: &str, reverse: bool) -> Cow<'_, str> {
1251 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*\{\s+(\w+):([^:])").unwrap());
1252 static RGX_GENERICS: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m):\s*\w+<\w").unwrap());
1253 if !reverse {
1254 if RGX.is_match(code) {
1255 if RGX_GENERICS.is_match(code) {
1256 RGX.replace_all(code, "struct __ZngFmt__ {\n$1:$2")
1258 } else {
1259 let mut r = RGX.replace_all(code, "static __fmt__: T = __A_ {\n$1:$2").into_owned();
1261
1262 const OPEN: &str = ": T = __A_ {";
1263 const CLOSE_MARKER: &str = "; /*__zng-fmt*/";
1264 let mut start = 0;
1265 while let Some(i) = r[start..].find(OPEN) {
1266 let i = start + i + OPEN.len();
1267 let mut count = 1;
1268 let mut close_i = i;
1269 for (ci, c) in r[i..].char_indices() {
1270 match c {
1271 '{' => count += 1,
1272 '}' => {
1273 count -= 1;
1274 if count == 0 {
1275 close_i = i + ci + 1;
1276 break;
1277 }
1278 }
1279 _ => {}
1280 }
1281 }
1282 r.insert_str(close_i, CLOSE_MARKER);
1283 start = close_i + CLOSE_MARKER.len();
1284 }
1285 Cow::Owned(r)
1286 }
1287 } else {
1288 Cow::Borrowed(code)
1289 }
1290 } else {
1291 Cow::Owned(
1292 code.replace("static __fmt__: T = __A_ {", "{")
1293 .replace("}; /*__zng-fmt*/", "}")
1294 .replace("struct __ZngFmt__ {", "{"),
1295 )
1296 }
1297}
1298
1299fn replace_bitflags(code: &str, reverse: bool) -> Cow<'_, str> {
1302 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)struct +(\w+): +(\w+) +\{").unwrap());
1303 static RGX_CONST: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s*const +(\w+) +=").unwrap());
1304 static RGX_REV: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)static __fmt_vis__: T = T;\s+impl __fmt_(\w+)__C_(\w+) \{").unwrap());
1305 if !reverse {
1306 let mut r = RGX.replace_all(code, "static __fmt_vis__: T = T;\nimpl __fmt_${1}__C_$2 {");
1307 if let Cow::Owned(r) = &mut r
1308 && let Cow::Owned(rr) = RGX_CONST.replace_all(r, "const $1: __A_ =")
1309 {
1310 *r = rr;
1311 }
1312 r
1313 } else {
1314 let code = RGX_REV.replace_all(code, "struct ${1}: $2 {");
1315 Cow::Owned(code.replace(": __A_ =", " ="))
1316 }
1317}
1318
1319fn replace_simple_ident_list(code: &str, reverse: bool) -> Cow<'_, str> {
1321 static WORD_RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\w+$").unwrap());
1322 if !reverse {
1323 assert!(code.starts_with('{') && code.ends_with('}'));
1324 let inner = &code[1..code.len() - 1];
1325 if inner.contains(',') {
1326 let mut output = "static __fmt: T = [".to_owned();
1327 for ident in inner.split(',') {
1328 let ident = ident.trim();
1329 if WORD_RGX.is_match(ident) {
1330 output.push_str(ident);
1331 output.push_str(", ");
1332 } else if ident.is_empty() {
1333 continue;
1334 } else {
1335 return Cow::Borrowed(code);
1336 }
1337 }
1338 output.push_str("];");
1339 Cow::Owned(output)
1340 } else {
1341 let mut output = "static __fmt_s: T = [".to_owned();
1342 let mut any = false;
1343 for ident in inner.split(' ') {
1344 let ident = ident.trim();
1345 if WORD_RGX.is_match(ident) {
1346 any = true;
1347 output.push_str(ident);
1348 output.push_str(", ");
1349 } else if ident.is_empty() {
1350 continue;
1351 } else {
1352 return Cow::Borrowed(code);
1353 }
1354 }
1355 if any {
1356 output.push_str("];");
1357 Cow::Owned(output)
1358 } else {
1359 Cow::Borrowed(code)
1360 }
1361 }
1362 } else {
1363 let code = if code.trim_end().contains('\n') {
1364 if code.contains("static __fmt: T = [") {
1365 code.replace("static __fmt: T = [", "{").replace("];", "}")
1366 } else {
1367 assert!(code.contains("static __fmt_s: T = ["));
1368 code.replace("static __fmt_s: T = [", "{").replace("];", "}").replace(',', "")
1369 }
1370 } else if code.contains("static __fmt: T = [") {
1371 code.replace("static __fmt: T = [", "{ ").replace("];", " }")
1372 } else {
1373 assert!(code.contains("static __fmt_s: T = ["));
1374 code.replace("static __fmt_s: T = [", "{ ").replace("];", " }").replace(',', "")
1375 };
1376 Cow::Owned(code)
1377 }
1378}
1379
1380fn replace_widget_impl(code: &str, reverse: bool) -> Cow<'_, str> {
1382 static RGX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)([:\w]+\((?:\w+: .+)\));").unwrap());
1383 if !reverse {
1384 RGX.replace_all(code, |caps: ®ex::Captures| {
1385 let (a, b) = caps[1].split_once('(').unwrap();
1387 format!("fn __fmt__{}({b};", a.replace("::", "__C__"))
1388 })
1389 } else {
1390 Cow::Owned(code.replace("fn __fmt__", "").replace("__C__", "::"))
1391 }
1392}
1393
1394#[derive(Clone)]
1401struct FmtFragServer {
1402 data: Arc<Mutex<FmtFragServerData>>,
1403 edition: String,
1404}
1405struct FmtFragServerData {
1406 requests: HashMap<String, FmtFragRequest>,
1408}
1409struct FmtFragRequest {
1410 pending: bool,
1411 response: Arc<Mutex<String>>,
1412}
1413
1414impl FmtFragRequest {
1415 fn new() -> Self {
1416 Self {
1417 pending: true,
1418 response: Arc::new(Mutex::new(String::new())),
1419 }
1420 }
1421}
1422impl FmtFragServer {
1423 pub fn spawn(edition: String) -> Self {
1424 let s = Self {
1425 data: Arc::new(Mutex::new(FmtFragServerData { requests: HashMap::new() })),
1426 edition,
1427 };
1428 let s_read = s.clone();
1429 std::thread::Builder::new()
1430 .name("rustfmt-frag-server".to_owned())
1431 .spawn(move || {
1432 loop {
1433 s_read.poll();
1434 }
1435 })
1436 .unwrap();
1437 s
1438 }
1439
1440 #[track_caller]
1441 pub fn format(&self, code: String) -> impl Future<Output = Option<String>> {
1442 let res = self
1443 .data
1444 .lock()
1445 .requests
1446 .entry(code)
1447 .or_insert_with(FmtFragRequest::new)
1448 .response
1449 .clone();
1450 std::future::poll_fn(move |_cx| {
1451 let res = res.lock();
1452 match res.as_str() {
1453 "" => Poll::Pending,
1454 "#rustfmt-error#" => Poll::Ready(None),
1455 _ => Poll::Ready(Some(res.clone())),
1456 }
1457 })
1458 }
1459
1460 fn poll(&self) {
1461 let requests: Vec<_> = self
1462 .data
1463 .lock()
1464 .requests
1465 .iter_mut()
1466 .filter_map(|(k, v)| {
1467 if v.pending {
1468 v.pending = false;
1469 Some((k.clone(), v.response.clone()))
1470 } else {
1471 None
1472 }
1473 })
1474 .collect();
1475 if requests.is_empty() {
1476 std::thread::sleep(Duration::from_millis(100));
1477 return;
1478 }
1479
1480 let edition = self.edition.clone();
1481 blocking::unblock(move || {
1482 match rustfmt_stdin(&Self::wrap_batch_for_fmt(requests.iter().map(|(k, _)| k.as_str())), &edition) {
1484 Some(r) => {
1485 let r = Self::unwrap_batch_for_fmt(r, requests.len());
1486 for ((_, response), r) in requests.into_iter().zip(r) {
1487 *response.lock() = r;
1488 }
1489 }
1490 None => {
1491 if requests.len() == 1 {
1492 *requests[0].1.lock() = "#rustfmt-error#".to_owned();
1493 } else {
1494 for (request, response) in requests {
1495 let r = match rustfmt_stdin(&Self::wrap_batch_for_fmt([request].iter().map(|r| r.as_str())), &edition) {
1496 Some(f) => Self::unwrap_batch_for_fmt(f, 1).remove(0),
1497 None => "#rustfmt-error#".to_owned(),
1498 };
1499 *response.lock() = r;
1500 }
1501 }
1502 }
1503 }
1504 })
1505 .detach();
1506 }
1507
1508 const PREFIX: &str = "fn __frag__() ";
1509
1510 fn wrap_batch_for_fmt<'a>(requests: impl Iterator<Item = &'a str>) -> String {
1511 let mut s = String::new();
1512 for code in requests {
1513 s.push_str("mod __batch__ {\n#![__zng_fmt_batch_tabs]\n");
1514 if code.starts_with("{") {
1515 s.push_str(Self::PREFIX);
1516 }
1517 s.push_str(code);
1518 s.push_str("\n}");
1519 }
1520 s
1521 }
1522 fn unwrap_batch_for_fmt(fmt: String, count: usize) -> Vec<String> {
1523 let mut item = String::new();
1524 let mut r = vec![];
1525 let mut lines = fmt.lines();
1526 let mut strip_tabs = String::new();
1527 while let Some(line) = lines.next() {
1528 if line.starts_with("mod __batch__") {
1529 if !item.is_empty() {
1530 let it = item.trim();
1531 if let Some(it) = it.strip_prefix(Self::PREFIX) {
1532 r.push(it.to_owned());
1533 } else {
1534 r.push(it.to_owned());
1535 }
1536 item.clear();
1537 }
1538
1539 let tabs_line = lines.next().unwrap();
1540 assert!(tabs_line.contains("#![__zng_fmt_batch_tabs]"));
1541 let count = tabs_line.len() - tabs_line.trim_start().len();
1542 strip_tabs.clear();
1543 for _ in 0..count {
1544 strip_tabs.push(' ');
1545 }
1546 } else if line.is_empty() {
1547 item.push('\n');
1548 } else if let Some(line) = line.strip_prefix(&strip_tabs) {
1549 item.push_str(line);
1550 item.push('\n');
1551 } else if line != "}" {
1552 item.push_str(line);
1553 item.push('\n');
1554 }
1555 }
1556 if !item.is_empty() {
1557 let it = item.trim();
1558 if let Some(it) = it.strip_prefix(Self::PREFIX) {
1559 r.push(it.to_owned());
1560 } else {
1561 r.push(it.to_owned());
1562 }
1563 }
1564 assert_eq!(r.len(), count);
1565 r
1566 }
1567}
1568
1569static SHOW_RUSTFMT_ERRORS: AtomicBool = AtomicBool::new(false);
1570fn rustfmt_stdin(code: &str, edition: &str) -> Option<String> {
1571 let mut rustfmt = std::process::Command::new("rustfmt");
1572 if !SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1573 rustfmt.stderr(Stdio::null());
1574 }
1575 let mut s = rustfmt
1576 .arg("--edition")
1577 .arg(edition)
1578 .stdin(Stdio::piped())
1579 .stdout(Stdio::piped())
1580 .spawn()
1581 .ok()?;
1582 s.stdin.take().unwrap().write_all(code.as_bytes()).ok()?;
1583 let s = s.wait_with_output().ok()?;
1584
1585 if s.status.success() {
1586 let code = String::from_utf8(s.stdout).ok()?;
1587 Some(code)
1588 } else {
1589 None
1590 }
1591}
1592
1593fn rustfmt_files(files: &[PathBuf], edition: &str, check: bool) {
1594 let mut rustfmt = std::process::Command::new("rustfmt");
1595 if !SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1596 rustfmt.stderr(Stdio::null());
1597 }
1598 rustfmt.args(["--config", "skip_children=true"]);
1599 rustfmt.arg("--edition").arg(edition);
1600 if check {
1601 rustfmt.arg("--check");
1602 }
1603 let mut any = false;
1604 for file in files {
1605 if let Some(ext) = file.extension()
1606 && ext == "rs"
1607 {
1608 rustfmt.arg(file);
1609 any = true;
1610 }
1611 }
1612 if !any {
1613 return;
1614 }
1615
1616 match rustfmt.status() {
1617 Ok(s) => {
1618 if !s.success() && SHOW_RUSTFMT_ERRORS.load(Relaxed) {
1619 error!("rustfmt error {s}");
1620 }
1621 }
1622 Err(e) => error!("{e}"),
1623 }
1624}
1625
1626#[derive(Default)]
1627struct FmtHistory {
1628 entries: Vec<(String, u128)>,
1630}
1631impl FmtHistory {
1632 const TIMESTAMP_ON_SAVE: u128 = u128::MAX;
1635
1636 const MAX_ENTRIES: usize = 30;
1637
1638 pub fn load() -> io::Result<Self> {
1639 let now = Self::time(SystemTime::now());
1640
1641 match std::fs::File::open(Self::path()?) {
1642 Ok(file) => {
1643 let reader = std::io::BufReader::new(file);
1644 let mut out = Self { entries: vec![] };
1645 for line in reader.lines().take(Self::MAX_ENTRIES) {
1646 if let Some((key, ts)) = line?.split_once(' ') {
1647 let t: u128 = ts.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
1648 if t > now {
1649 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid timestamp"));
1650 }
1651 out.entries.push((key.to_owned(), t));
1652 }
1653 }
1654 Ok(out)
1655 }
1656 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Self { entries: vec![] }),
1657 Err(e) => Err(e),
1658 }
1659 }
1660
1661 pub fn insert(&mut self, args: &FmtArgs) -> u128 {
1663 let mut args_key = sha2::Sha256::new();
1664 if let Some(f) = &args.files {
1665 args_key.update(f.as_bytes());
1666 }
1667 if let Some(f) = &args.manifest_path {
1668 args_key.update(f.as_bytes());
1669 }
1670 args_key.update(args.edition.as_bytes());
1671 let rustfmt_version = std::process::Command::new("rustfmt")
1672 .arg("--version")
1673 .output()
1674 .unwrap_or_else(|e| fatal!("{e}"));
1675 if !rustfmt_version.status.success() {
1676 fatal!("rustfmt error {}", rustfmt_version.status);
1677 }
1678 let rustfmt_version = String::from_utf8_lossy(&rustfmt_version.stdout);
1679 args_key.update(rustfmt_version.as_bytes());
1680 let args_key = format!("{FMT_VERSION}:{:x}", args_key.finalize());
1681
1682 for (key, t) in self.entries.iter_mut() {
1683 if key == &args_key {
1684 let prev_t = *t;
1685 assert_ne!(prev_t, Self::TIMESTAMP_ON_SAVE, "inserted called twice");
1686 *t = Self::TIMESTAMP_ON_SAVE;
1687 return if args.full { 0 } else { prev_t };
1688 }
1689 }
1690 self.entries.push((args_key, Self::TIMESTAMP_ON_SAVE));
1691 if self.entries.len() > Self::MAX_ENTRIES {
1692 self.entries.remove(0);
1693 }
1694 0
1695 }
1696
1697 pub fn save(&mut self) -> io::Result<()> {
1698 let now = Self::time(SystemTime::now());
1699 for (_, t) in self.entries.iter_mut() {
1700 if *t == Self::TIMESTAMP_ON_SAVE {
1701 *t = now;
1702 }
1703 }
1704
1705 let mut file = std::fs::File::create(Self::path()?)?;
1706 for (key, t) in self.entries.iter() {
1707 writeln!(&mut file, "{key} {t}")?;
1708 }
1709
1710 Ok(())
1711 }
1712
1713 pub fn time(time: SystemTime) -> u128 {
1715 time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_micros()
1716 }
1717
1718 fn path() -> io::Result<PathBuf> {
1719 let root_dir = workspace_root()?;
1720 let target_dir = root_dir.join("target");
1721 let _ = std::fs::create_dir(&target_dir);
1722 Ok(target_dir.join(".cargo-zng-fmt-history"))
1723 }
1724}
1725fn workspace_root() -> io::Result<PathBuf> {
1726 let output = std::process::Command::new("cargo")
1727 .arg("locate-project")
1728 .arg("--workspace")
1729 .arg("--message-format=plain")
1730 .stderr(Stdio::inherit())
1731 .output()?;
1732 if !output.status.success() {
1733 return Err(io::Error::new(io::ErrorKind::NotFound, "workspace root not found"));
1734 }
1735 let root_dir = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
1736 Ok(root_dir.to_owned())
1737}
1738
1739mod pm2_send {
1743 use std::{ops, str::FromStr};
1744
1745 pub use proc_macro2::Delimiter;
1746
1747 #[derive(Clone, Debug)]
1748 pub struct TokenStream(Vec<TokenTree>);
1749 impl From<proc_macro2::TokenStream> for TokenStream {
1750 fn from(value: proc_macro2::TokenStream) -> Self {
1751 Self(value.into_iter().map(Into::into).collect())
1752 }
1753 }
1754 impl FromStr for TokenStream {
1755 type Err = <proc_macro2::TokenStream as FromStr>::Err;
1756
1757 fn from_str(s: &str) -> Result<Self, Self::Err> {
1758 proc_macro2::TokenStream::from_str(s).map(Into::into)
1759 }
1760 }
1761 impl IntoIterator for TokenStream {
1762 type Item = TokenTree;
1763
1764 type IntoIter = std::vec::IntoIter<Self::Item>;
1765
1766 fn into_iter(self) -> Self::IntoIter {
1767 self.0.into_iter()
1768 }
1769 }
1770
1771 #[derive(Clone, Debug)]
1772 pub enum TokenTree {
1773 Group(Group),
1774 Ident(Ident),
1775 Punct(Punct),
1776 Other(Span),
1777 }
1778 impl From<proc_macro2::TokenTree> for TokenTree {
1779 fn from(value: proc_macro2::TokenTree) -> Self {
1780 match value {
1781 proc_macro2::TokenTree::Group(group) => Self::Group(group.into()),
1782 proc_macro2::TokenTree::Ident(ident) => Self::Ident(ident.into()),
1783 proc_macro2::TokenTree::Punct(punct) => Self::Punct(punct.into()),
1784 proc_macro2::TokenTree::Literal(literal) => Self::Other(literal.span().into()),
1785 }
1786 }
1787 }
1788 impl TokenTree {
1789 pub fn span(&self) -> Span {
1790 match self {
1791 TokenTree::Group(group) => group.span.clone(),
1792 TokenTree::Ident(ident) => ident.span.clone(),
1793 TokenTree::Punct(punct) => punct.span.clone(),
1794 TokenTree::Other(span) => span.clone(),
1795 }
1796 }
1797 }
1798
1799 #[derive(Clone, Debug)]
1800 pub struct Group {
1801 delimiter: Delimiter,
1802 span: Span,
1803 stream: TokenStream,
1804 }
1805 impl From<proc_macro2::Group> for Group {
1806 fn from(value: proc_macro2::Group) -> Self {
1807 Self {
1808 delimiter: value.delimiter(),
1809 span: value.span().into(),
1810 stream: value.stream().into(),
1811 }
1812 }
1813 }
1814 impl Group {
1815 pub fn delimiter(&self) -> Delimiter {
1816 self.delimiter
1817 }
1818
1819 pub fn span(&self) -> Span {
1820 self.span.clone()
1821 }
1822
1823 pub fn stream(&self) -> TokenStream {
1824 self.stream.clone()
1825 }
1826 }
1827
1828 #[derive(Clone, Debug)]
1829 pub struct Ident {
1830 span: Span,
1831 s: String,
1832 }
1833 impl From<proc_macro2::Ident> for Ident {
1834 fn from(value: proc_macro2::Ident) -> Self {
1835 Self {
1836 span: value.span().into(),
1837 s: value.to_string(),
1838 }
1839 }
1840 }
1841 impl<'a> PartialEq<&'a str> for Ident {
1842 fn eq(&self, other: &&'a str) -> bool {
1843 self.s == *other
1844 }
1845 }
1846
1847 #[derive(Clone, Debug)]
1848 pub struct Punct {
1849 span: Span,
1850 c: char,
1851 }
1852 impl From<proc_macro2::Punct> for Punct {
1853 fn from(value: proc_macro2::Punct) -> Self {
1854 Self {
1855 span: value.span().into(),
1856 c: value.as_char(),
1857 }
1858 }
1859 }
1860 impl Punct {
1861 pub fn as_char(&self) -> char {
1862 self.c
1863 }
1864 }
1865
1866 #[derive(Clone, Debug)]
1867 pub struct Span {
1868 byte_range: ops::Range<usize>,
1869 }
1870 impl From<proc_macro2::Span> for Span {
1871 fn from(value: proc_macro2::Span) -> Self {
1872 Self {
1873 byte_range: value.byte_range(),
1874 }
1875 }
1876 }
1877 impl Span {
1878 pub fn byte_range(&self) -> ops::Range<usize> {
1879 self.byte_range.clone()
1880 }
1881 }
1882}