Mix formatter for tabs instead of spaces

Go, which I am a massive critic of, got 2 things correct, in my estimation.

The first is gofmt, which actually inspired mix format. But we didn’t borrow enough, it seems, because…

The second is preferring tabs instead of spaces. Why?

You’re going to read this, probably be convinced, but then say “well, it’s too late”. Or you’ll say “but the community”.

Well, guess what. The community is already using mix format. And since mix format is both configurable and has a plugin architecture, if you mix format any code you’ve copied with tabs, it will happily convert it right back to spaces (or whatever format that the plugins and configuration in your project specify). Which means that the only argument that can truly be made here is that whitespace indentation decisions should be made at the project level, not the language level. The fact that the compiler is whitespace-agnostic (because it would be pretty insane not to be… Unless you’re an official formatter, apparently) is proof of this.

To that end, here’s a mix format plugin to make tab indentation project-wide: An Elixir formatting module for `mix format` that converts leading spaces to tabs. · GitHub

So whether it’s 2 spaces per tab, 3 spaces per tab, 4 spaces per tab, 8 spaces per tab, or actually using tabs as they were originally designed since the compiler doesn’t care; vision-impaired people DO care, and the formatter will align it to your project’s requirements anyway… So let’s choose freedom here

5 Likes

I don’t disagree with any of these arguments. The braille one is especially compelling. I think there are a couple of things that make this awkward for Elixir, though.

One thing you can’t do with tabs, which the Elixir formatter makes use of, is line up code with odd/random spacing. Take with for example of how multi-head anonymous functions get lined up when reaching the line length. There’s no way for the formatter to do that in a way will reliably look nice across editor configurations.

The other thing is line length. With Elixir being a dynamic language it encourages full names for variables whereas I believe Go encourages single letter variables. When you start having longer variable names, larger indentations are going to make for very long lines. Maybe I’m not thinking through it well enough, but it would require the formatter to figure out how to collapse indentation that isn’t necessarily even (as per the first paragraph) in order to figure out if the line length is all right.

Accessibility concerns are very important but it’s just so hard to please everyone. Different editor tools can help—dimming code outside the current block or highlighting ends differently, for example. I don’t like saying “just rely on your editor” though it applies here since that’s the idea behind using tabs. Editors are also capable of retabbing when opening a file and converting back before saving, though that is far from ideal.

2 Likes

My spaces-to-tabs formatter code (which is probably not perfect) acknowledges that spacings that do not evenly divide into tabs given an X spaces per tab assumption (which it makes just by looking at the 3 or 4 most common space indentations and computing the greatest common factor) were probably done for alignment reasons, and preserves those. So for example if you have tabs = 2 spaces for the most part but there are a few lines that have 7 spaces in front, it will replace 6 spaces with 3 tabs and then add a space. In theory, this would preserve things like with indentation.

Regarding line length- Python is whitespace-agnostic (it just asks for consistency within the same file) and also dynamic, I assume it’s faring well. Same as JavaScript.

Accessibility concerns are very important but it’s just so hard to please everyone

And yet, tabs were used just fine for literally decades of computing (C, C++, Bash heredocs still don’t work right with spaces, etc.) before the existence of crappy tooling that generally only handled spaces well, seemed to force everyone into a space-led world (or at least, that is the most common argument I see about it… I don’t understand how that’s the fault of the indentation character and not the tool, though)

Git, Github, VSCode, emacs and vim all handle either option well. That seems to cover the majority of tooling use-cases these days. I had to make minor changes to the config of my shell, my differ and pager (all suggested in the moduledoc of my module) to get 2 spaces per tab, but that’s it.

Speaking of “pleasing everyone,” the very person who started the post here wanted 3 spaces when they hit tab. Well, if they were typing actual tabs, that would be an extremely simple matter for their editor of choice to configure! As well as their diff’er, their pager, etc. Moreover, anyone looking at their code could switch to any tab stops they wanted to without affecting the source code.

1 Like

Hey @pmarreck I have moved this into its own thread, since it is really spawning a whole new set of discussions as compared to the 2 year old other thread.

How good is browser support for this sort of thing these days? EG if I want to type a code block in the forum and I hit [TAB] it moves me out of the text box entirely.

Finally, a topic worth a discussion.

6 Likes

Ya, it’s not something I think about too much, I was just trying to offer theories as to why. There is another kind of freedom in being able to line certain things up perfectly with spaces (like =s and whatnot, even though I personally hate that), but it’s cool if you plugin handles it. I’ll give it a try! My point around Elixir was just more about a stylistic choice for Elixir itself in allowing more space for variable names. Python rallies around 4 spaces and I don’t think vanilla JS ever had a standard. Also, horrifyingly enough, Python doesn’t need whitespace to be consistent by file, just by block!

Since @hst337 is clearly very genuinely interested in and excited about this topic, I’ll add that I’m not sure the historical arguments ring true outside of C-style languages. I’m not sure you can even use tabs with COBOL (can you?), I randomly know that Fortran uses spaces (I have this book from my great Uncle somewhere), and Lisps always seem to be lined up at very specific columns. I don’t believe the tab key was every intended as the one true way to indent programs being that it came from typewriters.

But again, all that said, I generally agree with you. I’m just in the mood to advocate for the devil while painting my bike shed :slight_smile:

1 Like

TIL.

15 years doing this job and trying to be nice about accessibility, and I never thought about that.

Thanks a lot, and I really hope this discussion could lead to a change. Or at least awareness.

There is this open issue for Prettier, which is the de facto standard formatter for Javascript, HTML and CSS (I believe). If they change their default, that could be a huge boost

Cheers

1 Like

That’s not as trivial as you make it sound, many teams I’ve been in throughout my entire (and fairly long) career have struggled to have and enforce one editor and one formatting policy.

Still, what a strange bikeshedding thread though, I very rarely see those here. And I fail to see the significance.

So the code is formatted with spaces.

…And?

1 Like

That is not fully true, as foo:bar is not the same as foo: bar. Elixir is whitespace sensitive language (it just to not use off-side rule).

Tabs weren’t designed for indentation. Tabulation character was designed for tabular data (it is in the name). So some people say “tabs for indentation, spaces for alignment” this is explicitly against design purpose of tab, as tab was exactly for alignment purposes (that is also why tabs are elastic, so you can adjust the tab stop to match your table data).


But going back to the argument, while I agree that for some visually impaired people tabs have more sense than spaces I still prefer spaces over tabs, because tooling around tabs is enormous pain in the arse.

  1. You want to enforce the maximum line length? Good luck with doing that just in editor when there are elastic tab widths. You can enforce it with formatter, but for me it will be enormously irritating during work if I would use different TS than rest of the team, as it would constantly move stuff around.
  2. Online tooling is super inconsistent wrt to the support of the tab stops. GitHub only recently (2-3 years ago) gained support for custom tab stops and support for defining tabs stops in the project (via .editorconfig). Earlier it was defaulting to 8 and you either like it or you can GTFO. Other tools not always provide such flexibility.
  3. Support for multiple tabletops in single project is poor in case of CLI/TUI tools. You want to have different TS for Makefile (8), Rust (4), and Elixir (2) files? Good luck when you will fire git diff and you will have changes in different files.

Some may say, “well, we can fix these tools to allow that stuff”. Yes, we can, but at the same time we can fix these tools to

  • Support displaying leading spaces with different width than the rest of line
  • Add support to the editor to support braille reader in a way that will for example start line with <indentation width><space> to provide a way for the user to know how deeply indented they are without shifting through N characters (no matter what these are) or just simply display the leading spaces differently from other spaces (just like editor in general often do).

So yes, this is a problem, but saying that “spaces are the problem, and the only solution is to move to tabs” is the same as saying “you are the problem, fix yourself”.

9 Likes

I actually very much understand the argument of “tooling for this M.O. still leaves too much desired to be motivated to make it permissive” (such as the “do web browsers actually handle this correctly?” argument). And you’re right that the default behavior of browser text fields (or any input) when you hit tab or shift-tab is to navigate between them. There’s a few browser plugins that change this behavior for TEXTAREAs, but we’d probably agree that it shouldn’t be necessary to install a browser extension just to get code editing in a TEXTAREA that doesn’t violate expectations.

In fact, I noticed that in this very comment editor, that’s exactly what it does. Case in point. (Tab here actually advances through every possible interactable element on the page, which is IMHO excessive and less useful since there’s almost a hundred from what I can tell, although this may be good for accessiblity, I guess)

One proposed solution to remove the ambiguity I’ve read is to change the behavior of tab in a textarea (and only textareas, for now) to split based on a heuristic. One possible heuristic is whether or not there is already text in the textarea or whether the insertion point is not at (0,0) of the textarea (so that if it’s just loaded and has content, tab will advance past it unless you’ve already started to edit it)

It’s not hard to implement and test this in pure JS (chatgpt4’s attempt initially looks mostly good but I haven’t tried it yet: JS Tab Behavior & Testing), but that of course requires the participation of whoever owns the code that generates the textarea.

  1. I did not consider computation of maximum line length (probably because I do not personally consider that very important, but I understand if others do!). Good point there; you’d have to default to some normally expected width given the file type. Which is… complicated. Though I’d consider this more an argument against having religiously-enforced line length limits. :wink:

  2. I’m glad GitHub supports it now. One less tooling problem! Not sure about “other tools”. I would imagine that the languages where tabs are prevalent (Go, Python) have already solved that problem.

  3. I don’t understand this argument. In your example, all of those “different” tab stops are just 1 character: A tab! If anything, you’re actually arguing IN FAVOR of tabs, here, if perhaps by accident…

