Skip to content

Add ghostel-spinner-progress for animated indeterminate progress#214

Merged
dakra merged 1 commit into
mainfrom
feat/spinner-progress
May 1, 2026
Merged

Add ghostel-spinner-progress for animated indeterminate progress#214
dakra merged 1 commit into
mainfrom
feat/spinner-progress

Conversation

@dakra
Copy link
Copy Markdown
Owner

@dakra dakra commented Apr 30, 2026

Summary

  • Adds ghostel-spinner-progress, a built-in handler for ghostel-progress-function that animates mode-line-process via spinner.el during indeterminate progress (the only state Claude Code emits) and falls back to the existing text indicator for set / error / pause / remove.
  • spinner.el is a soft dependency: when on the load-path at ghostel load time, ghostel-progress-function defaults to the new handler; otherwise it stays on ghostel-default-progress. The handler itself defers (require 'spinner nil t) to first call and signals user-error with an install hint if missing.
  • New ghostel-spinner-type defcustom (default progress-bar) selects the spinner style. Spinner state is buffer-local and torn down by ghostel--sentinel alongside the existing redraw / input / link timers, so the timer can't outlive the shell process.

Why not the README example

The previous README snippet had two latent bugs the built-in avoids:

  1. Spinner.el's tick overwrites mode-line-process every ~100 ms, so on a 'set event the percentage display was stomped almost immediately. The built-in stops the spinner on any non-indeterminate state before deferring to the text indicator.
  2. The spinner timer was never cleaned up on buffer kill. The built-in hooks into the existing sentinel-driven teardown path.

README updated to point at the built-in instead.

Test plan

  • make -j4 all clean (build, lint, 224/224 tests pass — 5 new spinner tests under the existing elisp test group, no native dep).
  • New unit tests cover: missing-spinner.el error path, idempotent indeterminate, spinner stopped + percentage shown on 'set, modeline cleared on 'remove, ghostel--spinner-stop helper.
  • Live verification: M-x ghostel, run claude on a multi-step task, confirm modeline animates during work and clears on completion. (Reviewer please sanity-check on your own setup; reporter saw [...] text in the prior fix and asked for animation.)
  • No-spinner.el path: with spinner.el absent from load-path, confirm ghostel-progress-function defaults to ghostel-default-progress and the existing text indicator behavior is preserved.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional spinner-based progress indicator for Ghostel’s OSC 9;4 progress integration, using spinner.el when available while preserving the existing plain-text indicator as a fallback.

Changes:

  • Introduces ghostel-spinner-progress, buffer-local spinner state, and sentinel cleanup to prevent spinner timers from outliving the shell process.
  • Updates ghostel-progress-function default selection to prefer the spinner handler when spinner.el is on load-path, and adds ghostel-spinner-type customization.
  • Adds ERT coverage for spinner behavior and updates docs (README + changelog) to describe the built-in handler.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
lisp/ghostel.el Adds spinner-backed progress handler, buffer-local spinner state, cleanup helper, and default selection logic.
test/ghostel-test.el Adds unit tests for missing-spinner error path, idempotent indeterminate behavior, stop-on-set/remove behavior, and spinner-stop helper.
README.md Replaces the previous spinner example with documentation for the new built-in handler and configuration.
CHANGELOG.md Documents the newly added spinner-based progress support and customization.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/ghostel-test.el Outdated
(should (eq 'fake-spinner ghostel--spinner))))))

(ert-deftest ghostel-test-spinner-progress-set-stops-and-shows-percent ()
"On `'set, the spinner is stopped and `mode-line-process' is the percent text.
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring quoting looks malformed: "'set" has an extra leading backtick before the quoted symbol. Consider changing this to set' (help-style) or 'set for readability.

Suggested change
"On `'set, the spinner is stopped and `mode-line-process' is the percent text.
"On `set', the spinner is stopped and `mode-line-process' is the percent text.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9b975a3 — changed to `set'.

Comment thread test/ghostel-test.el Outdated
(should (equal " [50%]" mode-line-process))))))

(ert-deftest ghostel-test-spinner-progress-remove-clears-modeline ()
"On `'remove, the spinner stops and `mode-line-process' is nil."
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring quoting looks malformed: "'remove" has an extra leading backtick before the quoted symbol. Consider changing this to remove' (help-style) or 'remove for readability.

Suggested change
"On `'remove, the spinner stops and `mode-line-process' is nil."
"On `remove', the spinner stops and `mode-line-process' is nil."

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9b975a3 — changed to `remove'.

Comment thread CHANGELOG.md Outdated
[spinner.el](https://github.com/Malabarba/spinner.el) during
indeterminate progress (e.g. while Claude Code is working) and
shows percentage text for determinate states. spinner.el is a
soft dependency: when it is on the `load-path' at ghostel load
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown inline-code formatting is broken here: load-path' starts a code span with a backtick but never closes it (it closes with an apostrophe instead). This will render incorrectly and can swallow the rest of the paragraph in some Markdown renderers; use load-path` (backticks on both sides) or plain text instead.

Suggested change
soft dependency: when it is on the `load-path' at ghostel load
soft dependency: when it is on the `load-path` at ghostel load

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9b975a3 — changed to backtick-on-both-sides for the markdown context.

Comment thread test/ghostel-test.el Outdated

(ert-deftest ghostel-test-spinner-progress-indeterminate-starts-once ()
"`ghostel-spinner-progress' creates the spinner once across repeat events.
Multiple `'indeterminate events during one working phase must not stack
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring quoting looks malformed: "'indeterminate" includes both a backtick and an apostrophe before the symbol name. If you meant to reference the symbol in a docstring, prefer either indeterminate' (Emacs help-style quoting) or just 'indeterminate.

Suggested change
Multiple `'indeterminate events during one working phase must not stack
Multiple `indeterminate' events during one working phase must not stack

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9b975a3 — changed to `indeterminate'.

@dakra dakra force-pushed the feat/spinner-progress branch 3 times, most recently from d781471 to 3676ba9 Compare May 1, 2026 13:53
Built-in handler for `ghostel-progress-function' that animates
`mode-line-process' via spinner.el during indeterminate progress
(typical for Claude Code, which only emits the indeterminate state)
and falls back to the existing text indicator for set/error/pause/
remove.  Stopping the spinner on any non-indeterminate state avoids
the failure mode where spinner.el's timer overwrites a percentage
display on its next tick.

spinner.el is a soft dependency.  When it is on the load-path at
ghostel load time, `ghostel-progress-function' defaults to the new
handler; otherwise it stays on `ghostel-default-progress'.  The
function itself defers `(require 'spinner nil t)' to first call and
signals `user-error' with an install hint if missing, so users who
pin the function explicitly get a clear message rather than a void
function error.

Buffer-local `ghostel--spinner' state is torn down by
`ghostel--sentinel' alongside the existing redraw/input/link timers,
so the spinner timer does not outlive the shell process.  New
`ghostel-spinner-type' defcustom (default `progress-bar') selects
the spinner style; see `spinner-types' in spinner.el for choices.

README's previous custom example had two latent bugs the built-in
avoids: percentages were stomped by the next spinner tick, and the
timer was never cleaned up on buffer kill.  Replaced with a brief
section pointing at the built-in plus the soft-dependency contract.
@dakra dakra force-pushed the feat/spinner-progress branch 2 times, most recently from f5d661e to 20356bf Compare May 1, 2026 14:10
@dakra dakra merged commit 20356bf into main May 1, 2026
14 of 18 checks passed
@dakra dakra deleted the feat/spinner-progress branch May 3, 2026 07:53
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.

2 participants