Formatting by Language

How to indent Go code on Mac

You can do this with Note My. Here is how to re-indent a .go file to hard tabs with the brace on the same line, which controls do the work, and the one thing worth doing before you press Apply.

A Go file that has been pasted from a web page, edited in three tools, or written at speed usually ends up indented with a mixture of tabs and spaces. Go is conventionally written with hard tabs, so the fix is not "add more spaces" — it is a pass that rewrites the leading whitespace of every line to the unit Go actually uses. Note My does that from one toolbar button, and Go is the one language where the tab setting is locked on for you.

This guide covers where that lock lives, what the pass touches, what it leaves alone, and what to do first so you can get back to the version you started with.

Go is the language Note My locks to tabs

Our indentation engine has a global tabs-or-spaces setting, and it defaults to spaces with a width of 4. Two languages get a lock of their own that overrides it, each set to what that language is actually written in:

The two per-language indentation unit locks and their defaults
LanguageLockDefault
GoIndent with tabsOn
PythonIndent with 4 spacesOff

So for Go you do not have to configure anything. Open a .go file, run the pass, and the leading whitespace comes back as tab characters — one tab per level — whatever the global setting says.

One glance is worth taking first. The tab lock follows the document's language, and the status bar is where you confirm it: if it says Go, the pass will give you tabs. A fragment dropped into a new untitled tab may still be sitting there as Plain Text, in which case your global setting is the one that applies. Check the status bar before you run the pass, or pick Go explicitly in the sheet, and the outcome is settled before you touch a thing.

Re-indent a Go file

  1. Make sure the file is saved

    Press ⌘S first. A re-indent changes the whole document at once, so it is worth having the version you started from sitting on disk. With the file saved, File ▸ Reload from Disk (⌘R) is a clean way back at any point: it discards the reformatted document and gives you exactly the version you saved, and reloading never changes the file on disk. Our auto-recovery safety net is there if the app closes unexpectedly; a save is the version you choose to return to.

  2. Confirm Note My knows it is Go

    The status bar shows the language Note My is treating the document as. Our advanced language identifier names it for you, so an open Go file is usually ready to re-indent the moment you look at it — and it does that on your Mac, with nothing uploaded and no account involved. A loose fragment pasted into an untitled tab may sit there as Plain Text instead. If the status bar does not say Go, click the Identify pill next to it: the Language Inspector opens, and you can set the language yourself before running the pass.

  3. Open the Indentation sheet

    Click Indent in the toolbar — the curly-braces glyph. It opens a sheet; it does not reformat anything on the way. The toolbar is the entry point, one click from wherever your cursor happens to be, and everything the pass is about to do is laid out in front of you before a single line moves.

  4. Select Go in the language picker

    The picker offers a global default plus a list of languages. Choosing Go snaps the brace style, indent width and tabs-or-spaces controls to the values you keep for Go — those three travel with the language, so the way you like your Go is remembered apart from everything else you write. The tab lock is a separate control: it sits with import-group preservation in the Go section of the sheet, both on by default, and it is what gives you tabs when you run the pass on a Go document.

  5. Leave the brace style on the same line

    There are two brace styles: same line (K&R) and next line (Allman). Same line is the default, and it is the one Go is written in. Unless you have a reason, leave it.

  6. Press Apply

    Nothing has changed in your document until you do. The preview card above the controls shows an if <condition> { … } snippet that redraws as you change the width, the unit and the brace style, so you can get the shape right in the sheet and then commit the whole file to it with one press.

Note

Re-indent is a whole-document action, which is exactly what an inherited file wants. One press of Apply and every line in the file comes back at a consistent depth, in the unit Go is written in, without your having to select anything or work through it function by function.

What the pass changes beyond the indent

The pass does more than put every line at the right depth. It also unpacks statements that have been crammed onto one line, so a compressed function comes back readable:

go
func main() { x := 1; fmt.Println(x) }

becomes:

go
func main() {
    x := 1
    fmt.Println(x)
}

What our indentation engine gives you back is the shape Go is normally written in: one statement per line, the brace on the same line as the declaration, one tab per level of nesting. It is worth running on inherited code before you read it: consistent depth makes a file far easier to scan.

Import groups are preserved

Go import blocks are usually grouped by hand — standard library, then everything else, separated by a blank line. Import-group preservation is a Go setting in the sheet and it is on by default, so a re-indent leaves that structure where you put it. The switch is in the Go section if you want it otherwise.

Comments, and the recovery pass

Ordinary comments are re-indented with the code around them. A separate, optional pass — the Recover damaged comments checkbox, off by default — handles one specific kind of damage: a line where a comment marker has swallowed real code, so that // Preserve state let x = 1 is one commented line instead of a comment and a statement. It is available for Go, and you switch it on in the sheet before you press Apply.

It applies to single-line comments, and doc markers — ///, //!, /**, /*! — are always left alone, so your documentation comments are never split. Turn it on for an inherited file with that damage in it, and the code that had been hiding behind a comment marker comes back as code.

Seeing the tabs you just got

The whole point of the pass is invisible by definition. Turn on Show Invisibles in the toolbar and each tab draws as →, each space as ·. Four independent toggles live in Settings ▸ Invisibles, all off by default: show whitespace, show end of line, highlight trailing whitespace, underline whitespace.

There are three marker styles: Symbols (the default, and the one you want here), Code Points, and Bytes, which shows hex bytes in the document's own encoding. None of it is written into the document — the markers are on screen only, and the file you save is unchanged by looking at it.

