Skip to content

Commit bff5851

Browse files
committed
Initial work on formatting headers
1 parent b944a32 commit bff5851

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

src/header.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//! headers are sets of consecutive keywords and tokens, such as
2+
//! `pub const unsafe fn foo` and `pub(crate) unsafe trait Bar`.
3+
//!
4+
//! This module contains general logic for formatting such headers,
5+
//! where they are always placed on a single line except when there
6+
//! are comments between parts of the header.
7+
8+
use std::borrow::Cow;
9+
10+
use rustc_ast as ast;
11+
use rustc_span::symbol::Ident;
12+
use rustc_span::Span;
13+
14+
use crate::comment::combine_strs_with_missing_comments;
15+
use crate::rewrite::RewriteContext;
16+
use crate::shape::Shape;
17+
use crate::utils::rewrite_ident;
18+
19+
pub(crate) fn format_header(
20+
cx: &RewriteContext<'_>,
21+
shape: Shape,
22+
parts: Vec<HeaderPart>,
23+
) -> String {
24+
debug!(?parts, "format_header");
25+
let shape = shape.infinite_width();
26+
27+
// Empty `HeaderPart`s are ignored.
28+
let mut parts = parts.into_iter().filter(|x| !x.snippet.is_empty());
29+
let Some(part) = parts.next() else {
30+
return String::new();
31+
};
32+
33+
let mut result = part.snippet.into_owned();
34+
let mut span = part.span;
35+
36+
for part in parts {
37+
debug!(?result, "before combine");
38+
result = combine_strs_with_missing_comments(
39+
cx,
40+
&result,
41+
&part.snippet,
42+
span.between(part.span),
43+
shape,
44+
true,
45+
)
46+
.unwrap_or_else(|| format!("{} {}", &result, part.snippet));
47+
debug!(?result);
48+
span = part.span;
49+
}
50+
51+
result
52+
}
53+
54+
#[derive(Debug)]
55+
pub(crate) struct HeaderPart {
56+
/// snippet of this part without surrounding space
57+
snippet: Cow<'static, str>,
58+
span: Span,
59+
}
60+
61+
impl HeaderPart {
62+
pub(crate) fn new(snippet: impl Into<Cow<'static, str>>, span: Span) -> Self {
63+
Self {
64+
snippet: snippet.into(),
65+
span,
66+
}
67+
}
68+
69+
pub(crate) fn ident(cx: &RewriteContext<'_>, ident: Ident) -> Self {
70+
Self {
71+
snippet: cx.snippet(ident.span).to_owned().into(),
72+
span: ident.span,
73+
}
74+
}
75+
76+
pub(crate) fn visibility(cx: &RewriteContext<'_>, vis: &ast::Visibility) -> Self {
77+
let snippet = match vis.kind {
78+
ast::VisibilityKind::Public => Cow::from("pub"),
79+
ast::VisibilityKind::Inherited => Cow::from(""),
80+
ast::VisibilityKind::Restricted { ref path, .. } => {
81+
let ast::Path { ref segments, .. } = **path;
82+
let mut segments_iter = segments.iter().map(|seg| rewrite_ident(cx, seg.ident));
83+
if path.is_global() {
84+
segments_iter
85+
.next()
86+
.expect("Non-global path in pub(restricted)?");
87+
}
88+
let is_keyword = |s: &str| s == "crate" || s == "self" || s == "super";
89+
let path = segments_iter.collect::<Vec<_>>().join("::");
90+
let in_str = if is_keyword(&path) { "" } else { "in " };
91+
92+
Cow::from(format!("pub({}{})", in_str, path))
93+
}
94+
};
95+
96+
HeaderPart {
97+
snippet,
98+
span: vis.span,
99+
}
100+
}
101+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ mod emitter;
7171
mod expr;
7272
mod format_report_formatter;
7373
pub(crate) mod formatting;
74+
pub(crate) mod header;
7475
mod ignore_path;
7576
mod imports;
7677
mod items;

src/macros.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::comment::{
2626
};
2727
use crate::config::lists::*;
2828
use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind};
29+
use crate::header::{format_header, HeaderPart};
2930
use crate::lists::{itemize_list, write_list, ListFormatting};
3031
use crate::overflow;
3132
use crate::parse::macros::lazy_static::parse_lazy_static;
@@ -35,8 +36,8 @@ use crate::shape::{Indent, Shape};
3536
use crate::source_map::SpanUtils;
3637
use crate::spanned::Spanned;
3738
use crate::utils::{
38-
filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
39-
remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt,
39+
filtered_str_fits, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces,
40+
rewrite_ident, trim_left_preserve_layout, NodeIdExt,
4041
};
4142
use crate::visitor::FmtVisitor;
4243

@@ -400,14 +401,21 @@ pub(crate) fn rewrite_macro_def(
400401
None => return snippet,
401402
};
402403

403-
let mut result = if def.macro_rules {
404-
String::from("macro_rules!")
404+
let mut header = if def.macro_rules {
405+
let pos = context.snippet_provider.span_after(span, "macro_rules!");
406+
vec![HeaderPart::new("macro_rules!", span.with_hi(pos))]
405407
} else {
406-
format!("{}macro", format_visibility(context, vis))
408+
let macro_lo = context.snippet_provider.span_before(span, "macro");
409+
let macro_hi = macro_lo + BytePos("macro".len() as u32);
410+
vec![
411+
HeaderPart::visibility(context, vis),
412+
HeaderPart::new("macro", mk_sp(macro_lo, macro_hi)),
413+
]
407414
};
408415

409-
result += " ";
410-
result += rewrite_ident(context, ident);
416+
header.push(HeaderPart::ident(context, ident));
417+
418+
let mut result = format_header(context, shape, header);
411419

412420
let multi_branch_style = def.macro_rules || parsed_def.branches.len() != 1;
413421

tests/target/keywords.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pub // a
2+
macro // b
3+
hi(
4+
// c
5+
) {
6+
// d
7+
}
8+
9+
macro_rules! // a
10+
my_macro {
11+
() => {};
12+
}

0 commit comments

Comments
 (0)