-
Notifications
You must be signed in to change notification settings - Fork 888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add unstable format_brace_macros
option
#5538
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like something like this, but turning on/off formatting for all "block macro" usages per project doesn't seem the way to go.
My personal opinion is that there should be default formatting applied when it makes sense (as with all other Rust code), but as long as there is an option for a global opt-in, I’m not going to spend too much effort on that battle. :) |
And when is this? Macros literally invent new syntax. How is rustfmt supposed to know which of those use syntax close enough to Rust to be able to use the same styles? |
Can you provide an example of a macro invocation body that parses as valid Rust code that would not make sense to format as Rust code? |
Aha, so this is the key |
I tested:
Shouldn't new options be off by default? This appears to be on by default. This does not respect Result: kas-gui/kas@b90f126 This formats within calls to |
@dhardy thanks for taking the time to look into this. Could you provide some code snippets that can be used as test cases?
Yes we would want this to be opt-in if we were to move forward with this. |
@ytmimi sure. Here's an example that is formatted correctly by this PR: impl_scope! {
/// A frame around content
///
/// This widget provides a simple abstraction: drawing a frame around its
/// contents.
#[autoimpl(Deref, DerefMut using self.inner)]
#[autoimpl(class_traits using self.inner where W: trait)]
#[derive(Clone, Debug, Default,)]
#[widget{
layout = frame(kas::theme::FrameStyle::Frame): self.inner;
}]
pub struct Frame<W: Widget> {
core: widget_core!(),
#[widget] pub inner: W,
}
impl Self {
/// Construct a frame
#[inline]
pub fn new(
inner: W
) -> Self {
Frame { core: Default::default(), inner }
}
}
} Here is an example which is not: fn main() -> kas::shell::Result<()> {
env_logger::init();
let window = kas::macros::singleton! {
#[widget{
layout = grid: {
1, 0: "Layout demo";
2, 0: self.check;
0..3, 1: ScrollLabel::new(LIPSUM);
0, 2: align(center): "abc אבג def";
1..3, 3: align(stretch): ScrollLabel::new(CRASIT);
0, 3: self.edit;
};
}]
#[derive(Debug)]
struct {
core: widget_core!(),
#[widget] edit = EditBox::new("A small\nsample\nof text")
.with_multi_line(true),
#[widget] check: CheckBox,
}
impl kas::Window for Self { fn title(&self) -> &str { "Layout demo" }
}
};
let theme = kas::theme::FlatTheme::new();
kas::shell::Toolkit::new(theme)?.with(window)?.run()
} (Note: I don't expect rustfmt to do anything with the attributes to the |
I would love if rustfmt could treat brace-macros (opt-in or opt-out) as trying to parse braced-items. That way even if there are things like the unparseable |
I'd love to have this functionality. I see it got stuck in review some months ago - is there anything left to move it forward? |
The point @dhardy raised is spot on, and there's various examples where this is problematic. A simple one is the arg to To be clear, I'm not saying that rustfmt can't do better here, nor am I saying that we won't consider something like this. However, my initial reaction is that this is likely too imprecise an approach, and I think there are some more practical (and precise) approaches that are discussed in other issues. Additionally for transparency, this is not something we'll have bandwidth to fully review and evaluate any time soon due to prioritization and limited bandwidth (especially since this has a big question around "should we", on top of the "how"/implementation if so) |
Any updates or this or similar functionality? Can we help somehow? We have lots of macros that are just wrappers around regular rust code and the inability to write them using the |
I tested with the latest changes and it does nothing to either of the samples above (fails to format). |
@dhardy Your second example has an unnamed |
An “aggressive” vs “conservative” setting would make sense to me.🤪 |
Also |
Even this fails to format (using the latest commit in this branch):
In this case the But my other hacks would need a custom (or slightly more permissive) formatter then? Syntax extensions are the whole purpose of this macro. |
It's off by default now. This worked for me:
Yes, if it doesn't parse as valid Rust code, it's out of scope for this PR. I'd love to work on how to do custom formatting of more exotic macros, but it seems rustfmt is in a bit of a purgatory at the moment. |
Could you expand on this statement? If you're implying that rustfmt isn't being maintained, then I 100% disagree. |
My experience is of seeing multiple new PRs open for months that have no feedback at all, and Caleb reiterating how little bandwidth he has available. I absolutely respect and appreciate your work, I understand that this is volunteer work, and I do not mean to imply that rustfmt is "unmaintained", but surely additional review and design capacity would be useful? It is very helpful and motivating to get concrete feedback and/or merged PRs to learn the codebase so that I could contribute more. I totally understand if this PR is too crazy to be merged. If I developed a different, more rigorous approach, would that idea or PR be reviewed or discussed? |
@trevyn thanks for the response. I can sort of see where you're coming from, but the PRs don't tell the whole story. There's a lot of work going on outside the rustfmt repo too. We have regular meetings on Zulip, we're triaging issues that come in, we're managing changes made to rustfmt directly in the rust-lang/rust subtree and syncing them back into rustfmt, etc. It's a lot. Furthermore, I'm personally putting a lot of my free time into the project, and reading comments like "rustfmt is in a bit of a purgatory at the moment" doesn't feel great. Please be mindful before you make comments like that. Yes, I agree with you that having more review and design capacity would be useful. If you're interested in contributing and learning more about the project I would suggest shooting me a message on Zulip. I can direct you towards issues that I think are more meaningful to address, and ones that I can commit to reviewing. Rewriting brace macros is a very low priority issue for the team and that's why this is unlikely to get much attention from us right now. I'm going to mark this as on hold for now. |
@ytmimi let me chime in here with what's probably going to be a controversial opinion so please don't take this personally. I know everyone is under pressure and the rust project as a whole seems to be understaffed, especially given the amount of attention and user growth it's seeing. It's the price of success! I think this is the wrong approach. It's not about what's important to your team, but what's important to the users of your software. There will always be niche use cases for any software, but those niches turn out to be unexpectedly large some times and enable a whole new set of use cases. That's why there are tests and unstable features etc. That's the whole point. Do a review, but don't expect to cover all the obscure corner cases etc. Make sure all the test pass and nothing obvious breaks and let it be. It's unstable so it can be fixed. Don't beat yourself up over it. If it breaks it gets fixed and new tests added to cover the changes. That way the feature gets a place in the codebase, it gets a maintainer (@trevyn) and gets you a potential new contributor of more cool stuff in him, and most importantly, you get new users and use cases out of the software. If you try to keep absolutely everthing under direct control as a small team, you will likely burn out which is the worst possible outcome for the project. On this specific feature: I'm sure @trevyn would be more than happy to do the legwork to get this merged, but you need to allow it to happen. You can't just go telling him to work on some other features you need. Frankly, that's a bit condescensing. He worked on this because he needs it. I am willing to work on this, because I need it. Maybe I want to work on some other stuff too, but this is at the top of our list, because we have specific things we'd like to do that are worth a lot to us and we can't do them currently unless we maintain a fork of rustfmt. Anyways, I hope I managed to get my point across without upsetting people too much. Processes need to be put in place that foster contributions better. I believe all the work you're doing is amazing and a huge value and a core pillar that makes rust amazing to use! |
This PR adds an unstable
format_brace_macros
option for formatting brace{}
delimited macro invocations that parse as valid Rust code, with fallback to existing behavior.Motivation
Users consistently expect some degree of formatting in brace delimited macro invocations. (See links below for examples.)
Discussion
It is true that the problem of formatting macro invocations is not solvable in the general case, but skipping formatting entirely is contrary to user expectations.
rustfmt
already uses heuristics for parentheses()
delimited and bracket[]
delimited macros, but does not currently attempt to format brace delimited macro invocations.By putting this behind an unstable option, we can:
Options are available for skipping formatting for individual macros, including
#[rustfmt::skip]
and the newerskip_macro_invocations
option, which can skip formatting for all invocations of a specified macro.Approach
Explicitly, this PR simply attempts to format brace-delimited fn-like macro calls
foo! {"bar"}
as they would be formatted if enclosed in parentheses, which is a common workaround:
foo!({ "bar" })
Yielding the final formatting result:
foo! { "bar" }
This formatting is only applied if the resulting TokenStream is identical to the original.
Future work
match
expressionstruct
instantiationSee also