Indentation guides are a second way to read the result. They are on by default, with ten styles to choose from in Settings ▸ Guides, so you can pick a line weight that sits quietly behind the code rather than competing with it. Read them as a depth cue: they trace the nesting down the page, and a block at the wrong level becomes obvious from across the room. When you want the exact answer — this character is a tab, that one is a space — Show Invisibles is the control to trust.

What one pass gives you back

It is worth being concrete about what a single press of Apply is worth on an inherited file, because it is more than the word "indent" suggests:

  • Consistent depth, top to bottom. Every line comes back at the level it actually sits at, one tab per level. Nesting you had been counting by eye becomes something you can simply see.
  • Crammed lines come apart. Statements that were sharing a line are given one each, so a function written at speed reads as Go again.
  • Your import grouping survives. The standard-library block, the blank line, the rest — all exactly where you put them.
  • Your documentation is left alone. Doc comments are never split, and ordinary comments travel with the code they belong to.
  • Nothing happens on save. Every re-indent is something you asked for, at the moment you asked for it. A file you have not run the pass on is the file you wrote, character for character.
  • Big Go files are welcome. Our large-file engine is built for the generated and vendored files that repay a re-indent most.
  • Your other languages keep their own habits. Setting Go to tabs says nothing about how the rest of your files are indented.

The habit worth forming is to run the pass before you read unfamiliar code rather than after. Consistent depth is what lets you skim a file for its shape — where the loops are, which branch is the long one, how deep the nesting really goes — and that is a far cheaper way in than reading top to bottom through whitespace that disagrees with itself. Our comparison view is there for the moment after, when you want two versions of a file side by side and a plain answer about what moved.

Troubleshooting

I pressed Indent and nothing changed

The Indent button opens the sheet; it does not act. Nothing in the document changes until you press Apply. Escape or Cancel closes the sheet and leaves the document exactly as it was, so you can open it, look at the preview card, and back out again as often as you like.

Apply gave me spaces, not tabs

The Go tab lock applies only when the document's language is Go. If the status bar says Plain Text, the global setting — spaces, width 4 — is what applied. Set the language from the Identify pill, or choose Go in the sheet's picker, and run the pass again. The second run replaces the spaces with tabs; you do not need to undo the first one.

Typing a new block still inserts spaces

Set the global tabs-or-spaces unit to tabs and this settles for good. Live auto-indent — the whitespace carried onto the next line when you press Return — follows that global setting, while the Go tab lock governs the re-indent pass, so the two agree the moment the global unit matches. That is the setting to reach for if you are writing new Go rather than tidying old Go. Either way, running the pass again brings the whole file back to tabs in a single press.

Getting back to the version I started with

⌘R — File ▸ Reload from Disk — discards the reformatted document and gives you back exactly the version you saved. That is why the first step in the procedure above is ⌘S: with a save behind you, the way back is one keystroke, you can run the pass with different settings as often as you like, and reloading never changes the file on disk.

Re-indenting a generated or vendored file

Machine-written and vendored Go is what our large-file engine is built for, and it is the code a single pass repays most: a wall of generated declarations becomes skimmable the moment every level sits at a consistent depth. Open the file, check that the status bar says Go, and press Apply once — the pass is a whole-document action, so you are not working through it section by section. Save first, as ever, and ⌘R remains your one-keystroke way back to the version you started from.

What the preview card is showing

The card shows an if <condition> { … } snippet that updates as you change the width, the unit and the brace style. Read it as the shape of your settings: get the snippet looking the way you want your code to look, and Apply takes the document there.

A scan reported mixed tab and space indentation

Mixed tab/space indent is one of the things our hidden-character scanner will tell you about, and it reports so that you can decide. It will not rewrite your indentation behind your back — nothing changes until you ask. When you do want it resolved, the re-indent pass is what resolves it, and one press of Apply settles the whole file at once.

Frequently asked questions

Does Note My use tabs for Go automatically?

For the re-indent pass, yes — the Go tab lock is on by default and overrides the global spaces setting, provided the document's language is Go. For the whitespace you get as you type, set the global tabs-or-spaces unit to tabs, as the troubleshooting note above describes, and new Go blocks come out the same way.

Will re-indenting rearrange my import block?

Your grouping survives. Import-group preservation is a Go setting in the Indentation sheet and it is on by default, so the standard-library block, the blank lines you put between groups and everything after them come back exactly where you left them.

How do I start a re-indent?

From the toolbar. The Indent button — the curly-braces glyph — is the entry point, one click from wherever your cursor happens to be, and it opens the Indentation sheet rather than acting immediately. Set the language, read the preview card, then press Apply when the snippet looks the way you want your code to look.

Does one press cover the whole file?

Yes. Re-indent is a whole-document action: press Apply once and every line comes back at a consistent depth in the unit Go is written in, from the first import to the last closing brace. There is nothing to select and nothing to work through function by function, which is what makes it quick on a file somebody else wrote.

Can I change the brace style for Go without changing it everywhere?

Yes. Brace style, indent width and tabs-or-spaces are the three settings you can set differently for one language, so a change made with Go selected in the picker applies to Go and leaves your other languages as they were. The rest of the sheet applies everywhere, so "Reset to global" clears only those three.

Does any of this send my code anywhere?

Everything in this guide happens on your Mac, offline, on the file you have open. Note My has no account and no sign-in, nothing is uploaded, and your files never leave your Mac.