| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
| |
The .claude/rules/ files (commits.md, testing.md, verification.md) were local copies that drifted from the canonical at ~/code/rulesets. I replaced them with absolute symlinks pointing at the canonical files. This repo now picks up rule updates automatically.
The symlinks are absolute. Cloning this repo on a new machine needs ~/code/rulesets at the same path. Otherwise the symlinks dangle.
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replaces the placeholder with full usage docs modelled on chime's
README but tighter — gloss is a smaller package, the doc shouldn't
out-bulk it.
Sections: nav links, status, why-not-quick-sdcv, features, installation
(package-vc-install / use-package :vc / straight / manual), quick start
(lookup and add flows), the C-h g keybinding table plus the
gloss-add-mode binding table, the four defcustoms with rationale,
extending the source registry for v2+, org-drill integration,
troubleshooting (libxml absent, network failures, file corruption,
cache out of sync, side-window dismiss), development (Cask + ert-runner
+ Make targets), license.
The keybinding table is the source of truth — every entry matches what
`gloss-prefix-map' actually binds. The defcustoms section names the
four actual customs only (gloss-file, gloss-fetch-sources,
gloss-fetch-timeout, gloss-debug); no aspirational knobs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The four decisions called out in the brainstorm now have their own
files under docs/decisions/, each with Context / Decision /
Consequences / Alternatives Considered.
- 0001 — storage path default: respects org-directory if set, falls
back to user-emacs-directory.
- 0002 — auto-fetch on local miss: silent fall-through, network
failures surface via the regular error rollup. No y/n prompt for
v1.
- 0003 — drill direction: every entry exports as twosided. One card
per entry, both directions over time, no per-entry override.
- 0004 — HTML strip strategy: libxml-parse-html-region. Plain text
only, no italic/bold preservation. Online fetch disabled package-wide
for the session if libxml is missing.
The "Open Questions" section in the design doc is now "Decisions
Recorded" with links into the ADRs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two pure-ish helpers and two interactive entry points.
`gloss--orchestrate-fetch-result' is a pure pattern-matcher that
classifies a fetch result into one of five decision symbols. The
ordering matters: definition count gates first, then the error
taxonomy. All-empty falls through to `:error-no-defs' so the user
never sees a silent no-op.
`gloss--lookup-flow' is the orchestration. Cache hit dispatches
straight to display. Cache miss runs a fetch, classifies via the
helper, and either auto-saves the lone definition, prompts the user
to pick, or messages the right error. The `force-fetch' arg lets
`gloss-fetch-online' reuse the same flow without duplicating logic.
`gloss-core-save' is called with the `replace' collision action so
force-fetch over an existing entry replaces it cleanly. On a real
cache miss the entry is fresh, so `replace' is moot — the action
only matters when the term is already there.
`gloss-lookup' and `gloss-fetch-online' are now thin interactive
wrappers around `gloss--lookup-flow'. The remaining stubs (add, edit,
list-terms, stats, reload, drill-export) still raise user-error.
111 tests pass in 0.23s — 98 prior plus 13 new across the two new
files.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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 >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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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).
|
| |
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Walks the `gloss-fetch--sources' registry in the order set by the
`gloss-fetch-sources' defcustom and aggregates per-source results into
the public `gloss-fetch-definitions' shape.
The Wiktionary REST fetcher GETs the page-definition endpoint, parses
JSON, walks only English (`en') entries, and HTML-strips each sense via
`libxml-parse-html-region'. A sense whose strip fails is dropped while
the source keeps its `:ok' status with N-1 entries.
The HTTP-status taxonomy is five values: `:ok', `:no-defs' (404 or no
English senses on a 200), `:rate-limited' (429), `:server-error' (5xx,
malformed JSON, schema mismatch, 4xx other than 404 or 429), and
`:unreachable' (nil from `url-retrieve-synchronously', or a signaled
error). The `:reason' string carries technical detail to *gloss-debug*
and never reaches the user.
libxml is probed once per session at first fetch. When absent, online
fetch is disabled package-wide and every call signals `user-error' with
the install hint. `url-retrieve-synchronously' is wrapped with the
`gloss-fetch-timeout' defcustom (default 5 seconds).
Tested with `make test'. 60 of 62 tests pass. The two pending failures
load Wiktionary fixtures via `gloss-test--load-wiktionary-fixture',
which is provided on a parallel branch and will pass once both branches
land. The implementation has been verified against the captured
fixtures end-to-end (anaphora returns 4 senses, SBIR returns 2,
matching the design's expected counts).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Eight test files cover the network layer's public and internal contract.
The boundary mock is `url-retrieve-synchronously', wrapped by a small
`testutil-gloss-fetch' helper that builds response buffers in the shape
the url library returns.
Tests cover the 200 happy paths (anaphora and SBIR fixtures), 404 to
:no-defs, 5xx and 4xx-other and malformed JSON to :server-error, 429 to
:rate-limited, nil-from-url to :unreachable, the libxml availability
probe (one-shot, signals user-error when absent), the registry walker
ordering, and the pure HTML strip helper across N/B/E.
Tests fail on missing `gloss-fetch--*' functions, as expected for red
phase.
|
| |
|
|
|
|
|
|
| |
Append `gloss-test--load-wiktionary-fixture' to tests/testutil-gloss.el. It takes a fixture name (e.g. "anaphora") and returns the raw JSON body from tests/fixtures/wiktionary-NAME.json, or signals `error' with the full path when the file isn't there. The helper resolves the fixtures directory from a `defconst' captured at load time. That way it works the same whether a test file requires testutil-gloss directly or pulls it in transitively through `make test'.
Three ERT cases under tests/test-testutil-gloss--load-wiktionary-fixture.el cover Normal (anaphora loads as a non-empty JSON string), Boundary (the smallest fixture, 404, loads), and Error (a missing fixture raises with the path embedded in the message).
Verified with `make test': 35 passed, 0 unexpected.
|
| |
|
|
|
|
| |
Save raw response bodies from the Wiktionary REST endpoint under tests/fixtures/. The fetch layer can replay them with a cl-letf on url-retrieve-synchronously instead of hitting the network in tests.
The five fixtures cover the cases that matter for the parser. anaphora is the simple single-sense English entry. SBIR is an acronym with multiple senses. API is highly polysemous and multi-language (en, fr, id, la, pt). hapax-legomenon is the multi-word case, so it exercises URL-encoding for the space. The 404 fixture captures the JSON error body Wiktionary returns when a term isn't there.
|
| |
|
|
|
|
|
|
| |
Four tests across lookup, list, find-buffer-position, and first-call-creates-file shared the same boilerplate. Each let-bound gloss-file to a randomized nonexistent path, wrapped in unwind-protect, reset the cache, and cleaned up file and buffer afterward.
Extracted as gloss-test--with-missing-glossary in testutil-gloss.el, parallel to the existing gloss-test--with-temp-glossary. The four call sites drop from 8-10 lines each to 2-3.
Tested by running the full 32-test suite. All 32 pass in 0.21 seconds.
|
| |
|
|
|
|
|
|
|
|
| |
Public API: gloss-core-lookup, gloss-core-save, gloss-core-list, gloss-core-find-buffer-position. Save inserts entries at the alphabetical position (case-insensitive compare), creates the file and parent directory on first call, prompts on collision via completing-read over Replace/Append/Cancel, and updates the in-memory cache directly.
Lookup checks gloss-file's mtime against the cached load time. If disk is newer than the buffer, it reverts the buffer first. Out-of-band edits land on the next read. Parser failures during reload preserve the existing cache and surface a one-line message.
Tested by the 32-test suite from the previous commit. All 32 pass in 0.16 seconds.
The defgroup and defcustoms (gloss-file, gloss-debug) live here rather than in gloss.el. That keeps the data layer self-contained when tests load it directly without the orchestration layer.
|
| |
|
|
|
|
|
|
| |
The commit lands eight per-function test files and a shared testutil. 32 tests across Normal/Boundary/Error categories cover the public API (lookup, save, list, find-buffer-position) and the internals that need observable behavior tests (mtime invalidation, corrupt-file resilience, alphabetical insert, first-call file creation).
All 32 fail with void-function on the gloss-core symbols. That is the intended red-phase signal. The next commit lands the implementation that turns them green.
testutil-gloss provides a with-temp-glossary macro. It binds gloss-file to a temp file, resets the cache before and after, and cleans up the visiting buffer.
|
|
|
Five layered files per the design at docs/design/gloss.org. gloss-core for the data layer, gloss-fetch for the network layer, gloss-display for the UI, gloss-drill for the spaced-repetition export, and gloss.el as the entry point. All five are skeletons. Implementation comes next.
The Makefile delegates to ert with the usual unit, integration, and per-file targets. It also runs paren and lint passes. The package is licensed GPL-3.0-or-later. README is a placeholder pointing at the design doc.
|