| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
| |
The region branch's `(while (< (point) end) (join-line 1))` ran one iteration too many. After the final in-region join, point sat just before the end marker, so the loop fired once more. That extra `join-line 1` consumed the next line's preceding newline and replaced it with a space. Then `(goto-char end)` + `(newline)` reinserted a newline at the original end position, before the inserted space, so the space ended up stranded at BOL of the next line.
I replaced the position-based loop with `count-lines` + `dotimes` to do exactly the right number of joins. I also swapped the trailing `(newline)` for `(forward-line 1)`. The bullet-list use case now lands directly on the next existing line with no blank gap.
The trailing-newline change ripples to `cj/join-paragraph` (which delegates here), so paragraphs now also stop adding a trailing newline when the input lacks one. `require-final-newline` handles file-end discipline on save anyway.
I added 3 new tests that fail against the old loop and pass against the fix. I also updated 11 existing tests whose assertions baked in the old trailing-newline behavior. While in there I wrapped the `cj/custom-keymap` defvar stub in `eval-and-compile` in both test files. The bare defvar wasn't evaluated at byte-compile time, so the `require` of `custom-line-paragraph` would hit a void symbol when the validate-el hook ran.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two small tightening passes on the formatter wiring tests just shipped.
Drops `(ignore-errors ...)` from each test file's
`(require 'prog-PKG)' call. Soft use-package warnings (e.g. lsp-pyright
not being installed) still emit messages without aborting the load. A
hard load failure (syntax error, missing required dep) would now
surface as a test error rather than being silently swallowed.
Adds a `(should (featurep PKG))' assertion per language so the test
output makes "package loaded" visible alongside the fboundp and
binding checks. For webdev the assertion is `(featurep 'prog-webdev)'
since the formatter command is defined directly in prog-webdev.el
(it shells out to the prettier CLI, no separate package to load).
17 tests total now (up from 13), all passing.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Four test files plus a shared testutil that locks in the formatter
bindings on C-; f across the four languages. Each test file checks:
- the format command is fboundp after the relevant package loads
- the C-; f binding resolves to that command (in the relevant
mode-map, or in the buffer-local map for hook-based wiring)
- the underlying executable is on PATH (skipped via ert-skip if
not installed)
No production change. The bindings were already at C-; f via two
mechanisms. Use-package :bind handles python and shell. The other
two install via local-set-key inside a hook. This regression net
catches silent breakage if any of those wirings get reshaped later.
The shared tests/testutil-format-wiring.el carries
format-test--ensure-packages-init, which calls package-initialize
once per batch run, since make test runs Emacs with --no-site-file
--no-site-lisp. Without it, use-package can't find blacken / shfmt
/ go-mode in elpa/. Also format-test--skip-unless-executable wraps
ert-skip with a clear "not on PATH" message so missing tools fail
informatively.
Per-language wiring inventory (no changes, just locked in):
- Python: blacken-buffer in python-ts-mode-map (use-package :bind)
- Shell: shfmt-buffer in sh-mode-map and bash-ts-mode-map
(use-package :bind, gated on :if executable-find)
- Go: gofmt via cj/go-mode-keybindings hook
- TS/JS/Web: cj/webdev-format-buffer via cj/webdev-keybindings hook
13 tests across 4 files, all passing.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two test files covering the extracted timestamp-validation helpers.
cj/--validate-timestamps-in-buffer (8 tests): empty buffer no-op,
buffer with no timestamps, buffer with all valid timestamps,
DEADLINE flagged with "DEADLINE" property, SCHEDULED flagged with
"SCHEDULED", inline-timestamp flagged with "inline timestamp",
multiple invalid collected in document order, mixed valid+invalid
returning only the invalid one. Tests use real org parsing and
mock org-time-string-to-absolute at the boundary so an arbitrary
timestamp can be marked invalid for a given test.
cj/--format-validation-report-section (4 tests): no-entries says
"No invalid timestamps found", single-entry produces the file: link
+ Property/Type + Invalid timestamp lines, multiple-entry preserves
input order, every section ends with a trailing blank line.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Six tests against real temp directories. Mocks only the actual
compile invocations (native-compile-async, byte-recompile-directory)
so the deletion side runs end-to-end against real files.
Covers: native dispatch returns 'native and calls native-compile-async,
byte dispatch returns 'byte and calls byte-recompile-directory,
recursive deletion of every .elc/.eln (including in subdirs), removal
of the eln cache dir on the native path, removal of the elc cache dir
on the byte path, and the missing-cache-dir no-op.
|
| |
|
|
|
|
|
| |
Four tests against a temporarily fbound test target: runs the symbol
once and propagates its return value, raises user-error on a nil
symbol, raises user-error naming the symbol when it isn't fboundp,
and verifies the with-timer announce/done messages fire.
|
| |
|
|
|
|
|
|
| |
Five tests against real temp directories: mixed .el/.elc/.eln content,
recursive descent through subdirectories, no-compiled-files no-op,
empty directory, and the suffix-vs-substring boundary that ensures a
file like "looks.elc.bak" isn't deleted (string-suffix-p, not
string-match).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Four new test files for the pure-and-near-pure helpers in
org-noter-config.el. The interactive heavyweights (cj/org-noter-start,
cj/org-noter-insert-note-dwim, cj/org-noter--toggle-notes-window,
cj/org-noter--find-notes-file, cj/org-noter--create-notes-file) are
out of scope for this pass — they need org-noter loaded and a real
session, or a substantial internal/wrapper split before they can be
tested without spinning up real PDFs and EPUBs.
- preferred-split: 5 tests walking the 1.4 width-to-height threshold
from both sides plus a square-frame boundary.
- title-to-slug: 7 tests covering multi-word, single-word, mixed
punctuation, leading/trailing junk, collapsed runs, digits, and
empty input.
- generate-notes-template: 5 tests with org-id-uuid mocked, asserting
the rendered template carries the UUID, the dual ROAM_REFS /
NOTER_DOCUMENT pointers, the title-and-category lines, the
ReadingNotes filetag, and the trailing Notes heading.
- predicates: 13 tests covering cj/org-noter--in-document-p,
cj/org-noter--in-notes-file-p, cj/org-noter--session-active-p,
cj/org-noter--get-document-path, and cj/org-noter--extract-document-title.
Mocks derived-mode-p, org-entry-get, and buffer-file-name at the
boundary so the suite doesn't require pdf-tools or nov.
The require chain in each test file is user-constants → keybindings →
org-noter-config because cj/org-noter-notes-directory captures
roam-dir from user-constants and the prefix-map binding at the bottom
of org-noter-config references cj/custom-keymap.
30 new tests, all passing. Full suite green.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Four new test files extending the existing coverage of
cj/emacs-build--format-build-time. The interactive heavyweights
(cj/recompile-emacs-home, cj/delete-emacs-home-compiled-files,
cj/benchmark-this-method, cj/validate-org-agenda-timestamps) are out
of scope for this pass — each needs an internal/wrapper split first
before tests can exercise the logic without UI.
- with-timer macro: 4 tests asserting it returns the FORMS' value,
evaluates the body exactly once, emits both announce and done
messages, and returns the last form when given multiple.
- cj/compile-this-elisp-buffer: 6 tests dispatching across native-async,
native-sync, and byte-compile fallbacks, plus the not-elisp /
no-buffer-file-name error paths and the sync-native error catch.
- cj/emacs-build--summary-string: 5 tests asserting the shape of the
multi-line report (Version, System, Build date, Capabilities section,
yes/no flag rendering) without locking exact wording.
- info-commands smoke: 5 tests exercising cj/info-emacs-build,
cj/info-loaded-packages, cj/info-loaded-features, cj/reload-init-file,
and cj/org-alert-list-timers via boundary-mocked pop-to-buffer and
load-file, asserting buffer creation, content shape, or echo-area
message as appropriate.
20 new tests, all passing. Full suite green.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
predicates
Four new test files extending the existing test-host-environment.el
(which already covered the two battery helpers).
- platform-predicates: env-linux-p, env-bsd-p, env-macos-p, env-windows-p
walked across every supported system-type value. 8 tests.
- display-predicates: env-x-p, env-x11-p, env-wayland-p, env-terminal-p,
env-gui-p exercised under every relevant combination of window-system,
WAYLAND_DISPLAY, and display-graphic-p. 13 tests.
- env-laptop-p: composition over the helpers, with Linux dispatch
isolated from non-Linux dispatch via system-type binding. 8 tests
including env-desktop-p as the inverse. battery-status-function is
forward-declared in this test file (initialized to nil) so cl-letf's
symbol-value place can read the prior value without hitting
void-variable.
- detect-system-timezone: the four-method priority chain. Mocks
cj/match-localtime-to-zoneinfo and getenv at the boundary; uses
cl-letf on file-exists-p / insert-file-contents to exercise the
/etc/timezone fall-through without touching real system files.
5 tests.
34 new tests for host-environment, all passing. Full suite green.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two test files for keybindings.el. cj/jump-open-var gets full N/B/E
coverage (6 tests): existing-file happy path, plus error paths for
unbound symbol, nil value, non-string value, empty string, and missing
file. The smoke file for the auto-generated cj/jump-to-NAME commands
asserts that each spec entry has an fbound command, that the command
is bound in cj/jump-map at the spec's key, that calling each command
invokes cj/jump-open-var with the spec's var, and that cj/jump-map is
mounted under cj/custom-keymap at "j".
The test fixture variable is declared at top level. If it were
let-bound inside a test under lexical-binding, the let would create a
lexical binding that shadows the dynamic one. The production code's
symbol-value would then miss what setq writes. find-file is mocked at
the boundary so the existing-file test doesn't actually open a buffer.
10 tests pass. No production change in keybindings.el.
|
| |
|
|
|
|
|
|
|
|
| |
Extends `lsp-file-watch-ignored-directories' with thirteen build, cache, and tooling directories: `node_modules', `dist', `coverage', `target', `__pycache__', `.venv', `venv', `.pytest_cache', `.mypy_cache', `.ruff_cache', `test-results', `playwright-report', `tf/.terraform'. Uses `add-to-list', so lsp-mode's own defaults (`.git', `.svn', `.idea', etc.) stay in place.
Setting these in a project's `.dir-locals.el' doesn't work. lsp-mode reads `lsp-file-watch-ignored-directories' once at workspace init, from the global value, so a buffer-local override never reaches the watch list. I confirmed this today: in a Python buffer where dir-locals had applied, `M-: lsp-file-watch-ignored-directories' returned the lsp-mode default, not the project's overrides. Setting it globally is what works.
The goal is to push typical workspaces under `lsp-file-watch-threshold' (1000), so the "watch all files? (y or n)" prompt stops firing on every fresh LSP start.
Also added a forward defvar for `lsp-enable-remote' to silence the matching free-variable warning under `make compile'.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Extracts two pure helpers from cj/open-file-with-command and cj/xdg-open so the file-resolution and launcher-detection logic becomes testable without mocking process launchers.
New helpers:
- cj/--file-from-context returns a file path from the current context, resolving in priority order (explicit arg, buffer-file-name, dired file at point). Returns nil when none apply.
- cj/--open-with-is-launcher-p is a predicate for whether a command is a desktop launcher (xdg-open, open, start) that needs call-process detachment.
Both commands now delegate. cj/open-file-with-command uses cj/--file-from-context with read-file-name as the final fallback, plus cj/--open-with-is-launcher-p for the launcher dispatch. cj/xdg-open uses cj/--file-from-context with user-error as the "no file" fallback.
Behavior preserved. The existing system-utils test suites still pass, and the shape of each command's final effect is identical.
New tests, 14 cases across two per-function files:
- tests/test-system-utils--file-from-context.el covers: explicit wins over buffer-file, explicit wins over dired, buffer-file fallback, dired fallback, all-nil returns nil, explicit-nil uses chain, dired-mode-but-no-file-at-point.
- tests/test-system-utils--open-with-is-launcher-p.el covers: each of the three launcher names returns t, non-launcher returns nil, empty string returns nil, case-sensitive check, nil input returns nil.
Coverage: system-utils.el went from 10/52 (19.2%) to 15/52 (28.8%). The remaining uncovered lines are mostly in the process-launching paths of cj/open-file-with-command and cj/xdg-open. Those are testability-blocked. Mocking call-process, start-process-shell-command, and generate-new-buffer would give a lot of mock surface for low value. cj/server-shutdown is not meaningfully testable because it kills Emacs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adds two per-function test files covering the easily-testable functions in system-utils.el. Takes the module from 0/52 (0%) to 10/52 (19.2%) coverage.
tests/test-system-utils-identify-external-open-command.el — five tests for cj/identify-external-open-command: one each for the three supported platforms (linux, macos, windows), one documenting the dispatch-order invariant when multiple predicates return t, and one for the unsupported-host error case. env-*-p predicates are stubbed via cl-letf so the tests run correctly on any host.
tests/test-system-utils-eval-buffer.el — three tests for cj/eval-buffer-with-confirmation-or-error-message: the valid-elisp success path, an empty-buffer boundary case, and the broken-elisp error path (confirming the condition-case wrapper catches errors rather than propagating). A small macro stubs message to capture what the user would see.
Split into two files per the project's per-function test convention (elisp-testing.md).
Not covered in this commit and flagged for follow-up:
- cj/open-file-with-command and cj/xdg-open mix user-input resolution with process launching. Per elisp-testing.md's "split interactive from internal" rule, a pure file-resolution helper should be extracted before tests are written.
- cj/server-shutdown kills Emacs, so it isn't meaningfully testable.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adds a fifth entry to the scope completing-read menu: "Whole project — all executable lines". Uses the existing cj/coverage-report flow, so the user still hits F7 and picks from the menu; the command dispatches based on the chosen scope.
Two new pure helpers back the scope:
- cj/--coverage-simplecov-executable-lines parses the simplecov JSON and returns every executable line per file (both hit lines and 0-hit lines, excluding null/non-executable entries). Symmetric with cj/--coverage-parse-simplecov, which returns only hit lines.
- cj/--coverage-format-summary renders intersect records as a per-file percentage summary sorted ascending by coverage (worst-covered first). Used instead of the line-detail format-report because an entire project's uncovered lines would be thousands of entries.
cj/--coverage-read-and-display now branches on scope: whole-project feeds executable-lines as the "changed" input to intersect; diff-aware scopes still shell git diff as before. cj/--coverage-render-to-buffer branches similarly to pick the format helper.
Tests cover the two new helpers: Normal (basic extraction, sorted output, percentages), Boundary (all-null coverage, multiple test-name keys unioned, empty records, not-tracked files excluded), and Error (missing file signals user-error).
Verified end-to-end on the current .coverage/simplecov.json: 2717 of 4559 lines covered across 44 files, sorted from keybindings.el at 0% up through high-coverage modules.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Completes the coverage v1 user-facing path. cj/coverage-report is the interactive entry point:
1. Resolves the backend for the current project (honoring cj/coverage-backend from .dir-locals.el).
2. Prompts for a git-diff scope via completing-read (Working tree, Staged, Branch vs parent, Branch vs main).
3. Reads the cached simplecov report, intersects with the diff, renders records into a *Coverage Report* buffer.
4. If the report doesn't exist, prompts to run coverage first. With a prefix argument, re-runs regardless.
The report buffer uses cj/coverage-report-mode, a compilation-mode derivative. Uncovered-line entries are formatted as path:line: uncovered so the standard gnu compilation-error-regexp-alist picks them up for next-error navigation. That means M-g n, M-g p, and C-x backtick walk through uncovered lines from any buffer without switching focus.
F7 is bound to the command globally, matching the F-key layout ticket's design (F4 compile+run, F5 debug, F6 test, F7 coverage).
Added to init.el: (require 'coverage-core) + (require 'coverage-elisp).
Tests cover the pure scope-label helpers (label to symbol, symbol to label, roundtrip) plus a smoke test that exercises the full command with stubbed backend, stubbed completing-read, stubbed shell-command-to-string, and a prepared simplecov fixture.
Coverage v1 is now functionally complete: make coverage produces the report, F7 drives the interactive flow.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure helper that renders intersect records into the text shown in the coverage report buffer. Takes the list of per-file plists from cj/--coverage-intersect and a scope label, returns the formatted string.
Output has three sections depending on what's present:
- "Uncovered lines" — one line per uncovered line, formatted as "<path>:<line>: uncovered" so compilation-mode's default regex picks them up for next-error navigation.
- "Not tracked" — files changed in the diff but absent from the coverage data (READMEs, test files, config).
- "Fully covered" — tracked files where every changed line is covered.
Files with empty :changed-lines (deletion-only hunks) are omitted. Summary counts cover only tracked files, so an all-README change shows "0 of 0" rather than a misleading percentage over nothing.
Tests cover Normal (partial, fully covered, mixed sections), Boundary (empty records, 100% coverage with no uncovered section, only-not-tracked case, deletion-only exclusion), and the output format that next-error relies on.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Completes the coverage v1 pipeline by adding the Makefile target, the undercover driver script, the exclusion list, and the .gitignore entry. Uses simplecov JSON rather than LCOV as the collection format.
The LCOV vs simplecov choice: Undercover's :merge-report t option only supports simplecov. Since the pipeline runs tests per-file (matching test-unit's isolation pattern) and accumulates coverage across runs, merge-report is required. LCOV is better-supported by external coverage viewers, but for a primarily interactive workflow the on-disk format is an internal detail.
Other moves in this commit:
- Renamed cj/--coverage-parse-lcov to cj/--coverage-parse-simplecov and rewrote its tests for the JSON schema. Same signature, same semantics (file to set of covered lines), different parser.
- Renamed the backend protocol's :lcov-path key to :report-path, format-neutral and matching the renamed cj/--coverage-elisp-report-path function.
- The coverage target deletes modules/*.elc before running so undercover can instrument the .el sources. Without this, byte-compiled versions shadow the instrumentation and only a handful of pre-loaded modules end up with coverage data.
- Excluded tests/test-all-comp-errors.el from make coverage runs. That test byte-compiles every module, which fails under undercover's instrumentation. Excluded only from coverage. Normal make test still runs it.
- Updated docs/design/coverage.org to reflect the simplecov pivot with a historical note on why we moved off LCOV.
Verified end-to-end: make coverage produces .coverage/simplecov.json with 2717 of 4559 executable lines hit across 44 tracked modules.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
First of the pluggable coverage backends. Registers itself with coverage-core on load.
- :name is elisp
- :detect returns non-nil when the project root has a Makefile, Eask, or Cask alongside .el files at root or under modules/. The heuristic is deliberately loose. For anything unusual, .dir-locals.el can pin the backend with cj/coverage-backend.
- :run invokes make coverage in a compilation buffer. On success the callback fires with the LCOV path. On failure the buffer stays visible so the user can read the error.
- :lcov-path resolves to <project-root>/.coverage/lcov.info.
undercover is declared via use-package with :defer t so it's installed but not loaded at Emacs startup. The make coverage target will require it explicitly.
Tests cover Normal (Makefile + modules/, Eask + root .el, Cask + modules/), Boundary (no build file, Makefile without .el, empty directory), and Error (nonexistent root returns nil). The registration-on-load case is also verified.
The Makefile coverage target and the cj/coverage-report user command arrive in follow-up commits.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The coverage-core module now has a registry protocol so per-language backends can plug in without touching the core.
A backend is a plist with :name, :detect, :run, and :lcov-path. cj/coverage-register-backend appends to cj/coverage-backends, or replaces an existing entry with the same :name at its original position (so first-registered wins on ties).
cj/--coverage-backend-for-project resolves which backend applies to a project root. Resolution order:
1. An OVERRIDE argument (typically buffer-local cj/coverage-backend from .dir-locals.el) wins if supplied, and errors if it names an unregistered backend.
2. Otherwise, walk the registry in order and return the first backend whose :detect returns non-nil for the given root.
Tests cover Normal (register and retrieve, re-register replaces in place, first detect wins), Boundary (empty registry, no match, override bypasses detect, detect receives the root), and Error (override names an unknown backend).
With the registry in place, the elisp backend (and later python / typescript / go) can self-register on load without any changes to coverage-core.
|
| |
|
|
|
|
|
|
|
|
| |
Third and final pure helper for the coverage-report command. Takes the hash tables produced by parse-lcov and parse-diff-output and returns per-file records ready for the report buffer.
Output is a list of plists sorted by file path. Each record has :path, :changed-lines, :covered-lines, :uncovered-lines, and :tracked. A file that appears in the diff but not in the LCOV data is :tracked nil with both line lists empty. That way the reporter can distinguish "coverage isn't looking at this file" (README edits, test files, config) from "tests didn't exercise this code."
Tests cover Normal (all covered, partial, multiple files sorted), Boundary (file not tracked, tracked file with no covered lines, empty changed-lines from deletion-only hunks, empty inputs), and Error (nil inputs return an empty list instead of erroring).
With this helper in place, the core data pipeline is complete: LCOV file + git diff scope go in, per-file records come out. Next up is the backend registry and the elisp backend, then the cj/coverage-report command ties it all together.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Second of three pure helpers for the coverage-report command.
cj/--coverage-parse-diff-output is pure. It takes a git unified-diff string and returns a hash table of file to set of added or modified line numbers (based on the +new_start,new_count hunk headers). Files with deletion-only hunks appear in the result with an empty set, so reporters can distinguish "coverage not tracked" from "no changes touched this file."
cj/--coverage-changed-lines wraps that parser with scope dispatch. Scopes are working-tree, staged, branch-vs-main, and branch-vs-parent. Branch-vs-parent takes an optional BASE arg; if omitted, falls back to @{upstream}. Unknown scopes signal user-error.
Tests cover Normal (single hunk, multiple files), Boundary (new file via @@ -0,0, deletion-only, binary markers, single-line hunks without a count, empty input), and Error (malformed hunk headers skipped; unknown scope). Git invocation is stubbed via cl-letf in the smoke test so the parser logic is exercised without shelling out.
Part of the coverage-core work per docs/design/coverage.org.
|
| |
|
|
|
|
|
|
| |
First of three pure helpers for the coverage-report command. Reads an LCOV file and returns a hash table of file to set of covered line numbers. Only the SF, DA, and end_of_record fields are interpreted. Other LCOV fields (FN, FNDA, LF, LH, BRDA) are ignored. Malformed DA lines are skipped silently so partial runs still yield usable data.
Tests cover Normal (single file, multiple files, mixed hit counts), Boundary (empty file, spaces in path, extra fields, all-zero hits), and Error (missing file, malformed DA lines).
Part of the coverage-core work per docs/design/coverage.org.
|
| |
|
|
|
|
|
|
| |
`env-laptop-p` treated any `battery-format "%B"` value that wasn't literally "N/A" as "has a battery." On a Linux desktop using `battery-upower`, the result is "unknown". The AC adapter and USB-C power entries exist in /sys but there's no BAT*. That made desktops look like laptops. The per-machine font height switch in `font-config.el` broke as a result.
The fix uses /sys/class/power_supply/BAT* as the canonical Linux signal. That's what the kernel exposes, and what upower itself reads. Other platforms keep the `battery-format` path, but the fallback now checks for a live battery status char ("!", "+", "-") instead of only excluding "N/A".
Two pure helpers (`env--battery-status-char-indicates-battery-p`, `env--power-supply-has-battery-p`) keep the logic testable. The new test file covers Normal, Boundary, and Error cases for each helper.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Put the full Hugo workflow inside Emacs. All of it lives in modules/hugo-config.el.
New functions:
- cj/hugo-open-draft reads all .org files under content-org/log, finds those with #+hugo_draft: true, and offers a completing-read picker.
- cj/hugo-preview toggles a local hugo server subprocess and opens the preview URL in the browser. A second press stops the server.
- cj/hugo-publish opens magit-status on the website repo. The server-side post-receive hook on cjennings.net already rebuilds and deploys on push, so committing and pushing is the deploy.
Two pure helpers support the picker: cj/hugo--post-metadata parses the front matter region of a post, and cj/hugo--collect-drafts walks a directory and filters to drafts. Seven ERT tests cover both helpers across normal, boundary, and error cases.
Keybinding note: C-; h d and C-; h D have swapped roles. Lowercase d now opens the draft picker. Uppercase D toggles the draft flag in the current buffer. The previous lowercase-d binding was toggle.
|
| |
|
|
|
|
|
|
|
|
|
| |
Two cleanups round out the transcription-config refactor:
- cj/--running-transcriptions: the 'status = running' filter used by
cleanup and count helpers is now one function. Existing counter tests
cover both callers.
- cj/--format-transcription-entry: the 13-line dolist body inside
cj/transcriptions-buffer becomes a testable pure function. 6 tests
cover status-face mapping, basename-only rendering, duration format,
trailing newline.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Break cj/--transcription-sentinel's seven inline side-effects into named
helpers:
- cj/--write-transcript-on-success: writes process output to .txt on success
- cj/--append-to-log: appends event marker + process output to log
- cj/--update-transcription-status: marks tracking-list entry complete/error
- cj/--notify-completion: sends success or critical notification
Also: switch the tautological (cj/--should-keep-log t) to use the local
success-p (equivalent but matches the function signature), and rename
the unused audio-file sentinel arg to _audio-file.
Sentinel shrinks from 48 lines with 7 inline blocks to 14 lines of
straight-line helper calls. 10 tests cover the extracted helpers.
|
| |
|
|
|
|
|
|
|
|
|
| |
Pull two more helpers out of cj/--start-transcription-process:
- cj/--init-log-file: writes the initial log header with timestamp,
backend, audio file, script path
- cj/--track-transcription: pushes a running-status entry and refreshes
the modeline
Start-process shrinks from 58 lines with 4 levels of nesting to ~25 lines
mostly at depth 1-2. 10 tests cover the extracted helpers.
|
| |
|
|
|
|
| |
Pull the per-backend env-var assembly out of cj/--start-transcription-process
into a standalone pure function. 9 tests cover: the three backends, parent-env
preservation, non-mutation, missing-key user-error, unknown-backend error.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Introduce cj/--transcription-backends alist mapping each backend to
(:script :auth-host :env-var). Replace:
- two near-identical cj/--get-{openai,assemblyai}-api-key functions with
a single parameterized cj/--auth-source-password helper
- the pcase in cj/--transcription-script-path with an alist lookup
- the pcase block in cj/--start-transcription-process that assembled
the API-key env var with an alist-driven assembly
Adding a new backend is now a single line in the alist. The existing
tests plus retargeted API-key tests (now 10, covering the parameterized
helper and the descriptor data) verify no behavior change.
|
| |
|
|
|
|
|
| |
8 tests pinning down current behavior of cj/--get-openai-api-key and
cj/--get-assemblyai-api-key (host query, string vs function secret,
missing-entry → nil). Baseline for upcoming consolidation into a
single parameterized helper.
|
| |
|
|
|
|
|
|
|
|
|
| |
The outer dolist in cj/check-for-open-work guarded its body with
(boundp 'base-dir), which always returns nil under lexical-binding
because base-dir is a lexical loop variable. Every repo under
projects-dir and code-dir was silently skipped; only org-dir and
user-emacs-directory (both top-level defvars) still got reconciled.
Remove the bogus boundp check. Add regression tests covering the
entry point itself — the existing suite only exercised the helpers.
|
| |
|
|
|
| |
Extract should-skip-p, pull-clean, pull-dirty from 6-level nested
reconcile-git-directory. Make find-git-repos recurse into sub-repos.
|
| |
|
|
|
|
| |
get-sink-index
Previously tested only indirectly via label-devices and sink-has-active-audio-p.
|
| |
|
|
|
| |
Cover occurrence-matches-exception-p (6), apply-single-exception (6),
exdate-matches-p (6), extract-cn (5), extract-email (4), unfold-continuation (5).
|
| |
|
|
| |
Function was defined but never called anywhere in the codebase.
|
| |
|
|
|
|
|
| |
Covers core parsing (parse-ics-datetime, parse-timestamp, format-timestamp,
split-events, parse-event), date utilities (add-months, add-days,
weekday-to-number, date-weekday, event-start-time), and timezone
(format-timezone-offset, convert-utc-to-local, localize-parsed-datetime).
|
| |
|
|
|
|
| |
Cover build-video-command (9 tests), select-from-labeled (5 tests),
and test-device (4 tests). All three were previously tested only
indirectly through their callers.
|
| |
|
|
|
|
|
| |
function
Identical 31-line parser logic differed only in "Source #" vs "Sink #" header.
Replaced with cj/recording--parse-pactl-verbose taking a record-type parameter.
|
| |
|
|
|
|
|
|
|
|
| |
Music: random mode now respected by next/previous keys. Previous navigates
a 50-track play history ring buffer. Fixed playlist replacement bug.
24 new tests.
Calendar-sync: consolidated duplicate parse functions, extracted timezone
localization helper, unified expand-daily/monthly/yearly into parameterized
function, removed dead code. 33 new characterization tests. -90 lines.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Add 47 new unit tests across 7 test files covering playlist-modified-p,
assert-valid-playlist-file, playlist-tracks, create-radio-station,
ensure-playlist-buffer, after-playlist-clear, and header-text.
Extract three helpers to reduce duplication: assert-m3u-files-exist (dedupes
2 identical guards), sync-playlist-file (dedupes 3 state-sync patterns),
and select-m3u-file (reusable M3U selection with cancel). Simplify
append-track-to-playlist nesting from 6 to 4 levels. Delete unused
cj/music-keymap-prefix variable.
|
| |
|
|
|
|
|
| |
date-to-time used (reverse date) which broke when RRULE UNTIL values
were parsed as 5-element lists (year month day hour minute) from UTC
timestamps. This caused recurring events with UTC UNTIL dates to
expand to 0 occurrences, producing stale calendar entries.
|
| |
|
|
|
|
|
| |
- Extract cj/gptel--build-model-list from cj/gptel-change-model
- Extract cj/gptel--current-model-selection from cj/gptel-change-model
- Add test-ai-config-build-model-list.el (9 tests)
- Add test-ai-config-current-model-selection.el (8 tests)
|
| |
|
|
|
|
|
|
|
|
| |
- Replace use-package gptel-magit hook with autoloads via
with-eval-after-load 'magit (loads gptel only on key press)
- Move org header defuns above use-package to fix load order
- Set gptel-include-reasoning to "*AI-Reasoning*" buffer
- Rebind rewrite to C-; a r, clear context to C-; a c
- Add test-ai-config-gptel-magit-lazy-loading.el (8 tests)
- Mark all ai-config cleanup items DONE in todo.org
|
| |
|
|
|
|
|
|
| |
- Add testutil-ai-config.el with gptel stubs for batch testing
- Add tests for cj/gptel--model-to-string (9 tests)
- Add tests for cj/gptel--fresh-org-prefix (8 tests)
- Add tests for cj/gptel-backend-and-model (8 tests)
- Remove dead cj/gptel-backends defvar (duplicates cj/gptel--available-backends)
|
| |
|
|
|
|
|
| |
New prog-json module: json-ts-mode with jq formatting (C-; f) and
jq-interactively (C-c C-q). Upgraded prog-yaml to yaml-ts-mode with
prettier formatting. Both use treesit-auto for grammar management.
Includes 18 new tests (10 JSON, 8 YAML), 185/185 passing.
|
| |
|
|
|
| |
Fill test gaps from quality-engineer review: 3 new test files (28 tests)
and error cases for get-available-mics and get-available-sinks (+4 tests).
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Rework quick-setup device picker:
- Labels: [in use], [ready], [available], [muted] instead of
PulseAudio jargon (RUNNING/IDLE/SUSPENDED)
- Muted devices now shown (previously hidden) so users can see
all connected hardware and understand why a device is unsuitable
- Sink step shows which apps are playing through each output:
"JDS Labs Element IV [in use] (Firefox, Spotify)"
- Prompt changed from "audio output to monitor" to "audio output
to capture" to avoid PulseAudio monitor terminology confusion
- Sort order: in use → ready → available → muted
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Replace hard-to-distinguish nerd font icons with clear text labels:
Jabra SPEAK 510 Mono [active - running]
Shure MV7+ Analog Stereo [active - idle]
Ryzen HD Audio Controller [inactive - suspended]
Both mic and sink steps use the same labeling. Devices sorted
running → idle → suspended. Removed mic-active-p and sink-active-p
helpers — state now comes directly from the verbose pactl parsers
via get-available-mics/sinks which return (name description state).
|
| |
|
|
|
|
|
|
|
| |
Mic step now shows with green/dim coloring to indicate which mics
are actively in use (RUNNING state), with active mics sorted to top.
Changed sink inactive icon from (slashed speaker, reads as
"broken/muted") to (plain speaker, reads as "no streams right
now"). Active sinks still show .
|