
Elastic TabStops: A Better Way to Indent and Align Code (2017) - signa11
http://nickgravgaard.com/elastic-tabstops/
======
vinceguidry
I think a more ideal solution would be to achieve the flexibility of elastic
TabStops without having to actually change the way it's represented under the
hood.

My conviction from personal experience is that your personal tools need to be
adaptable to fit any organization. This splits largely along tech stack
culture lines. Our shop was originally Ruby before we started moving to
NodeJS, we all use spaces for indentation.

So unless I want to force my whole team to start using tabs, I can't use
elastic tabstops.

This isn't a viable improvement until it can accommodate the existing state of
affairs. The other obstacle in the way is code linters. I have to make a case
to turn off linter rules at my shop and if the only objection I have is that
it makes a workflow I haven't even tried yet difficult to implement, it's not
going to be taken seriously if it requires work from the whole team.

The way I usually accomplish workflow improvement objectives is to find a way
to accommodate both my needs and the status quo while thinking deeply about
the underlying context creating the obstacle.

Environment managers get gnarly when you have to commit .env files to the
codebase. Every time someone changes the .env file and commits, on pull the
expected behavior of the system changes. Not having a .env file committed to
the codebase caused issues with the insufficient handling of environment by
our CI process.

So I use .env-local for overriding the environment and one line in the
codebase overrides the .env file in case .env-local exists. That way I don't
have to care about what's in .env and I won't get surprising behavior upon
pulling the repo.

One year later I got around to eliminating the 12 factor violation. One month
after that we decided to replatform. -_-

~~~
scrollaway
Nowadays with prettier, gofmt and similar, there's almost no languages
remaining without high quality, fast formatters which can run on save. So
changing from spaces to tabs should be as simple as updating to tabs:true in
prettier config.

~~~
vinceguidry
It still changes all the spaces into tabs. The only acceptable impingement on
the rest of the team's workflow for changes in my workflow is 'none'.

~~~
wwweston
Which is half of why this is an insoluble problem. There's no solution that
doesn't impose some workflow or wetware overhead on introduction.

Installing "Tabs for indentation, spaces for alignment" on wetware is probably
the best solution in terms of maximizing utility for minimized
development/workflow costs. But even that is going present a wetware tax for
some developers, and it's not superior in terms of utility to the article's
proposal.

~~~
vinceguidry
We don't know that there's no solution. One of your siblings suggested having
the editor just use space alignment. I can't see any reason why that wouldn't
work either and was the idea I had in mind.

But installing ideas onto wetware is perhaps the most expensive thing that can
ever be considered. Because it often involves fighting a war.

------
simias
Unless I'm missing something it doesn't solve the main issue with tabs vs.
space which is that most people and editors don't do it right, that is indent
with tabs and align with spaces. How does Elastic TabStops deal with that? For
instance in code like:

    
    
        int main() {
            printf("%s, %s, %d",
                   somevariable,
                   somefunction(),
                   42);
        }
    

Because beyond that I personally (and very subjectively) consider that adding
comments on the same line as the code is _usually_ a bad idea when you can put
them on the previous line and it's both better suited to long comments and
easier to match the comment with the line. In the example from the gif in the
article consider the distance between the `x();` code and the matching comment
all the way to the right. Putting it on the line before would make more sense
IMO. Most decent code editors will also be able to wrap the comment more
easily that way. That being said I'm anal about enforcing 80 columns in my
code so maybe I'm just a weirdo.

~~~
kuon
If I cannot fit a function call on one line, I usually prefer this indent:

    
    
        int main() {
            printf(
                "%s, %s, %d",
                somevariable,
                somefunction(),
                42
            );
        }

~~~
guitarbill
Why not always do this? Once you stop using column alignment/align with
opening delimiter, and use hanging indent, these issues go away:

    
    
        int someLongFunctionNameThatWillBeRefactored(int foo,
                                                     int bar);
    
        someLongFunctionNameThatWillBeRefactored(longVariableName1,
                                                 longVariableName2);
    

and do

    
    
        int someLongFunctionNameThatWillBeRefactored(
            int foo,
            int bar);
    
        someLongFunctionNameThatWillBeRefactored(
            longVariableName1,
            longVariableName2);
    

or even

    
    
        int someLongFunctionNameThatWillBeRefactored(
            int foo, int bar);
    
    

Now tabs work fine. Spaces work fine. Elastic TabStops? Ironic that he
mentions "Keep it simple, stupid!" and then overengineers this thing and every
editor along the way.

