[Proposal] Change how gofmt formats struct fields

852 views
Skip to first unread message

Manlio Perillo

unread,
Jan 29, 2020, 12:56:35 PM1/29/20
to golang-nuts
This is a proposal for the next version Go.

Currently gofmt formats struct fields to make the field name and type aligned.
However this may cause extra changes in a commit diff when one field is added or removed.

What I propose is to use a tab between the field name and type.
It is responsibility of the editor or html formatter to align the code.

To make the transition not disruptive I propose that:

- gofmt is updated to recognize the tab in a struct field and left it unchanged
- a new tool or go fix is made available to reformat existing code


Is this reasonable?


Thanks
Manlio Perillo

Amnon Baron Cohen

unread,
Jan 29, 2020, 2:38:56 PM1/29/20
to golang-nuts


On Wednesday, 29 January 2020 17:56:35 UTC, Manlio Perillo wrote:
Is this reasonable?


Not really, as it would generate many gratuitous changes to existing code.
This is one thing gofmt avoids. 

Manlio Perillo

unread,
Jan 29, 2020, 4:06:41 PM1/29/20
to golang-nuts
That's the reason why the reformatting should be done by a separate tool, not by gofmt.
gofmt should not reformat the code to the new style, but it should check if the tab is used and don't change it.


Thanks
Manlio Perillo 

Wojciech S. Czarnecki

unread,
Jan 29, 2020, 7:11:25 PM1/29/20
to golan...@googlegroups.com
Dnia 2020-01-29, o godz. 09:56:35
Manlio Perillo <manlio....@gmail.com> napisał(a):

> What I propose is to use a tab between the field name and type.
> It is responsibility of the editor or html formatter to align the code.

This ("between") assumes an agreed constant width of the tab representation.
Ie. it will never work as intended in the field as people are using tabwidths from 2 to 8.

> Manlio Perillo

Hope this helps,

--
Wojciech S. Czarnecki
<< ^oo^ >> OHIR-RIPE

Dan Kortschak

unread,
Jan 29, 2020, 7:46:37 PM1/29/20
to Wojciech S. Czarnecki, golan...@googlegroups.com
I suspect Manlio was referring to something like this
http://nickgravgaard.com/elastic-tabstops/.

Dan
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/golang-nuts/20200130011051.2e18a964%40xmint
> .


Wojciech S. Czarnecki

unread,
Jan 30, 2020, 8:11:33 AM1/30/20
to Dan Kortschak, golan...@googlegroups.com
Dnia 2020-01-30, o godz. 00:46:05
Dan Kortschak <d...@kortschak.io> napisał(a):

> I suspect Manlio was referring to something like this
> http://nickgravgaard.com/elastic-tabstops/.

Could be. So a small correction:

s/agreed constant width of the tab/agreed width of the tab/

Where width can be "elastic". No way more or even less will support it though.
Even vim-devs refused. Because this "feature" hides misformatted code just off
its very author. (Vim since 8.1.105 has vartabs — neither similar nor relevant here).

Had world be less divided, I would have proposed to use rusted and forgotten
013 VT char for this purpose. Alas, this would break some of contemporary
browsers and editors ;)


> Dan

Manlio Perillo

unread,
Jan 30, 2020, 8:46:59 AM1/30/20
to golang-nuts
Yes.

In fact I wrote that alignment should be done by the editor, not gofmt.


Manlio Perillo


On Thursday, January 30, 2020 at 1:46:37 AM UTC+1, kortschak wrote:
I suspect Manlio was referring to something like this
http://nickgravgaard.com/elastic-tabstops/.

Dan

On Thu, 2020-01-30 at 01:10 +0100, Wojciech S. Czarnecki wrote:
> Dnia 2020-01-29, o godz. 09:56:35
> Manlio Perillo <manlio...@gmail.com> napisał(a):
>
> > What I propose is to use a tab between the field name and type.
> > It is responsibility of the editor or html formatter to align the
> > code.
>
> This ("between") assumes an agreed constant width of the tab
> representation.
> Ie. it will never work as intended in the field as people are using
> tabwidths from 2 to 8.
>
> > Manlio Perillo
>
> Hope this helps,
>
> --
> Wojciech S. Czarnecki
>  << ^oo^ >> OHIR-RIPE
>
> --
> You received this message because you are subscribed to the Google
> Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to golan...@googlegroups.com.

Manlio Perillo

unread,
Jan 30, 2020, 8:50:36 AM1/30/20
to golang-nuts
Of course the tab is elastic **only** for a struct fields.  gofmt will continue to format other code normally.
Struct fields are the only block of code that gofmt will align automatically, and the alignment may change is you add or remove a field.

Manlio Perillo

Mine GO BOOM

unread,
Jan 30, 2020, 12:06:00 PM1/30/20
to golang-nuts
Code is looked at by more than just your favorite editor. I know some people who still open up Notepad to do some quick edits. I cat code all the time. gofmt ensures that every way you look at the code, be it through a terminal, one or two lines at a time, a web browser, a diff tool, or the most decked out editor out there, the code looks the same.

On a daily basis, I look at code in at least 5 different tools, of which I have full control over just two. I'd pay the costs that gofmt requires to a diff output to ensure that the code looks the same in every viewer.

Wojciech S. Czarnecki

unread,
Jan 30, 2020, 12:18:36 PM1/30/20
to golan...@googlegroups.com
Dnia 2020-01-30, o godz. 05:50:36
Manlio Perillo <manlio....@gmail.com> napisał(a):

> alignment should be done by the editor, not gofmt.

What you just said can be translated to:

"editors must support Go meaning of tab characters"

> Of course the tab is elastic **only** for a struct fields.

Now it adds:

"editors must also recognize Go struct fields, because tab is ‘elastic’ only there."

Tabs (011) are not "elastic" and never were. A fistful of editors in a few IDEs do
this — mostly using plugins. Then source read out of this particular environment
becomes ugly.

> Struct fields are the only block of code that gofmt will align
> automatically, and the alignment may change is you add
> or remove a field.

Gofmt does align struct fields automatically now.

Your OP calls to abandon this functionality for the sake of some six editors that
have "elastictab" plugins. Nonetheless you mentioned that current state make
source control changesets bigger than necessary, and that is a valid point.

But it calls for other solution. Namely:

===

Gofmt should have -u for "unformat" and -g for "format for a git difftool"
functionality added.

Called with -u it would produce source with minimal whitespace possible — to be
used in the commit pipeline.

Called with -g on the difftool pipeline it would try to nicely format diff chunk.

===

Manlio Perillo

unread,
Jan 30, 2020, 2:12:39 PM1/30/20
to golang-nuts

On Thursday, January 30, 2020 at 6:18:36 PM UTC+1, ohir wrote:
Dnia 2020-01-30, o godz. 05:50:36
Manlio Perillo <manlio...@gmail.com> napisał(a):


> [...]
 
But it calls for other solution. Namely:

===

Gofmt should have -u for "unformat" and -g for "format for a git difftool"
functionality added.

Called with -u it would produce source with minimal whitespace possible — to be
used in the commit pipeline.

Called with -g on the difftool pipeline it would try to nicely format diff chunk.

===

This seems a good idea, thanks.


Manlio Perillo 

Brian Candler

unread,
Jan 31, 2020, 3:26:28 PM1/31/20
to golang-nuts
I use "diff -ubB foo bar" to compare files where I don't care about whitespace differences (-b within line, -B for blank lines).

I believe "git diff" supports -b too.

David Riley

unread,
Jan 31, 2020, 6:45:23 PM1/31/20
to Manlio Perillo, golang-nuts
On Jan 30, 2020, at 8:46 AM, Manlio Perillo <manlio....@gmail.com> wrote:
>
> Yes.
>
> In fact I wrote that alignment should be done by the editor, not gofmt.

In this scenario, the editor needs to understand Go formatting and apply special formatting to tabs within struct fields? I strongly discourage this.

It will also make structs look TERRIBLE when viewed as plain text (witness the large corpus of C code which is only legible when viewed at a specific tab width because their authors used this approach).

Go's current hard-tab predilection works currently because tabs are always starting at a constant left offset (0) from the start of line; this is not the case in struct fields, variable declarations, etc.


Please don't do this. Or make it an opt-in. This is not a good change to force on users by default.


- Dave

Amnon Baron Cohen

unread,
Feb 1, 2020, 6:07:02 AM2/1/20
to golang-nuts
The indent man page (for formatting C code) has dozens of options to allow the user to tweak the output format.

The greatest thing about gofmt great thing about gofmt is that it has no such options. No style options. No new vs old mode.
Gofmt just produces a single canonical format.

Let's keep it that way.

Manlio Perillo

unread,
Feb 1, 2020, 9:00:12 AM2/1/20
to golang-nuts
On Saturday, February 1, 2020 at 12:45:23 AM UTC+1, David Riley wrote:
You are right, thanks.
I guess the only possible solution is for gofmt to not align the fields, using only one space character between field name and field type.


Manlio 

David Riley

unread,
Feb 3, 2020, 3:10:13 PM2/3/20
to Manlio Perillo, golang-nuts
I don't know if that's the *only* solution; I think the current solution isn't terrible, even if it does lead to somewhat larger diffs when things are changed. The fact that it already only aligns them in a "clump" makes it a bit easier to segment those changes out. The status quo is not, IMO, terrible (though again it is just my opinion).

Single space would definitely be preferable to single tab, since that's already fairly prevalent and reasonably legible. It ain't always pretty, but it gets the job done without a lot of weird jumping all over the place like tabs would invariably do.

If computing had evolved differently to the point where there were actual canonical tab stops at, say, 4 and 50 columns instead of just every n columns, this would be a fine proposal. Alas, we have the ambiguous settings we have today. This is, perhaps, one opportunity Unicode has to remedy things, but relying too much on fancy Unicode features to render code that is generally understood to be "sort of flexible ASCII", as Go is, is also a minefield when it comes to compatibility with existing tools.


- Dave

Manlio Perillo

unread,
Feb 18, 2020, 1:16:57 PM2/18/20
to golang-nuts
On Wednesday, January 29, 2020 at 6:56:35 PM UTC+1, Manlio Perillo wrote:
This is a proposal for the next version Go.

Currently gofmt formats struct fields to make the field name and type aligned.
However this may cause extra changes in a commit diff when one field is added or removed.


Here is an example of a diff with a lot of noise, where the actual change is very hard to see:

Note that using `git diff -w` is not a solution, since it can only be used locally.  The full diff will be shown by github and friends.

> [...]


Manlio

Wojciech S. Czarnecki

unread,
Feb 18, 2020, 9:16:30 PM2/18/20
to golan...@googlegroups.com
Dnia 2020-02-18, o godz. 10:16:57
Manlio Perillo <manlio....@gmail.com> napisał(a):

> Here is an example of a diff with a lot of noise, where the actual change
> is very hard to see:
> https://gist.github.com/perillo/c5b3bdff9e8db9c89f316670d129c0dd
>
> Note that using `git diff -w` is not a solution, since it can only be used
> locally. The full diff will be shown by github and friends.

Yes. It could be easily fixed with cosmetic changes to gofmt in a way that
is tab-width neutral [1] but I doubt it would be seriously considered given
current NIH-driven mood of dev's hive.

[1] use marker relative to the opening brace hinting at the desired comment
position, ie. compute type-start position relative to the opening brace then
comment-start position relative to the marker from the comment of the brace
line. Adjust both positions modulo 8 rounded up. Field name that gets over
past the established type-start position stays over.

