aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* refactor(theme-studio): restore studio state after mutating gates via ↵Craig Jennings9 days2-28/+70
| | | | | | | | | | | | | | | withSavedState Seven gates (locktest, mocktest, gallerytest, safetest, ndtest, viewlocktest, expandpersisttest) mutated PALETTE/MAP/SYNTAX/UIMAP/PKGMAP/LOCKED and restored nothing, so opening the studio at one of those #hashes left its state corrupted for interactive use. Wrap each in withSavedState(keys, body), scoped to the keys that gate touches: snapshot the named globals, run the body, restore in a finally. Uses a JSON-round-trip clone (the studio objects carry values structuredClone throws on — the same clone the gates' own local saves use). The 14 gates that already restore locally are left as-is. Verified: all 44 gates green, the restore round-trip holds for reassignment + in-place + Set mutation, and a forced assertion in a wrapped gate still reports FAIL.
* refactor(theme-studio): hoist the browser-gate boilerplate into a gate() helperCraig Jennings9 days2-228/+180
| | | | | | | | | | | | The 38 standard gates each repeated the same ok flag, notes list, A(cond,note) collector, and verdict postamble (title + result div), with the note separator drifted across them (' | ' vs ' fails='). gate(id, body) owns all of it and standardizes the note format to ' fails=note1,note2'; the gate name is the hash uppercased, the div id is the hash, matching what each gate set by hand. Each call site keeps its literal location.hash==='#NAMEtest' (run-tests.sh greps that to discover gates). Six custom gates (selftest, cursortest, planetest, oklchtest, readouttest, savetest) stay inline. Verified: all 44 gates green, and a forced A(false) in a converted gate still reports FAIL (gate() can't manufacture greens).
* chore(todo): mark refactoring program nearly complete, scope the deferred ↵Craig Jennings9 days1-52/+32
| | | | harness rewrite
* refactor(theme-studio): extract assertPreviewFaces for the 3 preview-face gatesCraig Jennings9 days2-48/+40
| | | | | | | | | | | The #mdtest, #mupreviewtest, and #gnustest browser gates each copy-pasted the same preview-face validation: render the preview, assert it exercises enough data-faces, that every data-face is real for the package, and that the required faces are present. Lift that into one assertPreviewFaces(A, html, faces, min, name, required) helper. Each gate keeps its literal location.hash==='#NAMEtest' check (run-tests.sh greps that to discover gates) and its own title/result-div. Verified: all 44 gates green, and a deliberately-broken required face still makes its gate report FAIL (the helper can't manufacture greens).
* refactor(theme-studio): defer generate.py's page build behind a lazy _build()Craig Jennings9 days1-63/+85
| | | | | | | | | | Importing generate.py ran the full assembly at module load -- DEFAULTS, the package inventory, palette/syntax/uimap construction, and the HTML fill -- so face_coverage.py, which imports it only for UI_FACES, paid the whole cost. Move that derivation into a cached _build(); expose the built attributes (HTML, MAP, APPS, ...) lazily via PEP 562 __getattr__. UI_FACES/CATS/COLS and the file bodies stay cheap module constants, the file write stays __main__-guarded, and the page is byte-identical (check-generated current; test_generate green via __getattr__).
* refactor(theme-studio): dedup the inline-integrity test scaffoldingCraig Jennings9 days5-40/+37
| | | | | | | | | Two test-DRY cleanups. The seven near-identical test_page_carries_*_verbatim methods in test_generate.py collapse into one subTest loop over the inlined-body names. The strip-exports helper -- reimplemented three times across the colormath, app-core, and app-util inline-integrity tests, each annotated 'same strip generate.py applies' -- moves to one shared inline-strip.mjs (stripInlinedBody), so the three copies can no longer drift from generate.py's strip_exports.
* refactor(theme-studio): drop the orphaned dropdownRowTextColor helperCraig Jennings9 days3-41/+2
| | | | | | | | dropdownRowTextColor was exported and unit-tested but had no runtime caller: it computed text color for a vertical-list dropdown row, a UI replaced by the swatch gallery popup (colored buttons, no per-row text), so its regression rationale is moot. Remove the function, its export, and its four tests; regenerate the page (theme-studio.html staged so check-generated stays green).
* refactor(theme-studio): unify the two condition_matches clause checkersCraig Jennings9 days1-32/+30
| | | | | | | | | condition_matches encoded the same four display-condition rules twice -- once for the dict spec shape, once for the list-of-clauses shape. Normalize both to a single {key: values} mapping and run the rules once in _condition_clauses_pass. Verified byte-identical over 31 representative conditions (dict, list, scalar, and malformed). The pre-existing Pyright complaints in choose_gui_light are unrelated and untouched.
* refactor(theme-studio): extract path_kind from the two face-coverage bucketersCraig Jennings9 days1-15/+23
| | | | | | | bucket_from_source and bucket_of_source each re-derived the elpa/user/builtin/ other origin of a defface path. Lift that into one path_kind(path) classifier; both functions now map its result to their own vocabulary. Verified byte-identical bucket output over a representative path set before and after.
* refactor(custom-comments): collapse the divider and box generator duplicationCraig Jennings9 days1-116/+51
| | | | | | | | | Three near-identical skeletons folded together: the simple divider becomes the padded divider with zero text padding; the plain box and the heavy box share one border/text/border emitter (cj/--comment-box-emit) with a HEAVY flag adding the interior blank lines; and the repeated cmt-start/doubled-semicolon/space line prologue becomes cj/--comment-emit-prefix. Output is byte-identical -- the per-generator characterization suites stay green -- and the file drops ~65 lines.
* refactor(dwim-shell): extract the branching command-string buildersCraig Jennings9 days2-30/+101
| | | | | | | | | | Lift the command-string construction out of three :config commands whose templates branch — video-trim (Beginning/End/Both), tar-gzip (single vs multi), text-to-speech (darwin say vs espeak) — into top-level pure builders cj/dwim-shell--video-trim-command / --tar-gzip-command / --text-to-speech-command, leaving thin interactive wrappers that prompt and delegate. The builders are now testable under make test (the :config defuns aren't), mirroring the existing dated-backup/zip-single builders. Adds 8 Normal/Boundary/Error tests.
* chore(todo): close the F9 3-window-collapse bug, file its GUI verifyCraig Jennings9 days1-14/+18
|
* fix(ai-term): keep the F9 toggle reversible in a 3+ window layoutCraig Jennings9 days2-9/+77
| | | | | | | | | | | | | | | | In a layout where the agent had its own split window (e.g. code on top, a working window, and the agent below), toggling the agent off deleted its window correctly, but toggling back on reused the working window at the edge -- displacing its buffer and collapsing three windows to two. The slot-reuse that avoids a third window on a fresh show was firing on a re-show after the agent's own window was already deleted. Flag the toggle-off that deletes the agent's own window; on the next toggle-on, reuse-edge-window consumes the flag and falls through to a fresh re-split, so the agent returns to its own window and the other windows are untouched. The flag only changes the 3+ window case -- after a delete in a 2-window slot-reuse layout one window remains, where re-split and reuse-edge already coincide, so the existing reuse-edge tests are unaffected.
* chore(todo): file the F9 3-window-collapse bug and the f9-fix manual testCraig Jennings9 days1-0/+26
|
* fix(ai-term): stop F9 toggle shrinking the agent window each cycleCraig Jennings9 days8-60/+93
| | | | | | | | | | | | | | | | | The F9 toggle captured the agent window's body-height and replayed it as body-lines. Body-height subtracts the mode line's pixel height, which differs between an active and an inactive mode line; the agent is captured active but redisplayed inactive, so under a theme whose mode-line-inactive is shorter than a text line the window lost ~1 line per toggle. Capture and replay total-height for the vertical axis instead, via the renamed cj/window-replay-size. Total-height is identical active or inactive and has no mode-line-pixel dependence, so the round-trip is a fixed point. Width keeps body-width (total-width has the position-dependent divider problem that total- height does not). The shared lib fix covers the F12 terminal toggle too. The shrink only manifests in a GUI frame, so it is not reproducible in the batch harness; the unit tests pin the new total-height contract.
* chore(todo): mark the 5 medium refactor extractions done, 8 items remainCraig Jennings9 days1-22/+11
|
* refactor(custom-case): extract per-word title-case decisionCraig Jennings9 days1-61/+59
| | | | | | | | | Lift the three-branch capitalize-this-word predicate out of cj/title-case-region into cj/--title-case-capitalize-word-p; the command now loops word boundaries and delegates the decision, dropping its cyclomatic complexity. The 32-test characterization suite is unchanged and green. The function is reindented from tabs to the 2-space house style in the same pass, which accounts for the line churn; no behavior change.
* refactor(dirvish): extract playlist-target resolution from the create commandCraig Jennings9 days2-21/+85
| | | | | | | Lift the name-validate plus overwrite-prompt loop out of cj/dired-create-playlist-from-marked into cj/--playlist-resolve-target, leaving the command a flat filter -> resolve -> write. Add Normal/Boundary/Error tests for the new seam (real temp music-dir, stubbed prompts only).
* refactor(calendar-sync): extract per-event recurrence-exception parserCraig Jennings9 days2-41/+107
| | | | | | | Lift the 14-binding let* body out of calendar-sync--collect-recurrence-exceptions into calendar-sync--parse-exception-event, which returns the exception plist (or nil) for one VEVENT; the collector's dolist becomes a thin uid + puthash. Add Normal/Boundary/Error tests for the new pure helper.
* refactor(ai-term): extract toggle-off teardown and working-buffer swapCraig Jennings9 days1-53/+61
| | | | | | | Lift the F9 toggle-off branch (~50 lines) out of cj/ai-term into cj/--ai-term-toggle-off, leaving the pcase arm a one-liner. Fold the three copies of the most-recent-non-agent-buffer swap idiom (two in the toggle-off cases, one in cj/--ai-term-close-buffer) into cj/--ai-term-swap-to-working-buffer.
* refactor(calibredb-epub): extract nov re-render and centering helpersCraig Jennings9 days1-18/+31
| | | | | | | Lift the render-preserving-position block and the margin/fringe block out of cj/nov-update-layout into cj/nov--rerender-preserving-position and cj/nov--center-in-window, dropping its nesting. Fold the thrice-repeated #E8DCC0 sepia literal in cj/nov-apply-preferences into one local binding.
* chore(theme-studio): restyle WIP orderless-match facesCraig Jennings9 days2-12/+16
| | | | New foregrounds plus italic slant on orderless-match-face-0..3; source flips default to user.
* chore(todo): file parent task for the remaining refactoring batchCraig Jennings9 days1-0/+124
|
* refactor(elfeed): extract HTML-entity decoder, drop leftover DEBUG loggingCraig Jennings9 days2-18/+43
| | | | cj/youtube-to-elfeed-feed-format hand-decoded an og:title with six sequential replace-regexp-in-string calls; extract cj/--decode-html-entities (alist-driven, & first) and call it. Also remove the leftover DEBUG cj/log-silently instrumentation from cj/extract-stream-url. Behavior unchanged; adds coverage of the decoder.
* refactor(prog-webdev): use the shared format-region helperCraig Jennings10 days1-26/+1
| | | | prog-webdev carried cj/--webdev-format-region, a third byte-identical copy of the format-region helper already extracted to system-lib in the prog-json/prog-yaml dedup. Drop it and call cj/format-region-with-program (system-lib is already required). Behavior unchanged; the existing format tests cover it.
* refactor(prog-general): dedup the deadgrep search tail, lift its helpersCraig Jennings10 days2-13/+65
| | | | cj/deadgrep-here and cj/deadgrep-in-dir repeated the same normalize-directory + read-term + invoke-deadgrep tail. Lift cj/deadgrep--initial-term out of :config and add cj/--deadgrep-run for the shared tail; each command resolves its root then delegates. Adds coverage for the term seeding and the run helper.
* refactor(prog-general): lift cj/find-project-root-file out of :configCraig Jennings10 days2-14/+62
| | | | The pure project-root file finder lived inside the projectile use-package :config, so it was unreachable under make test. Move it to top level (its forward declaration already existed); cj/open-project-root-todo and cj/project-switch-actions still call it. Adds unit coverage for string regexps, rx forms, no-match, and no-project.
* refactor(font-config): lift frame/icon helpers out of :configCraig Jennings10 days2-36/+114
| | | | cj/apply-font-settings-to-frame, cj/cleanup-frame-list (inside with-eval-after-load 'fontaine) and cj/maybe-install-all-the-icons-fonts (inside all-the-icons :config) carried real branching but were unreachable under make test. Move all three (and the cj/fontaine-configured-frames state) to top level; the :config/eval-after-load blocks keep only the hook wiring. Adds declare-function for the package calls and coverage of the apply/cleanup/install branches.
* refactor(erc): lift cj/erc-generate-buffer-name out of :configCraig Jennings10 days2-9/+40
| | | | The buffer-name function lived inside the erc use-package :config, so it was unreachable under make test (no package-initialize). Move it to top level; :config keeps the erc-generate-buffer-name-function setq. Adds unit coverage for the server-and-channel, server-only, and missing-piece cases.
* refactor(mousetrap): extract per-category event-binding loopCraig Jennings10 days2-16/+61
| | | | mouse-trap--build-keymap-1 nested its event-binding cond/dolists five deep. Extract mouse-trap--bind-events-to-ignore (spec prefixes map); build-keymap-1 now just walks the categories and delegates the binding. Adds coverage for the wheel and click-event paths.
* refactor(chrono-tools): extract tmr sound-file helpers, flatten the commandCraig Jennings10 days2-15/+73
| | | | cj/tmr-select-sound-file nested cond/let five deep, mixing the current-sound lookup, the completing-read, the setq, and the message. Extract cj/tmr--current-sound-name and cj/tmr--apply-sound-file (the testable parts) and flatten the inner conds to ifs. The command keeps the prefix-arg/no-dir/no-files guards and the prompt. Adds coverage for both helpers.
* refactor(jumper): extract marker-traversal and location-candidates helpersCraig Jennings10 days2-32/+93
| | | | jumper--location-exists-p and jumper--format-location both opened a marker with the same save-current-buffer/set-buffer/save-excursion/goto-char dance; jumper-jump-to-location and jumper-remove-location shared a verbatim candidate-list cl-loop. Extract jumper--with-marker-at (index fn) and jumper--location-candidates; the four callers delegate. Adds direct coverage of the candidate list.
* refactor(modeline): extract the clickable-segment keymap builderCraig Jennings10 days2-11/+42
| | | | The buffer-name, vc, and major-mode segments each hand-rolled the same make-sparse-keymap + define-key [mode-line mouse-1/3] construction. Extract cj/--modeline-click-map (mouse-1 &optional mouse-3); the three segments call it. Adds coverage for the one- and two-button cases.
* refactor(ai-config): extract gptel model-apply step, drop dead branchCraig Jennings10 days2-8/+61
| | | | cj/gptel-change-model applied the selection inline (scope dispatch + message) and re-checked (stringp model) on a value already interned to a symbol. Extract cj/--gptel-apply-model-selection (scope backend model backend-name), which sets the vars globally or buffer-locally and returns the message; the dead stringp branch is gone. Adds direct coverage of both scopes.
* refactor(ai-conversations): reuse the autosave-active predicate in the hooksCraig Jennings10 days1-8/+2
| | | | cj/gptel--autosave-after-send and cj/gptel--autosave-after-response re-inlined the four-clause "should we autosave?" boolean that cj/gptel--autosave-active-p already encapsulates. Call the helper instead (after-send keeps its extra autosave-on-send flag). Behavior unchanged; one predicate to maintain.
* refactor(org-agenda): extract the agenda base-file listCraig Jennings10 days2-5/+46
| | | | The fixed base list (inbox, schedule, and the three calendars) was spelled out as a literal in cj/--org-agenda-scan-files, cj/todo-list-single-project, and the chime initializer. Extract cj/--org-agenda-base-files so adding a calendar source is a one-place change; the single-project view prepends its todo.org with cons. Adds a test for the helper's contents and order.
* refactor(mail-config): build the account-nav keymaps from one templateCraig Jennings10 days2-18/+87
| | | | The cmail/dmail/gmail navigation maps were three near-identical defvar-keymap blocks differing only by maildir prefix, with the unread/flagged/large query clauses repeated in each. Add cj/--mail-account-search-queries (account -> the four search strings) and cj/--mail-make-account-map (builds the keymap), wrapped in eval-and-compile so org-msg's :preface can call the builder during byte-compilation. The three maps become one-line builder calls. Adds direct coverage of the query strings and the per-account closures.
* refactor(org-capture): extract the find-or-create-top-heading blockCraig Jennings10 days2-25/+69
| | | | cj/org-capture--goto-file-headline, cj/--org-capture-goto-open-work, and cj/--org-capture-goto-exact-headline each repeated the same positioning block: search from point-min, jump to the heading on a match, else append it at end of buffer and back up. Extract cj/--org-find-or-create-top-heading taking the search regexp and the heading line; the three sites delegate. Behavior unchanged; adds direct coverage of the helper with a plain regexp.
* docs(custom-ordering): correct point-position note on replace-region helperCraig Jennings10 days1-1/+1
|
* refactor(custom-text-enclose): extract the region-or-word dispatchCraig Jennings10 days2-48/+92
| | | | cj/surround/wrap/unwrap-word-or-region each repeated the same skeleton: target the active region, else the word at point, else show a message; then delete and re-insert the transformed text. Extract cj/--enclose-region-or-word, which takes the transform as a function and the no-target message, so each command reads its prompts then delegates. Behavior and messages unchanged; adds direct coverage of the dispatch helper.
* refactor(custom-datetime): generate the six inserters from one macroCraig Jennings10 days2-33/+42
| | | | The six cj/insert-* commands were identical except their format variable and a one-word docstring noun. Replace them with a cj/--define-datetime-inserter macro and six table-style calls; the format defvars and the keymap are unchanged. Adds a test asserting all six stay interactive commands.
* refactor(custom-ordering): dedupe region guard, replace tail, and ↵Craig Jennings10 days2-56/+98
| | | | | | arrayify-python Extract cj/--ordering-validate-region (the start>end guard copy-pasted across all seven pure helpers) and cj/--ordering-replace-region (the delete-region + insert tail repeated in every interactive command). Alias cj/arrayify-python to cj/arrayify-json, which it duplicated verbatim, leaving both keybindable. Behavior unchanged; adds direct Normal/Boundary/Error coverage for the two new helpers.
* refactor: extract shared format-region helper into system-libCraig Jennings10 days4-54/+100
| | | | prog-json and prog-yaml each carried a byte-identical cj/--<lang>-format-region that runs a formatter over the buffer via call-process-region and replaces it on exit 0. Hoist it to system-lib as cj/format-region-with-program with a generic output buffer, and point both formatters at it. Adds the first direct unit coverage of the helper (Normal, Boundary, Error).
* refactor: remove dead wrappers and commented-out blocksCraig Jennings10 days8-74/+1
| | | | Drop cj/apply-browser-choice (browser-config) and cj/load-fallback-theme (ui-theme), orphaned wrappers with no caller that just duplicated logic the live paths already inline, plus their tests. Delete commented-out blocks: a duplicate contact capture template (org-contacts-config), a disabled personal-info-dir :init (help-config), a stale TODO setq (org-config), and an old commented regex (test-runner).
* chore(todo): mark window pull-away manual check passedCraig Jennings10 days1-8/+0
|
* fix(windows): shrink the pull-away reveal to the minimum window heightCraig Jennings10 days2-2/+7
| | | | minimize-window floors at window-min-height (4 lines), leaving roughly a 10% reveal. Bind window-min-height to 1 around it so the reveal opens at the ~2-line floor and the current window keeps almost the whole frame before the windsize arrows take over.
* chore(todo): update window pull-away notes to shipped behaviorCraig Jennings10 days1-5/+6
|
* fix(windows): keep the pulled-away window on the arrow's edgeCraig Jennings10 days2-35/+45
| | | | The sole-window pull split toward the arrow at 50/50, so a fullscreen terminal jumped above the revealed buffer at half height. Now the reveal opens on the opposite side and is minimized to a sliver, so the current window keeps the arrow's edge near-full and the sticky windsize arrows shrink it step by step, matching the feel of resizing an existing split.
* chore(todo): close dirvish and window-pull tasks, file manual checksCraig Jennings10 days1-12/+19
|
* feat(windows): pull a window away from a sole window with C-; b + arrowCraig Jennings10 days2-8/+69
| | | | When the selected window fills the frame there is no divider to resize, so the arrow now splits toward its direction with the previous buffer and the original window shrinks from that edge. Multi-window resize is unchanged.