1 Like

The reason was the accessibility argument, as my original post linked to. That’s what actually changed my mind on this, so I’d suggest perhaps reading it. But in a broader sense, I was trying to argue for mix format not being as opinionated about leading whitespace, at least at a per project level (which you even argue for when you mention “struggling to have and enforce one editor and one formatting policy”). Personally I’d permit flexibility up to but NOT including “per project, per file extension”, so as not to pollute git diffs with spacing changes within the same filetype per project.

But sure, I don’t want to be known as “that guy who pushed for tabs” on this forum, so call it bikeshedding I guess. :confused:

1 Like

Yes, but when I do git diff I want to see them with different stops. Terminal cannot do that, at least not right now. This is IMHO huge problem as with some languages (like Elixir) we can have small stops and still be readable, while in others it is better to have larger indentation difference. In terminal you cannot display them at the same time with different tab stops without affecting other tab stops in the same view.

1 Like

I am sympathetic to people with poor or no eyesight – I am at -6.75 dioptres of myopia myself.

It’s just that statistically it doesn’t make sense to inconvenience most of the people to cater to a smaller portion of people. Plus those with special needs already have specialized tools, as far as I am aware.

Bringing this to VScode, Emacs and [Neo]Vim will cause a huge stink.

Though realistically I don’t see a problem to just f.ex. make an Emacs or NeoVim function that changes tab width at the press of a keyboard shortcut either.

2 Likes

This thread got me thinking about the issue but after reading through a bunch of the Prettier github issue linked by @carloratm as well as the other sources provided, I’m back to my original position that I just don’t care—it’s a bunch of bikeshedding. Other than spaces wasting characters on braille readers, all evidence and arguments presented that tabs cause an accessibility issue are purely anecdotal. There was one blind person in that Prettier thread that brought this up which everyone subsequently kept quoting for the remainder of the thread (it’s still going on a few years later). There were some people, one mostly blind, who provided anecdotal evidence that there are blind people who are fine with using spaces and use them in their personal projects, but of course no one really acknowledged these comments. And certainly no one asked for any data on how many people out there actually use braille readers vs other forms of accessibility tools. In my completely unscientific google searching it would appear that screen readers are far more common.

I realize the perceived accessibility issue isn’t unique to blind people and that is totally ignoring that tabs can cause other accessibility issues. One of these would be the aforementioned long lines. There is a reason that newpapers write in (physical) columns and large format books tend to have pages broken into columns (and as, as a massive stretch, why books don’t have text spanning both pages): too much repeated horizontal eye movement causes strain (there is a lot out there about this and easy to google so I’m not going to cite a source). If you have multiple people on a project using different indentation widths, they are going be optimizing for very different line lengths which could be chaotic.

Oops, I went off there a bit. I much prefer spaces but if for whatever reason the Elixir formatter switched to tabs, I wouldn’t make too big a stink about it. I just now think the accessibly argument isn’t a particularly solid one, at least not without real evidence from people who have these issues.

1 Like

Same, although I will say that I think it’s hilarious when I see editor configs with “indent-guides” to get around the fact that 2-space indents often make it hard to trace which indent-level you’re at :slight_smile:

image

1 Like

I hope you mean peoples’ personal configs and not EditorConfig configs forcing this on people—that would be pure evil :smiley: Is it only 2-space tabs that use these, though? Personally I find anything with too many levels of indentation pretty unreadable no matter much space there is or isn’t.

Anyway, I probably came off as very unsympathetic to people who have a hard time distinguishing smaller widths. I got all worked up because I looked at that Prettier discussion again :sweat_smile: I do think this is better solved at the editor level, though, as mentioned above. But that’s just me.

Indentation guides are very useful to distinguish separate blocks when you have several nested one into another. :person_shrugging:

No, I mean configs in general, although I noticed recently that Astro, Lunar and Doom nvim configs seem to have on by default. And yeah, looks to be the 2 space approach in general.

Right, so is being able to adjust your own tabstops, which was my point. I get a chuckle out of indent guides because being able to adjust your tabstop when faced with deeply nested code was one of the core arguments of the “pro-tab” argument back in the great space/tab holy wars of the 80s.

Ah, I get you now. Well I personally love the indentation guides because I find them immediately obvious / apparent. Not so with the tabs.

1 Like