-- Long lines (QoL Stage 3, framing docs/long-lines-framing.md). -- -- Declares `ui.line-wrap`. Everything that honors it is Rust: the grid -- renderer walks it through `Viewport`, the coordinate mapping takes it -- through `LayoutCtx`, and semantic frontends are told over -- `InstanceMessage::LineWrapFacts` at protocol v22 because they lay out -- locally and would otherwise never hear it. -- -- Buffer-local (Q#LL2). The registry already supports a per-buffer -- layer, and this is a property of the content: prose wants wrapping, -- a log file usually does not. The *anchor* the viewport scrolls to -- stays per-window, because two panes on one buffer scroll -- independently. -- -- `ui.`, not `editing.`: `editing.*` is buffer-editing behavior -- (auto-pair, trim-on-save, line endings) and this changes only how -- text is shown. The two existing `ui.*` settings carry a `gpu-` -- prefix to mark frontend-specific ones, so the ABSENCE of a prefix -- here is what says "both frontends". pmacs.config.define { name = "ui.line-wrap", -- The description names what `truncate` COSTS, because the toggle's -- status message is not enough: a user who sets this in `init.lua` -- never invokes the toggle and so never sees it. #221 shipped that -- gap (framing §6). -- -- In the TUI the cost is now only the GUI's, because Stage 4 gave the -- grid renderer horizontal scrolling. Stage 5 closes the rest, and -- this sentence shrinks back when it lands. description = "How a line wider than the window is shown: wrap onto following rows, or truncate at the edge. Truncated text is reachable by moving the cursor past the edge in the terminal UI; in the GUI it is not yet reachable at all.", -- A closed set, so an unknown value is impossible rather than -- handled. Adding "word" later is a clean additive change --- which -- is the plan, since character wrap is what both frontends can do -- identically today (Q#LL5) and word wrap is a deliberate future -- choice rather than an inherited library default. type = "enum", choices = { "wrap", "truncate" }, -- `wrap` is the only value that leaves every character reachable -- with this stage's machinery. It is also what the GPU already did, -- so the default is not a behavior change there --- but it IS one in -- the TUI, which truncated. No default can preserve both, because -- the two frontends disagreed before this setting existed; that is -- the defect, not a side effect of fixing it. default = "wrap", mutability = "live", } -- `truncate` leaves text past the right edge UNREACHABLE until Stage 4 -- adds horizontal scrolling. That is stated in the description above -- rather than left for a user to discover, and it is why `truncate` is -- not the default despite being the TUI's historical behavior. -- Buffer-local on BOTH sides, and the pairing matters. -- -- `pmacs.config.get(name)` reads the global chain and -- `pmacs.config.set(name, ...)` writes the global layer, so a toggle -- built from those two would be wrong in the case the setting exists -- for: a buffer pinned to `truncate` would report the GLOBAL value, -- flip the GLOBAL value, and leave that buffer exactly as it was --- -- while silently changing every buffer that had not been pinned. -- -- So: read with `get(name, buf)`, write with `set_local(buf, ...)`, -- and resolve the buffer ONCE for both. Resolving twice would be a -- narrower version of the same bug, since the active buffer can change -- between two calls. pmacs.command.define { name = "ui.toggle-line-wrap", description = "Toggle line wrapping for the current buffer", fn = function() local buf = pmacs.window.buffer() local current = pmacs.config.get("ui.line-wrap", buf) local next_mode = current == "wrap" and "truncate" or "wrap" pmacs.config.set_local(buf, "ui.line-wrap", next_mode) if next_mode == "truncate" then pmacs.editor.set_status("line wrap off — move the cursor past the edge to scroll (GUI: not yet)") else pmacs.editor.set_status("line wrap on") end end, }