<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/custom-ordering.el, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-25T00:59:28+00:00</updated>
<entry>
<title>refactor(load-graph): route C-; registration through the keymap API</title>
<updated>2026-05-25T00:59:28+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-25T00:59:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=08014b2f15e099a1c5e662a17a41290f37aeebf4'/>
<id>urn:sha1:08014b2f15e099a1c5e662a17a41290f37aeebf4</id>
<content type='text'>
Migrated all 31 cj/custom-keymap registration sites across 24 modules from direct (keymap-set cj/custom-keymap ...) calls to cj/register-prefix-map and cj/register-command. Consumers no longer reference cj/custom-keymap directly, so keybindings.el is the sole owner of the C-; prefix and modules reach it only through the API (each already requires keybindings from Phase 2).

Behavior-preserving: I dumped every C-; binding before and after the migration and they're identical: 279 bindings, each resolving to the same command. The which-key label blocks are untouched, since they use string key descriptions and never assumed the keymap existed. I byte-compiled all 24 files (no new free-variable warnings, because the cj/custom-keymap references are gone), and make test, validate-modules, and an init load all pass.
</content>
</entry>
<entry>
<title>docs(load-graph): classify text/editing command modules</title>
<updated>2026-05-24T21:20:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:20:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1dfd02da826d61d528fe42c5bee3c5dbcf098c82'/>
<id>urn:sha1:1dfd02da826d61d528fe42c5bee3c5dbcf098c82</id>
<content type='text'>
Second classification batch: the nine custom-* text/editing command helpers (case, comments, datetime, buffer-file, line-paragraph, misc, ordering, text-enclose, whitespace). I annotated each with the load-graph header contract and added a Batch 2 table to the inventory. They're all Layer 2, eager only to register a C-; submap at load, with no necessary eager reason, so all are Phase 3/4 deferral candidates.

The inventory records a second hidden dependency for Phase 2: custom-buffer-file guards its C-; b registration with (when (boundp 'cj/custom-keymap) ...) and declares the keymap only via eval-when-compile, so the binding silently drops when the module loads without keybindings.
</content>
</entry>
<entry>
<title>refactor: declare cross-module commands bound in custom keymaps</title>
<updated>2026-05-24T19:34:57+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T19:34:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ad173a77fbbe2e9ffb178b8765d0a3cc6b972aac'/>
<id>urn:sha1:ad173a77fbbe2e9ffb178b8765d0a3cc6b972aac</id>
<content type='text'>
custom-ordering.el binds cj/org-sort-by-todo-and-priority (owned by org-config) and custom-text-enclose.el binds change-inner/change-outer (the change-inner package). Both work at runtime — org-config loads eagerly and text-config autoloads change-inner via use-package :commands — but byte-compiling either module standalone warned "not known to be defined", and the dependency was implicit.

Added declare-function for each so the compile is clean and the cross-module relationship is explicit at the top of the file. No autoload needed: the runtime autoload/eager-load already exists, so only the compiler needed telling. custom-buffer-file.el byte-compiles clean already, so it needed no change.
</content>
</entry>
<entry>
<title>refactor(custom-editing): five hygiene fixes from the module-by-module re-review</title>
<updated>2026-05-16T07:48:18+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T07:48:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a9a4d8c7148c115a242a7b35d16dd536f9c0c700'/>
<id>urn:sha1:a9a4d8c7148c115a242a7b35d16dd536f9c0c700</id>
<content type='text'>
- Guard `cj/duplicate-line-or-region' when COMMENT is non-nil but the
  current mode has no `comment-start' (e.g. fundamental-mode).
  Previously the function silently produced malformed output via
  `comment-region'; now it signals a clear `user-error'.

- Factor the `find-file' advice install in external-open.el into
  `cj/external-open-install-advice'.  Same idempotent shape
  (remove-then-add) but the intent is named.

- Add `cj/--validate-decoration-char' in custom-comments.el and
  wire it into all six divider / border / box helpers.  Rejects
  multi-char strings, empty strings, and control characters like
  newline/tab that would corrupt subsequent `M-q' flows.  Updated
  the five nil-decoration ERT tests from `:type 'wrong-type-argument'
  (the old crash signal from `string-to-char' on nil) to
  `:type 'user-error', since the validator produces a clear
  message instead of a deep crash.

- Extract `cj/--require-spell-checker' in flyspell-and-abbrev.el.
  Both `cj/flyspell-toggle' and `cj/flyspell-then-abbrev' now call
  the shared helper; the checker list lives in
  `cj/--spell-checker-executables', so adding nuspell or any other
  checker is a one-line edit.

- Preserve trailing newlines in custom-ordering output.  Both
  `cj/--arrayify' and `cj/--unarrayify' now detect a trailing
  newline on the input region and re-append it to the result,
  matching the pattern custom-text-enclose.el already uses.
</content>
</entry>
<entry>
<title>chore(modules): pass validate-modules in batch by adding requires</title>
<updated>2026-05-08T00:25:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-08T00:25:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a41ef9774f6550da446a3ae8fbbcbcd5bf6c23c4'/>
<id>urn:sha1:a41ef9774f6550da446a3ae8fbbcbcd5bf6c23c4</id>
<content type='text'>
`make validate-modules` had 19 module-load failures, all the same shape: a module references a symbol or feature owned by another module without saying so. Production was fine because init.el orders requires correctly. The batch target loads each module in isolation, though, and surfaces the gap.

I added explicit `(require 'keybindings)` or `(require 'user-constants)` to each affected module. The requires are idempotent at runtime, so production load order is unchanged. For three optional packages (elpa-mirror, mu4e, org-contacts), I switched to `(require 'X nil t)` so the modules load cleanly when those packages aren't installed. The activation calls become no-ops in that case.

`make validate-modules` now reports 0 failures.
</content>
</entry>
<entry>
<title>feat: Fix modeline lag and add org multi-level sort with comprehensive tests</title>
<updated>2025-11-08T22:11:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-08T22:11:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=513dfd2a1d497da8bd9d5261458cf4140dce7ad6'/>
<id>urn:sha1:513dfd2a1d497da8bd9d5261458cf4140dce7ad6</id>
<content type='text'>
Performance improvement and new feature with full test coverage.

## Changes

### 1. Fix modeline line/column position lag (#A priority)
- Replace expensive line-number-at-pos with cached %l/%c format specifiers
- Enable line-number-mode explicitly for caching
- Result: Instant modeline updates, zero performance overhead
- Files: modules/modeline-config.el:81-83, modules/ui-config.el:53

### 2. Implement multi-level org sorting
- New function: cj/org-sort-by-todo-and-priority
- Sorts by TODO status (TODO before DONE) AND priority (A→B→C→D)
- Uses stable sorting: priority first, then TODO state
- Gracefully handles empty sections (no error)
- Bound to C-; o o (ordering → org sort)
- Files: modules/org-config.el:278-299, modules/custom-ordering.el:253,267

### 3. Comprehensive ERT test suite (12/12 passing)
- Normal cases: Mixed TODO/DONE, multiple of same type, same priority
- Boundary cases: Empty sections, single entries, no priorities
- Error cases: Non-org-mode buffer
- Test file: tests/test-org-sort-by-todo-and-priority.el

### 4. Testing improvements discovered
- Disable org-mode hooks to avoid package dependencies in batch mode
- org-sort-entries must be called from parent heading
- Preserve priority cookie in org-get-heading (t t nil t)
- Add condition-case to handle "Nothing to sort" gracefully

### 5. Minor cleanup
- Comment out chime-debug setting (org-agenda-config.el:267)
- Mark modeline lag task as DONE in todo.org

## Technical Details

Modeline optimization:
- line-number-at-pos is O(n) where n = current line
- %l and %c are O(1) lookups from cached values

Org sorting algorithm uses stable sort:
1. Sort by priority (A, B, C, D, unprioritized)
2. Sort by TODO status (preserves priority order within groups)
Result: TODO [#A], TODO [#B], DONE [#A], DONE [#B], etc.
</content>
</entry>
<entry>
<title>feat:which-key: Add descriptive labels for custom keymaps</title>
<updated>2025-10-27T23:45:23+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-10-27T23:45:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=87034eab17625165b884128d8058c1158fc2f50f'/>
<id>urn:sha1:87034eab17625165b884128d8058c1158fc2f50f</id>
<content type='text'>
Enhance which-key integration by providing detailed descriptions for
new key bindings across multiple modules. This improves the
usability of custom keymaps by clarifying the purpose of each
keybinding, making it easier for users to navigate and understand
different menus and options available within the configuration.

This update ensures that all custom keymaps now display a
descriptive label in the which-key popup to explain their
functionality, aiding users in identifying keymap purposes promptly.
</content>
</entry>
<entry>
<title>feat+test: custom-ordering: line manipulation utils + tests</title>
<updated>2025-10-27T02:55:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-10-27T02:55:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a225b8d96c0bc17502475ae49ce96c0c50a63aeb'/>
<id>urn:sha1:a225b8d96c0bc17502475ae49ce96c0c50a63aeb</id>
<content type='text'>
Introduce new functions cj/toggle-quotes, cj/reverse-lines, and
cj/number-lines for line manipulation. cj/toggle-quotes swaps quotes
within a region, cj/reverse-lines reverse the line order, and
cj/number-lines adds line numbers with a customizable format,
supporting zero-padding. Update keymap with new bindings. Add
comprehensive tests for each function.
</content>
</entry>
<entry>
<title>feat+test:ordering: add prefix/suffix to arrayify/convenience funcs</title>
<updated>2025-10-27T02:40:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-10-27T02:40:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=026323489e758cb1f21ab9fffb1ddd107ea739f3'/>
<id>urn:sha1:026323489e758cb1f21ab9fffb1ddd107ea739f3</id>
<content type='text'>
  Enhanced cj/--arrayify with optional prefix/suffix parameters for flexible formatting:
  - prefix: string to prepend (e.g., "[" or "(")
  - suffix: string to append (e.g., "]" or ")")

  Added convenience functions:
  - cj/listify: unquoted list (a, b, c)
  - cj/arrayify-json: JSON array ["a", "b", "c"]
  - cj/arrayify-python: Python list ["a", "b", "c"]

  Updated keybindings:
  - C-; o l → cj/listify
  - C-; o j → cj/arrayify-json
  - C-; o p → cj/arrayify-python
  - C-; o L → cj/comma-separated-text-to-lines (moved from 'l')
</content>
</entry>
<entry>
<title>refactor:keymaps: Replace define-prefix-command with defvar-keymap</title>
<updated>2025-10-26T00:56:40+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-10-26T00:56:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=746cefd4d674bfc6dd9fdfe4aeb79c7e760ec268'/>
<id>urn:sha1:746cefd4d674bfc6dd9fdfe4aeb79c7e760ec268</id>
<content type='text'>
Refactor the keymap definitions across multiple modules by replacing
`define-prefix-command` with `defvar-keymap`. This simplifies the
keymap setup and integrates better with the `which-key` package for
enhanced documentation and usability. Updated modules include
custom-ordering, custom-text-enclose, custom-whitespace,
mail-config, music-config, org-drill-config, test-runner, and
vc-config.
</content>
</entry>
</feed>
