diff options
Diffstat (limited to 'issues.org')
| -rw-r--r-- | issues.org | 1881 |
1 files changed, 0 insertions, 1881 deletions
diff --git a/issues.org b/issues.org deleted file mode 100644 index fa9bc74a..00000000 --- a/issues.org +++ /dev/null @@ -1,1881 +0,0 @@ -#+TITLE: Emacs Configuration Issues & Enhancement Roadmap -#+AUTHOR: Claude Code Analysis -#+DATE: 2025-10-30 - -* New Open Work - -** TODO [#A] Duplicate variable declaration in early-init.el :bug:architecture:critical: -:PROPERTIES: -:FILE: early-init.el -:LINES: 76-78, 104-105 -:END: - -The variable =cj/network-available= is declared twice (lines 76-78 and 104-105). - -*Impact:* Confusing code structure, potential initialization issues. - -*Fix:* Remove the duplicate declaration at lines 104-105. The first declaration at line 76 is sufficient. - -** TODO [#A] Missing function definition in vc-config.el :bug:critical: -:PROPERTIES: -:FILE: modules/vc-config.el -:LINE: 123 -:END: - -The keymap binding references =cj/goto-git-gutter-diff-hunks= (line 123) but this function is never defined anywhere in the config. - -*Impact:* Keybinding =C-; v d= will fail with "Symbol's function definition is void" error. - -*Fix:* Either implement the missing function or remove the keybinding. Likely should use =git-gutter:popup-hunk= or create a wrapper that calls =consult-line= filtered to lines with git-gutter markers. - -** TODO [#A] Missing function in wrap-up.el :bug:critical: -:PROPERTIES: -:FILE: modules/wrap-up.el -:LINE: 28 -:END: - -Calls =cj/log-silently= but function is never defined in the config. - -*Impact:* Init will fail at startup with undefined function error. - -*Fix:* Define =cj/log-silently= in system-defaults or config-utilities, or replace with a simple =(message ...)= call. - -** TODO [#A] Network check runs synchronously during startup :performance:critical: -:PROPERTIES: -:FILE: early-init.el -:LINES: 189, 206 -:END: - -The =cj/internet-up-p= function is called *synchronously* during early-init (lines 189, 206), which means Emacs startup blocks waiting for ping or network connection attempts. - -*Impact:* Adds 1+ seconds to every startup, even when network is available. Completely unacceptable UX. - -*Fix:* -1. Default =cj/network-available= to =t= (assume network is available) -2. Run network check asynchronously in background after startup -3. Only use network check when user explicitly runs =package-refresh-contents= -4. Consider removing network check entirely - let package.el handle its own errors - -Modern approach: Remove =cj/use-online-repos= flag entirely. Let package operations fail gracefully if network is down, rather than preemptively checking. - -** TODO [#A] Commented-out non-commented variable in org-config.el :bug:functionality: -:PROPERTIES: -:FILE: modules/org-config.el -:LINE: 129-130 -:END: - -Line 129 has a TASK comment about removing a non-existent variable =org-indent-indentation-per-level=, but line 130 actually tries to set it. - -*Impact:* Setting an undefined variable that does nothing. - -*Fix:* Remove lines 129-130 entirely if the variable doesn't exist. Verify in org-mode source whether this was renamed or removed. - -** TODO [#B] Replace Company with Corfu for better performance and maintenance :enhancement:architecture: -:PROPERTIES: -:FILE: modules/selection-framework.el -:LINES: 202-236 -:END: - -Currently using Company for completion, but Corfu is the more modern choice: - -*Why Corfu is better:* -- ~3x faster than Company (uses child frames vs overlays) -- Better maintained (company-mode development has slowed) -- Smaller, simpler codebase -- Works seamlessly with modern completion frameworks (Vertico/Consult) -- Native corfu-popupinfo is cleaner than company-quickhelp -- Better integration with Cape for completion-at-point backends - -*Migration path:* -1. Replace =company= with =corfu= + =corfu-popupinfo= -2. Use =cape= for additional backends (cape-file, cape-keyword, cape-dabbrev) -3. Keep company-mode disabled in mu4e (corfu already respects this) -4. Much simpler configuration, no company-box needed - -*Estimated effort:* 30 minutes. High value/low effort. - -** TODO [#B] Consolidate debug logging functions :architecture:maintainability: -:PROPERTIES: -:FILES: modules/org-agenda-config-debug.el, modules/wrap-up.el -:END: - -Debug logging is inconsistent: -- =org-agenda-config-debug.el= defines =cj/log-silently= -- =wrap-up.el= *calls* =cj/log-silently= but it's not loaded yet -- =config-utilities.el= has debug functions but not logging - -*Fix:* Create =modules/debug-logging.el= with consistent logging infrastructure: -- =cj/log-debug=, =cj/log-info=, =cj/log-warning=, =cj/log-error= -- Buffer-based logging with timestamps -- Easy toggle for debug mode -- Load early in init.el before other modules - -** TODO [#B] Missing error handling in org-agenda rebuild :stability: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:LINES: 91-105 -:END: - -=cj/build-org-agenda-list= recursively searches directories but has no error handling for: -- Permission denied errors -- Circular symlinks -- Very deep directory trees -- Network filesystem timeouts (if projects-dir is on NFS/CIFS) - -*Impact:* Agenda rebuild can hang or fail silently. - -*Fix:* Add condition-case wrapper with specific handlers: -#+begin_src elisp -(condition-case err - (cj/add-files-to-org-agenda-files-list projects-dir) - (file-error - (message "Warning: Could not scan %s: %s" projects-dir err)) - (error - (message "Error building agenda: %s" err))) -#+end_src - -** TODO [#B] Unify path handling functions :architecture:maintainability: -:PROPERTIES: -:FILES: modules/dirvish-config.el, modules/custom-buffer-file.el -:END: - -Path handling is duplicated across modules: -- =cj/get-project-root= in dirvish-config.el (lines 408-426) -- Similar logic needed in custom-buffer-file for relative paths -- Different approaches to home directory abbreviation - -*Fix:* Create =modules/path-utils.el= with: -- =cj/get-project-root= (shared implementation) -- =cj/abbreviate-path= (handles ~, project-relative, absolute) -- =cj/path-relative-to= (generalized relative path function) -- Consistent path handling across all modules - -** TODO [#B] Treesit-auto grammar pinning is fragile :stability:maintenance: -:PROPERTIES: -:FILE: modules/prog-general.el -:LINES: 106-113 -:END: - -Go grammar is pinned to v0.19.1 by directly modifying the treesit-auto-recipe-list vector with =aset=. - -*Issues:* -- Hardcoded slot index (6) will break if treesit-auto changes struct layout -- No validation that the recipe exists or that slot 6 is :revision -- Comment says "Emacs 30.2" but doesn't check version -- Will fail silently if treesit-auto changes - -*Fix:* -1. Use proper accessor functions if available (treesit-auto-recipe-revision setter) -2. Add version check: =(when (version< emacs-version "30.3") ...)= -3. Add error handling and warning if pinning fails -4. Document why pinning is needed in comments - -** TODO [#B] Test coverage gaps in critical modules :testing:quality: -:PROPERTIES: -:FILES: modules/org-agenda-config.el, modules/mail-config.el, modules/ai-config.el -:END: - -Excellent test coverage for utility functions (custom-*, undead-buffers, org-roam helpers), but missing tests for: - -*High-value untested areas:* -1. =org-agenda-config.el=: - - =cj/build-org-agenda-list= (file discovery logic) - - =cj/org-skip-subtree-if-*= functions (agenda filtering) - - =cj/org-agenda-skip-subtree-if-not-overdue= (date logic) - -2. =mail-config.el=: - - =cj/mu4e-toggle-remote-images= (security critical) - - Email header parsing/address completion logic - -3. =ai-config.el=: - - =cj/gptel-add-file= (file path handling) - - =cj/gptel--fresh-org-prefix= (timestamp formatting) - -4. =org-roam-config.el=: - - =cj/move-org-branch-to-roam= (complex string manipulation) - - Already has tests for helpers, needs integration test - -*Recommendation:* Prioritize testing the org-agenda filtering functions - they're date-sensitive and easy to break. - -** TODO [#B] System-defaults autoload has wrong function name :bug:minor: -:PROPERTIES: -:FILE: modules/system-defaults.el -:LINE: 24 -:END: - -Line 24 has =autoload= for =env-bsd-p= from =host-environment=, but this file *requires* host-environment at compile time anyway (line 20). - -*Impact:* Redundant autoload, no functional issue. - -*Fix:* Remove the autoload statement. The eval-when-compile require is sufficient. - -** TODO [#B] Inconsistent coding system declarations :maintainability: -:PROPERTIES: -:FILES: early-init.el, modules/system-defaults.el -:END: - -UTF-8 coding system is set in two places: -- =early-init.el= lines 252-261 (before package loading) -- =system-defaults.el= lines 60-69 (after package loading) - -*Impact:* Redundant configuration, harder to maintain, unclear which takes precedence. - -*Fix:* Consolidate all coding system setup in early-init.el since it needs to be set before packages load. Remove from system-defaults.el. - -** TODO [#B] Chime notification times documentation mismatch :documentation:usability: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:LINES: 268-270 -:END: - -Comment says "5 minutes before and at event time (0 minutes)" but the value is ='(5 0)=. - -*Issue:* It's unclear if 0 means "at event time" or "immediately". Better documentation needed. - -*Fix:* Improve comment: -#+begin_src elisp -;; Alert 5 minutes before event and again at event time (0 = event start) -(setq chime-alert-time '(5 0)) -#+end_src - -** TODO [#C] Org-roam template paths use string concatenation :maintainability: -:PROPERTIES: -:FILE: modules/org-roam-config.el -:LINES: 43, 48, 53, 144, 150 -:END: - -Template file paths are built with =concat= instead of using =expand-file-name= or constants. - -*Impact:* Harder to change template location, no validation that files exist. - -*Fix:* Define template constants at top of file: -#+begin_src elisp -(defconst cj/org-roam-template-dir - (expand-file-name "org-roam-templates" user-emacs-directory)) -(defconst cj/org-roam-template-v2mom - (expand-file-name "v2mom.org" cj/org-roam-template-dir)) -;; etc... -#+end_src - -Validate files exist during config load. - -** TODO [#C] Dirvish-config has long function that should be split :refactor:maintainability: -:PROPERTIES: -:FILE: modules/dirvish-config.el -:LINES: 350-406 -:END: - -=cj/dired-copy-path-as-kill= is 57 lines with multiple responsibilities: -- Validation -- Project root detection -- Home directory handling -- Org-link formatting -- Clipboard operations -- User feedback - -*Fix:* Extract smaller functions: -- =cj/--path-type-for-file= (returns :project-relative, :home-relative, or :absolute) -- =cj/--format-as-org-link= (wraps path in org syntax) -- =cj/dired-copy-path-as-kill= (orchestrates the above) - -Better testability, clearer logic flow. - -** TODO [#C] Consider Embark for file operations instead of custom dirvish bindings :enhancement:usability: -:PROPERTIES: -:FILE: modules/dirvish-config.el -:LINES: 298-334 -:END: - -Dirvish config defines many custom keybindings (d, l, L, o, O, f, P) for file operations. - -*Alternative approach:* Use Embark for contextual file actions: -- Embark already knows about files in dired -- Can define actions once, use everywhere (not just dired) -- More discoverable (embark-act shows all options) -- Less cognitive load (one keybinding to remember: C-.) - -*Recommendation:* Keep the custom functions (good implementations), but expose them via embark-act instead of direct keybindings. Cleaner keymap, more flexible. - -** TODO [#C] Missing :ensure nil for more built-in packages :correctness:minor: -:PROPERTIES: -:FILE: Multiple modules -:END: - -Some built-in package use-package declarations are missing =:ensure nil=: -- =selection-framework.el=: =delsel= (line 54) -- Several others throughout - -*Impact:* Minimal - use-package won't try to install built-ins anyway. But inconsistent with the pattern used elsewhere. - -*Fix:* Add =:ensure nil= for consistency, or document why some have it and others don't. - -** TODO [#C] Consider lazy-loading more packages :performance:startup: -:PROPERTIES: -:FILE: modules/selection-framework.el -:LINES: 22, 40, 48, 151, 166, 240-253 -:END: - -Several completion packages use =:demand t= (vertico, marginalia, orderless, prescient): -- Vertico: Reasonable to demand (core UI) -- Marginalia: Could be :after vertico -- Orderless: Could be :after vertico -- Prescient: Could be :after vertico -- Company-prescient: Could be :after (vertico company) - -*Impact:* All load at startup, adds ~100ms. Not critical, but could be optimized. - -*Fix:* Profile with esup to see actual impact. If it's <100ms, probably not worth the complexity. - -** TODO [#C] Add validation for chime.el local path :stability:enhancement: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:LINE: 264 -:END: - -Config loads chime from local directory =~/code/chime.el= without checking if it exists. - -*Impact:* If directory doesn't exist, load fails silently or with cryptic error. - -*Fix:* Add existence check with helpful error message: -#+begin_src elisp -:load-path (lambda () - (let ((path "~/code/chime.el")) - (if (file-directory-p path) - path - (warn "chime.el not found at %s" path) - nil))) -#+end_src - -** TODO [#C] Document why org-msg uses local fork :documentation:maintenance: -:PROPERTIES: -:FILE: modules/mail-config.el -:LINE: 287 -:END: - -Loading org-msg from local path =/home/cjennings/code/org-msg= instead of MELPA. - -*Question:* Is this a temporary fork with patches? Does it need to stay local? - -*Fix:* Add comment explaining: -- What changes are in local fork -- Whether it's temporary or permanent -- Link to upstream PR if patches are submitted -- When it can switch back to MELPA - -** TODO [#C] Performance: Company idle-delay is very high :performance:usability: -:PROPERTIES: -:FILE: modules/selection-framework.el -:LINE: 212 -:END: - -Company idle-delay is set to 2 seconds, which is quite slow. Modern editors typically use 0.1-0.3s. - -*Impact:* Completion feels sluggish, requires manual triggering often. - -*Recommendation:* -- If keeping Company: Try 0.3 seconds -- If switching to Corfu: Default 0.1s is perfect - -User preference, but current setting feels unresponsive. - -** TODO [#C] org-protocol may not be fully configured :functionality:enhancement: -:PROPERTIES: -:FILE: modules/org-config.el -:LINES: 94-95, 116 -:END: - -=org-protocol= is loaded (line 95) and added to org-modules (line 116), but: -- No desktop entry file mentioned -- No documentation on how to use it -- org-webclipper exists but unclear if it uses org-protocol - -*Fix:* Either: -1. Document org-protocol setup (desktop file, browser integration) -2. Remove if unused (org-webclipper might be the preferred approach) - -** TODO [#D] Consider adding use-package :diminish for minor modes :enhancement:ui: -:PROPERTIES: -:FILE: Multiple modules -:END: - -Many minor modes appear in the modeline even though doom-modeline hides them. - -*Enhancement:* Add =:diminish= to use-package declarations for: -- =symbol-overlay-mode= -- =git-gutter-mode= -- =flycheck-mode= -- =yas-minor-mode= -- =ws-butler-mode= -- =rainbow-mode= - -Cleaner modeline if doom-modeline isn't used or fails to load. - -** TODO [#D] Add .dir-locals.el for project-specific settings :enhancement:devex: -:PROPERTIES: -:FILE: N/A (new file) -:END: - -Create =.dir-locals.el= in =~/.emacs.d= to: -- Set =fill-column= to 80 for elisp files -- Enable =flycheck-mode= for all elisp -- Set =checkdoc= settings locally -- Configure =outline-minor-mode= for easy navigation - -Helps maintain consistency when editing config. - -** DONE [#D] Consider moving from doom-modeline to mood-line :enhancement:performance: -CLOSED: [2025-11-03 Sun] -:PROPERTIES: -:FILE: modules/modeline-config.el -:END: - -✅ **Completed: Built custom modeline (better than mood-line)** - -Went beyond mood-line and created a fully custom modeline using only built-in -Emacs functionality. Features include: -- Color-coded buffer names (green/red/gold by status) -- VC branch with git symbol and state-based coloring -- String truncation for narrow windows -- Mouse click handlers (vc-diff, describe-mode, etc.) -- Help-echo tooltips -- Active-window-only segments -- Emacs 30 built-in right-alignment - -Fast, reliable, no external packages, fully customizable. - -** TODO [#D] Add commentary headers to test utility files :documentation:testing: -:PROPERTIES: -:FILES: tests/testutil-general.el, tests/testutil-filesystem.el -:END: - -Test utility files should have Commentary sections explaining: -- What utilities are provided -- When to use each utility -- Examples of usage -- Relationship to quality-engineer.org guidelines - -Helps future test authors understand available infrastructure. - -** TODO [#D] keybindings.el trains user away from C-x u :ux:philosophy: -:PROPERTIES: -:FILE: modules/keybindings.el -:LINES: 126-130 -:END: - -The rebinding of =C-x u= to a message telling users to use =C-/= instead is... philosophically interesting. - -*Discussion points:* -- Enforces good habits (C-/ is indeed faster) -- But violates principle of least surprise -- Could frustrate users who know Emacs bindings -- Muscle memory is hard to retrain - -*Alternative:* Just leave =C-x u= working. Add which-key hint that C-/ is faster. - -** TODO [#D] Missing which-key labels for some custom keymaps :usability:discoverability: -:PROPERTIES: -:FILES: Multiple modules -:END: - -Some custom keymaps lack which-key descriptions: -- =cj/vc-map= is well documented (vc-config.el) -- =cj/jump-map= is well documented (keybindings.el) -- =cj/org-table-map= is well documented (org-config.el) - -But check all custom keymaps for completeness. Run: -#+begin_src elisp -(apropos-variable "^cj/.*-map$") -#+end_src - -And verify each has which-key labels. - -** TODO [#D] early-init.el could benefit from more section comments :documentation:maintainability: -:PROPERTIES: -:FILE: early-init.el -:END: - -File has good top-level commentary but individual sections could use more context: -- Why network check exists (explain offline package strategy) -- Why GC threshold is increased (explain impact) -- Benchmark-init section is good, replicate that style - -Makes it easier for others (or future you) to understand decisions. - -** TODO [#D] Consider adding persistent-scratch package :enhancement:usability: -:PROPERTIES: -:FILE: N/A -:END: - -The *scratch* buffer is very useful but loses content on restart. - -*Enhancement:* Add =persistent-scratch= package to: -- Auto-save scratch buffer contents -- Restore on startup -- Optionally persist multiple scratch buffers - -Useful for quick notes, code experiments, calculations that span sessions. - -* Priority Explanation - -** Priority A (Critical) -Issues that: -- Prevent functionality from working -- Cause errors or crashes -- Block startup or major features -- Have security implications -- Significantly impact performance (>1s delay) - -** Priority B (High) -Issues that: -- Affect architecture or maintainability significantly -- Better alternative packages exist -- Moderate performance impact (>100ms) -- Testing gaps in critical code -- Risk of future breakage - -** Priority C (Medium) -Issues that: -- Affect code quality or consistency -- Minor refactoring opportunities -- Documentation improvements -- Small usability enhancements -- Package optimization opportunities - -** Priority D (Low) -Issues that: -- Nice-to-have features -- Polish and refinement -- Alternative approaches to consider -- Documentation expansion -- Style/convention improvements - -* Package Recommendations Summary - -** Recommended Changes -1. *Company → Corfu*: Better performance, more modern, better maintained -2. *Consider mood-line over doom-modeline*: Much faster for minimal info needs -3. *Add Cape*: Modern completion-at-point backends (if switching to Corfu) -4. *Consider persistent-scratch*: QOL improvement for scratch buffer - -** Packages to Keep -- Vertico: Excellent choice, fast, well-maintained -- Consult: Perfect companion to Vertico -- Orderless: Best completion style available -- Embark: Powerful, could be used more extensively -- Marginalia: Great for annotations -- Magit: Industry standard, no alternatives -- Org-roam: Best Zettelkasten for Emacs -- Which-key: Essential for discoverability - -** Packages to Evaluate -- Dirvish: Heavy package. Consider if dired + nerd-icons + embark is sufficient -- Doom-modeline: Feature-rich but heavy. Mood-line is lighter alternative -- Projectile vs project.el: You're using Projectile. Project.el is built-in and lighter, but Projectile is more mature. Keep unless performance is an issue. - -* Testing Recommendations - -Based on your quality-engineer.org guidelines and existing test coverage: - -** Well-Tested Areas ✓ -- All custom-* utility functions (excellent coverage) -- undead-buffers (comprehensive tests) -- org-roam helpers (good coverage) -- keyboard-macros -- Test utilities themselves - -** Priority Testing Gaps - -*** High Priority -1. *org-agenda-config.el filtering functions* - - =cj/org-skip-subtree-if-habit= - - =cj/org-skip-subtree-if-not-overdue= (date-sensitive!) - - =cj/build-org-agenda-list= (file discovery) - -2. *custom-buffer-file.el interactive functions* - - Split =cj/move-buffer-and-file= into =cj/--move-buffer-and-file= (testable core) and interactive wrapper - - Same for =cj/rename-buffer-and-file= - - Same for =cj/delete-buffer-and-file= - -*** Medium Priority -3. *org-roam-config.el integration test* - - =cj/move-org-branch-to-roam= workflow test - - Test actual file creation and org-roam db update - -4. *path-utils consolidation* (after creating module) - - Test project-relative, home-relative, absolute path logic - - Test with symlinks, non-existent paths, permission errors - -5. *ai-config.el GPTel context management* - - =cj/gptel-add-file= with various file types - - =cj/gptel--fresh-org-prefix= timestamp formatting - -*** Lower Priority -6. *mail-config.el address completion* -7. *Browser/external open logic* -8. *Theme persistence functions* - -** Testing Patterns to Follow - -Your existing tests follow excellent patterns from quality-engineer.org: -- One file per function for unit tests -- Clear normal/boundary/error case organization -- Good use of test utilities (testutil-general, testutil-filesystem) -- Proper setup/teardown -- Descriptive test names - -*Keep this pattern!* It's working well for you. - -** Test-Driven Refactoring Opportunities - -When fixing Priority A-B issues, write tests first: -1. Test for the bug/missing function -2. See it fail -3. Fix the code -4. See it pass -5. Refactor with confidence - -This is especially important for: -- The missing =cj/goto-git-gutter-diff-hunks= function -- The duplicate =cj/network-available= variable -- Network check refactoring (mock the network!) - -* Architecture Observations - -** What You're Doing Right ✓ - -1. *Excellent module organization*: Clean separation of concerns -2. *Good use of user-constants.el*: Centralized configuration -3. *Comprehensive custom utilities*: Well-tested, reusable functions -4. *Modern completion framework*: Vertico/Consult/Embark stack is excellent -5. *Debug infrastructure pattern*: org-agenda-config-debug.el is a good model -6. *Test coverage philosophy*: Following your own guidelines well -7. *Early-init optimization*: Good understanding of startup optimization -8. *Custom keymap prefix*: C-; is a good choice, well-organized - -** Architectural Patterns to Consider - -1. *Consolidate common patterns*: - - Path handling → path-utils.el - - Debug logging → debug-logging.el - - File validation → file-utils.el - -2. *Reduce coupling*: - - Some modules depend on specific implementations (e.g., projectile vs project.el) - - Consider adapter pattern for switching between implementations - -3. *Error handling strategy*: - - Add condition-case wrappers around file operations - - Log errors consistently to *Messages* or debug buffer - - Fail gracefully (warn user but don't break session) - -4. *Configuration validation*: - - Add startup checks for critical paths/files - - Warn about missing optional dependencies - - Provide helpful error messages with solutions - -** Code Smells Observed - -1. *String concatenation for paths*: Use =expand-file-name= consistently -2. *Silent failures*: Some functions fail without user feedback -3. *Synchronous network operations*: Blocks startup unnecessarily -4. *Duplicate logic*: Path handling, project root detection -5. *Large functions*: Some functions >50 lines, should be split -6. *Global state*: Some functions modify global variables without clear ownership - -* Next Steps Recommendation - -** Week 1: Fix Critical Bugs (Priority A) -1. Fix duplicate =cj/network-available= declaration -2. Implement missing =cj/goto-git-gutter-diff-hunks= -3. Define =cj/log-silently= or remove usage -4. Fix network check blocking (biggest impact) - -** Week 2: Architectural Improvements (Priority B) -1. Evaluate Company vs Corfu (try Corfu for a week) -2. Consolidate debug logging -3. Create path-utils.el module -4. Add error handling to org-agenda rebuild - -** Week 3: Testing (Priority B) -1. Add tests for org-agenda filtering functions -2. Split interactive/non-interactive in custom-buffer-file -3. Add integration test for org-roam branch extraction - -** Week 4: Polish (Priority C-D) -1. Consolidate coding system setup -2. Improve documentation -3. Add which-key labels where missing -4. Clean up minor issues - -** Ongoing -- Add tests for any new functionality -- Profile startup time with esup periodically -- Review package updates for breaking changes -- Keep org-gcal, chime.el forks in sync with upstream - -* Questions for You - -1. *Company vs Corfu*: Do you have strong feelings about Company? Willing to try Corfu? - Answer: I'd be excited to try Corfu. I don't know how I'd configure it yet and it seems like a big change. However, I planned to move to Corfu sometime soon. - -2. *Network check*: Do you actually need to support offline operation, or can we simplify this? - Answer: We can simplify. There was a time when I was traveling a lot and didn't have network access. The current offline operation seemed to help startup performance. The sooner we realized we were offline, the sooner I'd have a working Emacs. Now, I don't think it provides much benefit. - -3. *Local forks*: Are chime.el and org-msg local forks temporary? When can they move to MELPA? - Answer: These are packages I currently either maintain or are in the process of creating. I need the local-paths set so I can test. If you have better ideas on how I can switch them from using a local package to using the stable version using vc-install, I'd be grateful. - -4. *Doom-modeline*: Are you using features beyond what I saw in config? Would mood-line be sufficient? - No, I'm not. Mood-line would be welcomed. - -5. *Testing coverage*: What modules do you most want tests for? Anything you're nervous about breaking? - I'll probably look for large methods with lots of LOC and multiple tasks, then break them up for better testability. I was hoping you would point those out for me so I could focus on them. Will you? - -6. *Performance*: Any particular operations feel slow? (Agenda rebuild? Mail sync? Completion?) - Org Agenda is problematic and slow. I could get some insights into how to optimize it if it were well tested. - - -Other than that, here's my roadmap for fixing/improving -- video/audio config could use some testing around which sinks to attach to. It doesn't matter to me that this is Linux centric since it's my config. -- I'm not sure the UX in flyspell and abbrev is right. It could use some more attention. -- Same with Elfeed. While I had Elfeed dashboard at some point, it doesn't bring any joy to see a mostly blank screen when I check Elfeed. -- DWIM shell commands is alright. I wonder if I can leverage them better into eshell. -- calibredb is slow, but that's the package working on 14k+ pdfs and epubs. The right thing to do is to shrink the ebook inventory to < 1k or so books. -- I was thinking of investigating bettter diffs with difftastic. If it's straightforward and simple, I should jump at it. -- I would like to implement code-maat and code-compass within Emacs -- It would be good to have a solid profiler workflow so I can see what's going on inside my own methods and packages. For instance, I don't have the tools to tell me why it takes so long to generate my daily agenda. There's little observability. -- I absolutely need a workflow so I can go from org-mode file to reveal.js presentation using httpd server in Emacs with the press of a button. I know it can be done. Export the org file with injected css into the httpd directory, start the httpd server, then open a browser fullscreen on the file. That's pretty much 90% of the work right there. -- If I could figure out the latex-config so that I could author something non-trivial, I'd be happy. Right now, it's a bunch of cut/paste snippets from everyone else's config and it doesn't work. -- mail-config: the whole attachments workflow is awkward, but so necessary for daily life. It really needs improvement. -- then there's the whole org-contacts issue working with chime.el and birthdays. sexp expressions for dates? I should either figure them out and support them in chime, or I should disable them and find another way. -- it would be good to work with llms the way I do with claude code. gptel feels like a step in that direction and you can switch backends besides. but I need to either build a bunch of tools or install an MCP server. Which way to go? -- I would like to extract the code in local-repository that creates the repo so I can unit test the hell out of it and use it as a library for my own local repo. Seems like there's too much extraneous functionality there. -- lorem seems to have an error pop up each session about the markhov chain resetting. either that's bad and should be fixed or it's not bad and lorem should stfu. -- I believe jumper should probably be my next package, but I haven't had the time to spend on it. - -* Final Thoughts - -Your config is **quite good overall**: -- Well-organized modular structure -- Excellent test coverage for utilities -- Modern package choices (mostly) -- Good documentation and comments -- Following elisp idioms reasonably well - -The critical issues (Priority A) are straightforward fixes. The architectural suggestions (Priority B) are about making the codebase more maintainable long-term. Everything else is refinement. - -Focus on: -1. Fix the bugs that break functionality -2. Consider Corfu migration (biggest ROI for effort) -3. Add tests for date-sensitive agenda logic -4. Everything else is gravy - -You're clearly thoughtful about your config and willing to refactor when needed. These issues are normal for a mature, actively-used configuration. Keep up the good work! - -* Second Opinion: Ruthless Prioritization & Reality Checks -:PROPERTIES: -:DATE: 2025-10-30 -:END: - -After reviewing your answers and additional roadmap items, here's honest feedback on what matters, what doesn't, and what you should actually do. - -** Key Insights from Your Answers - -*** Company → Corfu Migration: GREEN LIGHT -You're excited, you were planning it anyway. Corfu configuration is actually *simpler* than Company, not harder. This is a quick win with immediate UX improvement. - -*Reality:* 2 hours of work, noticeable performance improvement. Do it this weekend. - -*** Network Check: DELETE IT IMMEDIATELY -You confirmed it's technical debt from when you traveled. It's adding 1+ seconds to every startup for a problem you no longer have. - -*Reality:* 15 minutes to delete ~100 lines of code. Instant gratification. Do this first. - -*** Local Package Development Workflow: NEEDS PATTERN -You maintain chime.el and org-msg locally for testing. Current approach works but isn't elegant. See implementation pattern below. - -*** Mood-line Switch: EASY WIN -You don't need doom-modeline features. Mood-line is 10x faster and simpler. - -*Reality:* 30 minutes. Do it. - -*** Large Functions to Break Up: MOSTLY FINE -Analyzed your codebase. Most functions are reasonably sized. The ones you mentioned: -- =dirvish-config.el:350-406= =cj/dired-copy-path-as-kill= (57 lines) - Could split, but works fine -- =org-roam-config.el:250-298= =cj/move-org-branch-to-roam= (49 lines) - Actually well-designed with helpers -- =org-agenda-config.el:91-105= =cj/build-org-agenda-list= (15 lines) - Small but THIS IS YOUR PERFORMANCE BOTTLENECK - -*Reality:* Your code organization is fine. Your problem is *performance observability*, not function length. - -*** Org Agenda Performance: ROOT CAUSE IDENTIFIED -This is your actual pain point. But you're flying blind without profiling infrastructure. - -*Hypothesis:* =cj/build-org-agenda-list= recursively scans entire projects directory on every agenda open with: -- No caching (even when files unchanged) -- No error handling -- Synchronous file I/O blocking UI -- Probably hundreds of file-exists-p calls - -*Reality:* You need profiling tools FIRST, then optimization. Don't guess, measure. - -** TIER 1: Do These First (High Impact, Low Effort) -*Time investment: One weekend* -*Status: START HERE* - -*** TODO [#A] Remove network check entirely :performance:critical: -:PROPERTIES: -:FILE: early-init.el -:LINES: 76-78, 104-105, 189, 206 -:EFFORT: 15 minutes -:IMPACT: Removes 1+ seconds from every startup -:END: - -Delete the following: -1. =cj/network-available= variable declarations (both) -2. =cj/internet-up-p= function and all calls -3. =cj/use-online-repos= flag logic -4. Any package.el conditionals based on network status - -Let package.el handle its own errors gracefully. Modern Emacs does this fine. - -*** TODO [#A] Fix missing cj/log-silently definition :bug:critical: -:PROPERTIES: -:FILE: modules/wrap-up.el -:LINE: 28 -:EFFORT: 5 minutes -:END: - -Either: -1. Move =cj/log-silently= from =org-agenda-config-debug.el= to =config-utilities.el= (load early) -2. Or replace with simple =(message ...)= call - -The function is only used in one place, so simple message might be sufficient. - -*** TODO [#A] Remove duplicate cj/network-available declaration :bug:critical: -:PROPERTIES: -:FILE: early-init.el -:LINES: 104-105 -:EFFORT: 2 minutes -:END: - -Delete the duplicate at lines 104-105. Keep only the first declaration at line 76. - -*Note:* This becomes moot if you delete network check entirely (recommended). - -*** TODO [#A] Fix missing cj/goto-git-gutter-diff-hunks function :bug:critical: -:PROPERTIES: -:FILE: modules/vc-config.el -:LINE: 123 -:EFFORT: 15 minutes -:END: - -Keybinding =C-; v d= references undefined function. - -Implement as: -#+begin_src elisp -(defun cj/goto-git-gutter-diff-hunks () - "Jump to git-gutter diff hunks using consult." - (interactive) - (require 'git-gutter) - (consult-line "^[+\\-]")) ;; Or use git-gutter:popup-hunk -#+end_src - -Or remove the keybinding if not needed. - -*** TODO [#B] Migrate Company to Corfu :enhancement:performance: -:PROPERTIES: -:FILE: modules/selection-framework.el -:LINES: 202-236 -:EFFORT: 2 hours -:IMPACT: 3x faster completion, simpler config -:END: - -*Why Corfu:* -- ~3x faster (child frames vs overlays) -- Better maintained -- Simpler codebase (~500 LOC vs ~3000 LOC) -- Works seamlessly with Vertico/Consult -- Better integration with Cape for backends - -*Migration:* -#+begin_src elisp -(use-package corfu - :ensure t - :demand t - :bind (:map corfu-map - ("TAB" . corfu-next) - ("S-TAB" . corfu-previous)) - :custom - (corfu-auto t) - (corfu-auto-delay 0.1) ;; Much faster than your 2s company delay! - (corfu-auto-prefix 2) - (corfu-quit-no-match 'separator) - :init - (global-corfu-mode)) - -(use-package corfu-popupinfo - :after corfu - :hook (corfu-mode . corfu-popupinfo-mode) - :custom - (corfu-popupinfo-delay '(0.5 . 0.2))) - -(use-package cape - :ensure t - :after corfu - :init - (add-to-list 'completion-at-point-functions #'cape-dabbrev) - (add-to-list 'completion-at-point-functions #'cape-file) - (add-to-list 'completion-at-point-functions #'cape-keyword)) -#+end_src - -Remove entire company configuration. Keep mu4e completion settings (Corfu respects them). - -*Configuration is actually simpler than Company.* Just do it. - -*** DONE [#B] Switch doom-modeline to mood-line :enhancement:performance: -CLOSED: [2025-11-03 Sun] -:PROPERTIES: -:FILE: modules/modeline-config.el -:EFFORT: 30 minutes -:IMPACT: 10x faster modeline updates -:END: - -✅ **Completed: Custom modeline implementation** - -Built a custom modeline with advanced features inspired by Prot's design: -- Named segment architecture (easy reordering) -- Color-coded buffer status -- VC state faces (green=clean, yellow=edited, red=conflict) -- Git branch symbol (U+E0A0) -- Interactive mouse handlers -- String truncation for narrow windows -- Active-window-only segments -- Help-echo tooltips -- Position format: L:# C:# - -See commit c0c4b17 for full implementation. - -*** TODO [#C] Fix lorem markov chain error :bug:minor: -:PROPERTIES: -:FILE: modules/lorem-config.el (or wherever lorem is configured) -:EFFORT: 5 minutes -:END: - -The error message is probably harmless initialization noise. Either: -1. Wrap lorem initialization in =(with-demoted-errors ...)= -2. Configure lorem to suppress the warning -3. Disable lorem if you don't use it - -Check your config for lorem setup and add error suppression. - -** TIER 2: Build Observability Infrastructure (HIGHEST VALUE) -*Time investment: One week* -*Status: CRITICAL DEPENDENCY FOR EVERYTHING ELSE* - -*** TODO [#A] Create debug-profiling.el module :infrastructure:performance: -:PROPERTIES: -:FILE: modules/debug-profiling.el (new file) -:EFFORT: 3-4 hours -:IMPACT: Unlocks all performance optimization work -:END: - -This is your #1 blocker for fixing org-agenda and other performance issues. - -Create comprehensive profiling infrastructure: - -#+begin_src elisp -;;; debug-profiling.el --- Profiling and performance debugging tools -*- lexical-binding: t; -*- - -;;; Commentary: -;; Unified profiling infrastructure for measuring and optimizing performance. -;; Provides helpers for: -;; - Function timing (individual and batch) -;; - Memory profiling -;; - Startup profiling -;; - Interactive profiling sessions -;; -;; Usage: -;; M-x cj/profile-function RET cj/build-org-agenda-list RET -;; M-x cj/profile-startup -;; (cj/time-it "agenda rebuild" (cj/build-org-agenda-list)) - -;;; Code: - -(require 'profiler) -(require 'benchmark) - -(defvar cj/profiling-log-buffer "*CJ Profile Log*" - "Buffer name for profiling results.") - -(defvar cj/profiling-enabled nil - "When non-nil, enable automatic timing of instrumented functions.") - -(defun cj/time-it (description &rest body) - "Execute BODY and log timing with DESCRIPTION." - (let* ((start-time (current-time)) - (result (car body)) - (elapsed (float-time (time-subtract (current-time) start-time)))) - (with-current-buffer (get-buffer-create cj/profiling-log-buffer) - (goto-char (point-max)) - (insert (format "[%s] %s: %.3f sec\n" - (format-time-string "%H:%M:%S") - description - elapsed))) - (message "%s: %.3f sec" description elapsed) - result)) - -(defmacro cj/measure (&rest body) - "Measure execution time of BODY, return result and timing." - `(let ((start-time (current-time)) - (result (progn ,@body)) - (end-time (current-time))) - (cons result (float-time (time-subtract end-time start-time))))) - -(defun cj/profile-function (function &optional runs) - "Profile FUNCTION by calling it RUNS times (default 1). -Shows results in dedicated buffer with call tree." - (interactive - (list (intern (completing-read "Function to profile: " obarray 'fboundp t)) - (when current-prefix-arg - (read-number "Number of runs: " 10)))) - (setq runs (or runs 1)) - (profiler-start 'cpu) - (dotimes (_ runs) - (funcall function)) - (profiler-stop) - (profiler-report) - (message "Profiled %s (%d run%s)" function runs (if (> runs 1) "s" ""))) - -(defun cj/profile-startup () - "Profile Emacs startup by restarting with profiler enabled. -Results are saved to profile.el in user-emacs-directory." - (interactive) - (let ((profile-file (expand-file-name "startup-profile.el" user-emacs-directory))) - (profiler-start 'cpu+mem) - (message "Restart Emacs to profile startup. Results will be in %s" profile-file) - (add-hook 'emacs-startup-hook - (lambda () - (profiler-report) - (profiler-report-write-profile profile-file) - (profiler-stop) - (message "Startup profiling complete. See %s" profile-file))))) - -(defun cj/benchmark-function (function &optional iterations) - "Benchmark FUNCTION over ITERATIONS (default 100). -Shows average, min, max, and total time." - (interactive - (list (intern (completing-read "Function to benchmark: " obarray 'fboundp t)) - (read-number "Iterations: " 100))) - (setq iterations (or iterations 100)) - (let ((times '())) - (dotimes (_ iterations) - (let* ((result (cj/measure (funcall function))) - (elapsed (cdr result))) - (push elapsed times))) - (let* ((total (apply #'+ times)) - (avg (/ total iterations)) - (min-time (apply #'min times)) - (max-time (apply #'max times))) - (message "%s (%d iterations): avg=%.4fs min=%.4fs max=%.4fs total=%.2fs" - function iterations avg min-time max-time total)))) - -(defun cj/instrument-package (package) - "Instrument all functions in PACKAGE for profiling with elp." - (interactive - (list (intern (completing-read "Package to instrument: " obarray)))) - (elp-instrument-package (symbol-name package)) - (message "Instrumented package: %s. Run M-x elp-results to see data." package)) - -(defun cj/profile-agenda-rebuild () - "Profile org-agenda rebuild with detailed timing." - (interactive) - (require 'org-agenda) - (cj/time-it "Full agenda rebuild" (cj/build-org-agenda-list))) - -(defun cj/show-profile-log () - "Display the profiling log buffer." - (interactive) - (display-buffer (get-buffer-create cj/profiling-log-buffer))) - -(defun cj/clear-profile-log () - "Clear the profiling log buffer." - (interactive) - (with-current-buffer (get-buffer-create cj/profiling-log-buffer) - (erase-buffer))) - -;; Keybindings (add to your keybindings.el) -;; (global-set-key (kbd "C-; p f") #'cj/profile-function) -;; (global-set-key (kbd "C-; p b") #'cj/benchmark-function) -;; (global-set-key (kbd "C-; p l") #'cj/show-profile-log) -;; (global-set-key (kbd "C-; p a") #'cj/profile-agenda-rebuild) - -(provide 'debug-profiling) -;;; debug-profiling.el ends here -#+end_src - -Load this early in init.el: -#+begin_src elisp -(when (or (eq cj/debug-modules t) - (memq 'profiling cj/debug-modules)) - (require 'debug-profiling (expand-file-name "modules/debug-profiling" user-emacs-directory))) -#+end_src - -*** TODO [#A] Profile org-agenda-rebuild to find bottleneck :performance:critical: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:EFFORT: 1 hour -:DEPENDS: debug-profiling.el -:END: - -Once profiling infrastructure exists: - -1. Run: =M-x cj/profile-function RET cj/build-org-agenda-list RET= -2. Run: =M-x cj/benchmark-function RET cj/build-org-agenda-list RET 10= -3. Instrument org-agenda package: =M-x cj/instrument-package RET org-agenda RET= -4. Review results with =M-x elp-results= - -*Hypothesis to test:* -- Is time spent in file I/O (directory-files-recursively)? -- Is it parsing org files? -- Is it just too many files? -- Is there a slow helper function being called repeatedly? - -*Expected findings:* -- Probably 80%+ time in =cj/add-files-to-org-agenda-files-list= -- Likely no caching, runs every time -- Possible N+1 file access pattern - -*Next steps after profiling:* -- Add file-notify watchers to invalidate cache only when projects-dir changes -- Cache the file list -- Run rebuild async on startup - -*** TODO [#B] Add instrumentation to cj/build-org-agenda-list :performance:observability: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:EFFORT: 30 minutes -:DEPENDS: debug-profiling.el, profiling results -:END: - -After profiling identifies bottleneck, add instrumentation: - -#+begin_src elisp -(defun cj/build-org-agenda-list () - "Rebuilds the org agenda list with instrumentation." - (interactive) - (let ((start-time (current-time))) - ;; reset org-agenda-files - (setq org-agenda-files (list inbox-file schedule-file gcal-file)) - - ;; Time the expensive operation - (let ((scan-start (current-time))) - (cj/add-files-to-org-agenda-files-list projects-dir) - (message "Scanned projects-dir in %.3f sec" - (float-time (time-subtract (current-time) scan-start)))) - - (let ((total-time (float-time (time-subtract (current-time) start-time)))) - (message "Rebuilt org-agenda-files in %.3f sec (%d files)" - total-time - (length org-agenda-files))))) -#+end_src - -This gives you observability on every agenda rebuild. - -*** TODO [#B] Add caching to org-agenda rebuild :performance:critical: -:PROPERTIES: -:FILE: modules/org-agenda-config.el -:EFFORT: 2 hours -:DEPENDS: Profiling results -:END: - -*Only do this after profiling confirms file scanning is the bottleneck.* - -Implement caching with file-notify: - -#+begin_src elisp -(defvar cj/org-agenda-cache nil - "Cached list of org-agenda files.") - -(defvar cj/org-agenda-cache-dirty t - "When non-nil, agenda cache needs rebuilding.") - -(defun cj/invalidate-agenda-cache (&rest _) - "Mark agenda cache as dirty." - (setq cj/org-agenda-cache-dirty t)) - -(defun cj/build-org-agenda-list-cached () - "Build org-agenda list with caching." - (interactive) - (when (or (not cj/org-agenda-cache) - cj/org-agenda-cache-dirty) - (let ((start-time (current-time))) - (setq cj/org-agenda-cache (list inbox-file schedule-file gcal-file)) - (cj/add-files-to-org-agenda-files-list projects-dir) - (setq cj/org-agenda-cache org-agenda-files) - (setq cj/org-agenda-cache-dirty nil) - (message "Rebuilt agenda cache in %.3f sec (%d files)" - (float-time (time-subtract (current-time) start-time)) - (length cj/org-agenda-cache)))) - (setq org-agenda-files cj/org-agenda-cache)) - -;; Set up file watching -(require 'filenotify) -(file-notify-add-watch projects-dir - '(change) - #'cj/invalidate-agenda-cache) -#+end_src - -*** TODO [#B] Test org-agenda filtering functions :testing:critical: -:PROPERTIES: -:FILE: tests/org-agenda-config-test.el (new file) -:EFFORT: 2-3 hours -:END: - -These functions are date-sensitive and break silently. High-value tests: - -#+begin_src elisp -;;; org-agenda-config-test.el --- Tests for org-agenda-config -*- lexical-binding: t; -*- - -(require 'ert) -(require 'org-agenda-config) - -(ert-deftest test-cj/org-skip-subtree-if-habit () - "Test habit filtering." - (with-temp-buffer - (org-mode) - (insert "* TODO Test\n:PROPERTIES:\n:STYLE: habit\n:END:\n") - (goto-char (point-min)) - (should (cj/org-skip-subtree-if-habit)))) - -(ert-deftest test-cj/org-skip-subtree-if-not-overdue () - "Test overdue task detection." - (with-temp-buffer - (org-mode) - ;; Task scheduled yesterday (overdue) - (insert "* TODO Overdue\nSCHEDULED: <2025-10-29>\n") - (goto-char (point-min)) - (should-not (cj/org-skip-subtree-if-not-overdue)) - - ;; Task scheduled tomorrow (not overdue) - (erase-buffer) - (insert "* TODO Future\nSCHEDULED: <2025-10-31>\n") - (goto-char (point-min)) - (should (cj/org-skip-subtree-if-not-overdue)))) -#+end_src - -** TIER 3: Quick Wins (After Tier 1 & 2) -*Time investment: 1-2 hours each* -*Status: Only tackle after profiling infrastructure is done* - -*** TODO [#B] Implement org-to-reveal.js workflow :enhancement:usability: -:PROPERTIES: -:FILE: modules/org-config.el or new modules/org-present-config.el -:EFFORT: 2 hours -:IMPACT: Solves specific, concrete need -:END: - -You said: "I know it can be done. Export to reveal.js, start httpd, open browser." - -You're right, it's straightforward: - -#+begin_src elisp -(use-package ox-reveal - :ensure t - :after org - :custom - (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) - -(use-package simple-httpd - :ensure t - :custom - (httpd-port 8080) - (httpd-root "~/presentations")) - -(defun cj/org-present-reveal () - "Export current org file to reveal.js and present via httpd." - (interactive) - (unless (eq major-mode 'org-mode) - (user-error "Not in an org-mode buffer")) - - (let* ((httpd-dir (expand-file-name httpd-root)) - (output-file (expand-file-name - (concat (file-name-base) ".html") - httpd-dir))) - - ;; Ensure output directory exists - (make-directory httpd-dir t) - - ;; Export to reveal.js HTML - (org-reveal-export-to-html) - - ;; Move to httpd directory - (let ((exported-file (concat (file-name-base) ".html"))) - (rename-file exported-file output-file t)) - - ;; Start httpd if not running - (unless (process-status "httpd") - (httpd-start) - (message "Started httpd server on port %d" httpd-port)) - - ;; Open in browser (fullscreen) - (let ((url (format "http://localhost:%d/%s" - httpd-port - (file-name-nondirectory output-file)))) - (browse-url url) - (message "Presenting: %s" url)))) - -(with-eval-after-load 'org - (define-key org-mode-map (kbd "C-c C-v p") #'cj/org-present-reveal)) -#+end_src - -*That's it.* 30 lines. One keybinding. Done. - -Test with a simple org file: -#+begin_src org -,#+TITLE: Test Presentation -,#+REVEAL_THEME: moon - -,* Slide 1 -Content here - -,* Slide 2 -More content -#+end_src - -Press =C-c C-v p= and you're presenting. - -*** TODO [#B] Set up difftastic integration :enhancement:usability: -:PROPERTIES: -:FILE: modules/vc-config.el or modules/magit-config.el -:EFFORT: 30 minutes -:END: - -Install difftastic: =sudo pacman -S difftastic= or =cargo install difftastic= - -Then add to magit: - -#+begin_src elisp -(use-package magit-difftastic - :ensure t - :after magit - :bind (:map magit-status-mode-map - ("C-c d" . magit-difftastic-show)) - :custom - (magit-difftastic-executable "difft")) -#+end_src - -Or for native integration: -#+begin_src elisp -(setq magit-diff-refine-hunk 'all) ;; Already have this probably - -(defun cj/magit-difftastic-diff (args files) - "Show diff using difftastic." - (interactive (magit-diff-arguments)) - (require 'magit) - (let ((command (concat "difft " (mapconcat #'identity args " ")))) - (magit-git-command command))) - -(transient-append-suffix 'magit-diff '(-1 -1) - '("D" "Difftastic diff" cj/magit-difftastic-diff)) -#+end_src - -Simple and useful. Just do it. - -*** TODO [#C] Implement local package development workflow :enhancement:architecture: -:PROPERTIES: -:FILES: modules/org-agenda-config.el, modules/mail-config.el -:EFFORT: 1 hour -:END: - -You maintain chime.el and org-msg. You need to switch between local dev and stable versions easily. - -*Clean pattern:* - -In =user-constants.el=: -#+begin_src elisp -(defcustom cj/development-packages '() - "List of packages to load from local development paths. -When a package symbol is in this list, load from ~/code/<package>. -Otherwise use stable version via vc-install or MELPA. - -Example: (setq cj/development-packages '(chime org-msg))" - :type '(repeat symbol) - :group 'cj) - -(defun cj/use-local-package-p (package) - "Return non-nil if PACKAGE should be loaded from local path." - (memq package cj/development-packages)) -#+end_src - -In your package configs: -#+begin_src elisp -;; Chime configuration -(if (cj/use-local-package-p 'chime) - ;; Development mode: use local path - (use-package chime - :load-path "~/code/chime.el" - :demand t - ;; ... rest of config ... - ) - ;; Production mode: use stable version - (use-package chime - :vc (:url "https://github.com/yourusername/chime.el") - :demand t - ;; ... rest of config ... - )) -#+end_src - -To switch modes, just toggle the symbol in =cj/development-packages= and restart Emacs. - -*Benefits:* -- Clear intent: variable name says what it does -- Easy to toggle: add/remove symbols from list -- Version controlled: your config documents which packages you're actively developing -- No path duplication: define path logic once - -** TIER 4: Maybe/Someday (Proceed with Caution) -*Time investment: Unknown, possibly infinite* -*Status: Only do if you have concrete use case* - -*** HOLD [#D] Investigate code-maat/code-compass in Emacs :enhancement:tooling: -:PROPERTIES: -:REASON: Complexity vs value unclear -:END: - -*Hard truth:* This is a big project. Code-maat is a mature Java tool with significant functionality. - -*Questions before investing time:* -1. What would Emacs integration give you that CLI doesn't? -2. How often do you actually use code-maat? -3. Is this solving a real problem or is it intellectually interesting? - -*Recommendation:* Use the CLI tools. Call them from Emacs if needed: -#+begin_src elisp -(defun cj/code-maat-analyze (git-log-file) - "Run code-maat analysis on GIT-LOG-FILE." - (interactive "fGit log file: ") - (async-shell-command - (format "code-maat -l %s -c git2 -a coupling" git-log-file) - "*Code Maat Results*")) -#+end_src - -Don't reimplement it in elisp unless you have a very specific need the CLI can't meet. - -*** HOLD [#D] Fix LaTeX configuration :documentation:enhancement: -:PROPERTIES: -:REASON: No concrete use case yet -:END: - -You said: "Right now, it's a bunch of cut/paste snippets from everyone else's config and it doesn't work." - -*Hard truth:* LaTeX config is complex because LaTeX is complex. Don't invest time until you have a specific document you need to write. - -*When you do need it:* -1. Start with one concrete document (paper? letter? resume?) -2. Get that one document working -3. Extract the config that made it work -4. Then generalize - -*Don't* try to build a complete LaTeX environment speculatively. You'll waste time on edge cases you'll never hit. - -*Minimal starting point when you need it:* -#+begin_src elisp -(use-package auctex - :ensure t - :defer t - :mode ("\\.tex\\'" . LaTeX-mode) - :custom - (TeX-auto-save t) - (TeX-parse-self t) - (TeX-master nil)) -#+end_src - -That's it. Start there. Add more when you hit actual problems. - -*** HOLD [#D] Improve Elfeed dashboard UX :enhancement:ui: -:PROPERTIES: -:REASON: Need to clarify actual usage patterns -:END: - -You said: "it doesn't bring any joy to see a mostly blank screen when I check Elfeed." - -*Questions:* -1. Do you actually use RSS regularly, or is it aspirational? -2. If blank screen = no new items, is that a problem? -3. Would you use RSS more if the UX was better, or is RSS not part of your workflow? - -*Options:* -1. *If you don't use RSS:* Remove Elfeed. One less thing to maintain. -2. *If you want to use RSS more:* Start by curating better feeds, not fixing UI -3. *If blank screen bothers you:* Add a "no new items" message or show last N items regardless of read status - -Don't fix tooling for a workflow you're not sure you want. - -*** HOLD [#D] DWIM shell + eshell integration :enhancement:shell: -:PROPERTIES: -:REASON: Current solution works fine -:END: - -You have 815 lines of dwim-shell-config. It works. Leave it alone. - -*Hard truth:* This is a working system. "I wonder if I can leverage them better" is not a problem statement. - -Unless you have a specific workflow that's currently painful, don't touch this. - -*** HOLD [#D] Calibredb performance optimization :performance: -:PROPERTIES: -:REASON: Root cause is data volume, not code -:END: - -You correctly identified: "The right thing to do is to shrink the ebook inventory to < 1k or so books." - -*You already know the answer.* Don't optimize code when the problem is 14k+ files. - -Curate your library. Delete what you won't read. This is a data problem, not a code problem. - -*** HOLD [#D] Flyspell/abbrev UX improvements :enhancement:usability: -:PROPERTIES: -:REASON: Vague complaint, no specific pain points -:END: - -You said: "I'm not sure the UX is right. It could use some more attention." - -*This is not actionable.* Come back when you can say: -- "Pressing X does Y but I want Z" -- "It's too slow when..." -- "It conflicts with..." - -Until then, it's bikeshedding. - -*** HOLD [#D] Video/audio config sink testing :testing:enhancement: -:PROPERTIES: -:REASON: Nice to have, not blocking anything -:END: - -You said: "could use some testing around which sinks to attach to" - -*Questions:* -1. Does it currently work for your use case? -2. Do you switch audio sinks often? -3. Is this actually painful or just "would be nice"? - -If it works, leave it. If it doesn't work, describe the failure mode first. - -*** HOLD [#D] Build jumper package :project:packaging: -:PROPERTIES: -:REASON: Another package to maintain, unclear value -:END: - -You said: "I believe jumper should probably be my next package, but I haven't had the time." - -*Hard questions:* -1. What problem does jumper solve? -2. Do existing navigation packages (avy, ace-window, consult-line) not solve it? -3. Are you building this for yourself or for others? -4. How will you maintain it alongside chime and org-msg? - -If you're excited about it: Build it. But recognize it's a significant time investment. - -If you're not excited: Don't. You already maintain two packages. - -*** HOLD [#D] Extract local-repository creation logic :refactor:library: -:PROPERTIES: -:REASON: Unclear benefit -:END: - -You said: "extract the code... so I can unit test the hell out of it and use it as a library" - -*Questions:* -1. What would you use the library for beyond local-repository? -2. Is local-repository not working currently? -3. Are you planning to build other tools that need this? - -Don't extract libraries speculatively. Extract when you have two concrete use cases. - -*** HOLD [#D] Org-contacts + chime.el birthdays integration :enhancement:integration: -:PROPERTIES: -:REASON: Should be fixed in chime, not config -:END: - -You said: "I should either figure out sexp expressions and support them in chime, or disable them" - -*This is a chime.el issue, not a config issue.* - -File it as a chime.el feature request or bug. Your config shouldn't work around package limitations. - -** What NOT To Do - -*** DON'T: Optimize Before Profiling -You can't fix performance issues you can't measure. Build profiling infrastructure FIRST. - -*** DON'T: Build Speculative Libraries -Extract code into libraries when you have 2+ concrete use cases. Not before. - -*** DON'T: Fix Vague Complaints -"UX could be better" is not actionable. Come back with specific pain points. - -*** DON'T: Reimplement Mature CLI Tools -code-maat is Java. It works. Don't rewrite it in elisp. - -*** DON'T: Configure for Hypothetical Use Cases -LaTeX doesn't work? Come back when you need to write a specific LaTeX document. - -*** DON'T: Maintain More Packages Than You Can Support -You have chime and org-msg. That's plenty. Think hard before adding jumper. - -** Large Functions Analysis - -You asked me to identify large, complex functions that need breaking up. - -*Reality check:* Your functions are mostly fine. Here's what I found: - -*** Functions You Mentioned - -*1. dirvish-config.el:350-406 =cj/dired-copy-path-as-kill= (57 lines)* -- Has 6 responsibilities: validation, project detection, path formatting, org-link formatting, clipboard, feedback -- *Status:* Works fine, moderately complex -- *Recommendation:* Could extract helpers (=cj/--determine-path-type=, =cj/--format-path-for-display=), but not urgent -- *Priority:* [#C] - Refactor for testability when you have time - -*2. org-roam-config.el:250-298 =cj/move-org-branch-to-roam= (49 lines)* -- Already well-decomposed with helper functions! -- Uses =cj/org-link-get-description=, =cj/--generate-roam-slug=, =cj/--demote-org-subtree=, =cj/--format-roam-node= -- *Status:* Good design, clear logic flow -- *Recommendation:* Add integration test, but don't refactor further -- *Priority:* [#B] - Add tests, keep implementation - -*3. org-agenda-config.el:91-105 =cj/build-org-agenda-list= (15 lines)* -- Small function, but THIS IS YOUR PERFORMANCE BOTTLENECK -- Not because of size, but because it recursively scans directories without caching -- *Status:* CRITICAL ISSUE -- *Recommendation:* Profile it, add caching, add error handling -- *Priority:* [#A] - Profile and optimize ASAP - -*** Other Large Files - -*dwim-shell-config.el (815 lines)* -- Collection of DWIM commands -- *Status:* Works fine -- *Recommendation:* Leave it alone unless something breaks - -*custom-comments.el (636 lines)* -- Well-tested utility functions -- *Status:* No issues -- *Recommendation:* Keep as is - -*music-config.el (484 lines)* -- EMMS configuration -- *Recommendation:* Review when you mentioned video/audio sink testing - -*** The Real Issue - -Your problem isn't function length. Your problem is *performance observability*. - -You need: -1. Profiling infrastructure (debug-profiling.el) -2. Instrumentation in critical paths -3. Caching where appropriate -4. Error handling in file operations - -Focus on TIER 2 (observability infrastructure) before worrying about function decomposition. - -** Concrete Action Plan: This Weekend - -*** Saturday Morning (2-3 hours) - -1. *Delete network check* (15 min) - - Remove =cj/internet-up-p=, =cj/network-available=, all related code - - Test startup, should be 1+ seconds faster - -2. *Fix Priority A bugs* (45 min) - - Fix duplicate =cj/network-available= (if not deleted above) - - Implement =cj/goto-git-gutter-diff-hunks= or remove binding - - Fix =cj/log-silently= (move to config-utilities or use message) - -3. *Fix lorem error* (5 min) - - Find lorem config, suppress error message - -4. *Test everything still works* (15 min) - -*** Saturday Afternoon (2-3 hours) - -5. *Corfu migration* (2 hours) - - Remove company configuration - - Add corfu + corfu-popupinfo + cape - - Test completion in elisp, org, mu4e - - Verify mu4e still excludes completion - -6. *Switch to mood-line* (30 min) - - Remove doom-modeline - - Add mood-line configuration - - Restart, verify modeline works - -*** Sunday Morning (3-4 hours) - -7. *Create debug-profiling.el* (3 hours) - - Use the template I provided above - - Add to modules/ - - Configure in user-constants.el with =cj/debug-modules= - - Test basic commands work - -8. *Profile org-agenda* (30 min) - - Run =M-x cj/profile-agenda-rebuild= - - Run =M-x cj/profile-function RET cj/build-org-agenda-list= - - Document findings in *CJ Profile Log* - -9. *Write up findings* (30 min) - - What's slow? - - Why is it slow? - - What's the fix? - -*** Sunday Afternoon (Optional, 1-2 hours) - -10. *Reveal.js workflow* (2 hours) - - Implement =cj/org-present-reveal= - - Test with simple presentation - - Bind to key - -11. *Difftastic* (30 min) - - Install difftastic - - Configure magit integration - - Test on a real diff - -** Expected Outcomes - -After this weekend: -- ✅ Startup 1+ seconds faster (network check removed) -- ✅ No more broken keybindings or missing functions -- ✅ Completion is faster and more responsive (Corfu) -- ✅ Modeline updates are faster (mood-line) -- ✅ You have profiling infrastructure for all future optimization -- ✅ You understand why org-agenda is slow -- ✅ You have concrete data to guide optimization -- ✅ (Optional) You can present org files as reveal.js slides - -*** After This Weekend - -*Then stop and reassess.* - -- How do you feel about the changes? -- What's still bothering you? -- Is org-agenda faster after caching? (if you implement it) -- What's the next pain point? - -Don't commit to more work until you've lived with these changes for a week. - -** My Question for You - -Looking at your list of improvements, I see someone who: -- Likes building tools (jumper, code-maat integration, etc.) -- Gets excited by new technologies (difftastic, MCP, reveal.js) -- Has many "wouldn't it be cool if..." ideas - -But I also see someone who: -- Already has a working, sophisticated config -- Knows what's actually painful (org-agenda performance, mail attachments) -- Has limited time (maintaining chime, org-msg) - -*So here's my question:* - -*Are you building tools because they solve real problems, or because you enjoy building?* - -Both are valid answers! But the answer changes what you should prioritize: - -*If solving real problems:* -- Focus on TIER 1 & 2 (bugs, performance, observability) -- Ignore most of TIER 4 -- Build reveal.js workflow (concrete need) -- Fix mail attachments (daily pain) - -*If you enjoy building:* -- Pick ONE project (jumper? code-compass? profiling infrastructure?) -- Go deep on it -- Accept that other stuff will wait -- Be honest that it's for learning/fun, not necessity - -*My read:* You're mostly problem-solving, but you get distracted by shiny tools. - -*My advice:* Fix the pain points first (TIER 1 & 2). Then build one fun project. Then reassess. - -** gptel vs MCP: Specific Guidance - -You asked: "I need to either build a bunch of tools or install an MCP server. Which way to go?" - -*Answer: Build gptel functions. Here's why:* - -*** Why gptel + elisp Functions - -*Advantages:* -1. *Simpler:* Just elisp functions, no external server -2. *Integrated:* Native Emacs, shares your environment -3. *Debuggable:* Use edebug, step through code -4. *Maintainable:* You understand elisp, you wrote the config -5. *Flexible:* Can use any elisp API (org, dired, magit, etc.) -6. *Portable:* Works on any system with Emacs + gptel - -*What you can build:* -#+begin_src elisp -;; Example gptel tools -(defun cj/gptel-add-project-context () - "Add project README and key files to gptel context." - (interactive) - (let ((readme (expand-file-name "README.md" (project-root)))) - (when (file-exists-p readme) - (cj/gptel-add-file readme)))) - -(defun cj/gptel-analyze-function-at-point () - "Send function at point to LLM for analysis." - (interactive) - (let ((func (elisp--fnsym-in-current-sexp))) - (gptel-send (format "Analyze this function: %s" (symbol-function func))))) - -(defun cj/gptel-git-diff-review () - "Send current git diff to LLM for review." - (interactive) - (let ((diff (shell-command-to-string "git diff HEAD"))) - (gptel-send (concat "Review this diff:\n\n" diff)))) -#+end_src - -*This is much simpler than MCP and covers 90% of use cases.* - -*** When MCP Makes Sense - -*Only use MCP if:* -1. You want to share tools across multiple applications (VS Code + Emacs + CLI) -2. You need sandboxed execution for security -3. You're building reusable infrastructure for others -4. You want to use existing MCP servers someone else maintains - -*For Emacs-only workflows: Use gptel + elisp.* - -*** Recommended Approach - -1. Start with gptel + simple elisp functions -2. Build 3-5 useful commands (add-file, analyze-function, git-diff, org-context) -3. Use them for a month -4. If you find yourself wanting features MCP provides, reassess - -*Don't build MCP infrastructure speculatively.* Build it when gptel + elisp isn't enough. - -** Final Thoughts: Adjusted Priorities - -Your original document was thorough and well-analyzed. My additions: - -1. *Ruthless prioritization* - 4 tiers instead of flat list -2. *Observability first* - Can't optimize what you can't measure -3. *Reality checks* - Some "nice to haves" are rabbit holes -4. *Concrete implementations* - Not just "do this" but "here's the code" -5. *Hard questions* - Before building, ask "why?" and "for whom?" - -*Your config is good.* The issues are normal for a mature system. - -*Focus on:* -1. Fix the bugs (TIER 1) -2. Build profiling infrastructure (TIER 2) -3. Pick ONE fun project (reveal.js or difftastic) -4. Ignore everything else for now - -*Then live with it for a month* and see what still bothers you. - -You're thoughtful, skilled, and self-aware. You'll make good decisions. Just don't try to do everything at once. - -Good luck! 🚀 |
