Skip to content

Toggle folding for everything & comfortably fold at point#608

Open
codeluggage wants to merge 37 commits into
xenodium:mainfrom
codeluggage:feat/fragment-folding
Open

Toggle folding for everything & comfortably fold at point#608
codeluggage wants to merge 37 commits into
xenodium:mainfrom
codeluggage:feat/fragment-folding

Conversation

@codeluggage
Copy link
Copy Markdown

@codeluggage codeluggage commented May 27, 2026

Closes #607

Adding a thing my muscle memory desperately needs; org-mode folding. It's hard-wired to tab and shift+tab in my brain and it was tricky to do reliably in my config so I am scratching my own itch here.

Splitting folds by type (and the truncating turns to fold entire turns) was a bigger scope.

I added some tests but because there aren't many tests for things like this, I may not be structuring it right. Made it go red-green but that doesn't say much when this kind of test didn't exist yet.

Heads up that I get warnings for when-let as I'm on Emacs 31.0.60, but leaving that as-is.

I've reviewed all code in PR myself and will vouch for its quality

Yes but also no! I have reviewed it, I've been using it, but I can't attest to its quality beyond lots of reviewing by LLMs. I am an elisp beginner and have mostly done config stuff.

These technically aren't visual changes but here's a visual representation of the changes anyway (note that I have tab and shift+tab set as keybinds for this):

toggle-fold-in-place-and-toggle-all-folds

Checklist

  • I agree to communicate (PR description and comments) with the author myself (not AI-generated).
  • [-] I've reviewed all code in PR myself and will vouch for its quality.
  • I've read and followed the Contributing guidelines.
  • I've filed a feature request/discussion for a new feature.
  • I'm making visual changes, so I'm including screenshots so you can view and discuss.
  • I've added tests where applicable.
  • I've updated documentation where necessary.
  • I've run M-x checkdoc and M-x byte-compile-file.

@codeluggage
Copy link
Copy Markdown
Author

I don't have M-x checkdoc 🤷🏻 but M-x byte-compile-file is fine and I've been using this for ~1 day and it's working well for me!

xenodium added 29 commits May 30, 2026 17:50
- avoid-ranges is now a sorted vector; --in-avoid-range-p does binary search and returns the containing range.
- --replace-* passes use that return value to jump past avoid-ranges instead of re-matching inside them.
- --find-tables skips avoid-ranges in one hop and uses forward-line 1 between non-matches (table regex is bol-anchored).
Skip re-rendering already-processed prefix on each call

Streaming use of `agent-shell-markdown-replace-markup' calls the
renderer once per chunk, so every pass was re-walking the entire
buffer from `point-min' on each call — O(N^2) over N chunks.

Track a per-buffer "watermark": the position before which content
is fully rendered and stable.  Stored as an
`agent-shell-markdown-watermark' text property on the first
character (so a propertized string returned from
`agent-shell-markdown-convert' carries it without a buffer-local
variable).  Re-stamped at the end of each render to:
- start of the last line in the buffer; clamped back to
- start of any open fence (so a future closing ``` still matches),
- start of any rendered table whose extension is still possible
  (so streamed continuation rows still fold in).

The next call narrows to (watermark, point-max) and every pass
runs inside the narrow.

`:force' on `agent-shell-markdown-replace-markup' drops the
watermark and re-renders the whole buffer.
Reported in PR xenodium#597: pi-acp tool-call fragments rendered with the
indicator showing `▶' (collapsed) but the body fully visible.
xenodium and others added 8 commits May 30, 2026 17:50
Two new interactive commands for fragment folding:

- agent-shell-ui-toggle-fragment-dwim: toggle the fragment at or
  surrounding point, then restore point to its original position when
  the toggled fragment was the enclosing one.  Works whether point is
  on the fragment's indicator, inside its body, or between fragments
  (forward fallback).

- agent-shell-ui-cycle-all-fragments: cycle the global fold state of
  the current buffer between all-expanded and all-collapsed.  Tracks
  state in a buffer-local variable so successive invocations alternate.
  Only navigatable fragments (those with a fold indicator) are touched.

Neither command is bound by default — users wire them to TAB / S-TAB
(or similar) in their own config.  This preserves the existing
agent-shell-next-item / agent-shell-previous-item navigation bindings
for users who prefer them.

Internal helper agent-shell-ui--enclosing-fragment-position resolves
the nearest fragment given the current point: same fragment if point
is on a state-tagged char, otherwise backward-then-forward scan with
a block-range containment check.
Apply CONTRIBUTING.org style guidelines to the two new commands added
in dde21e1:

- Replace make-hash-table dedup with a list + member check. The project
  prefers alists/lists with seq/map.el over hashtables.
- Flatten enclosing-fragment-position with when-let* guard bindings
  instead of nested let + and. Same readability improvement applied to
  toggle-fragment-dwim.
- Drop the trailing (message "agent-shell folds: ...") in
  cycle-all-fragments. The surrounding nav/toggle commands
  (toggle-fragment-at-point, forward-block, backward-block) are silent
  on success and silent on no-target; match that.
- Default --fold-cycle-state to nil (was 'expanded) so first invocation
  derives the buffer's current majority state instead of guessing.
  Added a small helper --majority-collapsed-p for that lookup.
- Compare fragment identity via :qualified-id (map-elt) instead of eq
  on the state plist itself, which only worked because the same plist
  pointer was shared across a body's text-property range.
The inline-markdown branch (xenodium#597) uses the
aligned-under-section style for `add-text-properties' plists, e.g.

    `(agent-shell-ui-section label-left
                             help-echo ,qualified-id
                             read-only t
                             front-sticky (read-only))

instead of the flat indent.  Reformatting our touched call sites
to match avoids gratuitous diff churn when the experimental
renderer lands upstream, and brings em-dashes back into the
folding-command docstrings to match the surrounding prose style.

No behaviour change.
After the inline-markdown surgical-edit refactor, a body replace
mints a fresh `agent-shell-ui-state' plist on the new body chars.
`agent-shell-ui-toggle-fragment-dwim' resolves the target by
`:qualified-id', which is stable across the replace.  Lock the
behaviour with a regression test so a future refactor that
re-introduces pointer-identity comparison fails loudly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Folding QOL changes; toggle all, and toggle back and forth at point

2 participants