Skip to content
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

Feature request: have a fallback C style #4205

Open
eric-s-raymond opened this issue Jan 19, 2024 · 12 comments
Open

Feature request: have a fallback C style #4205

eric-s-raymond opened this issue Jan 19, 2024 · 12 comments

Comments

@eric-s-raymond
Copy link

I'm a scarred veteran of the C style wars of the 1980s. Recently, though, I've come to appreciate the virtues of brutally prescriptive code formatters like gofmt for Go and black for Python that have no options. I have learned their lesson: uniformity and predictability is far more valuable than being able to tweak dozens of configuration parameters.

Unfortunately, C formatters are still written as though they want to please all sides in ancient disputes. And tend to default unhelpfully (when they have defaults at all) to GNU style - which is not the modern consensus 1TBS used by the Linux kernel and most other projects that care (probably because it's so annoyingly difficult to read).

I want a C formatter that I can call without options to produce a reasonably clean modern 1TBS style. I don't want to write my own formattter. And I'm certain it would be doomed to try changing the defaults of indent or clang-format; the maintainers aren't going to want to annoy their usebase with such a change. But uncrustify doesn't have preset options for C.

Would you take a patch that sets up a default C style in uncrustify? As long as it looks broadly like kernel 1TBS I don't think its details matter much. I just want there to be such a thing so I can call uncrustify as an Emacs save hook without external dependencies or fuss.

@guy-maurel
Copy link
Contributor

It would be fine.
Your patch could be a new configuration file for uncrustify.
Let us try!

@micheleCTDE
Copy link
Collaborator

Would you take a patch that sets up a default C style in uncrustify?

Hi @eric-s-raymond,
may I suggest a slight variation?
Currently default.cfg tries to be as neutral as possible, although it is not 100% neutral at the moment (and probably never will). Rather than changing that file, may I suggest you create a etc/1tbs.cfg file and tweak the options to mimic the requires 1TBS style? We would then merge it into the code and you could simply use uncrustify with that config file.
This would serve your needs, make the style config very recognizable, would probably be welcomed by many other users and at the same time would not affect the default config. What do you think about it?

PS if you don't mind me asking: your username didn't go unnoticed. Is it "the" Eric S. Raymond? 🙂

@eric-s-raymond
Copy link
Author

eric-s-raymond commented Jan 28, 2024

'Is it "the" Eric S. Raymond? ' Guilty as charged.

Defining 1TBS turns out to be tricker than I thought. It used to be a synonym for K&R, but I have learned in the last week that to many people it is no longer identical; see the Wikipedia article "Indentation style" for discussion. Key sentence: "The main two differences are that functions have their opening braces on the same line separated by a space, and that the braces are not omitted for a control statement with only a single statement in its scope."

Speaking as the jargon certification authority :-), I think modern usage should prevail in this case - it's good to have a way of distinguishing current practice from the (now slightly archaic) K&R style.

I have found I can reflow to modern 1TBS using this command:

clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]")

So it looks like clang-format considers modern 1TBS the "neutral" style. Which I didn't know this when I started this issue. I also had not read the uncrustify manual closely enough to notice that "-c -" is a thing.

What does your "neutral" style look like? Is there a description somewhere?

@micheleCTDE
Copy link
Collaborator

micheleCTDE commented Jan 28, 2024

What does your "neutral" style look like? Is there a description somewhere?

uncrustify default options try to make as little changes as possible to the original code. The options are well described in the etc/defaults.cfg file. The idea is that the file is a starting point for users to experiment and configure the file to their liking, although it takes a while to try out all those options. In such file, most options of type ignore/add/remove/force are set to ignore. meaning they will leave the code as it is. Some options are boolean values and there is no ignore for them, so they will affect the source code. Mostly indentation and tab/spacing will be affected with the default configuration. This is due to the fact that uncrustify parses the source file into tokens (called chunks) and does not really remember how those tokens were separated in the source files. So when writing the output file, indentation, tab and spacing will be overwritten.

As additional info, a key difference between uncrustify and clang-format is that the latter parses and understand the code, so it has inherently the ability to do more with the source code, although it could take longer. Uncrustify instead is a text analyzer and although it strives its best to do the right thing, there are situations where it could badly mishap. Those are rare with reasonably well written code, but if you use some pre-processor heavily dependant code, you may get some surprises. The benefit is that uncrustify is rather quick.

Re 1TBS style, you may want to try out the config file tests/config/common/tde.cfg. It is actually an Allman style config, but I believe that you may be able to get a 1TBS style with little changes around the opening brace placements and tab/space width.

@eric-s-raymond
Copy link
Author

OK, I understand "neutral" now. Thanks.

Do you think uncrtustify has any advantages over clang-format other than speed? If so, what are they>

@micheleCTDE
Copy link
Collaborator

clang-format is that the latter parses and understand the code

First, I have to correct myself. Read more on this further down when I talk about clang-tidy.

