Skip to content

normalize-comment-spacing

normalize-comment-spacing puts one space between a comment's hash run and its text, and widens the gap before a trailing comment to at least two spaces, so x = 1 #note reads x = 1 # note and the delimiter looks the same on every line.

Whether the indentation inside a comment is deliberate depends on whether the comment stands in a column. A comment opening at the same column as the comment above or below it belongs to a block the author laid out, so every indent inside that block stays:

python
# options for the run:
#     --fast   skip the checks
#     --slow   run everything

A bare # continues such a block rather than ending it, since it opens at the same column, which keeps a spacer line from splitting one block into two. A comment sharing its column with nothing has no layout to keep, so a padded opener such as # note is reduced to the single space every other comment takes. A run opening on any whitespace other than a space is reduced either way, a lone tab included, since no author indents a block with one.

The space goes after the whole hash run rather than shortening it, so a ## or #### heading keeps its hashes as the section marker it was written as. space-statements reads the same form when it treats a comment block as a divider rather than a description of the statement below. A run with no text after it is a divider rather than an opener, so a line of hashes alone passes through unchanged, and a comment carrying whitespace and no text drops that whitespace down to a bare #. Where the run is followed by !, :, ', or |, the opener passes through untouched, covering the shebang line and Sphinx's #: attribute doc alongside the quoted and piped forms. A trailing comment with such an opener still moves out to the two-space gap, because the exemption covers what a comment opens with rather than where it sits.

Two spaces is the minimum gap before a trailing comment rather than the target, so a wider gap stays as written and a comment column the author laid out is still there for align-comments to read. The gap is counted in characters, so a lone tab falls short and is replaced with two spaces.

x = 1 carries a trailing #note one space after the code, and a full-line #tidy this sits below it. Both comments gain the space after their #, and the trailing one moves two columns clear of x = 1, so every comment opens on the same # and the eye reads the delimiter once rather than re-parsing it per line.

x = 1  # note
# tidy this
python

Three option comments listing --fast and --slow open at one column on consecutive lines, and the comment trailing value = 1 opens with a padded # already. The block keeps every indent inside it and the trailing opener closes to a single space, because the block is a layout the author wrote across consecutive lines, whereas the trailing comment shares a column with nothing.

#   options for the run:
#     --fast   skip the checks
#     --slow   run everything
value = 1  # already clear of the code
python

##notes, ### Section, and ####deeper open on runs of two, three, and four hashes. ##notes becomes ## notes and ####deeper becomes #### deeper, because the space is inserted after the whole hash run rather than the run being reduced to one #, so each line survives as the section marker it was written as. ### Section already carries its space and is left as written.

## notes
### Section
#### deeper
python

Configuration

KeyTypeDefaultMeaning
enabledbooltrueTurns the rule on or off.

The one-space opener and the two-space minimum are both fixed, so the rule carries enabled as its only facet. A comment that has to keep its own spacing takes an inline Suppression directive rather than a project-level key, since the exception is per-comment rather than per-project.

The Canonical Case

x = 1 carries a trailing #note one space after the code, and a full-line #tidy this sits below it. Both comments gain the space after their #, and the trailing one moves two columns clear of x = 1, so every comment opens on the same # and the eye reads the delimiter once rather than re-parsing it per line.

x = 1  # note
# tidy this
python

More Examples

Three option comments listing --fast and --slow open at one column on consecutive lines, and the comment trailing value = 1 opens with a padded # already. The block keeps every indent inside it and the trailing opener closes to a single space, because the block is a layout the author wrote across consecutive lines, whereas the trailing comment shares a column with nothing.

The comment on value = 1 opens with no whitespace between the 1 and the #. Two spaces are inserted before the #, the minimum gap before a trailing comment, and because the # right opener already carries its single space that insertion is the only edit the comment takes.

call( opens a multi-line call and carries # note on the opener one space after the bracket. The single space widens to two, because the two-space minimum applies wherever a comment trails code, an open bracket included. strip-stranded-padding exempts the gap in that position so the comment never fuses onto the bracket, leaving this rule to set its width.

The dividers ##### and ########## are runs of hashes with nothing after them, and #---- Section ---- draws its rule from dashes after a single #. The two bare hash runs are left as written and the dashed line gains a space after its #, because text follows the hash on the dashed line and nothing follows it on the other two.

##notes, ### Section, and ####deeper open on runs of two, three, and four hashes. ##notes becomes ## notes and ####deeper becomes #### deeper, because the space is inserted after the whole hash run rather than the run being reduced to one #, so each line survives as the section marker it was written as. ### Section already carries its space and is left as written.

A full-line block listing --fast and --slow sits above warm() and a_name_that_is_far_longer_here(), each of which carries a # opener padded to five spaces. The block keeps every indent inside it and each trailing opener closes to a single space, even though the two trailing comments share a column with each other, because the block is a layout the author wrote whereas a trailing hash sits wherever its code ends, with align-comments the rule that moves it afterwards.

value = 1 carries a Sphinx attribute doc opening on #: one space after the code. The #: opener stays as written and the gap before it widens to two spaces, because the exemption covers what the comment opens with rather than where the comment sits.

No Change

value = 1 and other = 22 each carry a trailing comment with a wide gap before it, and the two comments already share a column. Both gaps are left as written, because two columns is the minimum rather than the target, so the column they share stays in place for an alignment rule to read rather than being flattened before one can.

No Change

Four full-line comments open on #!, #:, #', and #|, covering the #!/usr/bin/env python shebang, a #:sphinx attribute doc, and the #'quoted and #|piped forms. Every line is left as written with no space inserted, because a # followed by !, :, ', or | carries meaning to a downstream tool, and a full-line comment carries no gap before the hash to set either.

  1. _handlers carries a trailing comment written #map of ..., tight against the hash, above _handlerList, and normalize-comment-spacing later writes one space after the #. _handlers and _handlerList stay unaligned, because align-equals counts that space before it sets its column and a shared column would carry the _handlers row past the 79-column budget, whereas _ok and _status below pad onto one column as usual.