| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
| |
The internal `cj/--comment-*` helpers were covered in the sibling test files; their interactive wrappers were the gap. 11 new tests drive each wrapper through its full prompt cycle (read-string / read-from-minibuffer / completing-read all stubbed) and assert the wrapper produces output containing the supplied text. The length-option branches for `cj/comment-simple-divider` are exercised across all three values (fill-column / half-column / match-text), and `cj/comment-hyphen`'s thin delegate to `cj/comment-inline-border` with "-" is covered too.
The wrappers split between `read-string` and `read-from-minibuffer` for their text input (box uses minibuffer; the rest use read-string); the tests stub each function explicitly to drive the right path.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
music-config has a large sibling-test fleet for the pure helpers; the gap was a handful of small dispatchers and assertion guards.
13 new tests, EMMS primitives stubbed throughout:
- `cj/music--assert-m3u-files-exist`: passes a non-empty list through, errors on empty.
- `cj/music--sync-playlist-file`: sets the buffer-local file ref + resets point.
- `cj/music--select-m3u-file`: returns the chosen path, returns nil on Cancel, errors when no files exist.
- `cj/emms--setup`: no-ops when emms already loaded, requires when absent.
- `cj/music-playlist-clear`: stops playback, clears the playlist, nils the file ref.
- `cj/music-add-directory-recursive`: calls `emms-add-directory-tree` for an existing dir, user-errors for non-dirs.
- `cj/music--find-track-in-playlist`: returns the buffer position when present, nil when absent.
|
| |
|
|
|
|
| |
The original fix (9600611) set a flag at toggle-off and let the next toggle-on detect it. The flag mechanism is right, but the toggle-off itself wasn't observable when bury-buffer couldn't switch the lone window onto a different buffer -- `bury-buffer' falls back to `switch-to-prev-buffer', which no-ops when the window's prev-buffer history contains only the agent itself (common right after a `C-x 1' that cleared the surrounding windows' histories). Without an observable swap, the second F9 found the agent still displayed and routed back through toggle-off, looping the user with no visible effect.
Dispatcher now explicitly forces the window onto another buffer (`(other-buffer agent-buf t)`) when the lone window is still showing the agent after `bury-buffer'. The round-trip test now exercises the real `bury-buffer' path instead of simulating it; a new test asserts the lone window's buffer is non-agent after toggle-off.
|
| |
|
|
|
|
|
|
|
|
| |
The main "d" agenda view grows two new blocks. A VERIFICATION block lists tasks in the VERIFY TODO state, placed just above the day's SCHEDULE. An IN-PROGRESS block lists tasks in the DOING TODO state, placed just under SCHEDULE. The full block order is now: OVERDUE -> HIGH PRIORITY -> VERIFICATION -> SCHEDULE -> IN-PROGRESS -> PRIORITY B.
Scope matches the other blocks (every entry in `org-agenda-files`). Scheduled and deadlined entries are included -- a VERIFY task with a date appears in both VERIFICATION and SCHEDULE, mirroring how HIGH PRIORITY behaves. Habits are skipped via `cj/org-skip-subtree-if-habit`; PROJECT-keyword parents wouldn't match `(todo "VERIFY")` exact-state filters anyway, so no extra skip there.
Two new header defvars (`cj/main-agenda-verify-title`, `cj/main-agenda-doing-title`) for symmetry with the existing four. Both blocks reference the shared `cj/--main-agenda-prefix-format` so a format tweak still lands in one place.
Five new tests in `test-org-agenda-config-skip-functions.el` lock the block order, each new block's header / prefix-format / skip-function, and the include-scheduled-entries contract.
|
| |
|
|
|
|
| |
The use-package form defaulted to `:ensure t`, which made init crash when MELPA's archive index pointed at a dated snapshot tarball (`telega-20260503.1332.tar`) that had already rotated off the server -- the install fetch hit a 404 and took the whole startup down with a `file-error`.
Switched to `:ensure nil` so the module configures telega without trying to install it. The `:commands telega` autoload stub still wires `C-; G`; pressing it before the package is installed signals a void-function instead. Commentary now documents the one-time install (`M-x package-refresh-contents` + `M-x package-install RET telega`).
|
| |
|
|
| |
The internal `cj/--*` helpers were already tested; the public dispatchers (`cj/surround-word-or-region`, `cj/wrap-word-or-region`, `cj/unwrap-word-or-region`, and the four `*-in-region-or-buffer` line helpers) were the gap. 18 new tests cover the region-active branch, the thing-at-point-word branch, and the no-word-no-region message branch for the first three; the region-vs-whole-buffer branches for the line-oriented four; plus a tab-vs-space pair for indent and a partial-dedent assertion. `read-string` is stubbed for the prompt-driven wrappers; `transient-mark-mode` is let-bound on so `use-region-p` returns t in batch.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The big-module coverage push starts with ai-config (was 53/191, 27.7%). New test file covers seven helpers that don't depend on a live gptel install:
- `cj/auth-source-secret`: returns string secrets, funcall's function secrets, errors on missing.
- `cj/anthropic-api-key` / `cj/openai-api-key`: cache the result so subsequent calls don't re-hit auth-source.
- `cj/gptel--add-file-to-context`: adds existing files, skips nil and missing paths.
- `cj/gptel-clear-buffer`: erases in a gptel-org buffer and reinserts the fresh prefix; no-ops + messages elsewhere.
- `cj/gptel-context-clear`: three cond branches (`remove-all`, `clear`, alist-fallback) with appropriate `fboundp` stubs.
- `cj/gptel-insert-model-heading`: inserts at the response-begin position with the backend/model heading.
External primitives (`auth-source-search`, `gptel-add-file`, `gptel-context-remove-all`, etc.) are stubbed. The fallback test declares `gptel-context--alist` as a top-level `defvar` so `setq` inside the function reaches the dynamic binding under lexical scoping.
|
| |
|
|
|
|
|
|
|
|
| |
Coverage rerun after this session's test additions: 67.2% -> 68.8% (4716/6853 lines across 73 files).
Four modules crossed the 70% threshold and are marked DONE: browser-config 69.7% -> 81.6%, ui-config 64.5% -> 90.3%, custom-whitespace 64.6% -> 97.6%, jumper 64.6% -> 98.0%.
system-defaults stays open with a note: 8 new tests cover its helpers (`cj/disabled`, the gc-threshold hooks, `unpropertize-kill-ring`, `cj/log-comp-warning`) but the gauge still reads 1/12 because the sandboxed `(load ...)` used to skip startup side effects doesn't register with undercover's instrumentation. Closing it needs either a refactor that lets the module load via plain `(require)` in batch, or a different coverage strategy.
Other numbers refreshed where they shifted (ui-navigation 8.7% -> 54.2% from prior session, org-agenda-config 60.0% -> 67.3% from this session's project-name category work, etc.).
|
| |
|
|
| |
`test-jumper.el` covered the internal helpers (`jumper--do-store-location`, `jumper--do-jump-to-location`, `jumper--do-remove-location`) thoroughly. The interactive entry points (`jumper-store-location`, `jumper-jump-to-location`, `jumper-remove-location`) and `jumper-setup-keys` were the gap. 10 new tests capture `message` and stub `completing-read` to cover: fresh-store messaging, duplicate detection, no-space full-registers path, no-locations / already-there / multi-prompt jump branches, no-locations / prompt-and-delete / cancel remove branches, and the prefix-key wiring.
|
| |
|
|
| |
The internal `cj/--*` helpers were exhaustively tested; the public dispatchers (`cj/remove-leading-trailing-whitespace`, `cj/collapse-whitespace-line-or-region`, `cj/ensure-single-blank-line`, `cj/delete-blank-lines-region-or-buffer`, `cj/delete-all-whitespace`, `cj/hyphenate-whitespace-in-region`) were not. 15 new tests cover the region / line / whole-buffer branches plus the two `yes-or-no-p` prompt paths (accept and decline). `transient-mark-mode` is let-bound on so `use-region-p` returns t in batch.
|
| |
|
|
| |
The previous test file covered only `cj/--buffer-cursor-state` and the cursor-color hook. `cj/apply-transparency`, `cj/toggle-transparency`, and `cj/set-cursor-type` were the gap. New tests stub the frame-mutating primitives so they run in batch and cover: alpha cons construction in both enabled and disabled states, the terminal-frame skip, the error-message path on `set-frame-parameter` failure, the toggle's flip + re-apply, double-toggle idempotence, and the cursor-type pass-through (including nil for invisible).
|
| |
|
|
|
|
| |
system-defaults.el is mostly `setq` configuration, but the testable helpers (`cj/disabled`, the two minibuffer-gc hooks, `unpropertize-kill-ring`, `cj/log-comp-warning`) were uncovered. New test file mirrors the sandbox pattern from test-system-defaults-vc-follow-symlinks.el and asserts each function's observable behavior: gc-threshold flip on minibuffer entry/exit, kill-ring property strip with empty-ring boundary, comp warning written with timestamp + non-comp type ignored so the default *Warnings* path still works.
8 new tests across Normal / Boundary cases.
|
| |
|
|
| |
The internal `cj/--do-*` helpers were already tested. The `cj/apply-browser-choice` and `cj/initialize-browser` wrappers were the gap -- they call `cj/--do-*` then `message` and own the wiring logic. New tests stub `message` and `cj/--do-apply-browser-choice` to cover each branch (success, invalid plist, loaded, first-available, no-browsers).
|
| |
|
|
|
|
| |
`org-agenda-custom-commands` inlined ` %i %-15:c%?-15t% s` four times across the "d" command's overdue / high-priority / schedule / priority-B blocks. New `cj/--main-agenda-prefix-format` defvar holds the literal once; every block now references the symbol so a format tweak lands in one place.
Regression test walks the "d" command's blocks and asserts each `org-agenda-prefix-format` cell resolves to the shared symbol -- a block that silently diverges fails the check.
|
| |
|
|
|
|
| |
Verifies docker is installed and the daemon is reachable, then either pulls a public image (when `TELEGA_DOCKER_IMAGE` is set) or announces the in-Emacs build path (`M-x telega-server-build`) for the user to run once. Telegram auth (phone + verification code) is interactive on first `M-x telega` and not scripted here.
Same shape as setup-email.sh: helpers are sourceable for bats, `main` runs only under direct execution. 7 bats tests stub `docker` and `command` so the suite never talks to the real daemon.
|
| |
|
|
|
|
| |
`dashboard-navigator-buttons' grows a Row 3 with a single Telegram entry (using the `nf-fa-telegram' icon, launching `telega'). The dashboard-mode-map gets a single-letter `g' shortcut to match the other launcher keys.
Two follow-up TODOs filed under the parent telegram task: the TDLib docker setup script (so a fresh-clone install can boot telega without a system-wide TDLib build) and a dashboard-icon-balance pass (Row 3 with one entry is asymmetric; decide whether to leave it or reorganize).
|
| |
|
|
|
|
|
|
| |
New `modules/telega-config.el` configures telega.el as an in-Emacs Telegram client. `telega-use-docker' is on so TDLib runs in a container instead of needing a system-level build -- pairs with a follow-up `scripts/setup-telega.sh' for fresh-clone installs. First-run auth (phone + verification code) is interactive inside `M-x telega' and isn't scripted here.
Launcher binding: `C-; G` (mnemonic: teleGram). `C-; t` and `C-; m t` were both taken (test-runner, music's "repeat track"), so the launcher landed on a free top-level letter.
Two tests cover the wiring: module loads, launcher is bound.
|
| |
|
|
|
|
| |
`cj/vterm-tmux-history' previously used `pop-to-buffer', which routed the history view through display-buffer-alist -- in an agent window that often meant a split or a hand-off to another window, costing the agent its frame slot. `switch-to-buffer' instead drops the history into the selected window directly; the existing quit handler already restores the origin in that same window via `set-window-buffer'.
New test asserts the in-place behavior: starting single-window with a vterm origin, invoking the command leaves `(one-window-p)` t with the history buffer in the original slot. The existing render test no longer needs its `pop-to-buffer' stub since `switch-to-buffer' works in batch.
|
| |
|
|
|
|
|
|
| |
When the agent buffer is the only window in the frame, F9 buries it (correct) but the next F9 redisplayed it as a side split instead of restoring the full-frame layout -- the display-saved path always called `display-buffer-in-direction`, which insists on a split.
New `cj/--ai-vterm-last-was-bury` flag tracks which toggle-off path ran. `cj/ai-vterm` sets it to t in the bury branch (one-window-p) and nil in the delete-window branch. `cj/--ai-vterm-display-saved` checks the flag at toggle-on: if t and the frame is still single-window, it replaces the selected window's buffer in place rather than splitting. Either branch consumes the flag so it never stays stale.
5 tests in test-ai-vterm--single-window-toggle.el cover the flag's set/clear paths, the still-one-window guard, and the end-to-end roundtrip.
|
| |
|
|
|
|
| |
Coverage snapshot rerun on 2026-05-13 -- selection-framework.el, keyboard-compat.el, and calibredb-epub-config.el all crossed the 70% threshold and got marked DONE; the remaining child tasks got their current numbers refreshed. The total moved from 65.4% to 67.2%.
Marked the [#A] "replace todo indicators with project name" enhancement DONE -- the agenda-category fix shipped in the prior commit. Also added a [#A] dashboard-icon follow-up on the Telegram task and a [#C] task to rebind org-noter insert-note onto =n=.
|
| |
|
|
|
|
| |
The %c column on agenda blocks rendered every project's todo.org as "todo:" -- org defaults the buffer category to the filename without extension, so every entry looked alike. An org-mode-hook now overrides org-category with the parent directory's basename (stripping a single leading dot, so ~/.emacs.d/todo.org reads as "emacs.d") whenever a todo.org file opens and its category is still the filename default. Explicit #+CATEGORY: keywords still win.
14 tests in test-org-agenda-config-category.el cover the helper's normal/boundary/error paths and the hook's override + explicit-category-preserved cases.
|
| | |
|
| |
|
|
|
|
|
|
|
| |
I added two open-work entries:
- F9 toggle should restore single-window layout when AI-vterm was the only
window before toggle-off. Current behavior buries (correct) but
toggle-on splits.
- AI-vterm scrollback history should replace the agent buffer in place,
not split or pop a separate window.
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
| |
After cc594fd moved private feed URLs out of modules/calendar-sync.el,
new machines need a hand-written calendar-sync.local.el to populate
calendar-sync-calendars. The template shows the file shape, where to
find the .ics URLs, and which user-constants variables fill the :file
slots. The real file stays gitignored.
|
| |
|
|
|
|
|
|
| |
toggle-window-split swapped buffers between two windows via two set-window-buffer calls. If either window was strongly dedicated (e.g. *Org Agenda* via the display-buffer-alist rule), the dedicated window rejected the second swap. Both panes ended up showing the dedicated buffer. The non-dedicated buffer never made it across.
The fix clears dedicated on both windows before the swap. The user explicitly invoked a layout change, so preserving per-window dedicated through the operation would just re-trigger the same wedge on the next toggle.
Tests in tests/test-ui-navigation--toggle-window-split.el cover the no-dedicated baseline, the bug-trigger (dedicated selected window), the post-toggle cleared state, and the one-window / three-window no-op boundaries.
|
| |
|
|
|
|
|
|
| |
The "d" command's (agenda ...) block had no org-agenda-skip-function. The global org-agenda-skip-scheduled-if-done is nil. CANCELLED tasks with a SCHEDULED date rendered in the forward-looking schedule unfiltered.
The fix adds an org-agenda-skip-function to the SCHEDULE block: (org-agenda-skip-entry-if 'todo '("CANCELLED")). The scope is deliberate. Only CANCELLED is filtered, not DONE or FAILED. A scheduled DONE task is a record of when something happened and stays visible.
Tests cover the configuration: the form must appear on the agenda block and must not appear on the overdue, hi-pri, or priority-B blocks.
|
| |
|
|
|
|
| |
The fallback chain was checking `nov-epub-filename` and `nov-epub-file`, but neither symbol exists in nov.el — the real var is `nov-file-name`, set by `nov-mode` from the visited file. Both `boundp` arms always returned nil, so the fallbacks were dead code. The bug was dormant rather than active: `buffer-file-name` always holds the EPUB path for normal nov buffers and covered the first arm of the `or`.
I replaced both wrong-named arms with a single live arm on `nov-file-name`, and added a Boundary test that exercises it.
|
| |
|
|
|
|
| |
I added tests for `cj/calibredb-clear-filters`, `cj/force-nov-mode-for-epub`, `cj/--nov-image-padding-cols` (the helper extracted in the previous commit), `cj/nov--natural-window-width`, `cj/nov--metadata-get`, `cj/nov--file-path`, and `cj/nov-jump-to-calibredb`. Each gets Normal, Boundary, and Error cases where they apply: 22 tests in all. The calibredb stubs sit in a new `test-calibredb-epub--with-calibredb-stubs` macro so the three jump-to-calibredb cases stay focused on their branch.
Coverage on `calibredb-epub-config.el` moves from 7/104 (6.7%) to 95/133 (71.4%).
|
| |
|
|
| |
I pulled the image-centering math out of `cj/nov-center-images` into `cj/--nov-image-padding-cols`. The wrapper still loops over the image text properties and writes the line/wrap-prefix. The helper takes col-width, img-px, and font-width-px and returns the left padding. With the math in a pure helper I can unit-test the centering rule directly instead of building a buffer with image-display properties.
|
| |
|
|
| |
`cj/webdev-setup` called `(electric-pair-mode t)`, which flips the global minor mode every time a TS/JS/TSX buffer opens. I switched it to `electric-pair-local-mode` so the pairing stays scoped to those buffers, like the other buffer-local settings in the hook. (Same fix I just made in `prog-python.el`.)
|
| |
|
|
| |
I added `tests/test-prog-webdev-format.el` — 7 ERT tests over `cj/--webdev-format-command` (plain path, spaces-quoted, `.tsx`) and `cj/webdev-format-buffer` (prettier present → the region command targets `buffer-file-name`; the `file.ts` fallback when the buffer has no file; point clamps to `point-max` when the format shrinks the buffer; prettier missing → `user-error`). `executable-find` and `shell-command-on-region` are stubbed. `cj/webdev-keybindings` was already covered by `test-prog-webdev--format-wiring.el`.
|
| |
|
|
| |
I pulled the prettier command-string construction out of `cj/webdev-format-buffer` into `cj/--webdev-format-command`. The wrapper stays thin: resolve `(or buffer-file-name "file.ts")`, hand the command to `shell-command-on-region`, clamp point. With the command shape in a pure helper I can unit-test it directly.
|
| |
|
|
|
|
| |
`cj/keyboard-compat-terminal-setup` (8 `define-key`s into `input-decode-map` for arrow escape sequences) and `cj/keyboard-compat-gui-setup` (18 `M-<UPPER>` → `M-S-<lower>` translations into `key-translation-map`) had no tests — that's the bulk of the module's executable lines. I added `tests/test-keyboard-compat-setup.el` — 7 ERT tests that `let`-bind those keymaps to fresh copies, stub `env-terminal-p` / `env-gui-p`, and check the decode/translate entries land, with completeness loops over all 8 arrow sequences and all 18 Meta-Shift letters, plus the gate-off boundary for each. `cj/--icon-blank-in-terminal` was already covered.
`lookup-key` on an ESC-prefixed string can return a meta-prefix event count instead of nil, so the "no-op when not on a terminal" case asserts the keymap is still empty rather than checking individual lookups.
|
| |
|
|
| |
`cj/consult-line-or-repeat` had only a keybinding test. I added `tests/test-selection-framework--consult-line-or-repeat.el` — 4 ERT tests covering the fresh-search branch, the repeat-on-second-call branch, the nil `last-command` boundary, and `commandp`, with `consult-line` and `vertico-repeat` stubbed. It reuses the `use-package`-shadow trick from the keybindings test, so the module loads without dragging in the vertico/consult/embark/company/prescient stack.
|
| |
|
|
|
|
|
|
| |
byte-compile warnings
The `cj/drill-*` defuns and `cj/drill-map` lived inside the `use-package org-drill` `:config` block, so the byte-compiler never registered them — every cross-reference between them warned ("function `cj/drill-this-file' is not known", and so on). I moved all of that to module top level, where the compiler sees it. The ten `(setq org-drill-...)` lines became a `:custom` block (no more "assignment to free variable"). Added `(require 'user-constants)` and `(require 'keybindings)` for `drill-dir` and `cj/custom-keymap`, plus `declare-function` for `org-drill`, `org-drill-resume`, `org-capture`, and `org-refile`. The module byte-compiles clean now, and `C-; D` still mounts the drill submenu with the same leaf keys.
I also gave `tests/test-org-drill-first-function.el` a `cj/custom-keymap` stub: its "loads without error" test does a bare `load` of the module, which now runs the keymap mount at load time instead of deferring it inside `:config`.
|
| |
|
|
|
|
| |
vterm binds `<f1>`..`<f12>` to `vterm--self-insert`, so a plain `<f9>` typed while point is in an agent buffer goes to the terminal program instead of the global toggle. That's invisible most of the time — you press F9 from another window — but it bites when the agent buffer is the only window in the frame, because there's nowhere else to press it from.
I re-bound the F9 family in `vterm-mode-map` (via `with-eval-after-load 'vterm`) so that `<f9>`, `C-<f9>`, and `M-<f9>` reach `cj/ai-vterm`, `cj/ai-vterm-pick-project`, and `cj/ai-vterm-pick-buffer` from there too. The C-/M- variants aren't actually in vterm's intercept set, but binding them keeps things uniform. New `tests/test-ai-vterm--f9-in-vterm.el`: 4 ERT tests over the `vterm-mode-map` and global bindings. F12's `cj/vterm-toggle` has the same shape of bug and isn't touched here.
|
| |
|
|
|
|
| |
`S` ("study") in `dirvish-mode-map` opens the `.org` file at point and runs `cj/drill-this-file` on it, so I can drill any deck straight from the file list. It `user-error`s on no file, on a directory, or on a non-`.org` file.
`D` and `O` were already taken (duplicate-file, open-with-command), so I went with `S`. It shadows dired's `dired-do-symlink`, which I never use from dirvish and which stays on `M-x` anyway. New `tests/test-dirvish-config-drill.el`: 6 ERT tests with `dired-get-filename`, `find-file`, and `cj/drill-this-file` mocked. I also fixed the stale `P` line in the module commentary — `P` is the print command now, not copy-path.
|
| |
|
|
|
|
|
|
| |
`org-drill` has no fixed home — with `org-drill-scope` left at its default it just drills the current buffer. So the only thing in my config tied to a location was `cj/drill-start`, which forced a pick from `drill-dir`.
`C-; D f` (`cj/drill-this-file`) drills whatever Org buffer is current, so a drill file living anywhere works. It `user-error`s when the buffer isn't an Org buffer.
`C-u C-; D s` (and `C-u C-; D e`) now prompts for the directory to pick from instead of always using `drill-dir`. Bare `C-; D s` is unchanged. I pulled the picking logic into `cj/--drill-files-in`, `cj/--drill-pick-dir`, and `cj/--drill-pick-file` so it's unit-testable. New `tests/test-org-drill-config.el`: 12 ERT tests over those helpers, `cj/drill-this-file`, `cj/drill-start`, and the keymap.
|
| |
|
|
| |
`cj/python-setup` called `(electric-pair-mode t)`, which flips the global minor mode every time a Python buffer opens. I switched it to `electric-pair-local-mode` so the pairing stays scoped to Python buffers, like the other buffer-local settings in the hook.
|
| |
|
|
| |
I added tests/test-prog-python-commands.el — 13 ERT tests over `cj/--python-mypy-command`, `cj/--python-debug-command`, `cj/python-mypy`, `cj/python-debug`, and `cj/python-mode-keybindings`. Normal, boundary, and error case per function. `executable-find`, `compile`, and `pdb` are stubbed at the boundary. None of the module's functions had coverage before this.
|
| |
|
|
| |
I pulled the command-string construction out of `cj/python-mypy` and `cj/python-debug` into `cj/--python-mypy-command` and `cj/--python-debug-command`. The wrappers stay thin — resolve the target, hand off to `compile` or `pdb`. With the command shape in a pure helper I can unit-test it directly instead of stubbing `compile` and `pdb`.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The 80% width from `4d9a206' wasn't actually narrowing the page: `cj/nov-apply-preferences' set `nov-text-width' to t (nov renders the text unfilled, one long line per paragraph) and counted on `visual-fill-column-mode' to set the window's display margins, but those margins never got applied in nov-mode buffers (even after manually re-running the layout), so the text wrapped at the full window width. The cause is still unknown.
This drops `visual-fill-column' from nov entirely:
- `nov-text-width' is a column count (~80% of the window's natural width), so nov's `shr' fills the text itself.
- `cj/nov-update-layout' sets the window's left/right margins directly to `(natural - text-width) / 2' each, centering the block, and pushes the fringes out to the window edge so they don't show as thin lines beside the text. When the width changes it re-renders, restoring the reading position approximately.
- `cj/nov-apply-preferences' adds a `kill-buffer-hook' that drops the margins and fringes when the EPUB buffer goes away, so a later buffer in that window isn't left indented.
- `+'/`=' and `-'/`_' adjust `cj/nov-margin-percent' and re-flow + re-center.
The text-width math moved into a `cj/nov--natural-window-width' helper alongside the existing `cj/nov--text-width'. Known nit: the centering is a touch left-of-center because shr wraps at word boundaries, so the rendered text is a bit narrower than `nov-text-width' and the right margin ends up slightly larger. Logged as a follow-up.
|
| | |
|
| |
|
|
| |
`1c5c8bd' had set `cj/nov-margin-percent' to 12 (~76% text); 10 gives a round 80%. Adjust per-buffer with the `+'/`-' keys; clamp is unchanged (0..25, i.e. 50%..100%).
|
| |
|
|
|
|
| |
`C-; p SPC' (`cj/reveal-present') is the new fast path: it inserts reveal.js headers if the buffer doesn't have them (prompting for a title), saves the buffer, exports to self-contained HTML, and opens it in the browser. It's the export-and-open you'd otherwise reach by doing `C-; p h' then `C-; p e'. `C-; p H' (`cj/reveal-remove-headers') strips the header block this module inserts, and `C-; p h' (`cj/reveal-insert-header') now errors instead of duplicating headers when a reveal block is already present.
The header logic moved into small helpers (`cj/--reveal-has-header-p', `cj/--reveal-remove-headers', `cj/--reveal-ensure-header', `cj/--reveal-keyword-regexp', and the `cj/--reveal-header-keywords' list), and `test-org-reveal-config-headers.el' drives them directly: detection on a reveal vs. a plain Org buffer, removal of the generated block (and only the leading block), body preservation, and the no-duplicate-headers error.
|
| |
|
|
|
|
| |
The agenda buffer's `display-buffer-alist' rule used `(window-height . fit-window-to-buffer)', so a sparse agenda opened as a sliver a few lines tall. The rule now takes `(window-height . cj/org-agenda-window-height)', a defcustom defaulting to 0.75 (the fraction of the frame the agenda window gets), and the rule itself moved into `cj/--org-agenda-display-rule' so it's testable. New `test-org-agenda-config-display.el' checks that the configured fraction flows through, that it's no longer `fit-window-to-buffer', and (integration) that `display-buffer' produces a window near that size.
`(use-package alert)' gained an `:if (or (not noninteractive) (require 'alert nil t))' guard: the batch test runner loads this module without `package-initialize', so the optional notification package may be installed but not yet on the load path, and the unconditional `:config' setq's would error.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
`cj/nov--text-width-for-window' computed the target column as a percentage of `(window-body-width)'. But body width is the column count *after* the display margins. `cj/nov-update-layout' runs from `window-configuration-change-hook': it sets `visual-fill-column''s margins, which changes the body width, which fires the hook, which re-runs the layout against the now-narrower body, and so on. It's a shrinking feedback loop that bottoms out at `cj/nov-min-text-width' (40 columns) no matter what `cj/nov-margin-percent' is. That's why the column was a thin strip regardless of the margin setting.
The width is now computed from the window's *natural* column count (body width plus any margins already set), so re-running the layout is idempotent. The margin math moved into a pure `cj/nov--text-width' helper, which is what the unit tests drive, and there's a regression test that the result is the same whether or not margins are already in place.
Also:
- `+'/`=' (`cj/nov-widen-text') and `-'/`_' (`cj/nov-narrow-text') step `cj/nov-margin-percent' by `cj/nov-margin-step' and re-lay-out, reporting the new percentage. `cj/nov-margin-percent' is now clamped to 0..25, so the text column runs from 50% (the floor) to 100% (the full window).
- `cj/nov-margin-percent' default is 12 (≈76% text) for a comfortable starting width.
- `cj/nov-apply-preferences' re-renders the document at the end again. `b3b537f' removed that on the theory `visual-fill-column' would re-trigger the render. The first page came up off-center until a manual resize, so it's back.
- `cj/nov-update-layout' is now a command.
The visible result (a ~75% centered column on first open, `+`/`-` to adjust) needs a restart to confirm. The tests cover the width math and clamping, idempotency, the adjust commands and their keybindings, the command status, and the re-render.
|
| | |
|