Do you think uncrtustify has any advantages over clang-format other than speed?

It depends on the use case/own needs.
clang-format has a bigger dev community and its development is therefore quicker. Uncrustify team is very limited.
As a consequence, clang-format is likely to be more exact, especially with recent c++ standards. On the other hand, having a change accepted in clang-format may take longer due to the bigger team, while in uncrustify a good patch could be merged even on the same day (personal experience: I got interested into uncrustify and soon started to contribute patches and changes and always had a good experience since the end of 2020).
Uncrustify is more configurable, with hundreds of options that go down to even small details. New options can be added if needed, so if there is a bug or a need for a new feature and someone provides a patch, it is likely to be fixed quickly.
One thing that really made me go for uncrustify over clang-format when I compared the two, is that clang-format was not able to add braces around single statements (clang-format being a code formatter, not a code modifier) and I needed to use clang-tidy. clang-tidy is the tool that compiles the code (not clang-format) and was way too slow for our needs. Also I didn't like having to use two tools and needed a tool that would work on individual files and fast enough to be able to be used in git hooks and regular linting. That comparison was more than 3 year ago, so maybe clang-format can now add braces and do more, so you should at least check it out to see what has changed.

Both uncrustify and clang-format/clang-tidy have pros and cons, so it comes down to what you need from it and how much effort you want to invest studying it and set it up.

@eric-s-raymond
Copy link
Author

eric-s-raymond commented Jan 29, 2024

"that clang-format was not able to add braces around single statements"

They've added that feature in in release 1.15; there is now an InsertBracess option. I haven't been able to test it yet, though - It's not packaged for Pop_Os! 22.04, which is what's on my desktop.

I'm going to do some experimentas with uncrustify before saying anything further.

@micheleCTDE
Copy link
Collaborator

They've added that feature in in release 1.15

Thanks for the info. I think it was 1.7 or 1.9 when I had a look at it, it definitely took a while for that to make it into cmake-format.

I'm going to do some experimentas with uncrustify before saying anything further.

Feel free to ask anytime, if you have further questions. 🙂

@eric-s-raymond
Copy link
Author

OK, I have now reflowed several C projects using combination of clang-format and uncrustify.

The best series of steps I've been able to work out is: reflow with clang-format, run uncrustify --set=mod_full_brace_do=true --set=mod_full_brace_if=true --set=mod_full_brace_for=true --set=mod_full_brace_while=true to insert braces around single-line statements, then run clang-format again. Unfortunately, for reasons I wiill soon explain, this is only practical for small projects,

The reason for this sequence is that, as you noted previously, clang-format's use of the clang parser makes it better at formatting decisions than uncrustify. One place this shows in a particularly annoying way is that uncrustify doesn't know that it should leave text in block comments alone. Or, at least, I couldn't find a way to make that happen.

Which takes us straight to uncrustify's other problem, the lack of documentation for the configuration
feature. I had to read default.cfg and puzzle out which of the options I needed by looking at their context in the file. clang-format does better than this, it actually has narrative descriptions of its options.

You say uncrustify is more configurable and I don't have any trouble believing that, but the way it clobbers block comments requires hand-fixing afterwards, and that means there's a fairly low upper limit on the size of project I'm willing to use it on before the hand-fixes become too much of a pain in the ass for me to wabt to deal with.

I think my remaining needs are going to be best served by waiting to reflow my large C projects until I can get my hands on a clang-format binary that supports InsertBraces. I might have more use for uncrustify if I wanted to do elaborate custom styling, but tht is the opposite of what I need.

@micheleCTDE
Copy link
Collaborator

Hi Eric,
could you please provide more info on the problem with block comments? Maybe an example of the problem as well? Is it with the indentation from the beginning of the line or are the change within the line text?
There are sevelar options related to comments, so perhaps it could be a matter of trying the right ones. Of course, it could also be an uncrustify bug/lack of feature, which is why I am asking for more details, if you don't mind.

Re lack of documentation: clang-format does indeed a better job than uncrustify on this, can't argue against that 🙂

@eric-s-raymond
Copy link
Author

Consider this comment:

/*****************************************************************************
 Ascii 8 by 8 regular font - only first 128 characters are supported.
*****************************************************************************/

That's a 1-space indent on the interior. If I run this through uncrustify it will be chanmged to a 4-space indent.

@micheleCTDE
Copy link
Collaborator

That's a 1-space indent on the interior. If I run this through uncrustify it will be chanmged to a 4-space indent.

That is configurable, you can preserve the space using proper options. I did a quick test using the config file you can find in the source folder tests/config/common/tde.cfg and it preserves the comment unchanged.

uncrustify -c <path-to-uncrustify-sources>/tests/config/common/tde.cfg -f <input-file>

I am not claiming that uncrustify is perfect, it surely has its own issues. But AFAICT many of the things can be tweak to someone's liking, with a bit of time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants