collapse

command
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: BSD-3-Clause Imports: 5 Imported by: 0

Documentation

Overview

Command collapse collapses runs of insignificant whitespace to a single space and leaves the elements where whitespace is significant alone.

The rule a browser applies is that a run of spaces, tabs and newlines in element content renders as one space, so a document can be indented for a reader and served without the indentation. Doing it in a stream is a state machine with one bit: was the last character written a space. That bit has to survive things the whitespace itself does not care about.

A run can be split across two writes, because a text node arrives in as many chunks as the writes it was fed in - so the bit outlives a chunk. A run can also be split by markup, since a tag is not a character:

<p>a  <b>  b</b></p>  ->  <p>a <b>b</b></p>

which is one run of five characters as far as rendering is concerned. So the bit outlives a text node too, and the program has no selector for "the text either side of an inline tag": it keeps the bit and lets the tags go past.

Where whitespace is significant the program has to keep out, and that is not a list it invents. A <pre> and a <textarea> render their whitespace, and every element whose content is not markup at all - a script, a style, an xmp - holds something that is not prose and must not be reflowed: collapsing inside a script would rewrite a template literal, and inside a style a selector. So the test is lolhtml.IsRawText plus pre and its obsolete synonym listing, and the program counts the regions it stayed out of.

Knowing when such a region ends is the hard part, and this program does the cheap version of it. It counts depth, and treats an end tag callback as the end of the element - which is right when the element has its own end tag and right when an ancestor's end tag closed it, and late when a sibling's start tag did:

<ul><li><pre>a  b<li>c  d</ul>

The pre was closed by the second <li> and the callback arrives at </ul>, so "c d" is treated as preformatted and comes out with its two spaces. Being late here means leaving whitespace alone, which is the harmless direction; getting it right needs the stack of open elements and the specification's implied end tags, which examples/gip/markdown and examples/gip/depth pay for. The case is measured rather than assumed - see TestAPreClosedByASiblingIsOverpaid.

A comment is not a character either, and the program treats it the way it treats a tag: it renders as nothing, so the whitespace on both sides of one is a single run. Its own text is left alone, because a comment can be a licence banner or a conditional and neither is prose.

Two things this deliberately does not do.

It never deletes a space entirely, only shortens a run to one. Whether the remaining space is visible depends on whether the surrounding elements are inline, which is a CSS question, and a minifier that answers it with a list of block element names gets it wrong on the first page that sets display in a stylesheet.

It works on the characters the document wrote, not on the characters a browser would decode: text is reported as the document spells it, so "a&#32;&#32;b" is twelve characters with no whitespace in them and is left as it is. Collapsing it would mean decoding references, and writing text back decoded would change every other reference on the page.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL