Skip to content

A paragraph's join space can be spent at the end of a wrapped line #58

Description

@gitosaurus

Impact: low. Cosmetic, invisible in a terminal.

Filing this for the context, not because it needs fixing soon. A wrapped line
can end with a space. Nothing renders differently: a terminal shows nothing,
HTML collapses it, and gameplay is unaffected. It shows up only in byte-exact
comparison of --width output, or when someone copies text out of a window.
The fix touches the trickiest function in WrappedOutput, so the cost is
plausibly larger than the defect.

Reproduction

room start_room
  desc : "probe room"
methods
  'INITIAL' : 'START HERE' -> player
  'LONGDESC' : {
>>Struggling, I manage to get the guard's uniform off of his fat,
>>sweaty---and increasingly cold---body.
    }
end
./build/archetype --silent --source=probe.arch --include=games --create=probe.acx
./build/archetype --silent --update=probe.acx --input="look" --width=36 | grep -n ' $'
2:guard's uniform off of his fat, <EOL>

Mechanism

Not the paragraph's fault. ParagraphOutputStatement::execute
(src/Statement.cc:528) already has exactly the right semantics -- it is
' '.join(pieces), emitting a separator between lines and nothing after the
last, with two spaces after . : ! ? and none at all when the previous
line already ended in whitespace. There is no paragraph-final space to trim.

The separator goes out as its own small put. At that moment the cursor is at
31 of 36, so the space fits and is committed to the line. Then the next
fragment arrives, its first word is 12 characters, 4 columns remain, and the
cut_p == s.begin() branch (src/WrappedOutput.cc:67) ends the line and moves
the word down. The space was already spent.

Nothing is wrong at either site. The space was emitted before the information
needed to judge it existed.

TestWrappedOutput::testWrapAcrossPuts_ (src/TestWrappedOutput.cc:128)
already covers the neighbouring case, where the space arrives with the cursor
exactly on the margin and the break correctly spends it. This is the case
where the space fits but the following word does not.

If it is ever fixed

A pending-whitespace buffer in WrappedOutput: hold whitespace found at the
tail of a put instead of emitting it, flush it only when non-whitespace
follows on the same line, and discard it at endLine. Cursor accounting has to
follow the deferral, which is the part that makes this more than a few lines.

Note that it would change output bytes wherever it fires, so it wants the same
before/after capture over Starship and Gorreven that the em-dash change used.

A new case belongs next to the existing one in testWrapAcrossPuts_.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions