<feed xmlns='http://www.w3.org/2005/Atom'>
<title>gloss/tests, branch main</title>
<subtitle>Emacs glossary lookup with Wiktionary fallback
</subtitle>
<id>https://git.cjennings.net/gloss/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/gloss/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/'/>
<updated>2026-04-30T12:27:44+00:00</updated>
<entry>
<title>refactor: rework gloss-add UX to single side-window buffer</title>
<updated>2026-04-30T12:27:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T12:27:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=9e90517a98785c450cd13cd940bd1787a4771529'/>
<id>urn:sha1:9e90517a98785c450cd13cd940bd1787a4771529</id>
<content type='text'>
The previous shape opened a regular (non-side) buffer for body input
and showed the saved entry in the side window after C-c C-c. That
left an extra window split during the typing phase and a side popup
the user didn't ask for.

New shape, modeled on `org-capture':

- `gloss-add' renders the term and underline as a read-only header in
  *gloss-add: TERM*, leaves the body region beneath it editable, and
  pops the buffer in the side-window slot. Point lands at the body
  start so the user can type immediately.
- `gloss-add-finish' reads the body via the `gloss-add--body-start'
  marker, saves with source `manual', kills the buffer, closes the
  side window, and echoes `gloss-add: saved TERM' for confirmation.
- `gloss-add-abort' kills the buffer and closes the side window.
- The shared `gloss--add-cleanup' helper handles kill + window-close
  for both finish and abort.

Read-only header uses text properties (`read-only', `front-sticky',
`rear-nonsticky') rather than narrowing, so the user can't escape the
restriction with `C-x n w'.

`gloss--add-finish-internal' no longer calls show-entry — the save
is its only responsibility. The display decision (show or not) is
the caller's, which lets `gloss-add-finish' choose "save and close"
while `gloss-lookup' still chooses "save and show."

The previous saved-window-config approach is dropped — the side-slot
takeover means there's nothing to restore. Layout returns to its
pre-add state on either C-c C-c or C-c C-k.

Adds `tests/test-gloss--add-flow-smoke.el' covering the four
interactive moments (open, finish, abort, empty-term guard) plus the
read-only-header invariant. Updates the
`gloss--add-finish-internal' tests to drop the show-entry assertion.

129 tests pass in 0.27s.
</content>
</entry>
<entry>
<title>feat: implement gloss secondary commands</title>
<updated>2026-04-30T06:11:27+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T06:11:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=5c7bb207081278e41122e62d8f6c282a18574665'/>
<id>urn:sha1:5c7bb207081278e41122e62d8f6c282a18574665</id>
<content type='text'>
Five interactive commands plus the supporting major mode and pure
helpers for `gloss-add'.

`gloss-add' opens a `*gloss-add: TERM*' buffer in `gloss-add-mode',
a `text-mode' derivative with C-c C-c to save and C-c C-k to abort.
The pure save side, `gloss--add-finish-internal', validates term and
body, trims trailing whitespace, and delegates to `gloss-core-save'
with source `manual' before showing the new entry.

`gloss-edit' resolves a term to its source-buffer marker, jumps point
there, unfolds the entry under both the legacy `org-show-entry' and
the post-9.6 `org-fold-show-entry' (with-no-warnings on the fallback),
and installs `gloss--after-save-refresh-cache' as a buffer-local
after-save hook so manual edits keep the cache honest.

`gloss-list-terms' prompts via `completing-read' over
`gloss-core-list' and dispatches the chosen term to
`gloss--lookup-flow'. Empty glossary raises a user-error.

`gloss-stats' formats a multi-line report (total / by-source /
drill-tagged / file size / cache mtime) via the pure helper
`gloss--stats-text' and shows it in `*gloss-stats*' under
`special-mode'. Drill counting walks the file via `org-map-entries'
to be safe against tag-substring false positives.

`gloss-reload' is a thin wrapper that resets the in-memory cache and
re-ensures it from disk.

`gloss-drill-export' is a thin wrapper around
`gloss-drill-export-all'.

Audit fold-in: renamed the stats-text missing-file test from
`-reports-zero-and-never' to `-reports-zero' to match what the
assertion actually checks (the file is auto-created on first call,
so mtime is set, not "never").

Filed as a v1.1 follow-up: `gloss.el' reaches into
`gloss-core--cache-reset' and `gloss-core--cache-ensure'
double-dash-private functions. Decide whether to treat double-dash as
"package-private" idiom or to expose public aliases.

125 tests pass in 0.25s — 111 prior plus 14 new across the six new
files. No byte-compile warnings.
</content>
</entry>
<entry>
<title>test: add gloss secondary commands test suite (red phase)</title>
<updated>2026-04-30T06:07:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T06:07:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=d313c37f14511564849c70c564c14ca51bd4ae7c'/>
<id>urn:sha1:d313c37f14511564849c70c564c14ca51bd4ae7c</id>
<content type='text'>
Six test files for the remaining stub commands. All 14 tests fail at
this commit because the implementations are stubs.

`gloss--add-finish-internal' (the pure save side of `gloss-add') gets
N/B/E coverage on validation and the persistence side effect.
`gloss--stats-text' (the pure stats string formatter) covers empty,
populated, and missing-file cases. The interactive commands
(`gloss-edit', `gloss-list-terms', `gloss-reload', `gloss-drill-export')
get smoke tests only — the design treats them as mode-glue with 70%
coverage targets, since prompts and `switch-to-buffer' are framework
behaviour Emacs already tests.

Two error-path tests assert the message contains a specific substring,
not just that `user-error' was raised. The stubs raise `user-error' too,
so a bare `should-error' would pass for the wrong reason. The substring
check anchors red against the real error path.
</content>
</entry>
<entry>
<title>test: add gloss orchestration core test suite (red phase)</title>
<updated>2026-04-30T06:03:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T06:03:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=540d805bd917a37d0fafa5393f3bfc7e3603570e'/>
<id>urn:sha1:540d805bd917a37d0fafa5393f3bfc7e3603570e</id>
<content type='text'>
Two test files for the orchestration core. All 13 tests fail at this
commit because the implementation is still stubbed.

`gloss--orchestrate-fetch-result' gets full N/B/E coverage on the
decision matrix: single def, multi def, the &gt;1 boundary, empty defs
with each combination of :no-defs and :failed populated, and the
all-empty degenerate case.

`gloss--lookup-flow' covers cache hit (no fetch), cache miss with one
def (auto-save), cache miss with multiple (picker), cancelled picker
(no save), no-defs error, and the force-fetch override that bypasses
the cache. Mocks live at network and UI boundaries; persistence runs
against a real temp glossary so the save side effect is validated.
</content>
</entry>
<entry>
<title>feat: implement gloss-drill org-drill export</title>
<updated>2026-04-30T05:30:36+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T05:30:36+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=5b5ac68e138950e2f8d502e22350a62570da88a6'/>
<id>urn:sha1:5b5ac68e138950e2f8d502e22350a62570da88a6</id>
<content type='text'>
Two public commands plus a small helper. `gloss-drill-export-all'
walks `gloss-file' via `org-map-entries' and adds `:drill:' tag and
`DRILL_CARD_TYPE: twosided' property to every top-level entry.
Membership and equality guards make the operation idempotent: re-running
adds nothing and writes nothing. `gloss-drill-untag-all' is the
reverse, and intentionally does not require `org-drill' to be installed
(the user might be cleaning up after uninstalling).

The walking logic factors into a single private helper
`gloss-drill--map-entries' that handles file open, modtime
verification, org-mode activation, the level-1 filter, and a
write-only-if-modified save. Both public commands compose it with their
respective per-entry mutators.

`org-drill' presence is checked with `featurep' before any walk so the
file is never touched when the dep is missing. The user-error message
includes the install command.

Folds in a small fix to the idempotency test helper: the original used
`throw' from inside `org-map-entries' but did not return the count to
the caller. Switched to `catch' / `throw' with the count as the throw
value.

98 tests pass in 0.24s — 88 prior plus 10 new across the four scenarios
named in the design doc (tags-untagged, skips-already-tagged,
no-orgdrill-installed, untag-all).
</content>
</entry>
<entry>
<title>test: add gloss-drill test suite (red phase)</title>
<updated>2026-04-30T05:28:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T05:28:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=e7938e9193ba1a39aab0e614bb3bf682508685b2'/>
<id>urn:sha1:e7938e9193ba1a39aab0e614bb3bf682508685b2</id>
<content type='text'>
Four test files plus a small testutil for toggling the `org-drill'
feature flag. All 10 tests fail at this commit because the implementation
is still a stub.

The suite covers Normal (untagged entries get the tag and property),
Boundary (empty file, idempotency, untag never-tagged), and Error
(org-drill not installed). The error path also asserts the file is left
untouched. Untag-all is tested under both feature states because the
user might want to remove tags after uninstalling org-drill.
</content>
</entry>
<entry>
<title>chore: add Cask + ert-runner test infrastructure</title>
<updated>2026-04-30T05:22:24+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T05:22:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=29c21851bddf76c5fd659d4225bb426fc1396750'/>
<id>urn:sha1:29c21851bddf76c5fd659d4225bb426fc1396750</id>
<content type='text'>
Replace the bare emacs --batch test runner with cask exec ert-runner.
The Makefile test targets now delegate. validate-parens, compile, and
lint stay on bare emacs --batch. Running cask once per file would slow
them down for no real win.

Adds:
- Cask file declaring sources and ert-runner as a dev dep
- .ert-runner config adding . and tests to load-path
- tests/test-helper.el placeholder for future test setup
- .gitignore entry for .cask/

All 88 tests pass in 0.19s via the new path.
</content>
</entry>
<entry>
<title>feat: implement gloss-display UI layer</title>
<updated>2026-04-30T05:02:27+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T05:02:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=6102ef81e7cc8af7cf847906cbc1bb27fc8a75e5'/>
<id>urn:sha1:6102ef81e7cc8af7cf847906cbc1bb27fc8a75e5</id>
<content type='text'>
Side-buffer display for gloss entries plus the picker shown when an
online fetch returns multiple candidates.

The major mode `gloss-mode' derives from `special-mode' so `q' quits
the window for free. Two pure helpers handle the formatting:
`gloss-display--format-candidate' renders a definition plist as a
single-line "[source] text" row for `completing-read', truncating with
an ellipsis when the row would exceed 80 chars.
`gloss-display--render-entry' produces the term, an underline matching
the term length, a blank line, then the body.

The picker maps the user's choice back to the original plist via an
alist, returning nil on `C-g' rather than letting the quit signal
propagate. `gloss-display-show-entry' opens the buffer in a
40%-width side window on the right, idempotent on repeat calls for the
same term.

All 88 tests pass, including 23 new tests across format-candidate
(full N/B/E), render-entry (N/B/E), pick-definition (mocking
completing-read at the boundary), and a show-entry smoke test that
mocks display-buffer to keep batch runs windowless.
</content>
</entry>
<entry>
<title>test: add gloss-display test suite (red phase)</title>
<updated>2026-04-30T04:59:30+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-30T04:59:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=735ecf082273c0cba2f5c5dd25876e2ccd83b0f7'/>
<id>urn:sha1:735ecf082273c0cba2f5c5dd25876e2ccd83b0f7</id>
<content type='text'>
Four test files covering the gloss-display public API and pure helpers.
All 23 tests fail at this commit because the implementation is still a
stub. Format-candidate gets full N/B/E coverage. Render-entry gets the
pure-helper treatment. Pick-definition mocks completing-read at the
boundary. Show-entry has a single smoke test.
</content>
</entry>
<entry>
<title>refactor: switch gloss-fetch result to uniform plist shape</title>
<updated>2026-04-29T00:13:05+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-29T00:13:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/gloss/commit/?id=01b75599a500d6276a962b47744166abb25d846c'/>
<id>urn:sha1:01b75599a500d6276a962b47744166abb25d846c</id>
<content type='text'>
The previous shape (:ok DEFS) | (:empty :no-defs (...) :failed (...)) was malformed as a plist. The :empty tag at position 0 shifted the plist alignment. plist-get on :no-defs or :failed returned nil. Tests had to use (plist-get (cdr result) ...) as a workaround.

The new shape is a uniform plist with all three keys always present: (:defs DEFS :no-defs (SYM ...) :failed (SYM ...)). Consumers branch on whether :defs is non-empty. There is no tag. plist-get works uniformly across success and empty cases.

Updated gloss-fetch.el (rollup function and docstrings), 7 test files, and the design doc (docs/design/gloss.org § Error Handling).

Tested by `make test`. 65 tests pass in 0.36 seconds.
</content>
</entry>
</feed>