Anyway, this will probably not go down well since everybody has been trained
to use the former style, and now people "prefer" it, out of what seems to me
to be habit.

~~~
simias
I don't think scare quotes are warranted, I do prefer the former style, I find
that it makes it easier to match the arguments to the function at a glance. Of
course it's purely subjective.

I do note that even your proposition doesn't really solve the problem of using
Elastic TabStops, because if you imagine a comment spanning all three lines:

    
    
        someLongFunctionCall(⇥   /* This function call */
        ⇥   longVariableName1,⇥  /* is really cool *
        ⇥   longVariableName2);⇥ /* and stuff */
    

You have an additional tab at the beginning of the two last lines which means
(as far as I understand it) that the first comment line won't necessarily line
up with the two others. Of course that showcases a potential issue with
Elastic TabStops, not your particular coding style.

~~~
guitarbill
Block comments interleaved with code are rare in most codebases I've worked
on, because it's almost always easier to put them on their own lines, above
the statement:

    
    
        /* This function call is really cool and stuff */
        someLongFunctionNameThatWillBeRefactored(
            longVariableName1,
            longVariableName2);
    

So I sort of dismissed that use-case as a toy example. But yes, if you did
that elastic tabstops would be useful... except the contents of the block
comment doesn't get reflowed when the tabstop width changes. Which I'm sure we
could do, but then we're getting into interesting territory w.r.t edge
cases/usability for sure.

------
gandreani
Not about to participate in any side of this flame war. I just wanted to point
out how nice the presentation is on this site. And the whole site is less than
1MB!

~~~
setzer22
Plus, everything worked just fine without any sort of JS, which is very much
welcome when delivering static content like this.

------
oftenwrong
The ultimate solution is to separate code display from code persistence.

A simple, if inelegant, way to achieve this: Use a standardised format on
save. On read, pretty-print based on user-defined settings.

~~~
rwmj
Storing the AST could also make source code branching and merging more
accurate.

~~~
WorldMaker
Most languages don't have ASTs that work well in "degenerate" cases like
unfinished/work in progress/sample code. These are things you often want to
have in source control. There are also very few AST standards between
languages making for a lot more language-dependent work for an SCM.

(For what it is worth, I toyed with what I think to be a useful compromise to
the idea, which is to use syntax highlighting tokenizers, which perform well
and interoperate well with character-based diffs:
[https://github.com/WorldMaker/tokdiff](https://github.com/WorldMaker/tokdiff))

~~~
ken
I agree that degenerate cases are one of the major problems with trying to
move to higher-level editing abstractions (and it's one of the big issues I
faced with my own application), but I don't see how it's a problem here.

If it doesn't have a valid AST, then it's not a valid program, either. If
you're using a formatting program (like prettier or gofmt) and pass it an
invalid program, you're either going to get an error, or undefined behavior.
And anyone else opening it in a different editor is going to have their
language-mode interpret the invalid program differently than yours, too.
Source code tools like structured search won't work predictably, either.

This sounds to me like saying "An XML structure editor? But what if I want to
put an invalid XML structure in a file called foo.xml and commit it to the
repository?" Or my first boss complaining that visual text editors didn't let
you see every byte. Or Mel needing to know drum addresses so he could use them
for constants.

As time goes by, we move to higher level abstractions, and (thanks to tools
like gofmt) we're already at the point where you probably shouldn't be pushing
source code which doesn't even have a valid AST, and expect all your tools to
work perfectly. There's plenty of ways to write and commit WIP code without
needing an invalid AST on disk.

~~~
WorldMaker
I thought similarly for many years. What's the use of code in source control
that doesn't compile anyway?

Then I started paying attention to all the reasons why we naturally might want
to check in "invalid" code.

All the cases where I'm never going to finish an entire refactor in a single
commit, and the whole refactor makes far more sense in documented steps where
many of the intermediate steps will never compile.

All the cases where sending broken code to a source control server at the end
of the day is both the best way to back it up and the easiest way to get fresh
eyeballs on it in the morning. Even with CI systems in place, sometimes the
errors that the CI bot can tell you are as useful as the ones your own
machine's build environment can tell you. (Especially in those weird cases
where it turns out to be that maybe its the build environment on your own
machine that's the problem and you've been beating yourself up over a bad
install of something that should be unrelated, and the build errors on the
remote machine lead you to the real problem.)

All the cases where I might build tests first, and the compiler is a test,
especially in a static typed language, before working backwards to make the
tests pass/run/compile.

A lot of people like to think of programming code as some purely logical
construct, but good code is poetry, even in its mistakes. Sometimes we need
drafts to tell our stories right. Sometimes we need bad poetry in source
control as a warning to others to help them realize what good poetry can be.

It's interesting to want code editors to keep us from ever writing bad poetry
to disk, but it's also somewhat inhumane. People write bad poetry all the
time, it's a natural skill. Saving bad poetry for posterity is sometimes the
only way we get better poetry.

------
Semaphor
Here is the discussion from 10 years ago (40 comments):
[https://news.ycombinator.com/item?id=333626](https://news.ycombinator.com/item?id=333626)

~~~
sillyquiet
Alas it seems much like A.I., flying cars, and human immortality, reconciling
tabs vs spaces is one of those technological dreams that must remain a dream.

~~~
blauditore
Flying cars have been around for a while[0]. Of course you need a different
license, and they're a bit more expensive on average.

[0]:
[https://en.wikipedia.org/wiki/Airplane](https://en.wikipedia.org/wiki/Airplane)

------
kazinator
I find this unacceptable:

    
    
      int func( type arg,
                type arg);
    

Specifically, the space there after ( . Syntax-aware editors already handle
this formatting situation fine.

The remaining use case for this elastic business is aligning reams of comments
on the right side.

The best solution solution is not to write those. They are never needed in
code, but sometimes they can be beneficial in data structure declarations.

The elastic effect shown can be a achieved without tabs. The text editor
simply recognizes that multiple lines have aligned comments, and maintains
that with spaces. However, that still leaves the version problem/infelicity
that semantic changes to one line trigger surrounding whitespace changes.

The best solution is to leave enough space for the longest declaration you
would ever use. Anything longer is then split into multiple lines.

    
    
      struct foo {
        int magic;                            /* magic number */
        int x, y;                             /* coordinates in bar space */
        void (*sudden_big_decl)(struct foo *, /* Phew, That was close! */
                                int, int,     /* Good thing we left all that room. */
                                double);
      };

------
hodgesrm
The tabs vs. spaces debate has to be one of the biggest wastes of time in all
of programming. Not to be uncivil but it's a bit depressing to see human
creativity lavished on it once again.

One of the first things I do when starting a new project is to adopt a code
formatting standard enforced by automated tools like Eclipse formatting for
Java or Pep8 for Python, then move on. I really don't care what the format is
as long as it's consistent and readable, hence usually let the team decide in
return for adhering to the choice. And I've stopped working with teams that
cannot get past obsession over tabs vs. spaces. It's a warning signal that
there's an unhealthy lack of focus on things that really matter to users like
clean interfaces.

Edit: typo

------
stefs
this page shows up here about once a year. "This page was first uploaded on
2006-07-02 and last updated on 2017-11-18." afaik there are a couple of
plugins for various IDEs, but (as oftenwrong said) go's semi-forced
autoformatter seems to be the more practical/successful solution.

~~~
dangoor
We're using Prettier for formatting our JavaScript and I totally agree with
you that the right answer here is to "stop thinking about formatting" and just
let the tools deal with it.

------
stevetodd
I shouldn’t have to install software or set a tab-stop correctly to read your
source code.

~~~
coldtea
Well, you still have to install lots of things: a source code editor, a way to
download the code, a compiler to compiler it, libraries it needs to run, etc.

Having to install software or set a tab-stop correctly would be just one of
those things.

~~~
mmphosis
Gedit, the one program I have used in the past, in the small list of _set a
tab-stop correctly_ programs:

 _My original Gedit plugin (no longer compatible with Gedit 's changed API)_

This seems broken, not because it's not a good idea. A lot of things that I
don't use and don't want are already installed (and somewhat broken.) If I use
the 'cat' command on a source code file formatted this way, how is that going
to work? A new shell or new Terminal program that will _set a tab-stop
correctly_ , or a _set a tab-stop correctly_ plug-in for bash or the Terminal
program? The default eight spaces in the shell for tabs isn't very pretty, so
maybe you're onto something.

As I mostly blindly click "Install", _libraries_ that had vulnerabilities are
updated or maybe newly installed. I didn't ask for these libraries, I try to
remove them and the package manager reports that they are not installed. I
guess I've got bigger problems to deal with, and will bump along using tabs
and/or spaces. There must be a better way.

------
nevir
Tabs for indentation, spaces for alignment

~~~
begriffs
I wasn't going to jump into this because arguing tabs vs spaces feels like a
parody at this point, but I think you're correct.

I started using spaces for indentation just because I worked with people who
did that. I kind of forgot I was even doing it because vim's auto-indent did a
lot of the work, but after some time I realized spaces don't make sense.

The familiar arguments for tabs:

* Clean mapping between the concept of scope depth to a single character * Configurable display of indentation width, everyone can see what they want

Tabs have problems only when people use them for alignment, which breaks when
tabwidth is changed.

Actually can someone try to change my mind? I know tabs-vs-spaces is an
eternal war, yet I feel the problem is completely solved, hence I must be
ignorant of the counter arguments.

(The only thing more logical than using tabs might be using no whitespace at
all and having the editor determine presentation. But that wouldn't work well
with line-oriented tools.)

~~~
mjevans
It's only even a problem if tabs and spaces are mixed; if EVERY level of
indent were denoted by a tab then it wouldn't really matter if the default
editor width were 2, 4, or 8 or any other number as it would still be
consistent in that editor.

~~~
RHSeeger
If you're trying to align text, then even 2 would fail when the thing you're
trying to align with is an odd number of characters (name of a method, etc).

~~~
mjevans
Absolute alignment and level of indent dependent code are two separate tasks.

The former is a pre-formatted fixed-width character document that can only be
viewed one way.

The latter is a document which is easily processed; editors should show the
intended logical representation of the information according to user
preferences for conveying such meanings (though for terminal compatibility
they often default to either 8 or 4 fixed width spaces in less powerful
editors; still the meaning is conveyed clearly).

------
boterock
What I like about this is that this would end up all fights about how to
display tables with raw text formatting... just use tabs to delimit table
values...

Wait it already exists... we had the answer from a long time ago!
[https://en.wikipedia.org/wiki/Tab-
separated_values](https://en.wikipedia.org/wiki/Tab-separated_values)

It even avoids the comma/semicolon issue when using CSV in excel in spanish...
maybe avoids some other problems in other locales.

------
Bjartr
Here's a crazy idea I just had. What if there were multiple kinds of tab
characters so that you could encode your intent more explicitly? i.e. a "one
level of indent" tab, an "align with with the first instance of this character
on preceding line" tab, "indent to one more level than previous" tab, "indent
to one less level than previous" tabl, etc.

~~~
mattnewton
Why not just a formatter tool that uses the correct number of spaces according
to some rules?

------
dahart
I like this idea. I feel like today’s landscape may be making it somewhat
irrelevant, everyone seems to be moving to automated lint-formatting, but I
might have to try this.

Having written a code auto-alignment tool, I like the idea of elastic tabstops
because it makes the alignment explicit. Thinking of it as a grid and having
an explicit separator between columns would be nice, it gives me control over
what gets aligned. Auto alignment is tricky and always has corner cases as
well as missing features, things you wish it would align but it doesn’t.

I don’t buy the argument mentioned several times here that elastic tab stops
have to degrade gracefully; tabs already don’t do that. From my point of view,
one of the larger downsides is it wouldn’t help with situations that call for
right-align, e.g. numbers. And since formatting requires look ahead, I’m
curious if the command line tool (“etst”) is enough to make command line
workflows good, e.g., ssh, grep, sed, perl, etc.

------
ben509
The only way this gets adopted is if it degrades gracefully to existing
editors and tooling.

This only works, obviously, with a well-behaved whitespace model: pure spaces
or 8-spaced tabs.

I can see a few ways to do this:

1\. The editor deduces elastic tabs by observing where text lines up. 2\.
Augment #1 with knowledge of the lexical structure (same complexity as a
syntax highlighter) to deduce where it _ought_ to line up. 3\. Use invisible
characters to identify tab-stops. (Repurpose vertical tab, maybe? Or a zero-
width char?) 4\. Use an external file to store tab-stops. (At which point you
can name your tab stops.) 5\. A 'prettifier' utility can use parsing knowledge
to generate 3 or 4.

------
hcs
Neat! I'm interested in visual programming languages, the semi-textual
intermediaries like forced indentation in Python and frame-based editing [1]
are fascinating, so I'm surprised I'd never heard of this. The first issue I
see is there are some styles and languages where things are more naturally
right-aligned within their "cells" (e.g. alignment against the left side of a
column of :s), which starts to complicate things.

[1] [https://www.greenfoot.org/frames/](https://www.greenfoot.org/frames/)

------
agumonkey
And now I understand what word processor tabs were meant to be ...

~~~
TeMPOraL
Yeah. Their UX in Microsoft Word is a bit PITA, and it took me much longer
than I'm willing to admit before I understood how they're supposed to work,
but themselves they're a really good idea.

I didn't make the connection with aligning source code before - but mostly
because "source code" is in the "plaintext" bucket in my head, as opposed to
"rich formats" \- that is, I expect to read and edit streams of bytes, the way
they're saved in memory, with no invisible, hidden metadata that could get
silently corrupted. While the word processor style tabs seem brilliant, I have
mixed feelings here, as I wouldn't want source code to stop being raw
plaintext.

~~~
agumonkey
Like a lot of things for this kind of software it required both culture (tab
stops were a legacy from physical typewriter) and RTFM to understand how
Microsoft designed the ergonomics under Word. As a noob I need only a
simplified version of it (static tabs) and the full fledged caused a lot of
reflex behavior from Word that threw me off. Not helped by the fact that word
tables (another alignment trick) was random as ..

------
ComputerGuru
Due to the way vim was written, it’s been an incredible pita trying to get
support for elastic tabs going there. It’s funny: it’s easier to code elastic
tabs for dozens of closed source IDEs and editors with only basic plugin
functionality than it is to add it to the most hacked editor of all time.

(On phone. Will link GitHub PR when I’m on a desktop and can use the internet
with proficiency somewhat greater than that of a caveman.)

------
saagarjha
> It's not hard to imagine new conventions evolving in a new environment where
> proportional fonts are possible and text always stays lined up properly.

This is only one of the reasons why I use a proportional font. The other, and
arguably more important reason, is that monospaced fonts make it easier to see
if I've made a mistake in repetitive code because it acts like a visual
"checksum" of the line.

------
SeriousM
We prefer spaces over tabs because the ident style requires it. Anyway, the
solution is simple: agree on one style and stick with it. Code is made to be
read many times more than it's modified (open close, you know), therefore one
shouldn't read a code line more than once to understand it. It's your job to
deal with the company style and adopt, otherwise you're not professional.

------
k__
In the times of standard code formatters for every language, isn't this just a
display problem?

For JavaScript, you could simply force the code to be saved as prettier
formatted without any config.

If you display it, however, you can let your editor apply whatever format you
like. So you would have one source of truth in the repo and whatever you like
in your editor.

------
MaysonL
Of course, this whole yak-shaving controversy is due to the initial mistake:
monospace fonts for code.

Using real typographic fonts, with proportional spacing, allows for more code
per screen (using multiple statements per line in many cases).

~~~
andrewmcwatters
> real typographic fonts

monospaced typefaces are "real", and I would hate to live in a world where
someone thought it was a good idea to write software, represented in columns
and lines, in anything other than monospaced font

~~~
CJefferson
Why?

I've always assumed the obsession with monospaced fonts in coding comes from
terminals, which are monospaced for traditional reasons.

I code in non-monospsaced fonts sometimes. I enjoy it. There are (for me)
basically no downsides except occasional other coders wanting things "lined
up" vertically, which I don't care about but enforce using a code formatter.

------
solidsnack9000
It would be great if the terminal did this, so tab-separated value would "just
work" and commands with tabular output (ls, ps) wouldn't need to worry about
alignment at all.

------
jmull
This is a "good idea" in one sense.

But I think it will simply fragment things further (if it gets traction, that
is).

------
iainmerrick
This is and always has been the right idea. Unfortunately there's no way to
get there from here.

------
rightbyte
Hmm, I kinda like this. I wonder if I would say the same after a "field test"?

------
w_t_payne
I'd love it if someone did a Sublime Text plugin that implements this ...

------
_zachs
Implement this for NVIM please!

------
X6S1x6Okd1st
No vim plugin?

------
tbiehn
This is a neat approach - but the current implementations adjacent-line tab-
stopping only works if you put block opens on separate lines.

------
kevinSuttle
I was _just_ reading about this at
[http://input.fontbureau.com/info/](http://input.fontbureau.com/info/)

------
tasty_freeze
There were N different ways to handle tabs, and now there are N+1 ways. The
solution to the problem isn't to add more ways to implement tabs but to reduce
the number of ways to implement tabs.