Done.

Everything, except overlong names, lines up on the screen with ts=2, ts=3, ts=4
as well as with ts=8. Changesets contain no whitespace changes, because there
is no such gofmt introduced changes anymore — ofc unless you change type
identifier past the rounded 8 boundary. Then whole struct will make to the
changeset, not just huge parts of it.

Hope this helps,

> Manlio

andrey mirtchovski

unread,
Feb 18, 2020, 9:20:20 PM2/18/20
to Wojciech S. Czarnecki, golang-nuts
?w=1 is an option.
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/20200219031558.77411fc7%40xmint.

David Riley

unread,
Feb 18, 2020, 9:21:13 PM2/18/20
to Wojciech S. Czarnecki, golang-nuts
On Feb 18, 2020, at 9:15 PM, Wojciech S. Czarnecki <oh...@fairbe.org> wrote:
>
> [1] use marker relative to the opening brace hinting at the desired comment
> position, ie. compute type-start position relative to the opening brace then
> comment-start position relative to the marker from the comment of the brace
> line. Adjust both positions modulo 8 rounded up. Field name that gets over
> past the established type-start position stays over.

If I'm understanding what's proposed here, I think I like it. The other option would be a wider fixed tab stop, but no one is ever going to agree on what that should be (but it worked for Fortran 77, I guess).


- Dave

Wojciech S. Czarnecki

unread,
Feb 19, 2020, 1:26:32 PM2/19/20
to golan...@googlegroups.com
Dnia 2020-02-18, o godz. 10:16:57
Manlio Perillo <manlio....@gmail.com> napisał(a):

> Here is an example of a diff with a lot of noise, where the actual change
> is very hard to see:
> https://gist.github.com/perillo/c5b3bdff9e8db9c89f316670d129c0dd

> Manlio

I just posted a brief proposal — keep tabs on it, as I neither have a time for,
nor I long for next round of defending the obvious again.

https://github.com/golang/go/issues/37299

TC,

Manlio Perillo

unread,
Feb 19, 2020, 5:02:15 PM2/19/20
to golang-nuts
On Wednesday, February 19, 2020 at 7:26:32 PM UTC+1, ohir wrote:
Dnia 2020-02-18, o godz. 10:16:57
Manlio Perillo <manlio...@gmail.com> napisał(a):

> Here is an example of a diff with a lot of noise, where the actual change
> is very hard to see:
> https://gist.github.com/perillo/c5b3bdff9e8db9c89f316670d129c0dd

> Manlio

I just posted a brief proposal — keep tabs on it, as I neither have a time for,
nor I long for next round of defending the obvious again.

https://github.com/golang/go/issues/37299

TC,


Thanks.  However I have to admit that is a bit complex and the cs-mark is a bit obtrusive.


Manlio 

Jan Mercl

unread,
Feb 20, 2020, 3:11:41 AM2/20/20
to Wojciech S. Czarnecki, golang-nuts
On Wed, Feb 19, 2020 at 7:26 PM Wojciech S. Czarnecki <oh...@fairbe.org> wrote:

> https://github.com/golang/go/issues/37299

"I propose to add an opt-in go fmt solution meant to minimize
whitespace changes to the block where a code author hinted at desired
comments position."

I believe the absence of any gofmt knobs (opt-ins) is a feature that's
not going away. You can always use your private fork, if you want. But
most people will not accept code that is not formatted by the
"standard" gofmt. Because again, the uniformity is the whole point of
gofmt's existence.

Amnon BC

unread,
Feb 20, 2020, 3:26:54 AM2/20/20
to Jan Mercl, Wojciech S. Czarnecki, golang-nuts
Good point Jan, and I totally agree.

I noticed that the proposal states that it can only be implemented by the core team.

I don't really understand the statement, as go fmt is written in Go and all the sources are available online.
So it might be worth expanding this statement to explain why this proposal can not be implemented by 
people outside the core team.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/kp5-wX2ONdc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAA40n-U%3DMomr0MMZcSpt5OdajMXF97G1XqmYhm8LLKVAiLXccA%40mail.gmail.com.

Wojciech S. Czarnecki

unread,
Feb 20, 2020, 4:50:04 AM2/20/20
to golang-nuts, Amnon BC
Dnia 2020-02-20, o godz. 08:25:57
Amnon BC <amn...@gmail.com> napisał(a):

> Good point Jan, and I totally agree.
>
> I noticed that the proposal states that it can only be implemented by the
> core team.
>
> I don't really understand the statement, as go fmt is written in Go and all
> the sources are available online.

> So it might be worth expanding this statement to explain why this proposal
> can not be implemented by people outside the core team.

Because of what Jan mentioned: uniformity. Change of such kind must be agreed
upon then implemented thorough all community. The core team is the only entity
to wield such power.

I work with forked fmt and godoc (even compiler). Once you get hooked to such
settings you can not easily get out of it. You also unintentionally cut yourself from
easy ways to giving code back. That's a dead-end for an individual (though I must
admit that from the business pov it also may give a huge advantage over competitors).

Wojciech S. Czarnecki

unread,
Feb 20, 2020, 4:56:35 AM2/20/20
to golang-nuts, Jan Mercl
Dnia 2020-02-20, o godz. 09:10:22
Jan Mercl <0xj...@gmail.com> napisał(a):

> I believe the absence of any gofmt knobs (opt-ins) is a feature that's
> not going away. You can always use your private fork, if you want.
I do not want anymore ;)

Knob is there _mostly_ because of all the past code in millions of repos.
The fixed `csPos := tsPos += 8` would work for most of Go code w/o knob.

> most people will not accept code that is not formatted by the
> "standard" gofmt.

Thats why it can be implemented "by the core team only".

Amnon Baron Cohen

unread,
Feb 20, 2020, 6:01:57 AM2/20/20
to golang-nuts

Gofmt's style is no one's favorite, yet gofmt is everyone's favorite.


From Go Proverbs.

https://www.youtube.com/watch?v=PAAkCSZUG1c&t=8m43s

Rob Pike answered this proposal in advance back in 2015.

Wise words...
 

Manlio Perillo

unread,
Feb 20, 2020, 8:35:20 AM2/20/20
to golang-nuts
This is not a matter of style.  It is a matter of having clean diffs in commits.


Manlio

David Riley

unread,
Feb 20, 2020, 8:59:12 AM2/20/20
to Manlio Perillo, golang-nuts
On Feb 20, 2020, at 8:35 AM, Manlio Perillo <manlio....@gmail.com> wrote:
>
> This is not a matter of style. It is a matter of having clean diffs in commits.

It is both.

You may prioritize clean diffs, and I understand your reasons for that. Many other people prioritize readability over clean diffs. Neither side is *wrong*, because it's all subjective, but I think it would be better to get some sense of how it would affect users either way before committing to a path. Otherwise, you will wind up with pitchforks and torches.

My belief is that the status quo is actually the best compromise, but I'm willing to be convinced otherwise. However, I'm unlikely to be convinced that a new "magic" tab that won't be supported by the vast majority of existing editors, terminals, printers, browsers and the like is going to be the solution.

You could always split the baby and use a default tabstop of, say, 30 or 40 columns and let longer identifiers just run over. That's unlikely to really satisfy anyone, but it's equally unlikely to dissatisfy folks to the extent that changing to a single space will.

Note that any change made is going to wind up producing ENORMOUS superfluous diffs the first time anyone uses it on existing code that's already been formatted, not unlike the problem of converting hard tabs to spaces or vice versa, so bear that in mind.


- Dave

Robert Engels

unread,
Feb 20, 2020, 9:25:49 AM2/20/20
to David Riley, Manlio Perillo, golang-nuts
Isn’t is easier to just set your diff display to ignore white space changes? Most modern IDEs have this option.

> On Feb 20, 2020, at 7:59 AM, David Riley <frave...@gmail.com> wrote:
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/81701ED1-D431-4B36-8E6D-0E4FF8EB763C%40gmail.com.

Wojciech S. Czarnecki

unread,
Feb 20, 2020, 9:52:40 AM2/20/20
to golang-nuts, David Riley
Dnia 2020-02-20, o godz. 08:57:51
David Riley <frave...@gmail.com> napisał(a):

> I'm willing to be convinced otherwise. [...]
> a new "magic" tab that won't be supported

This proposal is not about some "magic tab". It is about a gofmt formatting recipe
that allows for minimal changesets while keeping "pretty" autoformatting of structs
intact. ("Smart tabs" were mentioned by Manilo in other thread. :)

> Note that any change made is going to wind up producing
> ENORMOUS superfluous diffs the first time anyone uses it
> on existing code that's already been formatted,

This is the very reason for the "comment-mark" existence in this proposal.

Only new code will use it. Once you do a changes to the struct, you then may
chose new formatting as any change under old rules is going to produce
a massive changeset anyway.

Proposed recipe in fact needs none marker, it could as well used heuristics to set
csPos = tsPos +8, or +16. Marker gives an opt-in switch here.

> - Dave

Wojciech S. Czarnecki

unread,
Feb 20, 2020, 9:56:11 AM2/20/20
to golang-nuts, David Riley
> ("Smart tabs" were mentioned by Manilo in other thread. :)

Uh, it was in this one, excuse me. Got both my and Manilo's mixed.

Manlio Perillo

unread,
Feb 20, 2020, 5:53:16 PM2/20/20
to golang-nuts
On Thursday, February 20, 2020 at 3:52:40 PM UTC+1, ohir wrote:
Dnia 2020-02-20, o godz. 08:57:51
David Riley <frave...@gmail.com> napisał(a):

> I'm willing to be convinced otherwise.  [...]
> a new "magic" tab that won't be supported

This proposal is not about some "magic tab". It is about a gofmt formatting recipe
that allows for minimal changesets while keeping "pretty" autoformatting of structs
intact. ("Smart tabs" were mentioned by Manilo in other thread. :)


I proposed to use a tab in order for gofmt to recognize it and not reformat that field.
If we use a space, gofmt will not work correctly on existing code that is already formatted and should not be reformatted.

Maybe a better idea is to use a  U+00A0 NO-BREAK SPACE.  When gofmt see it, it should leave the formatting as is.
There is still the problem with older versions of gofmt if the code is also modified by others, and the fact that NO-BREAK-SPACE is not easy to input.
But gofmt may automatically insert it if it is used in the other fields.

> [...]

Manlio 

Robert Engels

unread,
Feb 20, 2020, 6:24:06 PM2/20/20
to Manlio Perillo, golang-nuts
I will re-iterate, this is something large distributed development teams have struggled with for a long-time - it's my experience that diffs that ignore formatting changes and provide "contextual changes" are far easier to use - especially for code reviews.

If you are performing reviews using line by line diffs you're really missing out (and creating a lot more work).

If you ever used the Eclipse Java code changes review you'll know exactly what I mean.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Brian Candler

unread,
Feb 21, 2020, 3:55:06 AM2/21/20
to golang-nuts
On Tuesday, 18 February 2020 18:16:57 UTC, Manlio Perillo wrote:
Here is an example of a diff with a lot of noise, where the actual change is very hard to see:

Note that using `git diff -w` is not a solution, since it can only be used locally.  The full diff will be shown by github and friends.


Then why not file a feature request against github?

BTW, I would use "git diff -b" rather than "git diff -w".  -b treats all sequences of one or more whitespace characters as equivalent, and ignores whitespace at end of line.  -w ignores whitespace completely, so "foobar" and "foo bar" are treated as the same.
Reply all
Reply to author
Forward
0 new messages