aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* docs: point the geolocation-command docstring at the bundled examplesrelease/0.4.0Craig Jennings17 hours1-1/+2
| | | | | C-h v wttrin-geolocation-command now mentions examples/geolocation/, so the ready-to-adapt scripts are discoverable from inside Emacs, not just the README.
* test: cover wttrin--reset-font-heightCraig Jennings17 hours1-0/+35
| | | | Restores the base height when a remap cookie exists, no-op without one.
* docs: document auto-fit and buffer centeringCraig Jennings17 hours1-0/+13
| | | | | | Add an "Auto-Fit and Centering" subsection to the README font settings: the weather block centers automatically, and wttrin-auto-fit-font (with the min/max floor and cap) opts into sizing the font to the window.
* fix: don't auto-fit the loading placeholder to a huge fontCraig Jennings17 hours2-1/+67
| | | | | | | With auto-fit on, the one-line "Loading..." placeholder was sized to fill the window height (capped huge), and a reused buffer kept the previous weather's auto-fitted font while loading. Auto-fit now runs only once real weather has rendered, and the placeholder resets to the base font.
* fix: don't steal focus when an async weather response rendersCraig Jennings19 hours3-5/+54
| | | | | | | | wttrin--display-weather ran switch-to-buffer from the async callback, so a response arriving after the user moved to another buffer yanked them back to *wttr.in*. It now renders with set-buffer; selecting the buffer is the interactive command's job (wttrin-query already does it at invocation), so a late response updates the buffer in place without changing the selected window.
* fix: drop stale async weather responses that overwrite a newer queryCraig Jennings19 hours2-2/+70
| | | | | | | wttrin-query reuses the single *wttr.in* buffer, so requesting Paris then Berlin and having Paris return last overwrote the Berlin buffer. Each query now stamps a monotonic request id on the buffer; the callback displays only when its captured id still matches, so a superseded response is ignored.
* fix: stop the mode-line cleanly when disabled before startup firesCraig Jennings20 hours3-5/+93
| | | | | | | | | The delayed initial fetch was scheduled with run-at-time but the one-shot timer was discarded, and when the mode is enabled before after-init the start is queued on after-init-hook. Disabling the mode before either fired could still start timers, hit the network, or mutate mode-line state after the user turned it off. Stop now cancels the stored startup timer and removes the after-init hook, and the startup callback no-ops when the mode is off.
* fix: always evict at least one cache entry when over maxCraig Jennings20 hours2-7/+9
| | | | | | wttrin--cleanup-cache-if-needed removed floor(count * 0.20) entries, which is 0 for a small cache (e.g. two entries at max one), so the cache could sit over its advertised maximum indefinitely. It now removes at least one whenever it is over.
* fix: key the cache on all response-shaping settingsCraig Jennings20 hours2-74/+70
| | | | | | | | | | | wttrin--make-cache-key keyed only on location and unit system, but the request body also depends on wttrin-display-options and wttrin-default-languages. Changing display options or language mid-session served the old cached response, and the staleness header then claimed it was current for the new settings. The key now includes every setting that shapes the response. Cache-key tests rewritten to assert behavior (distinct key per varying setting, stable key for identical settings) instead of an exact key string.
* fix: require subr-x for string-trimCraig Jennings20 hours1-0/+1
| | | | | wttrin.el calls string-trim throughout but never required subr-x, so a clean load could hit a void-function before something else pulled subr-x in.
* chore: clean up package metadataCraig Jennings20 hours3-3/+3
| | | | | Drop the "games" keyword (it miscategorizes a weather tool) from wttrin.el and Eask. Sync wttrin-geolocation.el's version to 0.3.2 to match the main module.
* style: normalize wttrin-default-locations indentation to spacesCraig Jennings20 hours1-5/+5
| | | | | The list mixed tabs and spaces. Aligned every entry on spaces, no behavior change.
* docs: fix verbatim markdown bold in the README geolocation sectionCraig Jennings20 hours1-5/+5
| | | | | Five labels used Markdown **bold**, which renders literally in the Org README. Switched them to Org *bold*.
* refactor: extract the auto-fit input guard, fix a unit-stale docstringCraig Jennings20 hours1-7/+14
| | | | | | | | Pull the eight-condition validity check in wttrin--fit-font-height into wttrin--fit-inputs-usable-p, so the arithmetic branch reads as one line. Reword wttrin--center-margin's docstring, which still said "column counts" after the pixel-based centering rewrite started passing it pixels.
* feat: fit and center the weather buffer in its windowCraig Jennings20 hours6-11/+322
| | | | | | | | | | | | | | | | | | Centering and auto-fit share one resize hook (wttrin--update-layout on window-configuration-change-hook). Centering is always on. The block centers via the window's left margin, measured in pixels from the buffer font so it stays exact under a font-height remap. It centers against the available width including the current margin, so the margin shrinking the body can't make the value oscillate. Auto-fit is opt-in (wttrin-auto-fit-font, off by default). When on, the font is sized so the whole buffer fits top to bottom (down to the row past the footer's last menu item) without the widest line truncating, clamped to wttrin-font-height-min and -max. The pixel-exact measurement needs window-font-width and window-font-height, so the minimum is now Emacs 25.1.
* fix: drop the alias query from history when making it the defaultHEADmainCraig Jennings2 days2-0/+19
| | | | Making a location the default saved it and dropped the favorite name from history, but not the underlying query. For an alias whose query differs from its name, that query lingered as a separate history candidate. Both saving branches now drop the name and the query, matching wttrin-save-location.
* fix: keep saved locations and search history disjointCraig Jennings2 days2-1/+37
| | | | | | Pressing d cleaned the location from history, but saving with s did not. A place saved under a friendly name kept its raw query as a separate history candidate, and removing the saved entry let that query resurface in the picker. wttrin-save-location now drops the saved name and its query from history, and wttrin-remove-location drops both the removed name and its query (captured before removal). A new wttrin--drop-from-location-history helper does the work. A place now lives in the directory or in history, never both.
* feat: save the location to the directory when making it the defaultCraig Jennings2 days2-6/+26
| | | | | | Pressing d on a typed or aliased location set the favorite but never added it to wttrin-saved-locations, so the default persisted only as the favorite string, not as a named directory entry. The coordinate branch already saved. The typed/alias branch did not. The typed/alias branch now puts the location into the directory before promoting it, so a typed default like "Reykjavik" persists as a ("Reykjavik" . "Reykjavik") entry. An empty-name coordinate detection still keeps the raw coordinates without saving an entry.
* docs: use a public landmark address in saved-location examplesCraig Jennings3 days2-4/+4
|
* feat: add named-locations directory with display namesCraig Jennings3 days14-152/+884
| | | | | | | | A saved location carries a display name distinct from its query target ("Mom's House" maps to coordinates or an address), stored in a savehist-persisted wttrin-saved-locations alist. The name shows everywhere the place appears (picker, buffer header, mode-line tooltip) while wttr.in is still queried by the target. Management commands wttrin-save-location, wttrin-rename-location, and wttrin-remove-location edit the directory, with refuse-on-collision rename and a favorite-fallback warning on remove. In the weather buffer, s/r/x reach those commands and d names a detected location before promoting it to the default. The footer is two columns: a "This view" column (another, refresh, quit) and a "Saved locations" column (save, make default, rename, remove). The buffer anchors to the top so the forecast isn't scrolled out of view in a short window. The mode-line follows favorite changes immediately rather than waiting for the next scheduled fetch, and its hover tooltip shows the saved name instead of the raw query. History holds named entries only: raw coordinate detections and saved names stay out of it.
* feat: add external-command geolocation, opt-out, and example adaptersCraig Jennings3 days11-21/+1121
| | | | | | | | | | | | These build on the current-location picker with the rest of the geolocation work. wttrin-geolocation-command runs a command that prints {lat,lng} (and optionally an address) and queries wttr.in by those coordinates. IP geolocation only finds the network's exit point, which is wrong on a VPN or hotspot. A command that scans nearby WiFi resolves to street level. It runs asynchronously, falls back to the IP provider when unset or failing, and assumes nothing about the OS, so it's inert until set. When the command returns an address, wttrin shows it on a "Location:" line, so the resolved place is readable even though the fetch is by raw coordinates. wttrin-use-current-location is a labeled command that sets the favorite to auto-detect, so the bare t value never has to be typed into init by hand. wttrin-geolocation-enabled (default t) turns every geolocation surface off for anyone who wants that: the picker entry, the auto-detect favorite, and the command. examples/geolocation/ ships two reference adapters for the command: google-geolocate.py (Google API, key via the environment or ~/.authinfo.gpg) and apple-wps.py (Apple's keyless WiFi positioning, which uses an undocumented endpoint, so read its caveat). Both are Python 3 standard library and scan via nmcli, with notes on adapting the scan to other systems.
* feat: add current-location detection to the weather pickerCraig Jennings3 days7-54/+399
| | | | | | | | | | Geolocation used to live in a separate command, so getting weather for where you are meant knowing a second command existed. I added a "Current location (detect)" entry pinned to the top of the M-x wttrin picker. Selecting it detects your location and shows its weather. If the guess is wrong (VPN, hotspot) the detected city is visible in the header, so you reopen the picker and type one. Press d to keep the detected city as the default. The entry is pinned first through a completion table whose display-sort-function holds against completion frameworks that re-sort, and it never enters location history or the cache as a place. A new wttrin-geolocation-enabled (default t) turns every geolocation surface off for users who want that: the picker entry, the auto-detect favorite, and the command. It stays on by default. The older wttrin-set-location-from-geolocation command is now obsolete in favor of the picker. Its customize-save-variable persistence advice is replaced by savehist, which already carries the favorite across sessions.
* fix: refresh mode-line weather when the default location changesCraig Jennings4 days2-2/+60
| | | | | | Pressing d to promote a displayed location to the favorite changed wttrin-favorite-location but left the mode-line untouched. Its cache still held the old location's weather. The next scheduled refetch could be up to an hour away, so the hover tooltip kept showing the previous default. wttrin--set-favorite-location now detects a real change and, when the mode-line is active, clears the stale cache and fetches the new location at once via a new wttrin--mode-line-refresh-now helper. The tooltip switches within seconds instead of waiting for the timer.
* feat: add 'd' key to make the displayed location the defaultCraig Jennings5 days5-16/+186
| | | | | | | | | | I bound d in the weather buffer to wttrin-make-default, which sets wttrin-favorite-location to the location on screen so it drives the mode-line and future sessions. The footer advertises "[d] to make default". Persistence rides savehist, not the Emacs custom-variable mechanism: wttrin--savehist-register registers wttrin-favorite-location alongside the search history, at load and on savehist-save-hook. Enable savehist-mode and the favorite survives restarts. Promoting a location drops it from the search history, the way wttrin-default-locations entries are kept out of history. The favorite shows in the picker instead: wttrin--completion-candidates prepends it when it's a string and not already a default, so it appears exactly once. The setter only assigns the variable and trims history. It doesn't register with savehist itself, because savehist-additional-variables is unbound until savehist loads, so a direct add-to-list would error for users without savehist. Registration stays on the load and save-hook path.
* feat: expose themeable faces for mode-line and buffer textCraig Jennings5 days7-32/+142
| | | | | | | | | | I added four customizable faces so themes and customize-face can restyle the text wttrin draws itself: wttrin-mode-line-stale (the dimmed stale mode-line emoji), wttrin-staleness-header (the "Last updated:" line), wttrin-instructions (the footer prose), and wttrin-key (the [a]/[g]/[q] chords). The package exposed no faces before and hardcoded one color. The stale-emoji dimming used a literal "gray60". Now it inherits a face, so the color tracks the theme. I changed make-emoji-icon's second argument from a color string to a face symbol applied via :inherit. wttrin-key inherits bold rather than help-key-binding, which is Emacs 28+ while the package supports 24.4. The weather ASCII art stays colored by xterm-color's ANSI faces. Only the package's own text is newly faced.
* fix: keep location history registered after a savehist setqCraig Jennings8 days3-2/+37
| | | | | | | | A one-time add-to-list put wttrin--location-history into savehist-additional-variables when savehist loaded. Any user who later setqs that list, a common config pattern for curating which histories persist, replaces it and drops the entry. The search history then silently never saves. Re-assert the registration on savehist-save-hook, which runs at the top of every savehist-save. The variable is now in the list whenever a save happens, regardless of init order or a clobbering setq. Restore was never affected, since savehist reloads the value from explicit setq forms in its file. The README persistence note now reflects this: the variable stays registered even if you set savehist-additional-variables yourself.
* feat: add persistent location search historyCraig Jennings8 days3-2/+283
| | | | | | | | | | Successful searches are now remembered and offered as completion candidates the next time you run wttrin, listed after the configured defaults. Only successful fetches are saved, so typos and not-found locations never enter the history. A location already in wttrin-default-locations isn't duplicated into it. The list is capped at wttrin-location-history-max (default 20), oldest entries falling off first. Persistence uses savehist: the history variable is registered with savehist-additional-variables, so enabling savehist-mode carries it across restarts with no custom file I/O. Without savehist it lasts the session. Two commands manage the list: wttrin-remove-location-history drops one entry, wttrin-clear-location-history clears all. I don't pass the history variable as the completing-read history argument, because the minibuffer would then save every typed string, including failed lookups. It stays a curated candidate source, updated only on success. I trim with butlast rather than seq-take to keep the Emacs 24.4 baseline.
* test: extract message-capture helper into testutilCraig Jennings8 days6-55/+45
| | | | | | Five test files hand-rolled the same scaffolding to capture the echo-area message: a nil-initialized var, a cl-letf on message, and a lambda that stored the formatted string. Centralize it as testutil-wttrin-with-captured-message, which binds the var and captures the last message shown. Behavior is unchanged. The full suite stays green. Sites that mock message for a different reason are left alone: the ones that silence it with #'ignore, and the one that accumulates every message into a list.
* fix: exclude generated package files from coverage summaryCraig Jennings8 days1-3/+7
| | | | | | undercover never instruments generated files like NAME-autoloads.el and NAME-pkg.el, so they have no report entry. The file-weighted summary counted each as untested at 0% and dragged the project number down (97.2% read as 72.9% after eask generated the autoloads file). Drop NAME-autoloads.el and NAME-pkg.el from the source-file scan. A genuinely untested source is still flagged. Only the build-generated files are skipped.
* feat: add typed error hierarchy for fetch failuresCraig Jennings8 days4-11/+191
| | | | | | | | | | Define a wttrin-error condition with children wttrin-invalid-input, wttrin-network-error, wttrin-not-found-error, wttrin-service-error, and wttrin-parse-error, so callers branch on the class of a failure instead of matching message text. Synchronous paths signal these directly: a nil query and an unknown geolocation provider now raise wttrin-invalid-input. The async fetch path can't signal across its callback, so it tags the error string with the class via a wttrin-error-type text property. The wttrin-error-message-type accessor reads it back, and two-arg callbacks are untouched. Retyping the classifier also closed two gaps: a missing status and a 2xx with an empty body used to go silent or get mislabeled "Unexpected HTTP status". Both are now parse errors. wttrin-geolocation.el now requires wttrin for the shared conditions. It's only ever loaded through wttrin, so the require is a no-op in practice and just makes the dependency explicit.
* chore: extend gitignore for local tooling and build artifactsCraig Jennings8 days1-0/+6
|
* test: add make coverage-summary with untested-module detectionCraig Jennings8 days4-60/+190
| | | | | | | | A module no test loads never appears in undercover's report, so a line-weighted total silently skips it. The coverage-summary target counts such a file as 0% and weights the project number by file, so untested modules stay visible. I replaced scripts/coverage-summary.py, which only summarized files already in the report. make coverage now chains the summary, and CI prints it in the coverage step instead of a separate python call. The helper runs on stock Emacs (built-in json and seq), so it needs no dev deps. It lives in tracked scripts/ so CI can reach it.
* chore: gitignore editor backup filesCraig Jennings2026-06-041-0/+1
|
* chore: track inbox directory, ignore its contentsCraig Jennings2026-06-042-0/+4
|
* feat: support t for auto-detect via geolocation in wttrin-favorite-locationCraig Jennings2026-05-053-33/+270
| | | | | | | | | | | | | | | | | | Completes the three-mode configuration the favorite-location feature was always meant to have: - nil — disabled (default; unchanged) - a string — explicit location (unchanged) - t — auto-detect via IP geolocation (NEW) When the user sets `wttrin-favorite-location` to t, wttrin runs the geolocation lookup once on first use and caches the result for the session. Subsequent reads return the cached string. The lookup happens in the background via the existing `wttrin-geolocation-detect`, so Emacs startup is never blocked. I added two private state vars (`wttrin--resolved-favorite-location`, `wttrin--favorite-location-pending`) and a resolver `wttrin--resolve-favorite-location` that maps the three modes onto a returned string or nil. When t is set and the cache is empty, the resolver kicks off the lookup and returns nil for that call — the next consumer tick after the callback completes gets the cached string. The pending flag prevents duplicate concurrent lookups when several consumers ask during the resolution window. Five consumer call sites now go through the resolver instead of reading `wttrin-favorite-location` directly: `wttrin--mode-line-fetch-weather`, `wttrin-mode-line-click`, `wttrin-mode-line-force-refresh`, `wttrin--buffer-cache-refresh`, and `wttrin--mode-line-start`. Two display sites (the placeholder and error tooltips) use a new `wttrin--favorite-location-display-name` helper that returns "current location" while a t-mode lookup is pending, instead of showing the literal `t` to the user. Tests cover the resolver across all three modes, including the pending state, the duplicate-suppression behavior, and detection-failure retry. Existing consumer tests stay green because the resolver returns the bound string unchanged when the variable is a string. One care: the test file requires wttrin-geolocation up front so cl-letf mocks of `wttrin-geolocation-detect` aren't undone by the resolver's lazy require — without that, the first run hit ipapi.co for real. README documents the new mode under "Setting the Favorite Location from IP Geolocation".
* feat: add wttrin-display-options for wttr.in flag customization (closes #3)Craig Jennings2026-05-054-1/+81
| | | | | | | | | | | | wttr.in accepts single-character flags appended to the URL that control what the report looks like — no Follow line (F), narrow output (n), quiet mode (q), forecast horizon (0/1/2), console-glyph mode (d), and so on. Until now wttrin always used the same default report shape with no way to opt into these. Added a `wttrin-display-options` defcustom that takes a string of concatenated flags, e.g. "0Fq" for current weather only with no Follow line and no header. The flags get appended to every request via `wttrin--build-url`. The defcustom defaults to nil so existing users see no change. I excluded `A` and `T` from the recommended set in the docstring since wttrin needs ANSI output for the xterm-color rendering to produce the colored glyphs. The user could still pass them, but the docstring nudges them away. Tests cover the normal cases (single, multi-flag, with and without unit system), the boundaries (nil and empty string both leave the URL unchanged from baseline, single character works), and a sanity check that the new flags slot in after the always-on `A`. Existing build-url tests stay green because they don't bind the new variable, and the default is nil. Also added `.claude/` to .gitignore — the scheduled-tasks lockfile from local wakeup scheduling shouldn't be tracked.
* fix(ci): rewrite mode-init-order test and retry deps installCraig Jennings2026-05-052-45/+83
| | | | | | | | | | | | | | | | | | | | | | | | | | Two CI failures from the first run, neither a real bug in production code. The Emacs-snapshot job failed on `test-wttrin-mode-initialization-order-normal-mode-before-buffer-local-vars-calls-mode-first`. The original test mocked the `set' primitive to detect when `xterm-color--state' was first set. That worked on the byte-code path some older Emacs versions used, but `setq-local' doesn't go through the `set' function on Emacs master, so the mock never fired and the assertion read nil. The test was already brittle in isolation locally too. Rewrote the test to use `advice-add :before' on `wttrin-mode' and `make-local-variable'. Both are ordinary advisable functions, and `make-local-variable' is on the code path for every form that defines a buffer-local binding (`setq-local', `defvar-local', etc.) so the observation holds across Emacs versions. Renamed the test to drop "calls-mode-first" and use "mode-runs-before-xterm-color-state-binding" since that's what the new advice actually observes. The Emacs 26.3 job failed with a transient DNS error from elpa.gnu.org during `make deps'. Wrapped the install step in a 3-attempt retry with a 15-second backoff so a runner-side network blip doesn't fail the build. Applied to all three jobs (test, lint, coverage) since they all hit the same archives.
* docs: add CI and Coveralls badges to READMECraig Jennings2026-05-051-0/+2
| | | | Both badges point at the GitHub mirror, which is where the workflow runs and where the coverage gets reported. The CI badge reflects the latest run on `main`. The Coveralls badge tracks the most recent coverage upload from that branch.
* ci: upload coverage to CoverallsCraig Jennings2026-05-051-0/+9
| | | | | | Added a step to the coverage job that uploads `.coverage/simplecov.json` to Coveralls via `coverallsapp/github-action@v2`. The token is read from the `COVERALLS_REPO_TOKEN` repo secret. The step is set to `continue-on-error: true` so a Coveralls outage doesn't block the build, and the artifact upload stays in place as a fallback for debugging. `run-coverage-file.el` is unchanged. Undercover still produces simplecov JSON locally and on CI; the action handles the upload to coveralls.io with the right CI metadata.
* ci: add github actions for tests, lint, and coverageCraig Jennings2026-05-052-0/+150
| | | | | | | | Tests run across an Emacs version matrix (26.3, 27.2, 28.2, 29.4, snapshot) on every push to main and every PR. Lint and coverage run once each on the latest stable Emacs. The coverage job runs `make coverage`, prints the per-file and overall percentages via `scripts/coverage-summary.py`, and uploads the simplecov JSON as a build artifact (30-day retention). I'm leaving the artifact-only path in place for now and we'll wire up Coveralls in a follow-up once the repo is registered there. The matrix floor is 26.3 even though Package-Requires says 24.4. The setup-emacs action doesn't reliably support 24.x or early 25.x anymore, and the recent if-let find shows we hadn't actually been testing the stated minimum. Honest CI floor here is more useful than an aspirational one.
* refactor: rename debug-wttrin-* commands to wttrin-debug-* with obsolete aliasesCraig Jennings2026-05-057-82/+150
| | | | | | | | | | | | | | | | The four interactive commands in `wttrin-debug.el` used `debug-wttrin-` as their prefix instead of the package's `wttrin-debug-` prefix. package-lint flags this as a convention violation, and it makes M-x discovery slightly less consistent for users. Renamed: - `debug-wttrin-show-raw` -> `wttrin-debug-show-raw` - `debug-wttrin-enable` -> `wttrin-debug-enable` - `debug-wttrin-disable` -> `wttrin-debug-disable` - `debug-wttrin-mode-line` -> `wttrin-debug-mode-line` The old names stay available as `define-obsolete-function-alias` entries marked since 0.4.0, so anyone with a keybinding or `(call-interactively 'debug-wttrin-enable)` in their config keeps working. The byte-compiler will emit an obsolescence warning to nudge migration. Aliases will be removed in a future release. Internal caller `wttrin--debug-mode-line-info` now invokes the new name. Test files renamed to match (`test-debug-wttrin-*.el` -> `test-wttrin-debug-*.el`); inside each, ert-deftest names and function calls were updated. Added `tests/test-wttrin-debug-aliases.el` to verify each old name resolves via `indirect-function` to the new name and carries `byte-obsolete-info` with the expected target and "0.4.0" version.
* fix: drop if-let and personal-config reference in debug-wttrin-mode-lineCraig Jennings2026-05-051-9/+6
| | | | | | `if-let` requires Emacs 25.1, but the package declares (emacs "24.4") in Package-Requires. Replaced with `let` + `if` so the debug module loads on the stated minimum. Also dropped the `cj/modeline-major-mode` branch. That symbol is from my personal Emacs config, so the conditional was effectively dead code for anyone else and a confusing reference in a published package. The diagnostic now always shows the formatted mode-name, which is useful for everyone.
* test: cover wttrin-debug.el and expand lint to all source filesCraig Jennings2026-05-058-16/+417
| | | | | | | | | | | | I added 24 unit tests across six new files for wttrin-debug.el. They cover enable/disable, the debug-log writer, clear-log, show-log, and the mode-line diagnostic dump. That lifts wttrin-debug.el coverage from 27% to 95%, and overall coverage from 84% to 94%. Each function gets Normal / Boundary / Error categories where applicable. Globals like `wttrin-debug` and `wttrin--debug-log` are isolated per test with let-bindings. The dynamic-scope rebinding restores state cleanly at exit. I expanded the `lint` target to run on all three source files instead of just `wttrin.el`. Checkdoc and elisp-lint run on every file. Package-lint stays scoped to `wttrin.el` because the others aren't standalone packages. The tricky bit: `elisp-lint-file` re-runs package-lint internally as one of its validators. So the explicit guard alone wasn't enough. The fix binds `elisp-lint-ignored-validators` to include "package-lint" for the secondaries, which suppresses the re-run at the validator level. I also added `*-autoloads.el` to .gitignore. Eask generates `emacs-wttrin-autoloads.el` during install, and it shouldn't be tracked. I skipped one function: `wttrin--debug-mode-line-info` is a one-line dispatcher to `debug-wttrin-mode-line`. Testing it would assert the dispatch happened, which only tests Emacs.
* build: switch Makefile to eask, wire up undercover coverageCraig Jennings2026-05-054-79/+196
| | | | | | | | | | I wanted a coverage number, so I added an Eask file declaring the runtime dep (xterm-color) plus three dev deps (undercover, package-lint, elisp-lint). The Makefile now runs every test and lint recipe through `eask emacs`. That drops the hand-rolled `(require 'package)` + `add-to-list 'package-archives` boilerplate that was duplicated across six recipes. I added a `make deps` target that runs `eask install-deps --dev`. I also added a `make coverage` target that loads `tests/run-coverage-file.el` before each unit-test file. Undercover instruments the three source files first, then the test loads pick up the instrumented copy. Per-file results merge into `.coverage/simplecov.json` in simplecov format. I expanded `validate-parens` and `compile` to cover all three source files instead of just `wttrin.el`. Lint stays scoped to the main file for now. Coverage right now is 84% overall: wttrin.el 92%, wttrin-geolocation.el 100%, wttrin-debug.el 27%. The debug module is low because only the integration test exercises it. The coverage loop runs unit tests only.
* chore: untrack .stignoreCraig Jennings2026-04-262-1/+1
| | | | | | Stop tracking the Syncthing ignore file. It's local-machine config, not project content. Add it to .gitignore so the file stays put locally without showing up in git status.
* test: cover wttrin-geolocation internal helpersCraig Jennings2026-04-261-0/+187
| | | | | | | | | | | | | Four pure helpers in wttrin-geolocation.el were exercised only indirectly through the parser tests: --decode-json, --format-city-region, --lookup-provider, and --extract-body. None of them had direct unit coverage. Edge cases like an empty JSON object, a missing-vs-empty city field, an unknown provider symbol, or a missing HTTP body separator weren't locked. The new file groups all four functions together. Each gets Normal, Boundary, and Error cases per testing.md. Highlights: - --decode-json: distinguishes nil input, empty string, and malformed JSON, all of which return nil for different reasons. - --format-city-region: separates "missing key" from "empty string" since the predicate `(and (stringp city) (> (length city) 0) ...)` short-circuits on either. - --lookup-provider: tests two of the three built-ins plus a `let`-bound synthetic provider, locking the documented extension point on `wttrin-geolocation--providers`. - --extract-body: real UTF-8 bytes inserted into a temp buffer (mirroring what `url-retrieve` delivers) verify the decoding path. 4xx, 5xx, and missing-separator paths each get their own test. 21 new tests, all green on first run since they characterize existing behavior.
* docs: explain mode-line state and update flowCraig Jennings2026-04-261-0/+20
| | | | | | Four variables hold the mode-line's runtime state: the cache, the rendered string, the stale-render flag, and the refresh timer. They get updated in a specific order across three different functions. Anyone reading `wttrin--mode-line-tooltip` for the first time hits a clever bit on every hover: the tooltip re-evaluates staleness and triggers a re-render of the icon if it flipped. Without a comment that ties this to the rest of the update flow, you have to trace through three other functions to understand why. The new comment block above the defvars lays out the order and the hover-driven re-render in one place, so future readers don't have to reconstruct it from the call graph.
* refactor: extract wttrin--mode-line-extract-emoji helperCraig Jennings2026-04-262-4/+50
| | | | | | | | The regex that pulls the emoji character out of a wttr.in mode-line response was inlined inside `wttrin--mode-line-update-display`, mixed in with the render logic. Six tests of the parser couldn't be written without invoking the whole render path. The new pure helper takes the weather string, runs the regex, and returns either the first non-whitespace character after the colon or "?" as a placeholder. The format-explanation comment that used to sit above the inline code is gone now that the same explanation lives in the helper's docstring. There's no risk of comment and code drifting apart. Six tests cover Normal (typical response, different emoji), Boundary (no whitespace after colon, multiple whitespace chars), and Error (no colon, empty string).
* refactor: extract wttrin--mode-line-stale-p helperCraig Jennings2026-04-262-5/+49
| | | | | | | | The staleness check `(> age (* 2 wttrin-mode-line-refresh-interval))` lived in two places, `wttrin--mode-line-tooltip` and `wttrin--mode-line-update-display`, along with the four-line preamble that read the timestamp out of the cache cons and computed age. Centralizing the rule in a single helper means the threshold lives in one spot. That makes it easy to add a `wttrin-mode-line-staleness-threshold` defcustom later if the magic 2× ever needs to be tunable. The helper takes a cache entry (or nil) and returns t/nil. Five new tests cover Normal (fresh, stale) and Boundary (just below the threshold, just past, nil entry). The boundary tests use 199s and 201s against a 100s refresh interval to lock the strict `>` semantics with comfortable float-time margins. In `wttrin--mode-line-update-display` the refactor also drops two locals (`timestamp` and `age`) that were no longer used after the helper call replaced the inline calculation. Behavior is unchanged at both call sites.
* bump version to 0.3.2v0.3.2Craig Jennings2026-04-261-1/+1
|