diff options
| -rw-r--r-- | docs/SESSION-HANDOFF-ACTIVE-PROJECT.org | 263 | ||||
| -rw-r--r-- | docs/emacs-config-v2mom.org | 299 | ||||
| -rw-r--r-- | docs/values-comparison.org | 107 | ||||
| -rw-r--r-- | issues.org | 1879 | ||||
| -rw-r--r-- | todo.org | 4950 |
5 files changed, 3003 insertions, 4495 deletions
diff --git a/docs/SESSION-HANDOFF-ACTIVE-PROJECT.org b/docs/SESSION-HANDOFF-ACTIVE-PROJECT.org new file mode 100644 index 00000000..379b11a8 --- /dev/null +++ b/docs/SESSION-HANDOFF-ACTIVE-PROJECT.org @@ -0,0 +1,263 @@ +#+TITLE: 🚨 ACTIVE PROJECT - READ THIS FIRST 🚨 +#+AUTHOR: Claude Code Session Notes +#+DATE: 2025-10-30 + +* CURRENT PROJECT STATUS + +** 🎯 What We're Doing +Working through a systematic approach to clean up and prioritize Craig's Emacs config work: + +1. ✅ *COMPLETE V2MOM* (Vision, Values, Methods, Obstacles, Metrics) - IN PROGRESS +2. ⏳ *TRIAGE todo.org* - Use V2MOM to ruthlessly cancel ~60% of tasks +3. ⏳ *EXECUTE TIER 1* - Ship quick wins (network check removal, Corfu, bug fixes) +4. ⏳ *BUILD OBSERVABILITY* - Create profiling infrastructure (TIER 2) +5. ⏳ *SYSTEMATIC EXECUTION* - Work through prioritized tasks one by one + +** 📍 Where We Are Right Now +*Session Started:* 2025-10-30 +*Current Step:* V2MOM Methods section (60% complete - Vision + Values done) +*Time Committed:* ~1 hour sessions, working systematically +*Status:* PAUSED between sessions - resuming later this evening + +** 📄 Key Documents + +*** Primary Working Documents +- *V2MOM:* [[file:emacs-config-v2mom.org][emacs-config-v2mom.org]] - Strategic framework (ACTIVELY EDITING) +- *Issues Analysis:* [[file:../issues.org][../issues.org]] - Claude's detailed analysis with TIER system and implementations +- *Current Todos:* [[file:../todo.org][../todo.org]] - Craig's existing task list (~50+ items, needs triage) + +*** Reference Documents +- *Config Root:* [[file:../init.el][../init.el]] +- *Modules:* [[file:../modules/][../modules/]] +- *Tests:* [[file:../tests/][../tests/]] + +** 🔑 Key Insights About Craig's Work Patterns + +*** Strengths +- Thoughtful and strategic thinker +- Good research skills (thorough specs, complete code examples) +- Does ship things (dashboard, dirvish, network check fixes) +- Recognizes need for V2MOM framework +- Uses config daily for real work + +*** Patterns to Address +1. *Research > Execution* - Has complete code for Corfu, difftastic, transcription workflow... still TODO +2. *Priority Inflation* - Too many [#A]/[#B] items, unclear what's actually urgent +3. *Incomplete Strategy* - V2MOM structure exists but sections are empty +4. *Hard to Say No* - [#C]/[#D] items should be CANCELLED but remain in list +5. *Side Projects Compete* - Dupre theme work competes with core config maintenance + +*** What Craig Told Us About Himself +> "I am building tools both because they solve problems, but also because I enjoy building." + +This is healthy! But need balance: +- Fix rough edges FIRST (daily pain points) +- Build fun stuff SECOND (after maintenance) +- Cancel distractions ALWAYS (Signal client, minimap, etc.) + +** 🎯 Agreed Goals for This Project + +*** Immediate (Next 2-3 Sessions) +1. ✅ Complete V2MOM (IN PROGRESS) +2. ⏳ Triage todo.org using V2MOM as filter +3. ⏳ Execute quick wins: network check, Corfu migration, bug fixes +4. ⏳ Build debug-profiling.el infrastructure + +*** Short Term (Next Month) +5. Profile and optimize org-agenda performance +6. Ship reveal.js presentation workflow +7. Establish weekly triage ritual + +*** Long Term (Ongoing) +8. Ship more than research +9. Maintain < 20 active todos +10. Measure metrics from V2MOM + +** 📋 TIER System from issues.org + +*** TIER 1: Do These First (High Impact, Low Effort) - 1 weekend +- Remove network check (15 min) +- Fix missing functions (30 min) +- Corfu migration (2 hours) +- Mood-line switch (30 min) +- Bug fixes (1 hour) + +*** TIER 2: Build Observability (HIGHEST VALUE) - 1 week +- Create debug-profiling.el module (3-4 hours) +- Profile org-agenda-rebuild (1 hour) +- Add instrumentation and caching (2 hours) +- Test org-agenda filtering functions (2-3 hours) + +*** TIER 3: Quick Wins (After Profiling) - 1-2 hours each +- Reveal.js presentation workflow (2 hours) +- Difftastic integration (30 min) +- Local package development workflow (1 hour) + +*** TIER 4: Maybe/Someday (Probably Never) +- Code-maat reimplementation (HOLD) +- LaTeX config (HOLD until concrete need) +- Elfeed dashboard (HOLD - unclear if actually used) +- DWIM shell integration (HOLD - current solution works) +- Jumper package (HOLD - already maintaining chime + org-msg) + +** 🚫 Items That Should Be CANCELLED + +From todo.org, these don't serve the vision: +- [#D] Signal Client - Not in vision +- [#D] Awesome-tray / mode-icons - Already have modeline +- [#C] Minimap - Interesting, not important +- [#C] Install Magit TODOs - Already works fine +- [#C] Git Timemachine litters buffers - Minor annoyance +- Many Dupre theme TODOs - Side project competing with maintenance + +## 💡 Key Recommendations for Craig + +### Week 1: Strategy + Quick Wins +1. Complete V2MOM (2-3 hours) +2. Triage todo.org using V2MOM (1-2 hours) +3. Execute items you already have code for (2-3 hours) + +### Week 2: Observability Infrastructure +4. Build debug-profiling.el (3-4 hours) +5. Profile org-agenda (1 hour) + +### Week 3: Fix Performance + Ship Presentation +6. Fix org-agenda based on profiling (2-3 hours) +7. Ship reveal.js workflow (2 hours) + +### Ongoing: Maintenance Discipline +- Weekly triage ritual (30 min every Sunday) +- Measure metrics (startup time, agenda time, todo count) +- Ship > Research + +** 🔄 Next Session Pickup Points + +When starting next session, Claude should: + +1. **Read this document first** to understand context +2. **Check V2MOM status** - If incomplete, continue there +3. **Reference issues.org** for detailed technical recommendations +4. **Reference todo.org** for items to triage +5. **Ask Craig:** "Where did we leave off? V2MOM? Triage? Execution?" + +** 📞 Questions to Ask Craig Next Session + +*IMMEDIATE (when resuming):* +- "Ready to continue V2MOM? We left off at Methods section." +- "How much time do you have?" + +*FOR METHODS SECTION:* +Show Craig the draft list and ask: +- "Which methods do you already do consistently?" +- "Which do you want to do but don't yet?" +- "Am I missing any important methods?" + +*AFTER V2MOM COMPLETE:* +- "Ready to triage todo.org using the V2MOM?" +- "Should we execute quick wins or continue systematic triage?" + +** 🎯 Success Metrics for This Project + +We'll know this is working when: +- ✅ V2MOM is complete and provides clear strategic direction +- ✅ todo.org shrinks from ~50 to < 20 active items +- ✅ Craig ships 3-5 items per week (small but consistent) +- ✅ Craig has profiling infrastructure to measure performance +- ✅ Org agenda rebuild time is measured and improving +- ✅ Weekly triage becomes habit + +** 💬 Craig's Words to Remember + +> "I think you should adjust issues.org with all your recommendations. They are exciting, eye-opening, and just feel right. Add even your guidance on latex. spot on. thanks for your honesty. I did ask for it and am genuinely grateful for your responses. I'll take action on them." + +> "What I need help with is integrating this in with my existing todo.org file... Some of the tasks I've listed should probably just be deleted or better yet, marked CANCELLED." + +> "I have about an hour to devote. You could lead me through it, I could do some questions/answer rounds with you to clarify my thinking." + +Craig is ready to execute. He asked for honesty and took it well. He recognizes the patterns and wants systematic help. + +** 🛠️ Technical Context + +*** Current Pain Points +1. Org agenda is slow (performance bottleneck) +2. Network check adds 1+ seconds to startup (technical debt) +3. Missing functions cause errors (cj/log-silently, cj/goto-git-gutter-diff-hunks) +4. Mail attachments workflow is awkward +5. No profiling infrastructure to measure performance + +*** Items Craig Already Has Code For +These can be executed immediately - just paste and test: +- Transcription workflow (complete bash + elisp in todo.org:2-99) +- Difftastic integration (complete config in todo.org:1211-1223) +- Corfu migration (complete config in todo.org:1611-1639) + +*** Architecture +- Modular structure: modules/*.el +- Good test coverage for utilities +- Modern packages: Vertico/Consult/Embark stack +- Local package development: chime.el, org-msg + +** 📚 Related Reading + +If Craig or Claude need more context: +- [[file:../issues.org::*Second Opinion: Ruthless Prioritization & Reality Checks][Second Opinion section in issues.org]] - Full analysis and recommendations +- [[file:../issues.org::*TIER 1: Do These First][TIER 1-4 breakdown]] - Prioritized task system +- [[file:../quality-engineer.org][quality-engineer.org]] - Testing philosophy (if exists) + +** 🚀 Current Session Notes + +*** 2025-10-30 Session 1 - V2MOM In Progress +*Time:* ~1 hour +*Status:* PAUSED - V2MOM 60% complete + +*What We Completed:* +1. ✅ Created docs/ directory structure +2. ✅ Created SESSION-HANDOFF-ACTIVE-PROJECT.org (this file) +3. ✅ Created emacs-config-v2mom.org +4. ✅ Created values-comparison.org (analysis doc) +5. ✅ Completed Vision (already existed, kept as-is) +6. ✅ Completed Values section (Intuitive, Fast, Simple) + - Intuitive: Muscle memory, mnemonics, which-key timing, "newspaper" code + - Fast: Startup < 2s, org-agenda is THE bottleneck, everything else acceptable + - Simple: Production software practices, simplicity produces reliability + +*What's Next:* +1. ⏳ *Methods* - IN PROGRESS (have draft list, need Craig's input) +2. ⏳ *Obstacles* - TODO +3. ⏳ *Metrics* - TODO +4. ⏳ *Finalize V2MOM* - Review and commit + +*Draft Methods List (Need Craig's Feedback):* +These were proposed but Craig stopped before reviewing: +1. Ruthless prioritization (V2MOM guides triage) +2. Profile before optimizing (build observability first) +3. Test-driven development (tests enable confident refactoring) +4. Ship > Research (execute existing specs before exploring new) +5. Weekly triage ritual (review todos, cancel stale, keep < 20 active) +6. Measure metrics (track startup, agenda, test coverage, todo count) +7. Extract packages (when custom code grows: chime, org-msg pattern) +8. Incremental execution (ship small, test, iterate) + +*Questions to Ask Craig When Resuming:* +- Which methods do you already do consistently? +- Which do you want to do but don't yet? +- Am I missing any important methods? + +*After Methods/Obstacles/Metrics Complete:* +Then move to triage todo.org using completed V2MOM as filter. + +*Key Files Modified This Session:* +- [[file:emacs-config-v2mom.org][emacs-config-v2mom.org]] - Main working document (60% complete) +- [[file:values-comparison.org][values-comparison.org]] - Analysis doc (reference only) +- [[file:SESSION-HANDOFF-ACTIVE-PROJECT.org][SESSION-HANDOFF-ACTIVE-PROJECT.org]] - This file + +*Next Session Starts With:* +1. Read this handoff document +2. Read emacs-config-v2mom.org to see what's complete +3. Ask Craig: "Ready to continue V2MOM with Methods section?" +4. Show Craig the draft Methods list +5. Get feedback and complete Methods +6. Move to Obstacles +7. Move to Metrics +8. Finalize V2MOM +9. Then triage todo.org diff --git a/docs/emacs-config-v2mom.org b/docs/emacs-config-v2mom.org new file mode 100644 index 00000000..06664103 --- /dev/null +++ b/docs/emacs-config-v2mom.org @@ -0,0 +1,299 @@ +#+TITLE: Emacs Configuration V2MOM +#+AUTHOR: Craig Jennings +#+DATE: 2025-10-30 +#+FILETAGS: :strategy:planning:v2mom: + +* What is V2MOM? + +V2MOM is a strategic framework used to clarify goals and priorities: +- *Vision:* What do you want to achieve? +- *Values:* What principles guide decisions? +- *Methods:* How will you achieve the vision? +- *Obstacles:* What's in your way? +- *Metrics:* How will you measure success? + +This document provides strategic clarity for prioritizing Emacs configuration work. + +* Vision + +Your personal ideal Emacs configuration allows you to work at the speed of your own thought. It's stable and never needs edge-case bug fixing. The functionality covers your workflow such that you can work all day in Emacs for any of the following task areas: + +- Programming in any language, including emacs-lisp, common-lisp, scheme, golang, and c +- Reading ebooks, including marginalia handling +- Listening to music, including playlist management +- Listening to podcasts, including saving episodes +- Task organizing and planning +- Calendaring, including syncing with google calendar +- Alerting and notifications on tasks and calendar events +- Timer/stopwatch functionality, including pomodoro presets +- Audio and desktop recording +- Memorization drills +- Email, including email-to-task integration +- Messaging, i.e., IRC +- File-management, including extensive integration, and DWM scratchpad integration outside Emacs +- Shell commands, leveraging vterm and eshell with custom methods, and DWM scratchpad integration outside Emacs + +Anytime you make a change in the config, you have unit tests to tell you quickly if the change created unexpected issues. You have a test-runner that integrates with your workflow. + +* Values + +** Intuitive + +*Definition:* Intuition comes from muscle memory, clear mnemonics, and just-in-time discovery that reinforces learning without blocking productivity. + +*** Keybindings Build Intuition Through Layers +1. *Muscle memory first* - C-; on home row becomes automatic habit +2. *Mnemonics provide "aha!" moments* - C-c **m** for mail creates dopamine hits that cement permanent memory +3. *Which-key teaches through active recall* - Appears after you try (not before), forcing attempt without wasting time + +The timing matters: which-key's delay is calibrated so you're challenged to remember, but not blocked from working. + +*** What Breaks Intuition +- *Physically awkward keybindings* - C-c 1 z C-c f requires hand contortions and offers no mnemonic +- *Syntax overhead* - Parentheses tracking steals time from real learning (logic bugs, architecture understanding) +- *Poor architecture* - Common actions (like "save all attachments") shouldn't require architectural archaeology + +*** Code Reads "Like a Newspaper" +- Each use-package declaration is a *story* (topic = package) +- Configuration settings are *facts* (what it does, how it behaves) +- Clear variable/method naming (yours + maintainers) makes code read like English +- Accessible to intermediate elisp readers without deep archaeology + +*** Principle +*Intuition is earned through repeated success, not instant magic.* The config should make success easy to achieve and memorable when it happens. + + + +** Fast + +*Definition:* Fast means staying in flow. Startup and org-agenda must not make me wait. Everything else is already acceptable. + +*** Performance Targets + +*Startup Time:* +- Current: ~3.8 seconds (real clock time, not Emacs-reported) +- Target: ~1 second (aspirational, may not be realistic given package count) +- Comfortable: ~2 seconds (±) + +*Measurement:* +#+begin_src bash +time emacs --eval '(save-buffers-kill-emacs)' # Real startup time +#+end_src + +*Note:* Emacs-reported time is inaccurate. Messages buffer continues populating and Emacs remains unresponsive after "loaded" message appears. + +*** Critical Performance Issues (Must Fix) + +1. *Emacs startup* - 3.8s is too slow + - Network check adds 1+ seconds (DELETE THIS) + - Eager loading vs lazy loading + +2. *Opening org-agenda for first time* - **THE BOTTLENECK** + - "Takes forever and a full work day" + - Likely: recursive directory scan, no caching, too many files + - Solution: Profile first (build debug-profiling.el), then optimize + +*** Must Be Instant (Flow-Critical) +- Keybinding response +- Changing projects in projectile (already good ✓) +- Opening large org files (already good ✓, except malformed files with stray "*"s) +- Buffer/window operations + +*** Can Take 1-2 Seconds (Acceptable) +- *Mail sync* - Consider putting on timer instead of manual trigger +- *Calibredb* (PDFs/Epubs) - Known solution: shrink inventory to <1k books or contribute optimizations +- *Org-noter* opening PDFs/Epubs - Needs reliability + reasonable speed +- *Looking up org-noter notes* - Should be easy to pull into other documents +- *Initial file indexing* (projectile, etc.) + +*** Disabled Due to Performance (Need Solutions) +- *Grammar checker* - Installed but turned off because it slows everything down + - No task exists to fix this yet + - Breaks concentration when writing + +- *Bibliography management* - Don't know how to do in Emacs + - Worried about broken/slow tools breaking writing flow + - Need solution that "just works" + +*** Principle +*Speed is about flow, not milliseconds.* Startup and agenda are flow-breakers. Everything else is fine. Don't optimize what doesn't hurt. + + + +** Simple + +*Definition:* Simple means clear, maintainable code following production software practices. Simplicity is a discipline that produces reliability. Config is production software - treat it as such. + +*** What Makes Code Simple +1. *Clear naming* - Variables and functions say what they do +2. *Small functions* - Easy to understand, test, and modify +3. *Good comments* - NOT obvious comments ("cat with post-it note saying 'cat'") + +*Good comments:* +- Navigation markers for long files +- Explain complex/non-obvious logic +- Document unobvious impacts of settings +- Answer "why" not "what" + +*Bad comments:* +#+begin_src emacs-lisp +;; declare a variable +(defvar variable 10) +#+end_src + +*** Abstractions and Magic +- *Abstractions are fine* if they serve a purpose +- *Macros are irritating* when unnecessary, but acceptable when warranted +- *Magic is acceptable* if it's reliable and you can build a mental model + +*Principle:* "I want to understand every line of what my config does, or at least have a decent mental model of how it probably works. Never use a tool you can't fully trust." + +*** Package Count +Package count itself isn't a concern. Quality and maintainability matter. + +*Concerns:* +- More packages = more potential breakage +- Need ability to roll back when packages break (localrepo for package snapshots) +- Maintenance burden is real, but manageable with good practices + +*Not concerned about:* +- Absolute number of packages +- Startup time impact (if lazy-loaded properly) +- Cognitive load (if organized well) + +*** Config IS Production Software + +*Key insight:* "My Emacs config IS production software. I use it more than probably every other application combined. The impact when it's broken can crush my productivity." + +This means: +- *Apply production practices:* Testing, modularity, documentation, version control +- *Plan for failure:* Rollback capability (localrepo), error handling, graceful degradation +- *Modular design:* Extract custom code into packages when it grows +- *Long-term maintenance:* Architecture matters, technical debt has real cost + +*** Personal Software Platform +"I see my config as a personal software project built on the software platform called Emacs. Emacs has taught me a great deal about how to build software platforms well." + +*Implications:* +- Config follows software engineering principles +- Custom functionality becomes packages (chime, org-msg, potentially jumper) +- Good architecture enables evolution +- Quality matters because uptime matters + +*** Why Simplicity Matters +When config breaks, productivity is crushed. The discipline of simplicity produces reliability: +- Simple code is easier to fix when it breaks +- Simple architecture enables graceful degradation +- Simple practices (testing, rollback) provide safety nets +- Simple means never using tools you can't trust + +*** Principle +*Simple means maintainable production software, not minimal or primitive.* Complexity is acceptable when it serves the vision and can be understood or trusted. **Simplicity is the practice that produces reliability as an outcome.** + + + +** Summary: Three Core Values + +1. *Intuitive* - Muscle memory, mnemonics, just-in-time discovery. Code reads like a newspaper. +2. *Fast* - Startup and org-agenda must not break flow. Everything else is acceptable. +3. *Simple* - Clear, maintainable, production-quality code. Simplicity produces reliability. + + + +* Methods + +#+begin_quote +Working section - HOW will you achieve the vision while honoring the values? + +*PAUSED HERE - NEED CRAIG'S INPUT* + +Draft methods proposed by Claude (need review): +1. Ruthless prioritization - V2MOM guides triage, cancel what doesn't serve vision +2. Profile before optimizing - Build observability (debug-profiling.el), measure first +3. Test-driven development - Tests give confidence to refactor, catch regressions +4. Ship > Research - Execute existing specs before exploring new ones +5. Weekly triage ritual - Review todos, cancel stale items, keep < 20 active +6. Measure metrics - Track startup time, agenda time, test coverage, todo count +7. Extract packages - When custom code grows, make it a package (chime, org-msg pattern) +8. Incremental execution - Ship small, test, iterate. Portions not all-at-once. + +*Questions for Craig:* +- Which of these do you already do consistently? +- Which do you want to do but don't yet? +- Am I missing any important methods? + +*Fill in below after discussion:* +#+end_quote + + + +* Obstacles + +#+begin_quote +Working section - What's preventing you from achieving the vision? + +*Questions to answer:* +- What technical challenges block you? +- What behavioral patterns get in your way? +- What external constraints limit you? +- What trade-offs are hardest to make? + +*Fill in below:* +#+end_quote + + + +* Metrics + +#+begin_quote +Working section - How will you know you're succeeding? + +*Questions to answer:* +- What numbers will you track? +- How will you measure performance? +- How will you measure productivity? +- What leading indicators show progress? + +*Fill in below:* +#+end_quote + + + +* How to Use This V2MOM + +** For Decision Making +When considering a new task, package, or feature: +1. Does it serve the *Vision*? +2. Does it align with the *Values*? +3. Is it addressed in the *Methods*? +4. Does it overcome an *Obstacle*? +5. Does it improve a *Metric*? + +If the answer is "no" to all five → CANCEL the task. + +** For Triage +When reviewing todo.org: +- Tasks aligned with V2MOM → Keep and prioritize +- Tasks not aligned with V2MOM → CANCEL +- Tasks that are interesting but not important → CANCEL +- Tasks with complete code already → Execute immediately + +** For Weekly Review +Every Sunday (30 minutes): +1. Review metrics - Are they improving? +2. Review obstacles - Any new ones? Any resolved? +3. Review active todos - Still aligned with V2MOM? +4. Cancel stale/misaligned tasks +5. Plan next week's focus + +* Completion Status + +- [X] Vision - Complete (kept from original todo.org) +- [X] Values - Complete (Intuitive, Fast, Simple) +- [ ] Methods - IN PROGRESS (have draft, need Craig's input) +- [ ] Obstacles - TODO +- [ ] Metrics - TODO + +*Last Updated:* 2025-10-30 (Session 1) +*Next Session:* Continue with Methods section (review draft list with Craig) +*Estimated Time to Complete:* 20-30 minutes (Methods + Obstacles + Metrics) diff --git a/docs/values-comparison.org b/docs/values-comparison.org new file mode 100644 index 00000000..128c6710 --- /dev/null +++ b/docs/values-comparison.org @@ -0,0 +1,107 @@ +#+TITLE: Values Comparison - Simple vs Reliability +#+DATE: 2025-10-30 + +* What Currently Supports Each Value + +| Theme/Practice | Simple | Reliability | +|---------------------------------------+--------+-------------| +| *Code Quality* | | | +| Clear naming | ✓ | | +| Small functions | ✓ | | +| Good comments (why not what) | ✓ | | +| Abstractions when warranted | ✓ | | +|---------------------------------------+--------+-------------| +| *Architecture* | | | +| Modular design | ✓ | | +| Extract custom code into packages | ✓ | | +| Good architecture enables evolution | ✓ | | +| Technical debt has real cost | ✓ | | +|---------------------------------------+--------+-------------| +| *Production Practices* | | | +| Testing | ✓ | ✓ | +| Documentation | ✓ | | +| Version control | ✓ | | +|---------------------------------------+--------+-------------| +| *Failure Planning* | | | +| Rollback capability (localrepo) | ✓ | ✓ | +| Error handling | ✓ | ✓ | +| Graceful degradation | ✓ | ✓ | +|---------------------------------------+--------+-------------| +| *Trust & Stability* | | | +| Never use tools you can't trust | ✓ | ✓ | +| Uptime matters | | ✓ | +| Config breakage crushes productivity | | ✓ | +| More packages = more breakage risk | | ✓ | +| Need confidence to refactor | | ✓ | +|---------------------------------------+--------+-------------| + +* Key Quotes Supporting Each + +** Simple +- "Config IS production software - treat it as such" +- "Clear naming, small functions, good comments" +- "Apply production practices: Testing, modularity, documentation" +- "Simple means maintainable production software, not minimal or primitive" + +** Reliability/Resilience +- "Never use a tool you can't fully trust" +- "The impact when it's broken can crush my productivity" +- "More packages = more potential breakage. Need ability to roll back" +- "Plan for failure: Rollback capability, error handling, graceful degradation" +- "Quality matters because uptime matters" + +* Analysis + +** If Reliability is part of Simple: +*Simple means:* Maintainable, trustworthy, production-quality code that fails gracefully + +*Pros:* +- Keeps to 3 values +- Failure planning is a consequence of good engineering (already in Simple) +- Less redundancy + +*Cons:* +- "Simple" might not clearly convey the "must not break" urgency +- Rollback/resilience themes could get lost + +** If Reliability is its own value: +*Simple means:* Clear, maintainable code +*Reliability means:* Never breaks, fails gracefully, always recoverable + +*Pros:* +- Explicit focus on uptime/stability +- Clear that this is mission-critical (crushes productivity when broken) +- Resilience practices (localrepo, testing, error handling) have clear home + +*Cons:* +- 4 values instead of 3 +- Some overlap with Simple + +* Decision Framework + +Ask yourself: + +1. When choosing between two approaches, would "reliability" change your decision differently than "simple"? + + Example: Complex caching solution that's faster but harder to debug + - Simple says: "Too complex, harder to maintain" + - Reliability says: "If it breaks less often and has rollback, worth the complexity" + +2. Does "config can't break" deserve equal weight with Fast and Intuitive? + +3. Is the "trust" theme strong enough to be its own value? + +* Recommendation + +If you had to choose, I'd suggest: **Keep Simple, absorb Reliability into it.** + +Why? Most of your reliability practices (testing, rollback, error handling) are already listed under "production software practices" in Simple. The "never use tools you can't trust" is already there too. + +**But** - you could rename Simple to "**Reliable**" or "**Solid**" if the stability theme is more important than the simplicity theme. + +Alternative value names that might capture both: +- *Solid* - Stable, maintainable, trustworthy +- *Resilient* - Fails gracefully, recovers easily, maintainable +- *Dependable* - Simple enough to trust, robust enough to rely on + +What resonates? diff --git a/issues.org b/issues.org new file mode 100644 index 00000000..c61b8517 --- /dev/null +++ b/issues.org @@ -0,0 +1,1879 @@ +#+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. + +** TODO [#D] Consider moving from doom-modeline to mood-line :enhancement:performance: +:PROPERTIES: +:FILE: modules/modeline-config.el +:END: + +Doom-modeline is feature-rich but heavy. For the minimal info you're displaying, mood-line might be better: + +*Mood-line advantages:* +- ~10x faster than doom-modeline +- Pure elisp (no external dependencies) +- Very simple, hackable codebase +- Shows exactly what you need: filename, position, major-mode +- No nerd-icons dependency (can still use them if wanted) + +*Doom-modeline advantages:* +- More battle-tested +- Better git integration +- More features (even if disabled) + +*Recommendation:* Try mood-line. If you miss doom-modeline features, easy to switch back. Performance difference is noticeable on older hardware. + +** 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. + +*** TODO [#B] Switch doom-modeline to mood-line :enhancement:performance: +:PROPERTIES: +:FILE: modules/modeline-config.el +:EFFORT: 30 minutes +:IMPACT: 10x faster modeline updates +:END: + +Replace doom-modeline config with: +#+begin_src elisp +(use-package mood-line + :ensure t + :demand t + :config + (mood-line-mode)) +#+end_src + +That's it. Mood-line shows: buffer name, position, major-mode, vc status. Pure elisp, very fast. + +If you miss doom-modeline features, easy to switch back. But try mood-line for a week. + +*** 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! 🚀 @@ -1,4 +1,102 @@ * Emacs Config Open Work +** TODO [#A] Add Transcription Org-capture Workflow + :PROPERTIES: + :CATEGORY: emacs + :END: + +*** Goal + Integrate audio-to-text transcription into Org-capture so meeting recordings + can be turned into timestamped notes automatically. + +*** Steps + +**** 1. Create CLI for transcription + - Option A — OpenAI API (Whisper): + #+begin_src bash + # ~/bin/oai-transcribe + #!/usr/bin/env bash + set -euo pipefail + AUDIO="$1"; LANG="${2:-en}" + : "${OPENAI_API_KEY:?Set OPENAI_API_KEY in your environment}" + curl -s -X POST "https://api.openai.com/v1/audio/transcriptions" \ + -H "Authorization: Bearer $OPENAI_API_KEY" \ + -F "model=whisper-1" \ + -F "response_format=text" \ + -F "language=${LANG}" \ + -F "file=@${AUDIO}" + #+end_src + + - Option B — Local Whisper: + #+begin_src bash + pip install openai-whisper # once + # ~/bin/local-whisper + #!/usr/bin/env bash + set -euo pipefail + AUDIO="$1"; MODEL="${2:-medium}"; LANG="${3:-en}" + whisper "$AUDIO" --model "$MODEL" --language "$LANG" \ + --task transcribe --output_format txt --verbose False >/dev/null + cat "${AUDIO%.*}.txt" + #+end_src + +**** 2. Emacs Lisp configuration + Add to init.el: + + #+begin_src emacs-lisp + (defcustom cj/transcribe-program + (or (executable-find "oai-transcribe") + (executable-find "local-whisper")) + "Program used to transcribe audio to plain text.") + + (defcustom cj/meetings-org-dir "~/org/meetings/" "") + + (defun cj/transcribe-audio (file) + (with-temp-buffer + (call-process shell-file-name nil t nil shell-command-switch + (format "%s %s" + (shell-quote-argument cj/transcribe-program) + (shell-quote-argument (expand-file-name file)))) + (buffer-string))) + + (defun cj/org-capture-meeting-template () + (let* ((audio (read-file-name "Audio: ")) + (participants (read-string "Participants: " "Mark; Craig")) + (location (read-string "Location: " "French Truck Coffee")) + (topic (read-string "Topic: " "Contract discussion")) + (transcript (cj/transcribe-audio audio))) + (format "* Meeting: %s\n:PROPERTIES:\n:DATE: %s\n:LOCATION: %s\n:PARTICIPANTS: %s\n:AUDIO: %s\n:END:\n\n** Summary\n- \n\n** Transcript\n#+begin_quote\n%s\n#+end_quote\n" + topic (format-time-string "%Y-%m-%d %H:%M") + location participants (abbreviate-file-name audio) transcript))) + + (with-eval-after-load 'org + (add-to-list 'org-capture-templates + `("m" "Meeting (transcribe audio)" plain + (file ,(expand-file-name (format-time-string "%Y-meetings.org") + cj/meetings-org-dir)) + "%(cj/org-capture-meeting-template)" + :empty-lines 1 :unnarrowed t))) + #+end_src + +**** 3. Usage + 1. Record meeting as .m4a or .wav + 2. Run `M-x org-capture m` → select the audio file + 3. Org inserts transcript and metadata under + `~/org/meetings/YYYY-meetings.org` + +**** 4. Optional quick recap template + #+begin_src emacs-lisp + (with-eval-after-load 'org + (add-to-list 'org-capture-templates + '("r" "Post-meeting recap" entry + (file+headline + (lambda () (expand-file-name (format-time-string "%Y-meetings.org") + cj/meetings-org-dir)) + "Recaps") + "* %U %^{Title|Meeting w/ Mark (Carrollton)}\n:LOCATION: %^{Location|French Truck (Dryades)}\n** Facts\n- %?\n** Next Steps\n- "))) + #+end_src + +**** 5. Notes + - Keep filenames descriptive: `2025-11-03_mark-frenchtruck.m4a` + - For privacy: use local-whisper variant to keep audio offline. ** TODO [#A] Finish Testing New Org-webclipper ** TODO [#A] Document Goals, Specs, and Architecture Docs for this Config :maintenance: *** Emacs Config V2MOM @@ -794,67 +892,337 @@ use case: - if the resolved.org file doesn't exist, it creates it. - If not in a projectile/project.el project, it falls back to the archive.org file. ** TODO [#B] M-y and show-kill-ring don't load first launch -** WAITING [#C] Fix Org-msg Issue With Undo Outside :bug: -*** 2025-09-19 Fri @ 17:45:35 -0500 Submitted PR Waiting for Merge -https://github.com/jeremy-compostella/org-msg/pull/213 -*** 2025-09-19 Fri @ 16:11:02 -0500 Fix in a PR with description. -ROOT CAUSE -On line 1556, in org-msg-edit-mode reads: -``` - (add-hook 'message-sent-hook 'undo t t) -``` -https://github.com/jeremy-compostella/org-msg/blob/59e2042e5f23e25f31c6aef0db1e70c6f54f117d/org-msg.el#L1556 - -When sending messages that extend past the visible space, org-msg prepares the content and may narrow the buffer. When the message-sent-hook fires in a narrowed buffer, calling undo errors when attempting to undo changes recorded outside the visible restriction. - -PROPOSED FIX -- Replace the raw undo hook with a helper which widens before performing the undo. Add that helper to message-send-hook based on the value of message-kill-buffer-on-exit. -This way: -- If message-kill-buffer-on-exit is t, there’s no need to restore the buffer as it will be killed right after the hooks. No undo → no error → no need to widen. -- If the buffer won’t be killed, we DO need to restore it, therefore we widen first and then perform the undo. - -ALTERNATIVES REJECTED -- Removing undo entirely. This fixes the error, but was rejected as it changes org-msg's behavior for users who keep the buffer open post-send. -- Saving/restoring the original buffer text explicitly. Rejected because this felt more invasive and might risk clobbering other transient state. -- Always add the hook to widen before performing the undo. Rejected to avoid executing more code than necessary. +** TODO [#B] create unfill paragraph (paragraph to single line) +#+BEGIN_QUOTE +I like to have a keybind to do the opposite of fill-paragraph. It’s taken from Stefan Monnier. -*** 2025-09-19 Fri @ 15:08:46 -0500 Tested workaround and posting it +(defun unfill-paragraph (&optional region) + "Takes a multi-line paragraph and turns it into a single line" + (interactive (progn (barf-if-buffer-read-only) '(t))) + (let ((fill-column (point-max)) + (emacs-lisp-docstring-fill-column t)) + (fill-paragraph nil region))) +(define-key global-map "\M-Q" 'unfill-paragraph) +#+END_QUOTE +[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] +Captured On: [2025-10-15 Wed 18:35] +** DOING [#B] Jumper Package :enhancement: +*** Specification +**** variable:custom: jumper-max-locations +maximum number of locations to store: default 10 +**** variable:internal: jumper--registers +vector of used registers: +(make-register jumper-max-locations nil) +**** variable:internal: jumper--last-location-register +register used to store the last location: ?z +**** method:internal: jumper--point-matches-register #+begin_src emacs-lisp -(with-eval-after-load 'org-msg - (add-hook 'org-msg-edit-mode-hook - (lambda () - (remove-hook 'message-sent-hook 'undo t)))) + ;; First store a position in register 'a' + (point-to-register ?a) + ;; Later, check if current position matches what's in register 'a' + (if (cj/point-matches-register ?a) + (message "Current position matches register 'a'") + (message "Current position differs from register 'a'")) + (defun cj/point-matches-register (register) + "Return t if current position matches position stored in REGISTER. + REGISTER is a character representing the register to check against. + Returns nil if the register doesn't exist or doesn't contain position information." + (let ((reg-val (get-register register))) + (when (and reg-val + (markerp reg-val) + (marker-buffer reg-val) + (eq (current-buffer) (marker-buffer reg-val)) + (= (point) (marker-position reg-val))) + t))) +#+end_src +**** method: +**** method: save-last-position +saves location to register z: (point-to-register ?z) +**** method: save-to-next-register +gets next free register in the sequence +calls (save-last-position) +saves using (point-to-register REGISTER &optional ARG) +*** 2025-09-02 Tue @ 14:06:03 -0500 Functionality Description +Tentative Package Name: Jumper +I typically use registers to jump between places, but I often forget the keybindings. Also, I sometimes overwrite registers by hitting the wrong key. Many Emacs users don't even know about registers. I've seen posts from software developers asking how to conveniently store and jump to specific locations in a large code base. +To solve this I'd like to write a little elisp package that leverages Emacs vanilla register functionality to make jumping between locations using registers simple and transparent. The user won't have to think about registers or what character they've stored their location in at all. All keybindings will be based on keys from home row of the keyboard. +Preliminaries: +We will map the functionality to the keybinding prefix C-; j, but the prefix should be easy to configure. +Let's imagine a set of characters in a sequence. They could be any characters, but for the sake of simplicity we'll use the numbers from 0 to 9, and a one more character used for the "last location" -- the character "z". +What follows is a description of the workflow: +**** Adding Locations +keybinding: <prefix> space (store destination) +If the user is visiting a buffer and presses <prefix> space, +- the current location is stored in the next free register. if this is the first time they are storing a location, the first free register is 0. +user feedback: a message is displayed in the echo area saying "location stored." +If the user stays on the same location and presses prefix <space> again, they should receive a message in the echo area saying the location is already stored, and no changes to the register is made. +If the user moves to a new location and presses prefix <space> again, the next free register is used to store the location, in this case it would be 1. They can then iterate and store additional locations up to the last character in the sequence up to the limit of 10 registers (0 through 9). If they try to store a register after all 10 registers are filled, they will receive a message ("sorry - all jump locations are filled!"). +**** Jump-To A Location +keybinding: <prefix> j (jump to destination) +NOTE: Whenever the user presses <prefix> j, that location is automatically stored in the "last location" register z. +***** When only one location is stored, and the user presses <prefix> j: +If there is only one location stored, the user IS at the location, and they press <prefix> j, they see an echo area message "you're already at the stored location." +If there is only one location stored in the sequence, and the user is NOT at that location, their location is stored in register z, then the user is immediately to their destination location via (jump-to-register). +user feedback: a message is displayed in the echo area saying "jumped to location." +If the user presses <prefix> J again, they are automatically taken back to the location in register z. and the same user feedback message is displayed. +In other words, after the user stores one location and moves elsewhere, pressing <prefix> j will jump back and forth between the two places. +***** When multiple locations are stored, and the user presses <prefix> j: +A completing read is displayed with all the locations between 0 and 9 along with register z (their last location) at the bottom. +Each line contains the letter as well as the content that the register would normally display. In other words, it could just be the register list is displayed, but narrowed down to 0 - 9 and z. +When the user chooses a location 0 -9, z from completing read: +- The current location is stored in register z, replacing their last location +- They are taken to the location via (jump-to-register). +user feedback: a message is displayed in the echo area saying "jumped to location." +**** Removing Locations +keybinding: <prefix> d +A completing read is displayed with the first item (where the cursor is on) being "Cancel". +The rest of the registers are displayed in descending order, i.e., from 9 to 0. +Note: the z register is not displayed. +Selecting a register from the list: +- removes that item from the list, and removes the location stored in the register. +- if the item is NOT the top one in the register, it reorders the rest of the sequence + in other words, if 0 - 9 are all stored, and: + the user removes item 7: + - location 8 is restored in 7 + - location 9 is stored in 8 + the user removes item 0 + - location 1 is stored in 0 + - location 2 is stored in 1 + - location 3 is stored in 2 + - location 4 is stored in 3 + - location 5 is stored in 4 + - location 6 is stored in 5 + - location 7 is stored in 6 + - location 8 is stored in 7 + - location 9 is stored in 8 +user feedback: a message is displayed in the echo area saying "location removed." +**** Open Questions +- Are there buffers which the user should not be able to store in a register? +- How can we prevent the user from creating issues by adding to the registers 0 through 9 by M-x point-to-register or it's keybinding? Is there a way to block or reserve those registers? Or is it better to just choose a sequence that is the least likely to be used by a human user? If so, what would that sequence be? +- Is 10 registers enough for a normal developer? +- I should probably +- Can I use a vector/list internally and map to registers behind the scenes? +Depending on context, can I add the +- Function name at point +- File name + line number +- First few words of the line +Do we think the reordering behavior when deleting locations might confuse users? How to simplify? +What happens if a buffer is deleted that doesn't have a file associated with it? If we're using registers underneath, how do registers handle this? +** TODO [#B] Get Tufte.css working and as a separate entry +Below is one way to get Org-mode’s HTML exporter to play nicely with Tufte-CSS. The basic recipe is: +1. Inject Tufte’s stylesheet into every HTML export +2. Teach Org to emit the little “margin-toggle” + “sidenote” markup that tufte.css expects for footnotes +3. (Optionally) wrap images in <figure> so you get tufte-style captions out of the box +Along the way you’ll see where Org’s default HTML‐classes line up with tufte.css and where you have to override them. +— 1 Inject tufte.css into your exports +Put tufte.css somewhere your exported HTML can see it (for example +~/.emacs.d/assets/tufte.css or a URL on your webserver). Then in your init.el: +(with-eval-after-load 'ox-html +;; 1a) tell Org to link in your tufte.css +(setq org-html-head-extra +"<link rel=\"stylesheet\" href=\"/assets/tufte.css\" type=\"text/css\"/>") +;; 1b) enable HTML5 “fancy” output (so you get <figure> around images) +(setq org-html-html5-fancy t +org-html-inline-images t)) +— 2 Turn Org footnotes into Tufte sidenotes +By default Org emits +<sup class=…><a href="#fn:1" id="fnref:1">[1]</a></sup> +…and then a big =<div id="footnotes">= at the bottom. +Tufte-CSS wants each footnote inline, wrapped in +<label class="margin-toggle">⊕</label> +<input type="checkbox" class="margin-toggle"/> +<span class="sidenote">…your note…</span> +We can override two Org variables: +(with-eval-after-load 'ox-html +;; format of each inline footnote reference +(setq org-html-footnote-format +(concat +"<label for=\"%1$s\" class=\"margin-toggle\">" +"⊕</label>" +"<input type=\"checkbox\" id=\"%1$s\" class=\"margin-toggle\"/>" +"<span class=\"sidenote\">%2$s</span>")) +;; drop Org’s default footnote list at the end +(setq org-html-footnote-separator "")) +Once you do that, exporting an Org file with footnotes will generate the markup tufte.css needs to float them in the margin. +— 3 (Optionally) get <figure> + <figcaption> around images +If you set =org-html-html5-fancy= to t (see step 1) Org will automatically emit: +<figure> +<img src="…"/> +<figcaption>Your caption</figcaption> +</figure> +and tufte.css already has rules for =<figure class="figure">= etc. +— 4 Common pitfalls +• Make sure your href in =org-html-head-extra= actually points to the css that the browser can load (absolute vs. relative). +• If you still see a “Footnotes” section at the bottom, double-check that =org-html-footnote-separator= is set to the empty string and that your init-file got re-evaluated. +• On Windows or if you’re testing locally, run e.g. =python3 -m http.server= inside your export folder so your browser can fetch the CSS. +— 5 Unit test for your footnote hack +Drop this in =~/.emacs.d/tests/test-org-tufte.el= and run =M-x ert RET t RET=: +(require 'ert) +;; load your config; adjust the path if necessary +(load-file "~/.emacs.d/init.el") +(ert-deftest org-tufte-footnote-format-test () +"Ensure each footnote reference becomes a margin-toggle + sidenote." +(let/ ((id "fn:42") +(content "My note.") +(html (format org-html-footnote-format id content))) +(should (string-match-p "class=\"margin-toggle\"" html)) +(should (string-match-p "<span class=\"sidenote\">My note\\.</span>" html)) +;; it must not accidentally reintroduce Org’s bottom-of-page footnote div +(should-not (string-match-p "div id=\"footnotes\"" html)))) +Once that test passes, you know your footnotes are being rewritten into Tufte-style side notes. From there, you can sprinkle in additional filters (e.g. wrap =<blockquote>= in a =.sidenote= class, override list/p table styles, etc.) or just let the rest of tufte.css style Org’s default tags (h1, p, ul, table, code, etc.). +Enjoy your beautifully-typeset Org → HTML exports in true Tufte style! +** TODO [#B] Get Dupre Theme Working :bug: +*** AI Suggestioms +Here are some ideas for making dupre-theme a bit more “complete” and future-proof as an Emacs theme. You don’t have to do all of them, of course, but most “modern” themes ship a fair number of these extra faces and integrations. +**** TODO [#A] Fill out the “standard” Emacs faces +• error / warning / success +• link / link-visited / shadow +• highlight / secondary-selection +• tooltip / tooltip-selection / tooltip-tip +• underline / strike-through +• match / search / lazy-highlight (you already have some isearch) +• info-title-1…info-title-5, info-header-node, help-*-face +**** TODO [#A] Cosmetic / code-style notes +• Factor out a helper macro to reduce repetition: +#+begin_src elisp + (defmacro dupre-face (face &rest specs) + `(,face ((t ,specs)))) +#+end_src +• Use =custom-theme-set-faces= only once; wrap your =when (>= emacs-major-version…)= inside it rather than nesting top-level. +• Move your ERT tests into a separate file like =dupre-theme-test.el= so that the main theme file stays lean. +**** TODO [#B] Make sure you define a “minimum package” set of faces so that folks don’t see glaring unstyled text in any part of Emacs, even if they never install extra packages. +**** TODO [#B] Improve terminal support: +• Detect true-color (=(display-graphic-p)=) vs 256-color vs 16-color and fall-back gracefully. +• Provide “256-color” approximations (you already do this), but you might want a simpler palette for legacy terminals. +**** TODO [#B] Other Packages +The rest is just more of the same—pick the most common modes/packages you and your users rely on and give them a quick coat of paint. Once you’ve rounded out those faces you’ll have hit what most people expect from a “complete” modern Emacs theme. +Based on a quick skim of your modules/ directory (and the packages they pull in via use-package), here’s a non-exhaustive list of the major packages you’re using—along with the faces you should think about adding to dupre-theme so that nothing ends up looking “unstyled.” +1. completion/mini-frameworks + • vertico (vertico-current, vertico-group-title, vertico-separator, vertico-doc) + • orderless (orderless-match-face-0…3) + • consult (consult-preview-line, consult-narrow, consult-region, consult-file) + • marginalia (marginalia-annotation, marginalia-document, marginalia-cache) + • embark (embark-general, embark-heading, embark-target, embark-helpful) + • corfu + cape (corfu-default, corfu-current, corfu-bar, cape-file, cape-keyword etc.) + • company (company-tooltip, company-tooltip-selection, company-scrollbar-thumb) +2. ivy/counsel/swiper (if you ever load them side-by-side) + • ivy-current-match, ivy-minibuffer-match-face-1…4 + • counsel-outline, swiper-match-face-1… +3. syntax checkers & LSP + • flycheck (flycheck-error, flycheck-warning, flycheck-info) + • eglot or lsp-mode (lsp-face-highlight-textual, lsp-face-highlight-read, lsp-face-semantic-type-…) + • tree-sitter (tree-sitter-hl-face:function, :variable, :property, …) +4. git & diffs + • magit (magit-section, magit-section-title, magit-hunk-heading, magit-branch, magit-log) + • diff-hl / git-gutter (diff-hl-face-insert, diff-hl-face-delete, git-gutter:added, git-gutter:deleted) + • diff-mode (diff-added, diff-removed, diff-context, diff-file-header) +5. project management & navigation + • projectile (projectile-project-name, projectile-project-root) + • treemacs (treemacs-root-face, treemacs-directory-face, treemacs-file-face) +6. Org-mode + • org-document-title, org-level-1…org-level-8 + • org-checkbox, org-todo, org-done, org-warning, org-date, org-code, org-block + • plus any “pretty” add-ons you use (org-bullets, org-modern, etc.) +7. Hydra / which-key / key-help + • which-key (which-key-key-face, which-key-group-description-face, which-key-local-map-description-face) + • hydra (hydra-face-red, hydra-face-blue, hydra-face-teal, hydra-face-amaranth) +8. visual helpers + • rainbow-delimiters-depth-1…9 + • highlight-indent-guides-odd/even + • highlight-parentheses-/ + • beacon (beacon), whitespace (whitespace-space, whitespace-tab, whitespace-newline) + • display-line-numbers (line-number, line-number-current-line—you already have these) +9. UI polishers + • all-the-icons (all-the-icons-blue, all-the-icons-red, …) on modeline or treemacs + • doom-modeline (doom-modeline-bar, doom-modeline-buffer-path) + • fancy-splash/dashboard-faces if you use a startup screen +10. terminals & shells + • vterm (vterm-copy-region-highlight) + • ansi-colors (ansi-color-face-bold, ansi-color-face-italic, ansi-color-[0…7]) +To figure out exactly which ones you have in your modules, you can do for example: +#+begin_src elisp + ;; from your ~/.emacs.d + (let (pkgs) + (dolist (f (directory-files "~/.emacs.d/modules" t "\\.el$")) + (with-temp-buffer + (insert-file-contents f) + (goto-char (point-min)) + (while (re-search-forward "use-package +\\([^ ]+\\)" nil t) + (push (intern (match-string 1)) pkgs)))) + (delete-dups pkgs)) +#+end_src +Then for each of those packages, grep in their repo for =defface= or check their manual to see which faces they define, and add reasonable dupre-colours for them. Once you’ve covered that list, you’ll have a “complete” theme that won’t leave any of your modules half-unstyled. +**** TODO [#C] Support the built-in “tab-bar” and “tab-group” faces (Emacs 27+) +• tab-bar / tab-bar-tab / tab-bar-tab-inactive +• tab-bar-close / tab-bar-separator +• tab-group-title / tab-group-separator +**** TODO [#C] Add faces for =dired=, =diff=, =flycheck=/=flymake=, =magit= (or git-gutter), +=ediff=, =erc= etc. For example: +• diff-added, diff-removed, diff-context, diff-file-header +• flycheck-error, flycheck-warning, flycheck-info +• magit-section, magit-log, magit-hunk-heading, magit-branch, etc. +**** TODO [#C] Org-mode +• org-level-1…org-level-8, org-document-title +• org-checkbox, org-code, org-date, org-todo, org-done, org-warning +• org-block, org-block-begin-line, org-block-end-line +**** TODO [#C] Completion frameworks & minibuffer enhancements +• ivy-current-match / ivy-minibuffer-match-face-1…4 +• swiper-line-face +• vertico-current / marginalia-… +• corfu-default / corfu-bar / cape / orderless-match-face-… +**** TODO [#C] LSP / tree-sitter / syntax-aware faces +• lsp-face-highlight-textual, lsp-face-highlight-read, etc. +• tree-sitter-hl-face:variable, :function, :property, etc. +**** TODO [#C] Which-Key, Hydras, rainbow-delimiters, etc. +• which-key-key-face, which-key-local-map-description-face +• hydra-face-red / teal / blue +• rainbow-delimiters-depth-1…9 +**** TODO [#C] Theme Metadata & Packaging +• Add =;; Package-Requires: ((emacs "26.1"))= if you need a minimum. +• Consider adding a README with screenshots. +• A simple Makefile or script to refresh “theme metadata” (=M-x theme-inspect=, MELPA recipe). +**** TODO [#C] Tests +• Extend your ERT suite to cover more faces (org, diff, flycheck…). +• You might write a small helper that loops over a list of faces and asserts they’re defined without errors. +**** Example Sample Faces +Below is a small excerpt showing how you might start adding some of the “missing” standard faces and a couple org faces: +#+begin_src elisp + (custom-theme-set-faces + 'dupre + ;; … your existing faces … + ;; standard global faces + `(error ((t (:foreground ,dupre-intense-red :weight bold)))) + `(warning ((t (:foreground ,dupre-yellow+1 :weight bold)))) + `(success ((t (:foreground ,dupre-green+1 :weight bold)))) + `(link ((t (:foreground ,dupre-blue+1 :underline t)))) + `(shadow ((t (:foreground ,dupre-gray)))) + `(highlight ((t (:background ,dupre-bg+1)))) + `(tooltip ((t (:foreground ,dupre-fg :background ,dupre-bg+2)))) + `(tooltip-selection ((t (:background ,dupre-bg+0)))) + ;; Org-mode sample + `(org-document-title ((t (:foreground ,dupre-yellow+2 :weight bold :height 1.2)))) + `(org-level-1 ((t (:foreground ,dupre-blue+2 :weight bold)))) + `(org-level-2 ((t (:foreground ,dupre-green+1)))) + `(org-checkbox ((t (:foreground ,dupre-yellow :weight bold)))) + `(org-todo ((t (:foreground ,dupre-red :weight bold)))) + `(org-done ((t (:foreground ,dupre-green+2 :weight bold)))) + ) +#+end_src +** TODO [#B] Emacs Add Difftastic Functionality :enhancement: +Linux Binary Install +https://github.com/Wilfred/difftastic +https://www.wilfred.me.uk/blog/2022/09/06/difftastic-the-fantastic-diff/ +https://difftastic.wilfred.me.uk/installation.html (basically $ sudo pacman -S difftastic) +Emacs Package +https://github.com/pkryger/difftastic.el +#+begin_src emacs-lisp + (use-package difftastic + :defer t + :vc (:url "https://github.com/pkryger/difftastic.el.git" + :rev :newest)) #+end_src -*** 2025-09-19 Fri @ 14:59:48 -0500 Current Status -The bug I filed after tracking the issue down: -https://github.com/jeremy-compostella/org-msg/issues/210 - -First, thanks for conttributing this package. It's made email via Mu4E in Emacs possible for me and others. - -Description -When sending long emails using org-msg, an error occurs after the message is successfully sent: "primitive-undo: Changes to be undone are outside visible portion of buffer". The email is sent successfully, but the compose buffer remains open in a mixed text+html state along with other buffers, instead of everything being closed as expected. - -Environment -Emacs version: 30.1 -org-msg version: 20240902.447 -Mail client: mu4e -OS: Linux, though I'd be surprised if it doesn't reproduce everywhere. -Reproduction Steps -Compose a new email with org-msg enabled -Write a long email (content must exceed the visible window) -Send the email with C-c C-c -Error occurs after successful send -Root Cause -The issue is in org-msg.el at line 1747 in the org-msg-edit-mode definition: - -(add-hook 'message-sent-hook 'undo t t) -During the sending process: - -org-msg-prepare-to-send processes the buffer and converts Org content to HTML/text -This processing narrows the buffer (confirmed by debugging) -After sending, message-sent-hook runs while the buffer is still narrowed -The undo function fails because it tries to undo changes outside the narrowed region -I'll work on this and hope to propose a fairly well tested fix soon. +** TODO [#B] Emacs error if no file in buffer-and-file operations :bug: +** TODO [#B] Add Hugo Blogging Functionality :enhancement: ** TODO [#C] Miscellaneous Maintenance *** TODO [#C] Move persistence and history files into subdir :enhancement: *** DOING [#B] Implement Performance Recommendations :bug: @@ -1009,21 +1377,7 @@ Org-roam and its database sync run at startup. Load Org-roam only when Org is ac ** TODO [#C] Git Timemachine Litters Empty Buffers :bug: Don't choose a revision and you'll see a blank buffer that needs to be killed ** TODO [#C] capture windows should pop up from bottom and 1/3 at most -** create unfill paragraph (paragraph to single line) -#+BEGIN_QUOTE -I like to have a keybind to do the opposite of fill-paragraph. It’s taken from Stefan Monnier. - -(defun unfill-paragraph (&optional region) - "Takes a multi-line paragraph and turns it into a single line" - (interactive (progn (barf-if-buffer-read-only) '(t))) - (let ((fill-column (point-max)) - (emacs-lisp-docstring-fill-column t)) - (fill-paragraph nil region))) -(define-key global-map "\M-Q" 'unfill-paragraph) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:35] -** install visible mark so you know where the mark is +** TODO [#C] install visible mark so you know where the mark is #+BEGIN_QUOTE Showing the location of the mark, probably helps with learning to use mark better. @@ -1037,113 +1391,7 @@ Showing the location of the mark, probably helps with learning to use mark bette #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:33] -** BrainCurses Emacs Mastermind Game -[[https://sourceforge.net/projects/braincurses/][BrainCurses download | SourceForge.net]] -Captured On: [2025-09-24 Wed 00:38] -* Emacs Config Next Release: 0.9 -** DOING [#A] Jumper Package :enhancement: -*** Specification -**** variable:custom: jumper-max-locations -maximum number of locations to store: default 10 -**** variable:internal: jumper--registers -vector of used registers: -(make-register jumper-max-locations nil) -**** variable:internal: jumper--last-location-register -register used to store the last location: ?z -**** method:internal: jumper--point-matches-register -#+begin_src emacs-lisp - ;; First store a position in register 'a' - (point-to-register ?a) - ;; Later, check if current position matches what's in register 'a' - (if (cj/point-matches-register ?a) - (message "Current position matches register 'a'") - (message "Current position differs from register 'a'")) - (defun cj/point-matches-register (register) - "Return t if current position matches position stored in REGISTER. - REGISTER is a character representing the register to check against. - Returns nil if the register doesn't exist or doesn't contain position information." - (let ((reg-val (get-register register))) - (when (and reg-val - (markerp reg-val) - (marker-buffer reg-val) - (eq (current-buffer) (marker-buffer reg-val)) - (= (point) (marker-position reg-val))) - t))) -#+end_src -**** method: -**** method: save-last-position -saves location to register z: (point-to-register ?z) -**** method: save-to-next-register -gets next free register in the sequence -calls (save-last-position) -saves using (point-to-register REGISTER &optional ARG) -*** 2025-09-02 Tue @ 14:06:03 -0500 Functionality Description -Tentative Package Name: Jumper -I typically use registers to jump between places, but I often forget the keybindings. Also, I sometimes overwrite registers by hitting the wrong key. Many Emacs users don't even know about registers. I've seen posts from software developers asking how to conveniently store and jump to specific locations in a large code base. -To solve this I'd like to write a little elisp package that leverages Emacs vanilla register functionality to make jumping between locations using registers simple and transparent. The user won't have to think about registers or what character they've stored their location in at all. All keybindings will be based on keys from home row of the keyboard. -Preliminaries: -We will map the functionality to the keybinding prefix C-; j, but the prefix should be easy to configure. -Let's imagine a set of characters in a sequence. They could be any characters, but for the sake of simplicity we'll use the numbers from 0 to 9, and a one more character used for the "last location" -- the character "z". -What follows is a description of the workflow: -**** Adding Locations -keybinding: <prefix> space (store destination) -If the user is visiting a buffer and presses <prefix> space, -- the current location is stored in the next free register. if this is the first time they are storing a location, the first free register is 0. -user feedback: a message is displayed in the echo area saying "location stored." -If the user stays on the same location and presses prefix <space> again, they should receive a message in the echo area saying the location is already stored, and no changes to the register is made. -If the user moves to a new location and presses prefix <space> again, the next free register is used to store the location, in this case it would be 1. They can then iterate and store additional locations up to the last character in the sequence up to the limit of 10 registers (0 through 9). If they try to store a register after all 10 registers are filled, they will receive a message ("sorry - all jump locations are filled!"). -**** Jump-To A Location -keybinding: <prefix> j (jump to destination) -NOTE: Whenever the user presses <prefix> j, that location is automatically stored in the "last location" register z. -***** When only one location is stored, and the user presses <prefix> j: -If there is only one location stored, the user IS at the location, and they press <prefix> j, they see an echo area message "you're already at the stored location." -If there is only one location stored in the sequence, and the user is NOT at that location, their location is stored in register z, then the user is immediately to their destination location via (jump-to-register). -user feedback: a message is displayed in the echo area saying "jumped to location." -If the user presses <prefix> J again, they are automatically taken back to the location in register z. and the same user feedback message is displayed. -In other words, after the user stores one location and moves elsewhere, pressing <prefix> j will jump back and forth between the two places. -***** When multiple locations are stored, and the user presses <prefix> j: -A completing read is displayed with all the locations between 0 and 9 along with register z (their last location) at the bottom. -Each line contains the letter as well as the content that the register would normally display. In other words, it could just be the register list is displayed, but narrowed down to 0 - 9 and z. -When the user chooses a location 0 -9, z from completing read: -- The current location is stored in register z, replacing their last location -- They are taken to the location via (jump-to-register). -user feedback: a message is displayed in the echo area saying "jumped to location." -**** Removing Locations -keybinding: <prefix> d -A completing read is displayed with the first item (where the cursor is on) being "Cancel". -The rest of the registers are displayed in descending order, i.e., from 9 to 0. -Note: the z register is not displayed. -Selecting a register from the list: -- removes that item from the list, and removes the location stored in the register. -- if the item is NOT the top one in the register, it reorders the rest of the sequence - in other words, if 0 - 9 are all stored, and: - the user removes item 7: - - location 8 is restored in 7 - - location 9 is stored in 8 - the user removes item 0 - - location 1 is stored in 0 - - location 2 is stored in 1 - - location 3 is stored in 2 - - location 4 is stored in 3 - - location 5 is stored in 4 - - location 6 is stored in 5 - - location 7 is stored in 6 - - location 8 is stored in 7 - - location 9 is stored in 8 -user feedback: a message is displayed in the echo area saying "location removed." -**** Open Questions -- Are there buffers which the user should not be able to store in a register? -- How can we prevent the user from creating issues by adding to the registers 0 through 9 by M-x point-to-register or it's keybinding? Is there a way to block or reserve those registers? Or is it better to just choose a sequence that is the least likely to be used by a human user? If so, what would that sequence be? -- Is 10 registers enough for a normal developer? -- I should probably -- Can I use a vector/list internally and map to registers behind the scenes? -Depending on context, can I add the -- Function name at point -- File name + line number -- First few words of the line -Do we think the reordering behavior when deleting locations might confuse users? How to simplify? -What happens if a buffer is deleted that doesn't have a file associated with it? If we're using registers underneath, how do registers handle this? -** Edit Indirect (Custom Code) Bugs and Improvements +** TODO [#C] Edit Indirect (Custom Code) Bugs and Improvements *** Bugs (by Severity) *** High Severity @@ -1271,69 +1519,6 @@ Handle cases where the parent buffer was modified while editing in the indirect *Add Support for Multiple Regions* Allow editing multiple non-contiguous regions in a single indirect buffer, useful for refactoring similar code blocks simultaneously. -** TODO [#B] Get Tufte.css working and as a separate entry -Below is one way to get Org-mode’s HTML exporter to play nicely with Tufte-CSS. The basic recipe is: -1. Inject Tufte’s stylesheet into every HTML export -2. Teach Org to emit the little “margin-toggle” + “sidenote” markup that tufte.css expects for footnotes -3. (Optionally) wrap images in <figure> so you get tufte-style captions out of the box -Along the way you’ll see where Org’s default HTML‐classes line up with tufte.css and where you have to override them. -— 1 Inject tufte.css into your exports -Put tufte.css somewhere your exported HTML can see it (for example -~/.emacs.d/assets/tufte.css or a URL on your webserver). Then in your init.el: -(with-eval-after-load 'ox-html -;; 1a) tell Org to link in your tufte.css -(setq org-html-head-extra -"<link rel=\"stylesheet\" href=\"/assets/tufte.css\" type=\"text/css\"/>") -;; 1b) enable HTML5 “fancy” output (so you get <figure> around images) -(setq org-html-html5-fancy t -org-html-inline-images t)) -— 2 Turn Org footnotes into Tufte sidenotes -By default Org emits -<sup class=…><a href="#fn:1" id="fnref:1">[1]</a></sup> -…and then a big =<div id="footnotes">= at the bottom. -Tufte-CSS wants each footnote inline, wrapped in -<label class="margin-toggle">⊕</label> -<input type="checkbox" class="margin-toggle"/> -<span class="sidenote">…your note…</span> -We can override two Org variables: -(with-eval-after-load 'ox-html -;; format of each inline footnote reference -(setq org-html-footnote-format -(concat -"<label for=\"%1$s\" class=\"margin-toggle\">" -"⊕</label>" -"<input type=\"checkbox\" id=\"%1$s\" class=\"margin-toggle\"/>" -"<span class=\"sidenote\">%2$s</span>")) -;; drop Org’s default footnote list at the end -(setq org-html-footnote-separator "")) -Once you do that, exporting an Org file with footnotes will generate the markup tufte.css needs to float them in the margin. -— 3 (Optionally) get <figure> + <figcaption> around images -If you set =org-html-html5-fancy= to t (see step 1) Org will automatically emit: -<figure> -<img src="…"/> -<figcaption>Your caption</figcaption> -</figure> -and tufte.css already has rules for =<figure class="figure">= etc. -— 4 Common pitfalls -• Make sure your href in =org-html-head-extra= actually points to the css that the browser can load (absolute vs. relative). -• If you still see a “Footnotes” section at the bottom, double-check that =org-html-footnote-separator= is set to the empty string and that your init-file got re-evaluated. -• On Windows or if you’re testing locally, run e.g. =python3 -m http.server= inside your export folder so your browser can fetch the CSS. -— 5 Unit test for your footnote hack -Drop this in =~/.emacs.d/tests/test-org-tufte.el= and run =M-x ert RET t RET=: -(require 'ert) -;; load your config; adjust the path if necessary -(load-file "~/.emacs.d/init.el") -(ert-deftest org-tufte-footnote-format-test () -"Ensure each footnote reference becomes a margin-toggle + sidenote." -(let/ ((id "fn:42") -(content "My note.") -(html (format org-html-footnote-format id content))) -(should (string-match-p "class=\"margin-toggle\"" html)) -(should (string-match-p "<span class=\"sidenote\">My note\\.</span>" html)) -;; it must not accidentally reintroduce Org’s bottom-of-page footnote div -(should-not (string-match-p "div id=\"footnotes\"" html)))) -Once that test passes, you know your footnotes are being rewritten into Tufte-style side notes. From there, you can sprinkle in additional filters (e.g. wrap =<blockquote>= in a =.sidenote= class, override list/p table styles, etc.) or just let the rest of tufte.css style Org’s default tags (h1, p, ul, table, code, etc.). -Enjoy your beautifully-typeset Org → HTML exports in true Tufte style! ** TODO [#C] Review Titlecase Functionality added in custom. Came from: https://codeberg.org/acdw/titlecase.el Originally seen at https://emacselements.com/true-titlecase-in-emacs.html @@ -1396,177 +1581,6 @@ Quick alternative: if you don’t need the full agenda machinery (dates, deadlin • You can duplicate the above snippet for other TODO states, agenda views, or matchers (deadlines, tags, etc.) With this in place you effectively get an agenda tailored to exactly the bit of tree you’re working on. ** TODO [#C] Find Another Keymapping for Emojify -** TODO [#D] Emacs: org project should be yellow or green like todo -** TODO [#D] Emacs Signal Client -*** TODO investigate: net/signel.org · master · undefined · GitLab -[[https://gitlab.com/jaor/elibs/-/blob/master/net/signel.org][net/signel.org · master · undefined · GitLab]] -Captured On: [2025-05-29 Thu 04:24] -*** TODO investigate: signel, a barebones signal chat on top of signal-cli -[[https://jao.io/blog/signel.html][signel, a barebones signal chat on top of signal-cli]] -Captured On: [2025-05-29 Thu 04:23] -** TODO [#D] Add test document to test graphviz-dot-mode -** TODO [#D] Consider Replicating Some of This Auctex and PDF Tools Code -[[https://www.reddit.com/r/emacs/comments/cd6fe2/how_to_make_emacs_a_latex_ide/][How to make Emacs a Latex IDE? : r/emacs]] -Captured On: [2025-08-14 Thu 03:43] -** TODO [#D] Install ZOxide Integration into Emacs -[[https://gitlab.com/Vonfry/zoxide.el][Vonfry / zoxide.el · GitLab]] -Captured On: [2025-06-07 Sat 17:11] -** TODO [#D] manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer. -[[https://github.com/manateelazycat/awesome-tray][manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer.]] -Captured On: [2025-06-07 Sat 13:29] -** TODO [#D] ryuslash/mode-icons: Show icons instead of mode names -** TODO [#B] Get Dupre Theme Working :bug: -*** AI Suggestioms -Here are some ideas for making dupre-theme a bit more “complete” and future-proof as an Emacs theme. You don’t have to do all of them, of course, but most “modern” themes ship a fair number of these extra faces and integrations. -**** TODO [#A] Fill out the “standard” Emacs faces -• error / warning / success -• link / link-visited / shadow -• highlight / secondary-selection -• tooltip / tooltip-selection / tooltip-tip -• underline / strike-through -• match / search / lazy-highlight (you already have some isearch) -• info-title-1…info-title-5, info-header-node, help-*-face -**** TODO [#A] Cosmetic / code-style notes -• Factor out a helper macro to reduce repetition: -#+begin_src elisp - (defmacro dupre-face (face &rest specs) - `(,face ((t ,specs)))) -#+end_src -• Use =custom-theme-set-faces= only once; wrap your =when (>= emacs-major-version…)= inside it rather than nesting top-level. -• Move your ERT tests into a separate file like =dupre-theme-test.el= so that the main theme file stays lean. -**** TODO [#B] Make sure you define a “minimum package” set of faces so that folks don’t see glaring unstyled text in any part of Emacs, even if they never install extra packages. -**** TODO [#B] Improve terminal support: -• Detect true-color (=(display-graphic-p)=) vs 256-color vs 16-color and fall-back gracefully. -• Provide “256-color” approximations (you already do this), but you might want a simpler palette for legacy terminals. -**** TODO [#B] Other Packages -The rest is just more of the same—pick the most common modes/packages you and your users rely on and give them a quick coat of paint. Once you’ve rounded out those faces you’ll have hit what most people expect from a “complete” modern Emacs theme. -Based on a quick skim of your modules/ directory (and the packages they pull in via use-package), here’s a non-exhaustive list of the major packages you’re using—along with the faces you should think about adding to dupre-theme so that nothing ends up looking “unstyled.” -1. completion/mini-frameworks - • vertico (vertico-current, vertico-group-title, vertico-separator, vertico-doc) - • orderless (orderless-match-face-0…3) - • consult (consult-preview-line, consult-narrow, consult-region, consult-file) - • marginalia (marginalia-annotation, marginalia-document, marginalia-cache) - • embark (embark-general, embark-heading, embark-target, embark-helpful) - • corfu + cape (corfu-default, corfu-current, corfu-bar, cape-file, cape-keyword etc.) - • company (company-tooltip, company-tooltip-selection, company-scrollbar-thumb) -2. ivy/counsel/swiper (if you ever load them side-by-side) - • ivy-current-match, ivy-minibuffer-match-face-1…4 - • counsel-outline, swiper-match-face-1… -3. syntax checkers & LSP - • flycheck (flycheck-error, flycheck-warning, flycheck-info) - • eglot or lsp-mode (lsp-face-highlight-textual, lsp-face-highlight-read, lsp-face-semantic-type-…) - • tree-sitter (tree-sitter-hl-face:function, :variable, :property, …) -4. git & diffs - • magit (magit-section, magit-section-title, magit-hunk-heading, magit-branch, magit-log) - • diff-hl / git-gutter (diff-hl-face-insert, diff-hl-face-delete, git-gutter:added, git-gutter:deleted) - • diff-mode (diff-added, diff-removed, diff-context, diff-file-header) -5. project management & navigation - • projectile (projectile-project-name, projectile-project-root) - • treemacs (treemacs-root-face, treemacs-directory-face, treemacs-file-face) -6. Org-mode - • org-document-title, org-level-1…org-level-8 - • org-checkbox, org-todo, org-done, org-warning, org-date, org-code, org-block - • plus any “pretty” add-ons you use (org-bullets, org-modern, etc.) -7. Hydra / which-key / key-help - • which-key (which-key-key-face, which-key-group-description-face, which-key-local-map-description-face) - • hydra (hydra-face-red, hydra-face-blue, hydra-face-teal, hydra-face-amaranth) -8. visual helpers - • rainbow-delimiters-depth-1…9 - • highlight-indent-guides-odd/even - • highlight-parentheses-/ - • beacon (beacon), whitespace (whitespace-space, whitespace-tab, whitespace-newline) - • display-line-numbers (line-number, line-number-current-line—you already have these) -9. UI polishers - • all-the-icons (all-the-icons-blue, all-the-icons-red, …) on modeline or treemacs - • doom-modeline (doom-modeline-bar, doom-modeline-buffer-path) - • fancy-splash/dashboard-faces if you use a startup screen -10. terminals & shells - • vterm (vterm-copy-region-highlight) - • ansi-colors (ansi-color-face-bold, ansi-color-face-italic, ansi-color-[0…7]) -To figure out exactly which ones you have in your modules, you can do for example: -#+begin_src elisp - ;; from your ~/.emacs.d - (let (pkgs) - (dolist (f (directory-files "~/.emacs.d/modules" t "\\.el$")) - (with-temp-buffer - (insert-file-contents f) - (goto-char (point-min)) - (while (re-search-forward "use-package +\\([^ ]+\\)" nil t) - (push (intern (match-string 1)) pkgs)))) - (delete-dups pkgs)) -#+end_src -Then for each of those packages, grep in their repo for =defface= or check their manual to see which faces they define, and add reasonable dupre-colours for them. Once you’ve covered that list, you’ll have a “complete” theme that won’t leave any of your modules half-unstyled. -**** TODO [#C] Support the built-in “tab-bar” and “tab-group” faces (Emacs 27+) -• tab-bar / tab-bar-tab / tab-bar-tab-inactive -• tab-bar-close / tab-bar-separator -• tab-group-title / tab-group-separator -**** TODO [#C] Add faces for =dired=, =diff=, =flycheck=/=flymake=, =magit= (or git-gutter), -=ediff=, =erc= etc. For example: -• diff-added, diff-removed, diff-context, diff-file-header -• flycheck-error, flycheck-warning, flycheck-info -• magit-section, magit-log, magit-hunk-heading, magit-branch, etc. -**** TODO [#C] Org-mode -• org-level-1…org-level-8, org-document-title -• org-checkbox, org-code, org-date, org-todo, org-done, org-warning -• org-block, org-block-begin-line, org-block-end-line -**** TODO [#C] Completion frameworks & minibuffer enhancements -• ivy-current-match / ivy-minibuffer-match-face-1…4 -• swiper-line-face -• vertico-current / marginalia-… -• corfu-default / corfu-bar / cape / orderless-match-face-… -**** TODO [#C] LSP / tree-sitter / syntax-aware faces -• lsp-face-highlight-textual, lsp-face-highlight-read, etc. -• tree-sitter-hl-face:variable, :function, :property, etc. -**** TODO [#C] Which-Key, Hydras, rainbow-delimiters, etc. -• which-key-key-face, which-key-local-map-description-face -• hydra-face-red / teal / blue -• rainbow-delimiters-depth-1…9 -**** TODO [#C] Theme Metadata & Packaging -• Add =;; Package-Requires: ((emacs "26.1"))= if you need a minimum. -• Consider adding a README with screenshots. -• A simple Makefile or script to refresh “theme metadata” (=M-x theme-inspect=, MELPA recipe). -**** TODO [#C] Tests -• Extend your ERT suite to cover more faces (org, diff, flycheck…). -• You might write a small helper that loops over a list of faces and asserts they’re defined without errors. -**** Example Sample Faces -Below is a small excerpt showing how you might start adding some of the “missing” standard faces and a couple org faces: -#+begin_src elisp - (custom-theme-set-faces - 'dupre - ;; … your existing faces … - ;; standard global faces - `(error ((t (:foreground ,dupre-intense-red :weight bold)))) - `(warning ((t (:foreground ,dupre-yellow+1 :weight bold)))) - `(success ((t (:foreground ,dupre-green+1 :weight bold)))) - `(link ((t (:foreground ,dupre-blue+1 :underline t)))) - `(shadow ((t (:foreground ,dupre-gray)))) - `(highlight ((t (:background ,dupre-bg+1)))) - `(tooltip ((t (:foreground ,dupre-fg :background ,dupre-bg+2)))) - `(tooltip-selection ((t (:background ,dupre-bg+0)))) - ;; Org-mode sample - `(org-document-title ((t (:foreground ,dupre-yellow+2 :weight bold :height 1.2)))) - `(org-level-1 ((t (:foreground ,dupre-blue+2 :weight bold)))) - `(org-level-2 ((t (:foreground ,dupre-green+1)))) - `(org-checkbox ((t (:foreground ,dupre-yellow :weight bold)))) - `(org-todo ((t (:foreground ,dupre-red :weight bold)))) - `(org-done ((t (:foreground ,dupre-green+2 :weight bold)))) - ) -#+end_src -** TODO [#B] Emacs Add Difftastic Functionality :enhancement: -Linux Binary Install -https://github.com/Wilfred/difftastic -https://www.wilfred.me.uk/blog/2022/09/06/difftastic-the-fantastic-diff/ -https://difftastic.wilfred.me.uk/installation.html (basically $ sudo pacman -S difftastic) -Emacs Package -https://github.com/pkryger/difftastic.el -#+begin_src emacs-lisp - (use-package difftastic - :defer t - :vc (:url "https://github.com/pkryger/difftastic.el.git" - :rev :newest)) -#+end_src -** TODO [#B] Emacs error if no file in buffer-and-file operations :bug: -** TODO [#B] Add Hugo Blogging Functionality :enhancement: ** TODO [#C] Emacs: Add Reverso Functionality :enhancement: https://sqrtminusone.xyz/packages/reverso/ check his config for a usage example @@ -1582,7 +1596,7 @@ reverso.el is a package of mine that provides Emacs interface for https://revers (setq reverso-languages '(russian english german spanish french portuguese)) (reverso-history-mode)) #+end_src -** Install Magit TODOs +** TODO [#C] Install Magit TODOs #+BEGIN_QUOTE It’s nice for magit to display in-tree TODOs in the status buffer: @@ -1594,7 +1608,7 @@ It’s nice for magit to display in-tree TODOs in the status buffer: #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:40] -** Move from company to corfu +** TODO [#C] Move from company to corfu #+BEGIN_QUOTE Completion A completion framework—I used to use company but corfu seems to integrate with stock Emacs better. @@ -1623,7 +1637,7 @@ Use more completion at point functions from cape. dabbrev completion means that #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:37] -** Config recentf to exclude mail +** TODO [#C] Config recentf to exclude mail #+BEGIN_QUOTE recentf provides a persistent list of recently-opened files. @@ -1636,7 +1650,7 @@ recentf provides a persistent list of recently-opened files. #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:36] -** consider installing minimap +** TODO [#C] consider installing minimap #+BEGIN_QUOTE A mini-map (miniaturised window showing more of the buffer than the main window) is nice for longer documents: @@ -1648,7 +1662,7 @@ A mini-map (miniaturised window showing more of the buffer than the main window) #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:31] -** check into org-download for drag/drop images into org-buffers +** TODO [#C] check into org-download for drag/drop images into org-buffers #+BEGIN_QUOTE Drag and drop images into Org buffers to insert links: @@ -1658,4079 +1672,25 @@ Drag and drop images into Org buffers to insert links: #+END_QUOTE [[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] Captured On: [2025-10-15 Wed 18:30] -* Emacs Config Ideas/References -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -Sometimes you want dummy text, say if you’re testing layouts for a poster but you haven’t written the text yet. Look no further than lorem-ipsum. - -(use-package lorem-ipsum - :init - (setq - lorem-ipsum-sentence-separator " " - lorem-ipsum-list-bullet "- ") - ;; map from C-c l - (lorem-ipsum-use-default-bindings)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:37] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -I think a vertical “chooser” interface is key, it get used a lot due to find-file, buffer switching etc. I’m happy with vertico, which I switched to from selectrum because it appeared to rely more on built-in Emacs functionality which I think is a good thing for ecosystem compatability and the sustainability of Emacs as a platform in the future. - -vertico-resize allows the minibuffer to grow and shrink to accomodate more or fewer candiates. vertico-cycle allows wrapping around the candidate list. - -(use-package vertico - :ensure (vertico :files (:defaults "extensions/*")) - :custom - (vertico-scroll-margin 0) - (vertico-count 10) - (vertico-resize t) - (vertico-cycle t) - :init - (vertico-mode) - (vertico-mouse-mode)) -vertico-directory extension. - -(use-package vertico-directory - :ensure nil - :after vertico - :bind (:map vertico-map - ("RET" . vertico-directory-enter) - ("DEL" . vertico-directory-delete-char) - ("M-DEL" . vertico-directory-delete-word)) - ;; Tidy shadowed file names - :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) -Use orderless searching, i.e M-x m o suggests org-mode. When completing at point in a buffer, after pressing TAB to bring up the completion interface, M-SPC allows you to insert a space in the query without breaking the completion. - -(use-package orderless - :custom - (completion-styles '(orderless flex basic)) - (completion-category-overrides '((file (styles . (partial-completion))) - (eglot (styles orderless))))) -consult offers some nice alternatives to standard Emacs interfaces based on completing-read. consult-buffer in particular is better than the stock command it replaces, providing recent files, bookmarks and buffers at-a-glance, with previews. - -(use-package consult - :config - (setq consult-fontify-max-size 1024) - :bind - ("C-x b" . consult-buffer) - ("C-c <C-m>" . consult-global-mark) - ("C-c C-a" . consult-org-agenda) - ("C-x O" . consult-outline) - ("M-g M-g" . consult-goto-line) - ("M-g i" . consult-imenu) - ("M-s s" . consult-line) - ("M-s M-s" . consult-line-multi)) -embark allows inverting the usual Emacs command flow. Generally, you pick a verb (like find-file) and then pick a noun (like a filename). With embark, you can change your mind—once a candidate selected, press embark-act to use a different verb. - -(use-package embark - :bind - (("C-." . embark-act) - ("M-." . embark-dwim) - ("C-h B" . embark-bindings)) - :custom - (prefix-help-command #'embark-prefix-help-command) - (embark-quit-after-action '((t . nil))) - :config - (add-to-list 'display-buffer-alist - '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" - nil - (window-parameters (mode-line-format . none))))) -embark-consult—additional integration of consult with embark. - -(use-package embark-consult - :after (embark consult) - :demand t ; only necessary if you have the hook below - ;; if you want to have consult previews as you move around an - ;; auto-updating embark collect buffer - :hook - (embark-collect-mode . consult-preview-at-point-mode)) -marginalia takes advantage of vertical choosers by annotating candidates with exta information, such as file properties or function docstrings, making Emacs more self-explanatory. - -(use-package marginalia - :defer t - :init - (marginalia-mode) - (setq marginalia-annotators '(marginalia-annotators-heavy nil))) - -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:34] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -Also show icons in find-file, consult-buffer etc. - -(use-package all-the-icons-completion - :after (marginalia all-the-icons) - :hook (marginalia-mode . all-the-icons-completion-marginalia-setup) - :init - (all-the-icons-completion-mode)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:32] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -I prefer a hybrid of plain text and WYSIWYG, so only show org emphasis markers if point is on the word. - -(use-package org-appear - :custom - (org-appear-autolinks t) - (org-appear-autosubmarkers t) - (org-appear-autoentities t) - (org-appear-autokeywords t) - :hook (org-mode . org-appear-mode)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:29] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -Org mode (and stock Emacs in general) can be quite ugly, org-modern helps a lot. - -(use-package org-modern - :config - (setq org-modern-keyword - '((t . t) - ("bibliography" . "") - ("cite_export" . "⮭") - ("include" . "⇤") - ("setupfile" . "⇚") - ("header" . "›") - ("caption" . "☰") - ("name" . "⁝") - ("results" . "∴"))) - (setq org-modern-block-name - '((t . t) - ("src" "»" "∥") - ("example" "»–" "∥") - ("quote" "❝" "❞"))) - :hook (org-mode . org-modern-mode)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:29] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -For HTML, I rarely want images to link to my on-disk versions, as this makes the file unportable. - -(defun jdm204-org-export-to-html-with-monolith () - "Export the current Org buffer to HTML and process it with Monolith." - (interactive) - (let* ((html-file (org-html-export-to-html))) - (shell-command (format "monolith %s -o %s" html-file html-file) nil nil) - (message "HTML exported and post-processed with Monolith to %s" html-file))) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:28] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -Refiling config. - -(setq org-refile-targets - '((nil :maxlevel . 3) - (org-agenda-files :maxlevel . 3))) -(setq org-refile-use-outline-path 'file) -(setq org-outline-path-complete-in-steps nil) -Org agenda config. - -(setq - org-directory my/org-dir - org-default-notes-file my/org-default-notes - org-agenda-files `(,my/org-dir) - org-agenda-span 'month - diary-file my/diary - org-agenda-prefix-format '((agenda . " %i %-10:c%-12t %-6e %s") (todo . " %i %-12:c") - (tags . " %i %-12:c") (search . " %i %-12:c")) - org-agenda-scheduled-leaders '("Scd: " "Scd.%2dx: ") - org-agenda-skip-scheduled-if-done t - org-agenda-skip-deadline-if-done t - org-agenda-compact-blocks t) -Custom agenda commands: - -(setq org-agenda-custom-commands - '(("w" "Weekly Review" - ((agenda "" - ((org-agenda-skip-function '(org-agenda-skip-entry-if 'nottodo 'done)) - (org-agenda-span 'week))) - (agenda "" - ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) - (org-agenda-span 'week))))) - ("d" "Dashboard View" - ((agenda "" - ((org-agenda-span 'week) - (org-agenda-overriding-header "\n--- Time-Sensitive & Scheduled Tasks ---"))) - (alltodo "" - ((org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled 'deadline)) - (org-agenda-overriding-header "\n--- General TODOs (Ready to be Worked On) ---"))))) - ("r" "Review/Triage Inbox" - ((tags ".*" ((org-agenda-files '("~/cloud/org/inbox.org")))))))) -Org agenda grouping functionality: - -(use-package org-super-agenda - :init (org-super-agenda-mode) - :config - (setq org-super-agenda-groups - '((:name "Scheduled" :time-grid t) - (:name "Important" :priority "A" :priority "B") - (:name "Personal" :tag "home" :tag "personal") - (:name "Low Priority" :priority<= "D" :order 100)))) -A mini agenda with calendar: - -(use-package nano-agenda - :bind ("C-c C" . nano-agenda)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:26] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -Present org sub-trees from Emacs: - -(use-package org-tree-slide - :custom - (org-tree-slide-slide-in-effect nil) - :bind (:map org-mode-map - ("C-c P" . org-tree-slide-mode))) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:25] -** config.org · master · jdm204 / dotfiles · GitLab -#+BEGIN_QUOTE -More org blocks, notably including a details block, which is foldable, can include src and other blocks inside it, and exports (at least in HTML) to a hidden-by-default, revealable panel. - -(use-package org-special-block-extras - :init (org-special-block-extras-mode)) -#+END_QUOTE -[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]] -Captured On: [2025-10-15 Wed 18:25] - -** My PDF Tools Settings -[[https://emacselements.com/pdf-tools-settings.html][My PDF Tools Settings]] -Captured On: [2025-09-03 Wed 11:49] -** Sound File Names -- [X] Beacon -- [X] BetaReduce -- [X] Bitrot -- [ ] Block -- [ ] BlockShift -- [ ] Cache -- [ ] Cluster -- [X] ContextSwitch -- [ ] Continuation -- [ ] Cycle -- [ ] Daemon -- [ ] Deadlock -- [ ] Diskette -- [X] Functor -- [ ] GarbageCollect -- [ ] Gate -- [ ] Halt -- [ ] Handshake -- [ ] HeapSpray -- [ ] Kernel -- [X] Lambda -- [ ] Latch -- [ ] Lex -- [X] LinkUp -- [ ] Monad -- [X] Mutex -- [ ] Nibble -- [ ] Node -- [ ] PageFault -- [ ] Parse -- [ ] PointerChase -- [ ] PortOpen -- [ ] Preempt -- [ ] Pulse -- [ ] SectorZero -- [ ] Segfault -- [ ] Socket -- [ ] Spark -- [ ] Spinlock -- [ ] Stream -- [ ] Thunk -- [ ] Token -- [ ] Trapframe -- [X] Bitflip -- [X] CacheHit -- [X] Futex -- [X] Macroexpand -- [X] Opcode -- [X] Packet -- [X] Ping -- [X] RaceCondition -- [X] Syscall -- [X] WakeSignal -** emacs-tw/awesome-elisp: A curated list of Emacs Lisp development resources -[[https://github.com/emacs-tw/awesome-elisp][emacs-tw/awesome-elisp: 🏵️ A curated list of Emacs Lisp development resources]] -Captured On: [2025-06-07 Sat 13:42] -** emacs-tw/awesome-emacs: A community driven list of useful Emacs packages, libraries and other items. -[[https://github.com/emacs-tw/awesome-emacs#layout][emacs-tw/awesome-emacs: A community driven list of useful Emacs packages, libraries and other items.]] -Captured On: [2025-06-07 Sat 13:30] -** bastibe/org-static-blog: A static site generator using org-mode -[[https://github.com/bastibe/org-static-blog][bastibe/org-static-blog: A static site generator using org-mode]] -Captured On: [2025-05-29 Thu 04:32] -** Better org-mode Agenda display-buffer-alist Settings • Christian Tietze -[[https://christiantietze.de/posts/2022/12/updated-org-mode-agenda-display-buffer-alist/][Better org-mode Agenda display-buffer-alist Settings • Christian Tietze]] -Captured On: [2025-08-01 Fri 03:21] -** org mode - org-link to the exact page-position in a pdf file - Emacs Stack Exchange -[[https://emacs.stackexchange.com/questions/68013/org-link-to-the-exact-page-position-in-a-pdf-file][org mode - org-link to the exact page-position in a pdf file - Emacs Stack Exchange]] -Captured On: [2025-07-11 Fri 18:17] -** Craft an Email Workflow with Org Mode - YouTube -[[https://www.youtube.com/watch?v=dSZu4jwvaSs][Craft an Email Workflow with Org Mode - YouTube]] -Captured On: [2025-08-30 Sat 11:42] -** Enhance Your Emails with Org Mode - System Crafters -[[https://systemcrafters.net/emacs-mail/enhance-email-with-org-mode/][Enhance Your Emails with Org Mode - System Crafters]] -Captured On: [2025-08-30 Sat 11:42] -** Emacs As Mail Client Specifically as Mu4E | Unixbhaskar's Blog -[[https://unixbhaskar.wordpress.com/2023/09/05/emacs-as-mail-client-specifically-as-mu4e/][Emacs As Mail Client Specifically as Mu4E | Unixbhaskar's Blog]] -Captured On: [2025-08-29 Fri 16:12] -** Blogging with Emacs, and Emacs only | Diego Vicente -[[https://diego.codes/post/blogging-with-org/][Blogging with Emacs, and Emacs only | Diego Vicente]] -Captured On: [2025-08-18 Mon 17:57] -** Using Emacs and Org-mode as a static site generator -[[https://ogbe.net/blog/emacs_org_static_site][Using Emacs and Org-mode as a static site generator]] -Captured On: [2025-08-18 Mon 17:54] -** The best latex Editor : r/emacs -[[https://www.reddit.com/r/emacs/comments/akmwko/the_best_latex_editor/][The best latex Editor : r/emacs]] -Captured On: [2025-08-13 Wed 19:29] -** gregoryg/emacs-gregoryg: My emacs settings for use across Linux, Windows, OS X -[[https://github.com/gregoryg/emacs-gregoryg?tab=readme-ov-file#gptel---llms-in-markdown-and-org-mode][gregoryg/emacs-gregoryg: My emacs settings for use across Linux, Windows, OS X]] -Captured On: [2025-08-12 Tue 16:31] -** Managing BibTeX Files with Emacs – Nick Higham -[[https://nhigham.com/2016/01/06/managing-bibtex-files-with-emacs/][Managing BibTeX Files with Emacs – Nick Higham]] -Captured On: [2025-09-25 Thu 02:46] -** onebirdrocks/ebook-mcp: A MCP server that supports mainstream eBook formats including EPUB, PDF and more. Simplify your eBook user experience with LLM. -[[https://github.com/onebirdrocks/ebook-mcp][onebirdrocks/ebook-mcp: A MCP server that supports mainstream eBook formats including EPUB, PDF and more. Simplify your eBook user experience with LLM.]] -Captured On: [2025-09-24 Wed 23:53] -** Battleship Game in Emacs -[[http://www.catb.org/~esr/bs/][Resource page for bs 2.13]] -Captured On: [2025-09-24 Wed 00:38] -** pinoaffe/org-vcard: Export and import vCards from within GNU Emacs' Org mode. -[[https://github.com/pinoaffe/org-vcard][pinoaffe/org-vcard: Export and import vCards from within GNU Emacs' Org mode.]] -Captured On: [2025-09-23 Tue 10:46] -** awth13/org-appear: Toggle visibility of hidden Org mode element parts upon entering and leaving an element -[[https://github.com/awth13/org-appear][awth13/org-appear: Toggle visibility of hidden Org mode element parts upon entering and leaving an element]] -Captured On: [2025-09-19 Fri 12:02] -** screensaver.el/screensaver.el at master · larsmagne/screensaver.el -[[https://github.com/larsmagne/screensaver.el/blob/master/screensaver.el][screensaver.el/screensaver.el at master · larsmagne/screensaver.el]] -Captured On: [2025-09-18 Thu 15:47] -** Capturing Content for Emacs -#+BEGIN_QUOTE -Capturing Content for Emacs - -Let’s suppose you are current investigating a new code base, system or other problem, and you are following my advice and copying code, storing output, and taking notes along the way. All of this gets stored into your engineering notebook, aka primary org mode file (for me, this is often my current Sprint page). - -Sure, selecting code, switching buffers or windows, pasting the code (maybe even jotting down some notes), and then popping back to your original file, may not be many keystrokes, but it exacts a bit of mental tax that mounts. - -The typical solution to this problem is to use the org-capture feature (If you are not familiar with this Org feature, check out my gentle introduction or see Step 3 of Sacha Chua’s essay, Learn how to take notes more efficiently in Org Mode). While org-capture makes copying content into your org file easy, I am trying to improve on it, and here some of my experiments. - -The “Current” Task - -One mentally taxing aspect of org-capture is determining where something should go. Do you have a dozen (file) reference destinations? I have found the (clock) reference ideal for altering a default destination. Specifically, I begin work on a task, and designate it the focus of my attention (i.e. the destination of my work), by clocking in, using org-clock-in (C-c C-x C-i or , I in Spacemacs). - -Now, we can add the following to the org-capture list: - -(add-to-list 'org-capture-templates -`("c" "Item to Current Clocked Task" item -(clock) -"%i%?" :empty-lines 1)) - - -This capture destination allows me to easily specify any header as a special destination with a simple clock in. However, we do have the mental interruption associated with creating a new buffer. Let’s minimize that by allowing us to put something on the kill ring, and send it to that clocked-in task: - -(add-to-list 'org-capture-templates -`("K" "Kill-ring to Current Clocked Task" plain -(clock) -"%c" :immediate-finish t :empty-lines 1)) - - -The trick here is the use of :immediate-finish, where it doesn’t even bother with a buffer, but just injects the kill-ring contents to the clocked in task without even a sneeze. Don’t want the hassle of sending something to the kill-ring? With this one, you only have to select the text, then kick off the capture: - -(add-to-list 'org-capture-templates -`("C" "Contents to Current Clocked Task" plain -(clock) -"%i" :immediate-finish t :empty-lines 1)) - - -In fact, create the following function and keybinding, and you can select text, and immediately copy it to your clocked in task without bothering with the org-capture menu: - -(defun region-to-clocked-task (start end) -"Copies the selected text to the currently clocked in org-mode task." -(interactive "r") -(org-capture-string (buffer-substring-no-properties start end) "C")) - -(global-set-key (kbd "C-<F17>") 'region-to-clocked-task) - - -This is great for general textual content, but much of what I want to copy is code, which could bring along a bit of meta data. - -Code References - -Much of my ideas got started after reading this blog entry where the idea is to have a function gather meta data associated with the currently selected text, and help to leave a back trace to the original code file. - -I wanted to copy both code and regular text, so I made ha/org-capture-clip-snippet for wrapping the region in an EXAMPLE: - -(defun ha/org-capture-clip-snippet (f) -"Given a file, F, this captures the currently selected text -within an Org EXAMPLE block and a backlink to the file." -(with-current-buffer (find-buffer-visiting f) -(ha/org-capture-fileref-snippet f "EXAMPLE" "" nil))) - - -And ha/org-capture-code-snippet for getting function name and the code type: - -(defun ha/org-capture-code-snippet (f) -"Given a file, F, this captures the currently selected text -within an Org SRC block with a language based on the current mode -and a backlink to the function and the file." -(with-current-buffer (find-buffer-visiting f) -(let ((org-src-mode (replace-regexp-in-string "-mode" "" (format "%s" major-mode))) -(func-name (which-function))) -(ha/org-capture-fileref-snippet f "SRC" org-src-mode func-name)))) - - -Both of these function do not do much, but given some values to Nick’s original function (which I’ve modified the format to fit my personal style): - -(defun ha/org-capture-fileref-snippet (f type headers func-name) -(let* ((code-snippet -(buffer-substring-no-properties (mark) (- (point) 1))) -(file-name (buffer-file-name)) -(file-base (file-name-nondirectory file-name)) -(line-number (line-number-at-pos (region-beginning))) -(initial-txt (if (null func-name) -(format "From [[file:%s::%s][%s]]:" -file-name line-number file-base) -(format "From ~%s~ (in [[file:%s::%s][%s]]):" -func-name file-name line-number -file-base)))) -(format " -%s - -#+BEGIN_%s %s -%s -#+END_%s" initial-txt type headers code-snippet type))) - - -However, content I want to store in an org-mode comes from more than just Emacs buffers. - -Output from Terminal Commands - -What if the end result of a command sequence on the Terminal was a pipe to a program that could use cat to gather textual data from standard input, and then use emacsclient call org-capture to store it? - -Yeah, and interesting idea when sent to the current clocked in task: - -#!/bin/bash - -TITLE="$*" -CONTENT=" -#+BEGIN_EXAMPLE -$(cat | sed 's/^/ /g') -#+END_EXAMPLE -" - -if [[ -n $TITLE ]] -then -CONTENT=" - ${TITLE}\n${CONTENT}" -fi - -/usr/local/bin/emacsclient -c -n \ --e "(progn (org-capture-string \"$CONTENT\" \"C\") (delete-frame))" - - -Here I’m using our latest C capture template to that just takes textual context and stores is. Let’s try it in action by typing the following in a shell: - -date | ec - - -Works like a charm: - -#+BEGIN_EXAMPLE -Thu Jun 7 22:45:23 PDT 2018 -#+END_EXAMPLE - -Content from Browsers - -Like many software people, I have a love-hate relationship with browsers. I often find myself copying/pasting information from a web site into my engineering notebook. Pasting text data into an org-mode file looses all text formatting as well as hyperlink references. But operating system clipboards can store some of this formatting data, so we just need to tap into it. - -The downside is that accessing this information is operating system dependent… - -Version for Mac - -Let’s start from the top and work our way down. I need a way, outside of Emacs, to run a command to copy the selected region to the clipboard, and then use emacsclient to start a function to copy that information into the currently clocked in task. - -I use Alfred to start a Workflow, as it will allow me to trigger these scripts in succession as shown in this diagram: - -The trigger (in this case, just about every meta-key on a laptop), will start the first script that basically issues the Command-C to copy the selected text to the clipboard: - -tell application "System Events" to keystroke "c" using command down - - -This works with any Mac application, including browsers. - -The next script basically takes the contents of the clipboard (as HTML), render that to an org-compatible format with pandoc (which you’ll need to install), and then use emacsclient to call my org-capture routine with the “C” selection, so that the contents go directly to my clocked in task. My first attempt was a modified version from Roland Crosby: - -query=$(osascript -e 'the clipboard as "HTML"' | \ -perl -ne 'print chr foreach unpack("C*",pack("H*",substr($_,11,-3)))' | \ -/usr/local/bin/pandoc -f html -t org | \ -sed 's/"//g' | sed 's/^/ /' ) - -/usr/local/bin/emacsclient -c -n \ --e "(progn (org-capture-string \"${query}\" \"C\") (delete-frame))" - - -While the above code works well from a browser, if I copy text from something else (like Slack), the as "HTML" bit throws an error, as the clipboard contains plain text. After working on a fairly involved Perl script (that didn’t handle Unicode characters well), I ended up writing the entire part in Emacs Lisp. Now the shell script is nothing more than: - -/usr/local/bin/emacsclient -c -n -e "(ha/external-capture-to-org)" - - -Unpacking Apple’s encoding plus dealing with both HTML and Text content was a bit more involved, but you can see the functions on Gitlab. The end result is great. After selecting some text on the homepage at orgmode.org, and clocked this header as my current task, I ended up with this getting pasted: - -The stable version of Org is *9.1.13*, as of May 2018. See the -[[https://orgmode.org/Changes.html][release notes]]. - -Get it with =M-x package-install RET org RET= (see -[[https://orgmode.org/elpa.html][Org ELPA]]). - -Or download it as a [[https://orgmode.org/org-9.1.13.tar.gz][tar.gz]] or -[[https://orgmode.org/org-9.1.13.zip][zip]] archives. - - -Keep in mind, that this copy/pasting business happens completely in the background while I am still surfin’ the web. - -Version for Linux - -Like everything, I suppose, getting this feature working on my Linux laptop is both easier and harder. Unlike the Mac, I can find no way to automatically copy the current selection to the clipboard like I can with an Applescript. However, once the content is on the clipboard, I can more easily grab it and throw it into Emacs. - -Under the Keyboard section of the Settings app (at least on Ubuntu), you can create shortcuts to run commands: - -Begin by scrolling to the bottom of the panel, and selecting the + button, and assigning your favorite hot-key (I decided to hold down the Control, Alt, and Super keys along with V since that seems most memorable to me), and have it run our function: - -Of course, we’ll need to modify our function to use xclip. This isn’t install by default on Ubuntu (but neither is pandoc), so install those first: - -sudo apt install xclip pandoc -y - - -Now, let’s create a function to call xclip, and like before, we ask for HTML, and if it fails, we’ll get it as regular text: - -(defun ha/get-linux-clipboard () -"Return the clipbaard for a Unix-based system. See `ha/get-clipboard'." -(destructuring-bind (exit-code contents) -(shell-command-with-exit-code "xclip" "-o" "-t" "text/html") -(if (= 0 exit-code) -(list :html contents) -(list :text (shell-command-to-string "xclip -o"))))) - - -Since I want my same code to work with both my Mac and my Linux systems, I create a simple little dispatcher: - -(defun ha/get-clipboard () -"Returns a list where the first entry is the content type, -either :html or :text, and the second is the clipboard contents." -(if (eq system-type 'darwin) -(ha/get-mac-clipboard) -(ha/get-linux-clipboard))) - - -The rest of the code is same. See Gitlab for file updates. - -Summary - -My workflow proposal amounts to gathering data from a web browser, shell commands, and source code, and be able to fling it into my engineering notebook without switching out of that application. - -Later, I will return to my notebook in Emacs and clean up and summarize my capturing. Once clean, the issues or knowledge I wish to share can then be easily exported from org. - -The side-benefit, is that I automatically remind myself to clock in to my task. - - -#+END_QUOTE -[[https://howardism.org/Technical/Emacs/capturing-content.html][Capturing Content for Emacs]] -Captured On: [2025-10-03 Fri 08:59] -** A Refined Emacs LLM Environment with gpt.el & mcp.el | Kaoru's Blog -[[https://blog.kaorubb.org/en/posts/gpt-mcp-setup/][A Refined Emacs LLM Environment with gpt.el & mcp.el | Kaoru's Blog]] -Captured On: [2025-10-08 Wed 18:22] -** Org as a spreadsheet system: a short introduction -[[https://orgmode.org/worg/org-tutorials/org-spreadsheet-intro.html][Org as a spreadsheet system: a short introduction]] -Captured On: [2025-09-16 Tue 03:36] -** Capturing Content for Emacs -[[https://www.howardism.org/Technical/Emacs/capturing-content.html][Capturing Content for Emacs]] -Captured On: [2025-09-16 Tue 03:46] -** howardabrams/hamacs: My personal VIM-like configuration of Emacs inspired by Doom and Spacemacs. -[[https://github.com/howardabrams/hamacs/tree/main][howardabrams/hamacs: My personal VIM-like configuration of Emacs inspired by Doom and Spacemacs.]] -Captured On: [2025-09-16 Tue 04:35] -** Creating Timestamps (The Org Manual) -[[https://orgmode.org/manual/Creating-Timestamps.html][Creating Timestamps (The Org Manual)]] -Captured On: [2025-09-17 Wed 12:04] -** jgru/consult-org-roam: A bunch of convenience functions for operating org-roam with the help of consult -[[https://github.com/jgru/consult-org-roam][jgru/consult-org-roam: A bunch of convenience functions for operating org-roam with the help of consult]] -Captured On: [2025-09-08 Mon 12:25] -** Sending Emacs Emails Asynchronously - -dotfiles/emacs/.emacs.d/readme.org at 3aa87cc5c2e57895c03ec8b716065a9a6e56614a · joedicastro/dotfiles -#+BEGIN_QUOTE -Sending emails asynchronous - -This is useful to send emails with attachments and do not block emacs until end the transmission. - -(use-package smtpmail-async -:config -(setq -send-mail-function 'async-smtpmail-send-it -message-send-mail-function 'async-smtpmail-send-it)) -#+END_QUOTE -[[https://github.com/joedicastro/dotfiles/blob/3aa87cc5c2e57895c03ec8b716065a9a6e56614a/emacs/.emacs.d/readme.org#L3973][dotfiles/emacs/.emacs.d/readme.org at 3aa87cc5c2e57895c03ec8b716065a9a6e56614a · joedicastro/dotfiles]] -Captured On: [2025-09-11 Thu 21:28] -** Update the header symbols in mu4e view -dotfiles/emacs/.emacs.d/readme.org at 3aa87cc5c2e57895c03ec8b716065a9a6e56614a · joedicastro/dotfiles -#+BEGIN_QUOTE -;; more cool and practical than the default -(setq mu4e-headers-from-or-to-prefix '("" . "➜ ")) -#+END_QUOTE -[[https://github.com/joedicastro/dotfiles/blob/3aa87cc5c2e57895c03ec8b716065a9a6e56614a/emacs/.emacs.d/readme.org#L3973][dotfiles/emacs/.emacs.d/readme.org at 3aa87cc5c2e57895c03ec8b716065a9a6e56614a · joedicastro/dotfiles]] -Captured On: [2025-09-11 Thu 21:29] -** Add interactive delete-from alist-by-key -#+BEGIN_QUOTE -2.16.5. other tools -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -;; why isn't it a part of standart lib -(defun delete-from-alist-by-key (list-var elem) -"Interactively remove element from alist." -(interactive -(let ((var (helpful--read-symbol -"Variable: " -(helpful--variable-at-point) -#'(lambda (symb) (and (boundp symb) (listp (symbol-value symb)) (helpful--variable-p symb)))))) -(list var -(completing-read "Choose element to delete from: " (symbol-value var) nil t)))) -(message "deleting %s from %s=%s" elem list-var (symbol-value list-var)) -(set list-var (assoc-delete-all elem (symbol-value list-var)))) -#+END_QUOTE -[[https://codefastdieyoung.com/knowledge-base/emacs-config/][Emacs config - codefastdieyoung]] -Captured On: [2025-09-11 Thu 22:20] -** Google Translate Integration -#+BEGIN_QUOTE -; crazy, finally it works -(use-package google-translate -:disabled -:ensure -;:init -;(require 'google-translate) -;(require 'google-translate-smooth-ui) -;(defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130)) -:functions (google-translate--search-tkk) -:config -(require 'google-translate-smooth-ui) -(defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130)) -;todo - fix minibuffer keymap with evil for directions -;(evil-define-key '(motion normal insert) minibuffer-local-map) -:custom -(google-translate-input-method-auto-toggling t) -(google-translate-preferable-input-methods-alist '((nil . ("en")) (ukrainian-computer . ("ru" "uk")))) -(google-translate-translation-directions-alist '(("uk" . "en") ("ru" . "en") ("en" . "uk"))) -;(google-translate--tkk-url "http://translate.google.com/") -;(google-translate-base-url "http://translate.google.com/") -;(google-translate-backend-debug t) -(google-translate-backend-method 'curl) -(google-translate-show-phonetic t) -:bind -("C-c C-t t" . google-translate-smooth-translate) -("C-c C-t p" . google-translate-at-point) -("C-c C-t q" . google-translate-query-translate) -("C-c C-t d" . google-translate-query-translate-using-directions) -) -#+END_QUOTE -[[https://codefastdieyoung.com/knowledge-base/emacs-config/][Emacs config - codefastdieyoung]] -Captured On: [2025-09-11 Thu 22:36] -** dot-files/emacs-eshell.org at master · howardabrams/dot-files · GitHub -[[https://github.com/howardabrams/dot-files/blob/master/emacs-eshell.org][dot-files/emacs-eshell.org at master · howardabrams/dot-files · GitHub]] -Captured On: [2025-09-11 Thu 23:34] -** dot-files/emacs-elisp.org at master · howardabrams/dot-files · GitHub -#+BEGIN_QUOTE -Suggesting -Intrigued to play with Wilfred Hughes’ project, suggest: - -(use-package suggest -:bind ((:prefix-map lisp-evaluation-map -:prefix "C-c e" -("s" . suggest)))) -#+END_QUOTE -[[https://github.com/howardabrams/dot-files/blob/master/emacs-elisp.org][dot-files/emacs-elisp.org at master · howardabrams/dot-files · GitHub]] -Captured On: [2025-09-11 Thu 23:30] -** dot-files/emacs-client.org at master · howardabrams/dot-files · GitHub -#+BEGIN_QUOTE -Dimming and Finding Cursor -When I type, I would rather not see the cursor, for at the point where I am typing, I know where I am typing. Of course, when I’m moving around the screen (or even when Emacs is idle), I want to know clearly the location of cursor. - -I’ve previously made the cursor an obnoxious color, and let’s save that value: - -(defvar cursor-initial-color (face-attribute 'cursor :background) -"The initial value of the cursor that will be use when re-showing it.") -If I type a character or maybe two, I don’t want to hide the cursor… only when I start to really get typing: - -(defvar cursor-number-chars-before-hiding 2 -"The number of characters one needs to type before we hide the cursor.") -Of course, I need to have some sort of state variable that counts the number of characters that have been typed: - -(defvar cursor-current-number-chars 0) -Now let’s have a couple of functions that will show and hide the cursor (or at least, lessen its visibility): - -(defun cursor-hide (&optional arg) -"Hides cursor by setting the color to the background color. -This only happens after a number of characters have been -typed (defined by the `cursor-number-chars-before-hiding' -variable)." -(setq cursor-current-number-chars (1+ cursor-current-number-chars)) -(when (> cursor-current-number-chars cursor-number-chars-before-hiding) -(set-cursor-color (or (background-color-at-point) "#1d1f21")))) - -(defun cursor-show () -"Shows the cursor. At least, sets it to a particular -color (stored/defined in `cursor-initial-color'. Note: This also -resets the `cursor-current-number-chars' variable, so that it -won't be hid until a number of characters have been typed." -(setq cursor-current-number-chars 0) -(set-cursor-color cursor-initial-color)) -After entering a text that is inserted into a buffer, hide the cursor by using some advice: - -(advice-add 'self-insert-command :after #'cursor-hide) -After I’m done typing (that is, when the cursor is idle), show the cursor: - -(setq my-cursor-timer (run-with-idle-timer 1 t #'cursor-show)) -Let’s make the cursor more noticeable whenever the window scrolls (which seems to be the time when I need to visually look for it): - -(use-package beacon -:ensure t -:defer t -:diminish 'beacon-mode -:init (beacon-mode +1) -:config (setq beacon-color cursor-initial-color)) -#+END_QUOTE -[[https://github.com/howardabrams/dot-files/blob/master/emacs-client.org][dot-files/emacs-client.org at master · howardabrams/dot-files · GitHub]] -Captured On: [2025-09-11 Thu 23:23] -** dot-files/emacs-browser.org at master · howardabrams/dot-files · GitHub -#+BEGIN_QUOTE -External Web Browsing -Need to be able to switch and have a link in an org-mode file show up in the default, graphical browser: - -(defun ha-switch-default-browser () -"Switches the default browser between the internal and external web browser." -(interactive) -;; | Variable | Function -(if (equal browse-url-browser-function 'browse-url-default-browser) -(if (fboundp 'w3m) -(setq browse-url-browser-function 'w3m-browse-url) -(setq browse-url-browser-function 'eww-browse-url)) -(setq browse-url-browser-function 'browse-url-default-browser)) - -;; Now we need to display the current setting. The variables are -;; pretty typical and have the goodies, but I just need to get rid -;; of the word "url" or "browser", and the results are pretty close: -(cl-flet ((remove-bad-parts (l) -(-filter (lambda (s) (pcase s -("url" nil) -("browse" nil) -("browser" nil) -(_ t))) l))) -(message "Browser set to: %s" -(-> (symbol-name browse-url-browser-function) -(split-string "-") -remove-bad-parts -car)))) - -(global-set-key (kbd "C-c w d") 'ha-switch-default-browser) -#+END_QUOTE -[[https://github.com/howardabrams/dot-files/blob/master/emacs-browser.org][dot-files/emacs-browser.org at master · howardabrams/dot-files · GitHub]] -Captured On: [2025-09-11 Thu 23:17] -** GitHub - howardabrams/dot-files: My Emacs initialization code and standard scripts I use for client and servers alike. -[[https://github.com/howardabrams/dot-files][GitHub - howardabrams/dot-files: My Emacs initialization code and standard scripts I use for client and servers alike.]] -Captured On: [2025-09-11 Thu 23:12] -** rememberYou/.emacs.d: 🎉 Personal GNU Emacs configuration -[[https://github.com/rememberYou/.emacs.d][rememberYou/.emacs.d: 🎉 Personal GNU Emacs configuration]] -Captured On: [2025-09-12 Fri 05:48] -** Howardism: Tools to Focus -[[https://www.howardism.org/Technical/Emacs/focused-work.html][Tools to Focus]] -Captured On: [2025-09-16 Tue 04:24] -** I created a tutorial for Org Mode Tables that contains lots of practical examples, check it out : r/orgmode -[[https://www.reddit.com/r/orgmode/comments/1iaw2je/i_created_a_tutorial_for_org_mode_tables_that/][I created a tutorial for Org Mode Tables that contains lots of practical examples, check it out : r/orgmode]] -Captured On: [2025-09-16 Tue 03:36] -** dump-jump using xref-show-definitions-functions -- codefastdieyoung -#+BEGIN_QUOTE -11.5. jump-to -1 -2 -3 -4 -5 -(use-package dumb-jump -:functions xref-show-definitions-completing-read -:ensure -:init (setq xref-show-definitions-function #'xref-show-definitions-completing-read) -:config (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)) - -#+END_QUOTE -[[https://codefastdieyoung.com/knowledge-base/emacs-config/][Emacs config - codefastdieyoung]] -Captured On: [2025-09-11 Thu 22:31] -** Good emacs config files - codefastdieyoung -[[https://github.com/caisah/emacs.dz][GitHub - caisah/emacs.dz: Awesome emacs config files]] -Captured On: [2025-09-11 Thu 22:39] -[[https://codefastdieyoung.com/knowledge-base/emacs-config/][Emacs config - codefastdieyoung]] -Captured On: [2025-09-11 Thu 22:11] -** buffer/file compare: current version with saved via diff -#+BEGIN_QUOTE -Auxiliary functions for buffers management -Some custom functions to manage buffers. - -(defun joe-alternate-buffers () -"Toggle between the last two buffers" -(interactive) -(switch-to-buffer (other-buffer (current-buffer) t))) - -(defun joe-revert-buffer () -"Revert the buffer to the save disk file state" -(interactive) -(revert-buffer nil t)) - -(defun joe-diff-buffer-with-file () -"Compare the current modified buffer with the saved version." -(interactive) -(let ((diff-switches "-u")) -(diff-buffer-with-file (current-buffer)))) -#+END_QUOTE -[[https://github.com/joedicastro/dotfiles/blob/master/emacs/.emacs.d/readme.org][dotfiles/emacs/.emacs.d/readme.org at master · joedicastro/dotfiles · GitHub]] -Captured On: [2025-09-11 Thu 23:01] -** Async Module for Doing Asynchronous Processing -#+BEGIN_QUOTE -async -async.el is a module for doing asynchronous processing in Emacs. - -(use-package async -:defer t -:ensure t -:config -(setq async-bytecomp-package-mode t)) -#+END_QUOTE -[[https://github.com/joedicastro/dotfiles/blob/master/emacs/.emacs.d/readme.org][dotfiles/emacs/.emacs.d/readme.org at master · joedicastro/dotfiles · GitHub]] -Captured On: [2025-09-11 Thu 23:03] -** Review Org's New Citation Syntax -URL: https://blog.tecosaur.com/tmio/2021-07-31-citations.html -Captured On:[2025-09-25 Thu 02:39] -[ ] - -[[https://orgmode.org][*]] -[[https://blog.tecosaur.com/tmio/][This Month in Org]] -[[https://blog.tecosaur.com/tmio/rss.xml][RSS icon]] - -2021-07-31, TEC - -July 2021 - -Introducing citations! - -Last month I not-at-all-subtly hinted that a certain long-awaited -feature was arriving imminently. At this point, I think it’s a good idea -to set the tone for the rest of this post. - -celebrate-citations.svg - -Citations[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citations][#]] - -After years of (on and off) discussion[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fn.1][1]], I am elated to be able to -present Org’s new native citation syntax. Org has grown a thoroughly -designed, modular, capable citation system. At last you can refer to Org -for all your attribution needs. Special thanks must go to Nicolas -Goaziou for leading the charge, John Kitchin for paving the way with the -org-ref package, Bruce D’Arcus for driving a lot of careful -consideration of design decisions and starting to document some of the -details — and the many other denizens of the mailing list who have -contributed to the discussion over the years. - -András Simonyi’s also deserves a special mention for his work creating -the Elisp CSL library Citeproc.el, which while not directly included in -Org is crucial to providing robust CSL support, and integrates with -oc-csl.el. - -Outline[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#outline][#]] - -Citations have been carefully designed in such a way that users and -Elisp tinkerers will be able to easily adapt and extend it to fit their -needs. To that end, Org Cite (or OC for short) has been split into two -halves: - -• oc.el which defines the syntax and provides some machinery to interact - with citations -• Citation processors which interface with oc.el to produce - nicely-formatted citations to be inserted in your bibliography, within - the text, and even rendered in the buffer[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fn.2][2]] - -There are four capabilities that Org Cite uses the processors for - -1 Inserting and editing citations -2 Following citations to their definition -3 Fontifying the citations in the buffer -4 Exporting the citations - -Each capability can have a particular citation processor assigned, -independently of the others. Out of the box, Org uses the basic -processor for all of these tasks. - -The basic citation processor is one of four currently bundled with Org: - -• basic, which has no dependencies and provides all four capabilities. - It export to all formats, but only provides very simple citations. -• biblatex and natbib, which provide the export capability to create - citations via [[http://biblatex-biber.sourceforge.net/][Biber]] and (to a lesser extent) [[https://ctan.org/pkg/natbib][natbib]], but only for - LaTeX. -• csl, which provides the export capability using the [[https://citationstyles.org/][Citation Style - Language]], and exports to HTML, LaTeX, Org, and plain text (with an - [[https://github.com/andras-simonyi/citeproc-el/issues/23][open issue]] for ODT) — but depends on [[https://github.com/andras-simonyi/citeproc-el][citeproc.el]]. - -This provides a solid foundation for other packages to build off, and -despite Org Cite being yet to be released or documented in the manual we -are already seeing the development of packages like [[https://github.com/jkitchin/org-ref-cite][org-ref-cite]] (by -John Kitchin). - -Basic usage[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#basic-usage][#]] - -To get started with Org Cite, we must have some form of bibliography. -This can either be a BibTeX file or a CSL-JSON file. - -As an example, say we have a file orgcite.bib containing the following - -bibtex -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#basic-usage,code--1][#]] ⎘ -@article{OrgCitations, - author={org, mode and Syntax, Citation and List, Mailing and Effort, Time}, - journal={Journal of Plain Text Formats}, - title={Elegant Citations with Org-Mode}, - year={2021}, - month={7}, - volume={42}, - number={1}, - pages={2-3}} - -First we need to let Org know about this bibliography file (which must -have a .bib, .bibtex, or .json extension), which we do either via the -#+bibliography keyword, or the variable org-cite-global-bibliography. - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#org367f817][#]] ⎘ - -#+bibliography: orgcite.bib - -Once you have a bibliography source, you can start referencing to your -heart’s content! The basic citation syntax is as follows: - -citation-structure-basic.svg -Figure 1: The new citation syntax, for simple citations - -Using the default style [cite:@OrgCitations] produces ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al. 2021]]). -For more information on the styles currently available, see [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#cite-styles][1]]. - -Finally, to insert a bibliography somewhere, we just need to insert the -#+print_bibliography keyword, like so: - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#orgf39282d][#]] ⎘ - -#+print_bibliography: -org, mode, Citation Syntax, Mailing List, and Time Effort. 2021. -“Elegant Citations with Org-Mode.” Journal of Plain Text Formats 42 (1): -2–3. - -So, to summarise, all one needs to get started is: - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#orgb88a259][#]] ⎘ - -#+bibliography: references.bib -[cite:@key] -#+print_bibliography: - -That’s it! 🎉 - -The cite syntax[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#cite-syntax][#]] - -Don’t let the simplicity in the examples above fool you, the new syntax -is quite capable of expressing more complex forms. Here’s the full -version of the new cite syntax: - -citation-structure-full.svg -Figure 2: The new citations syntax, in full - -• The style and variant determine what form the exported citation takes -• The common prefix and suffix and put at the start and end of the - generated citation, respectively -• The citation key refers to a Bib(La)TeX or CSL-JSON key - - • The citation prefix and suffix are put before and after the - reference to the key - • Some citation processors recognise locators, which refer to a - particular part of the work, for example: p. 7 to refer to page 7. - -Using the default CSL citation style (Chicago author-name) [cite/l/b:see -@OrgCitations pp. 7 for fun] becomes see [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al. 2021, 7]] for fun. - -The citation styles and variants, and recognised locators are handled by -the citation processors. Org cite’s bundled processors currently -supports the following citation styles. - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#cite-styles][#]] - - Table 1: The current set of supported citation styles with variants, - with samples generated by oc-csl.el and citeproc.el. - Style Variant Sample Bib(La)TeX NatBib - a author cf caps-full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Org,]] Citeauthor - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Syntax,]] - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][List, and]] - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Effort]]) - a author f full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org,]] citeauthor citeauthor* - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Syntax,]] - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][List, and]] - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Effort]]) - a author c caps ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Org et al.]]) Citeauthor* Citeauthor - a author ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]]) citeauthor* citeauthor - na noauthor b bare [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]] citeyear - na noauthor ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) autocite* citeyearpar - l locators bc bare-caps [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Org et al.]] Notecite - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021, 2]] - l locators b bare [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] notecite - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021, 2]] - l locators bc caps , [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Org et al.]] Pnotecite - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021, 2]] - l locators (, [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et]] pnotecite - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][al. 2021, 2]]) - n nocite ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] nocite nocite - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text b bare org et al. citealp - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text c caps Org et al. Textcite Citep - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text f full org, Syntax, citep* - List, and - Effort - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text bc bare-caps org et al. Citealp - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text bf bare-full org et al. citealp* - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text cf caps-full Org, Syntax, Citep* - List, and - Effort - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text bcf org et al. Citealp* - bare-caps-full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - t text org et al. textcite - ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - (default) b bare [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] cite citealp - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]] - (default) bc bare-caps [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][Org et al.]] Cite Citealp - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]] - (default) f full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] citep* - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - (default) bf bare-full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] citealp - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - (default) cf caps-full ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] Citep* - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - (default) bcf ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] Citealp* - bare-caps-full [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - (default) ([[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][org et al.]] autocite citep - [[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#citeproc_bib_item_1][2021]]) - -The CSL processor supports the following locators: - -• book, bk., bks. -• chapter, chap., chaps. -• column, col., cols. -• figure, fig., figs. -• folio, fol., fols. -• number, no., Os. -• line, l., ll. -• note, n., nn. -• opus, op., opp. -• page, p, p., pp. -• paragraph, para., paras., ¶, ¶¶, §, §§ -• part, pt., pts. -• section, sec., secs. -• sub verbo, s.v., s.vv. -• verse, v., vv. -• volume, vol., vols. - -More on exporting[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#more-exporting][#]] - -The style of the citations and the bibliography depend on three things: - -1 The citation processor used -2 The citation style -3 The bibliography style - -The citation processor is automatically selected based on -org-cite-export-processors based on the export format being used, but -can be set on a per-document basis via the #+cite_export keyword. Here, -I shall use the csl processor, - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#org0920610][#]] ⎘ - -#+cite_export: csl - -With org-cite-export-processors, you can also set the bibliography and -citation style by giving a triplet of parameters (PROCESSOR -BIBLIOGRAPHY-STYLE CITATION-STYLE) instead of just the processor. You -can also use this triplet of values with the #+cite_export keyword - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#orgf8da086][#]] ⎘ - -#+cite_export: processor bibliography-style citation-style - -There are also some more options about how the bibliography is produced. -These options are handled by the active citation processor. For example, -while the CSL processor does not currently support any options, the -BibLaTeX processor passes options to a \printbibliography command, -allowing for the following: - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#org29efa9f][#]] ⎘ - -#+print_bibliography: :section 2 :heading subbibliography -#+print_bibliography: :keyword abc,xyz :title "Primary Sources" - -Using CSL[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#using-csl][#]] - -[[https://github.com/andras-simonyi/citeproc-el][Citeproc]] is currently available on [[https://melpa.org/#/citeproc][MELPA]], and so can be installed via -your package manager of choice so long as MELPA is included in your -package-archives. When available, it will be automatically loaded by -oc-csl.el. - -It currently supports exporting to: - -• HTML -• LaTeX -• Org -• Plain text - -Should you be interested in other formats, know that Citeproc is -designed to easily support adding new formats (see -citeproc-formatters.el for examples). - -Citeproc can currently retrieve bibliographic information from the -following formats: - -• CSL-JSON -• Bib(La)TeX -• org-bibtex - -Though support for Bib(La)TeX and [[https://gewhere.github.io/org-bibtex][org-bibtex]] is rudimentary compared to -CSL-JSON. - -When exporting, you can set the style by providing a path to CSL style -files, either absolute or relative to org-cite-csl-styles-dir. For -example, if I download apa.csl I can use it like so: - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#org678d1e6][#]] ⎘ - -#+cite_export: csl ~/Downloads/apa.csl - -When no style is given org-cite-csl--fallback-style-file will be used, -which defaults to a bundled Chicago author-date style. - -Working with Zotero[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#working-with-zotero][#]] - -There are quite a few reference managers available, however, the list -rapidly shrinks if you restrict yourself to applications which are: - -• somewhat feature-rich -• open source software -• not owned by a parasitic company[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fn.3][3]] - -[[https://www.zotero.org/][Zotero]] is a good option, and if you’re using it it’s quite easy to use -it with Org Cite. Out of the box, you can tell it to export your -library, or parts of it, to a .bib file and automatically keep it in -sync. I’d recommend installing the [[https://retorque.re/zotero-better-bibtex/][Better BibTeX]] extension though. - -zotero-export-library.png -Figure 3: Zotero library right click context menu, showing the export -option -zotero-export-options-prompt.png -Figure 4: Zotero collection export dialog - -Zotero also works well with CSL. In addition to supporting CSL-JSON -exports, Zotero also features an easy way to install CSL styles within -the preferences. - -zotero-cite-styles-menu.png -Figure 5: Zotero CSL style management within preferences - -Since these files are put under ~/Zotero/styles, you can use them with -Org Cite and Citeproc simply by setting org-cite-csl-styles-dir to the -Zotero styles directory. - -Emacs Lisp -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#working-with-zotero,code--1][#]] ⎘ -(setq org-cite-csl-styles-dir "~/Zotero/styles") - -To then use the citation style defined by ~/Zotero/styles/apa.csl one -can then simply refer to apa.csl when using the #+cite_export keyword. - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#org5f6c51c][#]] ⎘ - -#+cite_export: csl apa.csl - -A bright future[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#bright-future][#]] - -Org Cite has only just been merged in the past month, and is yet to be -included in an Org release, but we’re seeing a tremendous degree of -community interest. There are already promising developments with -third-party packages, such as [[https://github.com/bdarcus/bibtex-actions][bibtex-actions]] and [[https://github.com/jkitchin/org-ref-cite][org-ref-cite]]. I can’t -wait to see how the ecosystem continues to develop 😃. - -Footnotes: - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fnr.1][1]] - -Citations were first being mentioned on the mailing list back in 2007, -in [[https://lists.gnu.org/archive/html/emacs-orgmode/2007-05/msg00146.html][a thread about footnotes]]. - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fnr.2][2]] - -There is currently an [[https://github.com/andras-simonyi/org-cite-csl-activate][ongoing effort]] to use oc.el and citeproc.el to -produce citation overlays in the buffer. - -[[https://blog.tecosaur.com/tmio/2021-07-31-citations.html#fnr.3][3]] - -I’m talking about a certain company [[https://moneyweek.com/505757/great-frauds-in-history-robert-maxwell][created by a British Fraudster]] that -has a [[https://www.theguardian.com/science/2017/jun/27/profitable-business-scientific-publishing-bad-for-science][40% profit margin, engages in blackmail-like practices with -universities]], prompted [[http://thecostofknowledge.com/][19,000 researchers]] to boycott them, [[https://www.the-scientist.com/the-nutshell/elsevier-published-6-fake-journals-44160][published six -fake journals]], vigorously [[https://web.archive.org/web/20200129202353/http://legacy.earlham.edu/~peters/fos/2007/08/publishers-launch-anti-oa-lobbying.html][lobbys against Open Access]], [[https://rossmounce.co.uk/2017/02/14/elsevier-selling-access-to-open-access-again/][charged for Open -Acess articles]] (repeatedly), made [[https://www.michaeleisen.org/blog/?p=807][financial contributions to politicians -who then tried to prevent publicly accesible reaserch]], and whose -reference manager [[https://www.zotero.org/support/kb/mendeley_import#mendeley_database_encryption][encrypted reaserchers’ own databases]] “to comply with -GDPR”. - -[[http://creativecommons.org/publicdomain/zero/1.0/][CC0]] -To the extent possible under law, [[https://blog.tecosaur.com/][TEC]] has waived all copyright and -related or neighboring rights to This Month in Org. -** [[https://karthinks.com/software/fifteen-ways-to-use-embark/][Emacs: Fifteen ways to use Embark]] -URL: https://karthinks.com/software/fifteen-ways-to-use-embark/ -Captured On:[2025-09-23 Tue 01:42] - -• [[https://karthinks.com/software/fifteen-ways-to-use-embark/#embark-act-actually-dot-dot-dot-dot-and-but-first-dot-dot-dot][embark-act: Actually…. & But first…]] - - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#open-any-buffer-by-splitting-any-window][Open any buffer by splitting any window]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#copy-a-file-to-a-remote-location-when-finding-a-file][Copy a file to a remote location when finding a file]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#insert-a-minibuffer-candidate-into-the-buffer][Insert a minibuffer candidate into the buffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#run-a-shell-command-on-a-minibuffer-candidate-file-without-losing-your-session][Run a shell command on a minibuffer candidate file without losing - your session]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#open-a-file-as-root-without-losing-your-session][Open a file as root without losing your session]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#upload-a-region-of-text-to-0x0][Upload a region of text to 0x0]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#visit-a-package-s-url-from-the-minibuffer][Visit a package’s URL from the minibuffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#set-a-variable-from-anywhere-it-appears-in-a-buffer][Set a variable from anywhere it appears in a buffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#add-a-keybinding-for-a-command-name-from-anywhere-it-appears][Add a keybinding for a command name from anywhere it appears]] - -• [[https://karthinks.com/software/fifteen-ways-to-use-embark/#embark-export-i-want-a-gist-so-give-me-a-list][embark-export: I want a gist, so give me a list]] - - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#export-emacs-package-candidates-to-a-package-menu][Export Emacs package candidates to a package menu]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#collect-imenu-candidates-in-an-imenu-list][Collect imenu candidates in an “imenu-list”]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#export-file-candidates-to-a-dired-buffer][Export file candidates to a dired-buffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#export-buffer-candidates-to-ibuffer][Export buffer candidates to ibuffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#export-variable-candidates-to-a-customize-buffer][Export variable candidates to a customize buffer]] - • [[https://karthinks.com/software/fifteen-ways-to-use-embark/#export-grep-or-line-candidates-to-a-grep-buffer][Export grep or line candidates to a grep buffer]] - -• [[https://karthinks.com/software/fifteen-ways-to-use-embark/#bonus-use-embark-actions-like-helm][BONUS: Use Embark Actions like Helm]] -• [[https://karthinks.com/software/fifteen-ways-to-use-embark/#33][33%]] - -Fifteen ways to use Embark - -2021-10-06 -[[https://karthinks.com/tags/emacs][emacs]] - -Update (2024-10-02): Update for Emacs 29 - -Update (2024-09-08): Read this article in Japanese at [[https://emacs-jp.github.io/tips/fifteen-ways-to-use-embark][Qiita]] or at -[[https://emacs-jp.github.io/tips/fifteen-ways-to-use-embark][Emacs-JP]]. - -Update (2021-10-16): While this list was intended as a demonstration of -the kinds of things you can do with Embark, there has been some interest -by readers in reproducing these demos exactly on their machines. So I -have added a “Play by play” section under each demo listing the sequence -of actions in the demo. - -[[https://github.com/oantolin/embark/tree/98121bacef39abaf6f6849f87a439ba2184c03e2][Embark]] is a fantastic and thoughtfully designed package for Emacs that -flips Emacs’ action → object ordering without adding a learning curve. -It’s completely changed how I use Emacs, and I’m going to show you why. - -By default, Emacs’ action model is to specify the action (find-file), -then the object (the file): -[[https://karthinks.com/img/emacs-pattern.png][*]] - -This mirrors how one does things in a shell: -[[https://karthinks.com/img/shell-pattern.png][*]] -The difference is that before you submit your shell command, you’re free -to edit both the action and the object, since it’s just a line of text. -In Emacs you can change the object freely, but to change the action -you’d have to hit C-g and call a different command. - -Things work the other way in a GUI program like a file manager. You -select some representation of the object (usually an icon), then choose -the action you would like to perform on it: -[[https://karthinks.com/img/gui-pattern.png][*]] - -Either paradigm works fine, but this is Emacs, there’s no reason to -choose just one! Embark lets you go back and forth between the two -patterns. So you can call find-file (say) and pick a file, only to -realize that you want to do something else with it, like open it in -another window, or copy the file to a different directory: - -[[https://karthinks.com/img/embark-pattern.png][*]] - -With Embark, this is a breeze. - -embark-act: Actually…. & But first… - -embark-act is your “Actually…” command. As in, I called package-install -and picked a package but actually I’d like to read the package -description instead! - -embark-act is your “Yes, but first…” command as well. As in, I called -find-file but first I’d like to copy it elsewhere to be safe, then -continue to open this file! - -Or perhaps you want to think of it as a keyboard driven analog of a -“right-click menu” in a GUI environment. That works too, but the former -maps better to the idea of “late-binding” and laziness that I think of -Embark as enabling. - -Emacs makes you specify and fix the action/verb first (find-file, say), -then choose the thing it acts on (the file). If you call embark-act, -this is reversed. Now the object (file) is fixed, and you’re free to -choose the action. - -I know: It sounds like I’m describing Helm actions. The difference is -that Embark works everywhere, across all types of “objects”, and with -every initial and wait-I-changed-my-mind command. There is no -predetermined set of alternative actions configured to work with another -predetermined set of initial actions. No one (including yourself) needs -to have anticipated in advance what actions go together.[[https://karthinks.com/software/fifteen-ways-to-use-embark/#fn:1][1]] This uniform, -consistent integration into Emacs makes the difference between them one -of kind and not of quantity, although it takes a bit of time to see -this. - -This means you can start a command and select a candidate in the -minibuffer, then call embark-act and M-x some-other-command to run that -command on the candidate instead. If you are about to kill a buffer with -C-x k but want to switch to it instead, you can call embark-act followed -by C-x b. You can even do this without losing the kill-buffer prompt if -you just want a quick peek at the buffer! - -The categories of objects Embark understands covers most common cases: -filenames, buffers, bookmarks, URLs, text regions, variables, commands, -symbols and more. - -When you call embark-act, Embark also activates a keymap with direct -access to common actions you might want to run for each category of -object. This makes it unnecessary to use M-x to run your -I-changed-my-mind action all the time, although you always have that -option. You can, of course, add your own commands to this keymap as I do -below. - -I use embark-act literally hundreds of times every day. Here are a few -of my common uses. A few of these are built in, others need some elisp -to work, all are surprisingly useful. To be clear, this list barely -scratches the surface of the sphere of possibilities with Embark. - -A recipe for reproducing these demos - -I use Embark in conjunction with various Consult commands (consult-grep, -consult-locate, consult-dir etc) in Emacs 27.2. If you want to reproduce -these demos exactly in your Emacs, you will need the following packages: - -• embark -• marginalia -• vertico -• consult -• embark-consult -• orderless -• consult-dir -• ace-window -• 0x0 - -Additionally you may need to bind the relevant commands (embark-act, -embark-export, consult-*) to suitable keys. - -Open any buffer by splitting any window - -This needs a little background. The ace-window package allows you to -switch to a window based on keyboard hints. A less well known feature is -that it also provides a “dispatch menu” that lets you act on windows in -ways beyond simply switching to them: - -[[https://i.imgur.com/EWC4jNH.gifv][[video]]]Play by play - -1 With two or more windows open, call ace-window -2 Press ? to bring up the dispatch menu. -3 Press the dispatch key to split a window horizontally (v in my video) -4 Press the ace-window key corresponding to the buffer you want to split - (e in my video) -5 Repeat steps 1 and 2 -6 Press the dispatch key to split a window vertically (s in my video) -7 Press the ace-window key corresponding to the buffer you want to split - (w in my video) - -So you can kill windows, move them around, split them and more by using -the dispatch keys. (Hit ? to bring up the dispatch menu.) - -Now: You can call ace-window via Embark to display a candidate anywhere, -including in splits that you create using the above dispatch menu. This -means any buffer/file/bookmark I open is always placed exactly where I -want it to be on the screen. - -In the below demo, I open a bookmark (with consult-bookmark), a file -(with find-file) and a buffer (with consult-buffer) in sequence. Each -time, I run embark-act and select the ace-window action, which activates -ace-window. You can then display the buffer in any existing window by -making a selection with ace-window. I actually go one step further in -the demo: I split one of the existing windows using ace-window’s -dispatch feature from above and display the relevant buffer in that -split! - -[[https://i.imgur.com/5hwqpQD.gifv][[video]]]Play by play - -1 Run a command that requires selecting a file, bookmark or buffer, - perhaps switch-to-buffer -2 Select one and run embark-act -3 Run the my/embark-ace-action with o (see below) -4 Select the window where you want the buffer to be placed, OR -5 Split an existing window with v or b (see aw-dispatch-alist) followed - by a window selection, and display the buffer in the new split. - -Update for Emacs 29 - -Emacs 29 provides a bunch of “window-prefix” actions: [[https://karthinks.com/software/emacs-window-management-almanac/#the-other-window-prefix--built-in][these are commands -you can call to display the output of the next command in specified -windows]]. For example, calling other-tab-prefix and then find-file will -open the file you select in a new tab. Emacs provides -same-window-prefix, other-window-prefix, other-tab-prefix and -other-frame-prefix. We can [[https://karthinks.com/software/emacs-window-management-almanac/#a-window-prefix-command-for-ace-window][go one step further and define]] an -ace-window-prefix. Calling it will activate ace-window after the next -command and show its result in the window you select, including possibly -in splits you create using ace-window actions: - -(defun ace-window-prefix () - "Use `ace-window' to display the buffer of the next command. -The next buffer is the buffer displayed by the next command invoked -immediately after this command (ignoring reading from the minibuffer). -Creates a new window before displaying the buffer. -When `switch-to-buffer-obey-display-actions' is non-nil, -`switch-to-buffer' commands are also supported." - (interactive) - (display-buffer-override-next-command - (lambda (buffer _) - (let (window type) - (setq - window (aw-select (propertize " ACE" 'face 'mode-line-highlight)) - type 'reuse) - (cons window type))) - nil "[ace-window]") - (message "Use `ace-window' to display next command buffer...")) - -Now the interesting bit: Instead of defining separate Embark actions for -opening files, bookmarks or buffers in specified windows or splits, we -can embed the window prefix actions into Embark so they can be used with -any category of object. - -First we define a keymap with these actions: - -(defvar-keymap my/window-prefix-map - :doc "Keymap for various window-prefix maps" - :suppress 'nodigits - "o" #'ace-window-prefix - "0" #'ace-window-prefix - "1" #'same-window-prefix - "2" #'split-window-vertically - "3" #'split-window-horizontally - "4" #'other-window-prefix - "5" #'other-frame-prefix - "6" #'other-tab-prefix - "t" #'other-tab-prefix) - -What we’d like: When running embark-act, pressing any of the keys in the -above keymap should run Embark’s default action – i.e. the command that -you originally invoked – but with the right window-prefix applied. This -will supersede the below, older method of defining individual Embark -commands for each kind of object. Getting there involves hooking into -Embark’s event loop, so this is a little convoluted. Strap in! - -• We define a function that looks up keys in the above keymap and calls - the right -window-prefix command before running Embark’s default - action. This is intended as a hook function for Embark’s - embark-around-action-hooks, which lets you insert specific behavior - around each Embark action. This is the code we’d like Embark to - actually run: - - ;; Look up the key in `my/window-prefix-map' and call that function first. - ;; Then run the default embark action. - (cl-defun my/embark--call-prefix-action (&rest rest &key run type &allow-other-keys) - (when-let ((cmd (keymap-lookup - my/window-prefix-map - (key-description (this-command-keys-vector))))) - (funcall cmd)) - (funcall run :action (embark--default-action type) :type type rest)) -• But this behavior is not attached to any Embark action yet. To access - this behavior from Embark, we define a dummy Embark action whose - behavior will be replaced by the above function: - - ;; Dummy function, will be overridden by running `embark-around-action-hooks' - (defun my/embark-set-window () (interactive)) - - ;; When running the dummy function, call the prefix action from above - (setf (alist-get 'my/embark-set-window embark-around-action-hooks) - '(my/embark--call-prefix-action)) -• A wrinkle: to support multi-category commands like those provided by - Consult (where the candidates can be a mix of bookmarks, buffers, - files and others), we need to explicitly set the “default” Embark - action for relevant categories: - - (setf (alist-get 'buffer embark-default-action-overrides) #'pop-to-buffer-same-window - (alist-get 'file embark-default-action-overrides) #'find-file - (alist-get 'bookmark embark-default-action-overrides) #'bookmark-jump - (alist-get 'library embark-default-action-overrides) #'find-library) -• Finally, we map this dummy embark action to all the keys in - my/window-prefix-map: - - (map-keymap (lambda (key cmd) - (keymap-set embark-general-map (key-description (make-vector 1 key)) - #'my/embark-set-window)) - my/window-prefix-map) - - The fact that all keys in my/window-prefix-map are mapped to the same - action is not a problem – in my/embark--call-prefix-action we dispatch - on the key that was actually pressed. - -If you’re on Emacs 29 or later, you can enable behavior like the above -for all commands and categories at once, without needing to define -individual actions. The below is a more manual (and backward-compatible) -approach. - -To get this to work, you’ll need to add a few ace-window functions to -the Embark file actions map: - -(eval-when-compile - (defmacro my/embark-ace-action (fn) - `(defun ,(intern (concat "my/embark-ace-" (symbol-name fn))) () - (interactive) - (with-demoted-errors "%s" - (require 'ace-window) - (let ((aw-dispatch-always t)) - (aw-switch-to-window (aw-select nil)) - (call-interactively (symbol-function ',fn))))))) - -(define-key embark-file-map (kbd "o") (my/embark-ace-action find-file)) -(define-key embark-buffer-map (kbd "o") (my/embark-ace-action switch-to-buffer)) -(define-key embark-bookmark-map (kbd "o") (my/embark-ace-action bookmark-jump)) - -I also add actions to open the buffer in a vertical or horizontal split. -But you probably don’t need this, since you can do this and a lot more -with ace-window’s dispatch menu! - -(eval-when-compile - (defmacro my/embark-split-action (fn split-type) - `(defun ,(intern (concat "my/embark-" - (symbol-name fn) - "-" - (car (last (split-string - (symbol-name split-type) "-"))))) () - (interactive) - (funcall #',split-type) - (call-interactively #',fn)))) - -(define-key embark-file-map (kbd "2") (my/embark-split-action find-file split-window-below)) -(define-key embark-buffer-map (kbd "2") (my/embark-split-action switch-to-buffer split-window-below)) -(define-key embark-bookmark-map (kbd "2") (my/embark-split-action bookmark-jump split-window-below)) - -(define-key embark-file-map (kbd "3") (my/embark-split-action find-file split-window-right)) -(define-key embark-buffer-map (kbd "3") (my/embark-split-action switch-to-buffer split-window-right)) -(define-key embark-bookmark-map (kbd "3") (my/embark-split-action bookmark-jump split-window-right)) ------------------------------------------------------------------------- - -Copy a file to a remote location when finding a file - -[[https://i.imgur.com/mpArf9q.gifv][[video]]]Play by play - -1 Run any command that requires selecting a file, perhaps - find-file-other-window -2 Select one and run embark-act -3 Run the copy-file action with c. Embark has a key for this but you can - also M-x copy-file here. -4 Navigate to the destination path. In the video I used the [[https://github.com/karthink/consult-dir][consult-dir]] - package to instantly switch the path to one of my bookmarks, a remote - location. -5 Press RET to copy the file. You can type in a name to copy it as. - -Here’s what happened. In any file prompt, you can call embark-act and -select the copy action to copy the file instead. (You could just as well -call M-x copy-file.) In this case I then use [[https://github.com/karthink/consult-dir][consult-dir]] to insert a -bookmark that points to my server into the destination prompt, and the -file is copied using Tramp. - -You can even do this without losing the find-file prompt! Calling -embark-act with a prefix argument keeps the prompt alive: - -[[https://i.imgur.com/BIw0Tht.gifv][[video]]]Play by play - -1 Run any command that requires selecting a file, perhaps - find-file-other-window -2 Select one and run embark-act with a prefix argument. That is, if your - binding for embark-act is C-., run C-u C-.. -3 Run the copy-file action with c. Embark has a key for this but you can - also M-x copy-file here. -4 Navigate to the destination path. In the video I used the [[https://github.com/karthink/consult-dir][consult-dir]] - package to instantly switch the path to one of my bookmarks, a remote - location. -5 Press RET to copy the file. You can type in a name to copy it as. -6 Continue to use your find-file-other-window prompt as before. - -At the end I quit the find-file prompt manually and check the remote -directory to ensure that the file has been copied. - ------------------------------------------------------------------------- - -Insert a minibuffer candidate into the buffer - -Simple but very convenient: - -[[https://i.imgur.com/SrAD4Oa.gifv][[video]]]Play by play - -1 Run any command that requires you to make a selection using the - minibuffer. The selection can be anything, it just has to display some - text. -2 In the video I chose a distant directory with consult-dir and selected - a file in that directory. -3 Run embark-act -4 Press i to insert the text of the selection into the main buffer. In - the video I used I instead to insert the selected file’s relative - path. I does different things based on the type of object you’re - selecting. For example, I with a buffer candidate inserts the contents - of the buffer instead. - ------------------------------------------------------------------------- - -Run a shell command on a minibuffer candidate file without losing your -session - -A perfect example of But First I need to…: - -[[https://i.imgur.com/9Wf6mQc.gifv][[video]]]Play by play - -1 Run any command that requires selecting a file, perhaps find-file -2 I switched to a distant directory using consult-dir. -3 Select a file and run embark-act with a prefix argument. That is, if - your binding for embark-act is C-., run C-u C-.. -4 Press & to run the async-shell-command action. Embark has a key for - this in its keymaps but you could run M-x async-shell-command or call - its default keybinding (M-&) instead. -5 Type in the command at the prompt. The file name is already filled in - for you. I used the file shell command for more info on a file. -6 Press RET to run the command and return to the find-file prompt. - -I called the “file” shell command for more info on the file without -ending the find-file prompt. - ------------------------------------------------------------------------- - -Open a file as root without losing your session - -Emacs’ version of forgetting to add sudo before the command. In the -shell you can go back to the start of the prompt and type it in, or -engage in the sudo !! ritual. In Emacs I use an Embark action: - -[[https://i.imgur.com/ccjvizN.gifv][[video]]]Play by play - -1 Run any command that requires selecting a file. I used consult-locate - to locate a root-owned file on my filesystem. -2 Select one and run embark-act with a prefix argument. That is, if your - binding for embark-act is C-., run C-u C-.. -3 Select the sudo-find-file action with S. Note: You’ll need to add this - action to the keymap, see below. Alternatively you can run M-x - sudo-find-file or its global binding. - -Like before, this works from any file prompt but the command I started -with was consult-locate. For the sudo program there is the sudo-edit -package, although I used a snippet from my init file that I can’t -ascertain the provenance of anymore: - -(defun sudo-find-file (file) - "Open FILE as root." - (interactive "FOpen file as root: ") - (when (file-writable-p file) - (user-error "File is user writeable, aborting sudo")) - (find-file (if (file-remote-p file) - (concat "/" (file-remote-p file 'method) ":" - (file-remote-p file 'user) "@" (file-remote-p file 'host) - "|sudo:root@" - (file-remote-p file 'host) ":" (file-remote-p file 'localname)) - (concat "/sudo:root@localhost:" file)))) - -To use sudo-find-file as an Embark action, you can run it (with M-x or a -global keybinding) after calling embark-act, or shorten the process -further by adding an entry to Embark’s file actions map: - -(define-key embark-file-map (kbd "S") 'sudo-find-file) ------------------------------------------------------------------------- - -Upload a region of text to 0x0 - -[[https://i.imgur.com/i06G4vT.gifv][[video]]]Play by play - -1 Select a region of text in a buffer. -2 Run embark-act. -3 Press U to choose the 0x0-dwim action. Note: You’ll need to add this - action to the keymap, see below. -4 The region text will be uploaded to 0x0 and URL added to the - kill-ring. (See message at the end of the video.) - -I’m using the [[https://melpa.org/#/0x0][0x0]] package for the 0x0-dwim function. When called as an -Embark action on a URL, this shortens it. When called on a file, it -uploads the file. The echo area message at the end (from 0x0-dwim) tells -me the upload URL has been copied to the kill ring. As with the other -examples, you can call 0x0-dwim after running embark-act or define a -short key for it in one of Embark’s keymaps: - -(define-key embark-region-map (kbd "U") '0x0-dwim) ------------------------------------------------------------------------- - -Visit a package’s URL from the minibuffer - -[[https://i.imgur.com/b6BTODS.gifv][[video]]]Play by play - -1 Run any command that requires selecting a package, perhaps - describe-package (C-h P by default) -2 Select a package and run embark-act -3 Press u to run the embark-browse-package-url action. - -In this case I ran the describe-package command before going “Actually… -URL please”, but in this example as all the others, there’s nothing -special about describe-package. Any command that gives you a list of -packages at the minibuffer will proffer the same set of Embark actions. - ------------------------------------------------------------------------- - -Set a variable from anywhere it appears in a buffer - -Super handy for quickly setting variables, especially when testing code. - -[[https://i.imgur.com/wltZaGq.gifv][[video]]]Play by play - -1 Move point to a variable in a buffer. (Alternatively, run a command - that requires selecting a variable at the minibuffer, like - describe-variable) -2 Run embark-act. -3 Press = to run the set-variable action. Embark has a key for this in - its keymaps, but you could call M-x set-variable instead. -4 Set the new value of the variable. - -In this case Embark has an entry for set-variable in its variables -keymap (bound to =), but you can just call M-x set-variable. - ------------------------------------------------------------------------- - -Add a keybinding for a command name from anywhere it appears - -Set all the keys. - -[[https://i.imgur.com/A0hHGhi.gifv][[video]]]Play by play - -1 Move point to a command name in a buffer. (Alternatively, run a - command that requires selecting a command at the minibuffer, like - describe-command) -2 Run embark-act. -3 Press g to run the global-set-key action. Embark has a key for this in - its keymaps, but you could call M-x global-set-key instead. -4 Set the new keybinding for the command. - -Embark provides an action in its keymap to run global-set-key, but you -could just call M-x global-set-key after running embark-act with the -point on a command name. There is also local-set-key in the embark -keymap. - ------------------------------------------------------------------------- - -embark-export: I want a gist, so give me a list - -If that was everything Embark did I’d be a happy camper. But embark-act -isn’t even its best feature. That would be the gem of composability that -is embark-export (and its lesser kin embark-collect). These commands -create persistent collections from minibuffer candidate lists: It’s one -part ivy-occur and one part glue that ties together Emacs libraries -better than Emacs does. The examples illustrate why. - ------------------------------------------------------------------------- - -Export Emacs package candidates to a package menu - -Want a package-menu-mode buffer with all packages involving shells in -Emacs? embark-export has you covered: - -[[https://i.imgur.com/oaDGV3X.gifv][[video]]]Play by play - -1 Run any command that requires selecting a package, perhaps - describe-package (C-h P by default) -2 (Optional) Type in some text to filter the completions list -3 Run embark-export - -The clever idea behind embark-export is to reuse Emacs’ built-in -functionality whenever possible: the package-menu library already -handles displaying packages. If you’re generating a list of packages -with user-specified conditions, why reinvent the wheel? - ------------------------------------------------------------------------- - -Collect imenu candidates in an “imenu-list” - -embark-collect creates persistent collections of minibuffer completion -candidates (filtered by user input) in a way that basically obsoletes -every “listing” package for me. In this example I create a filtered list -of imenu items that sticks around and that I can use to navigate around -the file: - -[[https://i.imgur.com/MJlM9cL.gifv][[video]]]Play by play - -1 When visiting any file, run imenu or a version of it (I ran - consult-imenu-all) -2 (Optional) Type in some text to filter the completions list -3 Run embark-export. (This actually runs embark-collect under the hood. - You could run embark-collect directly instead, but it’s simpler to - just use one command.) -4 Press RET in the persistent collections buffer to navigate to that - symbol in the file. - -I didn’t show this in the demo, but all embark-act actions are available -in the Collections buffer, and you can even call them directly (i.e. -without calling embark-act first) by turning on -embark-collect-direct-action-minor-mode. - ------------------------------------------------------------------------- - -Export file candidates to a dired-buffer - -Have a list of files you arrived at in a tortuous manner that you want -to keep around? dired was created to list files, and embark-export -respects this: - -[[https://i.imgur.com/6ZY08H2.gifv][[video]]]Play by play - -1 Run any command that requires selecting a file. I used consult-fd to - find all files matching a pattern below a directory. -2 (Optional) Type in some text to filter the completions list. With - consult-fd you do this by adding a # to end the string to search on - and begin the string used to filter the results. -3 Run embark-export. - -This obsoletes find-name-dired, another “listing” based feature. - ------------------------------------------------------------------------- - -Export buffer candidates to ibuffer - -You saw this coming: Any list of buffers gets exported to an ibuffer. - -[[https://i.imgur.com/pmBTb6q.gifv][[video]]]Play by play - -1 Run any command that requires selecting a buffer. I used - consult-buffer. -2 Type in some text to filter the completions list: - - • I pressed b SPC to narrow the consult-buffer list to buffers, then - • typed in ! followed by * to omit all buffers[[https://karthinks.com/software/fifteen-ways-to-use-embark/#fn:2][2]] that begin with a *, - i.e. “special” buffers. - -3 Run embark-export. - ------------------------------------------------------------------------- - -Export variable candidates to a customize buffer - -A list of variables is exported by embark-export into a customize -buffer: - -[[https://i.imgur.com/NIQBwKS.gifv][[video]]]Play by play - -1 Run a command that requires selecting a variable, like - describe-variable -2 Type in some text to filter the completions list -3 Run embark-export - -This is a great way to transition from looking up a variable to a -full-fledged apropos on relevant items when you need to. - ------------------------------------------------------------------------- - -Export grep or line candidates to a grep buffer - -Any occur-like results (from consult-line, grep, xref etc) get exported -into a grep buffer. - -[[https://i.imgur.com/PjS1Yx6.gifv][[video]]]Play by play - -1 Run a command that generates a list of grep-style matches. I used - consult-ripgrep in the demo. Other Consult-based options are - consult-grep, consult-git-grep and consult-line. -2 Type in some text to find grep results. -3 Not necessary: I flipped through the various files that matched with - vertico-next-group and vertico-previous-group. -4 Run embark-export. -5 Turn on next-error-follow-minor-mode in the grep buffer with C-c C-f. - This jumps each match as I… -6 …navigate the grep buffer with M-n and M-p (compilation-next-error and - compilation-previous-error), and move to the next/previous matching - file with } and {. - -Note that this is a regular grep buffer, so you can use all your tricks, -like wgrep to edit the grep buffer and save changes in all the files. - ------------------------------------------------------------------------- - -BONUS: Use Embark Actions like Helm - -In the above examples, the available embark actions were displayed in -some window in the frame. Embark has multiple “prompters” listing the -preset actions, and with a little elbow grease you can set up something -similar to Helm[[https://karthinks.com/software/fifteen-ways-to-use-embark/#fn:3][3]]: - -[[https://i.imgur.com/LH9uYM6.gifv][[video]]]Play by play - -1 Run any command involving minibuffer selection, consult-buffer in the - video. -2 (Optional) type in something to filter the completions list or select - a buffer. -3 Press TAB to switch to the list of embark actions. -4 Press TAB again to switch back to the list of candidates. -5 Search for an action by name (in this case “kill”) to filter the list - of actions -6 Erase and search for a different action (“diff”) and choose the - diff-buffer-with-file action -7 Press RET to run diff-buffer-with-file on the selected buffer -8 (Optional) Navigate the diff hunks with diff-hunk-next and - diff-hunk-prev -9 (Optional) Fold the diff sections with outline-cycle -10 Run consult-buffer again and select a buffer -11 Switch to the actions list again with TAB -12 Press @ to call an action by its keybinding instead of selecting it - by name -13 Call the embark action for killing a buffer with k to kill the - selected buffer. - -Here I switch back and forth between the list of actions and the list of -candidates (like in Helm) with C-<tab>. In the actions list you can -either type the action (matched with completing-read), or call the -action directly by prepending its keybinding with @. - -Elbow grease: - -(defun with-minibuffer-keymap (keymap) - (lambda (fn &rest args) - (minibuffer-with-setup-hook - (lambda () - (use-local-map - (make-composed-keymap keymap (current-local-map)))) - (apply fn args)))) - -(defvar embark-completing-read-prompter-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "<tab>") 'abort-recursive-edit) - map)) - -(advice-add 'embark-completing-read-prompter :around - (with-minibuffer-keymap embark-completing-read-prompter-map)) -(define-key vertico-map (kbd "<tab>") 'embark-act-with-completing-read) - -(defun embark-act-with-completing-read (&optional arg) - (interactive "P") - (let* ((embark-prompter 'embark-completing-read-prompter) - (embark-indicators '(embark-minimal-indicator))) - (embark-act arg))) - -Replace vertico-map above with your completion system of choice’s active -minibuffer keymap. The default is minibuffer-local-completion-map. - -Remember that unlike with Helm, you’re not restricted to these actions -when you use Embark! You can call literally any command that it makes -sense to with its keybinding or with M-x after running embark-act. - ------------------------------------------------------------------------- - -33% - -That’s fifteen useful Embark thingamajigs, and I didn’t get to mention -embark-become. Or embark-prefix-help-map, embark-which-key-prompter, or -Embark’s targets and target cycling, or half a dozen more thoughtful -features and niceties about Embark. Maybe next time. - -I’ll conclude instead by mentioning the main packages I used in the -above demos: - -• [[https://github.com/oantolin/embark][embark]] by Omar Antolin Camarena, who’s been a pleasure to interact - with and pester with my requests for features. To add custom actions - to the embark keymaps or otherwise customize Embark, I suggest - perusing the README. It’s as readable and informative as they come. -• [[https://github.com/minad/consult][consult]] for its various enhancements to Emacs’ builtins. - consult-locate and consult-find (actually consult-fd) to find files, - consult-imenu for a colorful imenu with grouping and consult-ripgrep - to grep across a directory. -• [[https://github.com/minad/marginalia][marginalia]] for the annotations in the minibuffer. Co-maintained by - Omar Antolin and Daniel Mendler. -• [[https://github.com/minad/vertico][vertico]] as the minibuffer completion interface. Consult, Vertico and - Marginalia are all authored by Daniel Mendler, who I’m convinced never - sleeps. I didn’t even mention Corfu. -• The [[https://github.com/oantolin/orderless][orderless]] completion style, also by Omar Antolin, to match pieces - of text against minibuffer candidates independently. Together these - five packages form the MOVEC pentagram, a composable enhancement suite - that integrates Emacs’ loosely bound libraries into a modern and - cohesive whole. -• [[https://github.com/karthink/consult-dir][consult-dir]] to switch directories quickly. I used this multiple times - above to navigate to distant directories when in the minibuffer - prompt. -• [[https://github.com/karthink/popper][popper]] to make embark-collect, help and other ephemeral buffers behave - when they appear on screen. -• [[https://github.com/abo-abo/ace-window][ace-window]] by abo-abo, whose dispatch-keys idea in Ace-Window and Avy - I promptly ripped off for Popper. If I understand correctly his - Ivy-Occur was an early influence on what became Embark-Collect as - well. -• [[https://git.sr.ht/~willvaughn/emacs-0x0][0x0]] by William Vaughn. I use this far more often than I thought I - would. - -Finally a quick note for Doom Emacs users: Doom ships with Embark out of -the box (as of Sep 2021), you don’t need to do anything besides looking -up the keys for embark-act and embark-collect. - -Despite what these examples suggest, I estimate that I use less than a -third of what Embark provides. Even so, in allowing me to change or -chain actions at any time, it lets me pilot Emacs by the seat of my -pants. A second, unforeseen benefit is that it makes commands and -listings that I would never use available in a frictionless way: -commands like transpose-regions and apply-macro-to-region-lines, or -custom dired, ibuffer and package-menu listings that are interactively -inaccessible otherwise[[https://karthinks.com/software/fifteen-ways-to-use-embark/#fn:4][4]]. The ability to quickly whip up such buffers -makes knowhing how to use dired or ibuffer pay off several fold. In -composing such features seamlessly with minibuffer interaction or with -text-regions, Embark acts as a lever to amplify the power of Emacs’ -myriad built in commands and libraries. - ------------------------------------------------------------------------- - -1 Although of course, Helm and Embark both do a good job with their - presets. [[https://karthinks.com/software/fifteen-ways-to-use-embark/#fnref:1][↩︎]] - -2 To match the inverse of an input string with !, I used a [[https://github.com/oantolin/orderless#style-dispatchers][feature of - the orderless package]] for Emacs. [[https://karthinks.com/software/fifteen-ways-to-use-embark/#fnref:2][↩︎]] - -3 Yes, it’s not fully Helm-style since it still uses the minibuffer - instead of a buffer to show the candidates/actions. You could use - [[https://github.com/minad/vertico#extensions][vertico-buffer]] if that’s a sticking point. [[https://karthinks.com/software/fifteen-ways-to-use-embark/#fnref:3][↩︎]] - -4 Technically custom package-menu listings are accessible. From the full - package listing (M-x list-packages), you can filter package names by - regexp with / n. [[https://karthinks.com/software/fifteen-ways-to-use-embark/#fnref:4][↩︎]] - -[[https://karthinks.com/software/fifteen-ways-to-use-embark/#][Menu]] [[https://karthinks.com/software/fifteen-ways-to-use-embark/#][TOC]] [[https://karthinks.com/software/fifteen-ways-to-use-embark/#][share]] -Copyright © 2025 Karthik Chikmagalur - -• [[https://karthinks.com/][Home]] -• [[https://karthinks.com/about][About]] -• [[https://karthinks.com/blog][Misc]] -• [[https://karthinks.com/software][Coding]] -** [[https://kristofferbalintona.me/posts/202203130102/][Cape | Kristoffer Balintona]] -URL: https://kristofferbalintona.me/posts/202203130102/ -Captured On:[2025-09-23 Tue 01:41] -[[https://kristofferbalintona.me/][Kristoffer Balintona]] -[[https://kristofferbalintona.me/posts/][Archive]] Submit - -Cape - -Published Mar 13, 2022 | 1461 words, 7 minutes -[[https://kristofferbalintona.me/categories/guides/][Guides]] [[https://kristofferbalintona.me/tags/emacs/][Emacs]] -Table of Contents - -• [[https://kristofferbalintona.me/posts/202203130102/#what-is-cape][What is cape?]] -• [[https://kristofferbalintona.me/posts/202203130102/#basic-usage-keybinds][Basic usage: keybinds]] -• [[https://kristofferbalintona.me/posts/202203130102/#adding-backends-to-completion-at-point-functions][Adding backends to completion-at-point-functions]] -• [[https://kristofferbalintona.me/posts/202203130102/#cape-company-to-capf-and-cape-super-capf][Cape-company-to-capf and cape-super-capf]] -• [[https://kristofferbalintona.me/posts/202203130102/#pcomplete-nicety][pcomplete nicety]] -• [[https://kristofferbalintona.me/posts/202203130102/#my-completion-at-point-functions][My completion-at-point-functions]] -• [[https://kristofferbalintona.me/posts/202203130102/#changelog][Changelog]] - -What is cape? - -I will be going over the basic usages of [[https://github.com/minad/cape][Cape]], namely how to add -completion functions to completion-at-point-functions and how to use -cape's built-in completion utilities (e.g. cape-company-to-capf and -cape-capf-buster) to create backends with desired behavior. (Also see -[[https://www.reddit.com/r/emacs/comments/td0nth/comment/i0hz6re/][u/JDRiverRun’s informative comment]] about the advantages of using -completion-at-point-functions over company.) - -Cape is to corfu as company-backends are to company: - - Cape provides a bunch of Completion At Point Extensions which can be - used in combination with my Corfu completion UI or the default - completion UI. The completion backends used by completion-at-point - are so called completion-at-point-functions (Capfs). In principle, - the Capfs provided by Cape can also be used by Company. - -Consequently, cape is only used if you utilize the built-in -completion-at-point, which is best complemented by corfu text-completion -[[https://kristofferbalintona.me/posts/202203130102/#fn:1][1]]. - -You can also see the list of built-in completion-at-point-functions in -[[https://github.com/minad/cape#available-capfs][the README]]. Several of these completion-at-point-functions are quite -niche but others, such as cape-file and cape-symbol have common use -cases. - -Note: I use straight.el for package management and general.el to set my -keybindings. Both of these packages have integration with use-package -which come in the form of the :straight and :general keywords, -respectively. - -Basic usage: keybinds - -The most basic way to use cape is to bind its built-in -completion-at-point-functions to their own keys. For instance: - - 1 (use-package cape - 2 :general (:prefix "M-c" ; Choose a particular completion function - 3 "p" 'completion-at-point - 4 "t" 'complete-tag ; etags - 5 "d" 'cape-dabbrev ; basically `dabbrev-completion' - 6 "f" 'cape-file - 7 "k" 'cape-keyword - 8 "s" 'cape-symbol - 9 "a" 'cape-abbrev - 10 "i" 'cape-ispell - 11 "l" 'cape-line - 12 "w" 'cape-dict - 13 "\\" 'cape-tex - 14 "_" 'cape-tex - 15 "^" 'cape-tex - 16 "&" 'cape-sgml - 17 "r" 'cape-rfc1345)) - -Additionally, if having completion-at-point-functions readily available -through keybinds is desirable, then one can use cape-interactive-capf to -turn an already existing completion-at-point-function into a command -(i.e. interactive function) that can be bound. - -Adding backends to completion-at-point-functions - -However, cape is powerful because these functions can be added to -completion-at-point-functions, meaning you can configure when each -functions is used and where. The simplest way to accomplish this is by -adding backends to completion-at-point-functions in a hook. Here is a -simple example: - - 1 (defun kb/cape-capf-setup-git-commit () - 2 (let ((result)) - 3 (dolist (element '(cape-symbol cape-dabbrev) result) - 4 (add-to-list 'completion-at-point-functions element)))) - -I then add this to the appropriate hook: - - 1 :hook (git-commit-mode . kb/cape-capf-setup-git-commit) - -Consequently, when making commits to git, via magit, for instance, -completion-at-point-functions looks like this[[https://kristofferbalintona.me/posts/202203130102/#fn:2][2]]: - - 1 '(cape-symbol - 2 cape-dabbrev - 3 tags-completion-at-point-function) - -There are a few additional things to keep in mind when adding backends -to completion-function-at-point: - -1 add-to-list prepends elements to a list, that is, place an element at - the front of a list[[https://kristofferbalintona.me/posts/202203130102/#fn:3][3]]. -2 add-to-list is almost always preferable to push because push adds an - element to a list even if it already in the list, whereas add-to-list - will not. -3 Elements earlier in dolist will be added to the list before later - elements. This means that elements which should be deeper within - completion-at-point-functions should be placed first. (Notice how - cape-dabbrev is added after cape-symbol.) - -Cape-company-to-capf and cape-super-capf - -I think the killer feature of cape is cape-company-to-capf. This -function is able to convert any company backend and convert it into a -completion-at-point-function which corfu can use[[https://kristofferbalintona.me/posts/202203130102/#fn:4][4]]. For this reason, I -regard cape as quite an underrated package since it achieves almost full -feature parity with company. Here is an example with company-yasnippet: - - 1 (defun kb/cape-capf-setup-lsp () - 2 "Replace the default `lsp-completion-at-point' with its - 3 `cape-capf-buster' version. Also add `cape-file' and - 4 `company-yasnippet' backends." - 5 (setf (elt (cl-member 'lsp-completion-at-point completion-at-point-functions) 0) - 6 (cape-capf-buster #'lsp-completion-at-point)) - 7 (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-yasnippet)) - 8 (add-to-list 'completion-at-point-functions #'cape-dabbrev t)) - -Another useful function is cape-super-capf. This function combines -multiple completion-at-point-functions into a single function. -Effectively, this means candidates from multiple backends can appear -jointly. For instance, one can combine cape-ispell and cape-dabbrev: - - 1 (defun kb/cape-capf-setup-org () - 2 (require 'org-roam) - 3 (if (org-roam-file-p) - 4 (org-roam--register-completion-functions-h) - 5 (let (result) - 6 (dolist (element (list - 7 (cape-super-capf #'cape-ispell #'cape-dabbrev) - 8 (cape-company-to-capf #'company-yasnippet)) - 9 result) - 10 (add-to-list 'completion-at-point-functions element))) - 11 )) - -For other cape transformers, see the [[https://github.com/minad/cape#other-capf-transformers][appropriate section of the README]]. - -pcomplete nicety - -Finally, I have the following advice to make usage with pcomplete, what -eshell uses for completion: - - 1 :config - 2 ;; For pcomplete. For now these two advices are strongly recommended to - 3 ;; achieve a sane Eshell experience. See - 4 ;; https://github.com/minad/corfu#completing-with-corfu-in-the-shell-or-eshell - 5 - 6 ;; Silence the pcomplete capf, no errors or messages! - 7 (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) - 8 ;; Ensure that pcomplete does not write to the buffer and behaves as a pure - 9 ;; `completion-at-point-function'. - 10 (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify) - -My completion-at-point-functions - -I have very hesitantly included my WIP code which leverages cape and -completion-at-point utilities. I have not described in detail each of -the following functions. I have, however, attempted to provide useful -docstrings and comments. - -I hesitate to publish this code because it was very haphazardly written -and highly dependent on my configuration. The reason for this is the -order in which the completion-at-point-functions are added: any -peculiarities in another’s configuration may lead to undesirable -results. As a result, do not directly copy-and-paste this code and -expect proper functionality. Rather, I put it here as a reference for -what can be done. - -Here they are -Warning! This code may produce undesirable effects! Copy at your own -risk. - - 1 (use-package cape - 2 :hook ((emacs-lisp-mode . kb/cape-capf-setup-elisp) - 3 (lsp-completion-mode . kb/cape-capf-setup-lsp) - 4 (org-mode . kb/cape-capf-setup-org) - 5 (eshell-mode . kb/cape-capf-setup-eshell) - 6 (git-commit-mode . kb/cape-capf-setup-git-commit) - 7 (LaTeX-mode . kb/cape-capf-setup-latex) - 8 (sh-mode . kb/cape-capf-setup-sh) - 9 ) - 10 :general (:prefix "M-c" ; Particular completion function - 11 "p" 'completion-at-point - 12 "t" 'complete-tag ; etags - 13 "d" 'cape-dabbrev ; or dabbrev-completion - 14 "f" 'cape-file - 15 "k" 'cape-keyword - 16 "s" 'cape-symbol - 17 "a" 'cape-abbrev - 18 "i" 'cape-ispell - 19 "l" 'cape-line - 20 "w" 'cape-dict - 21 "\\"' cape-tex - 22 "_" 'cape-tex - 23 "^" 'cape-tex - 24 "&" 'cape-sgml - 25 "r" 'cape-rfc1345 - 26 ) - 27 :custom - 28 (cape-dabbrev-min-length 3) - 29 :init - 30 ;; Elisp - 31 (defun kb/cape-capf-ignore-keywords-elisp (cand) - 32 "Ignore keywords with forms that begin with \":\" (e.g. - 33 :history)." - 34 (or (not (keywordp cand)) - 35 (eq (char-after (car completion-in-region--data)) ?:))) - 36 (defun kb/cape-capf-setup-elisp () - 37 "Replace the default `elisp-completion-at-point' - 38 completion-at-point-function. Doing it this way will prevent - 39 disrupting the addition of other capfs (e.g. merely setting the - 40 variable entirely, or adding to list). - 41 - 42 Additionally, add `cape-file' as early as possible to the list." - 43 (setf (elt (cl-member 'elisp-completion-at-point completion-at-point-functions) 0) - 44 #'elisp-completion-at-point) - 45 (add-to-list 'completion-at-point-functions #'cape-symbol) - 46 ;; I prefer this being early/first in the list - 47 (add-to-list 'completion-at-point-functions #'cape-file) - 48 (require 'company-yasnippet) - 49 (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-yasnippet))) - 50 - 51 ;; LSP - 52 (defun kb/cape-capf-setup-lsp () - 53 "Replace the default `lsp-completion-at-point' with its - 54 `cape-capf-buster' version. Also add `cape-file' and - 55 `company-yasnippet' backends." - 56 (setf (elt (cl-member 'lsp-completion-at-point completion-at-point-functions) 0) - 57 (cape-capf-buster #'lsp-completion-at-point)) - 58 ;; TODO 2022-02-28: Maybe use `cape-wrap-predicate' to have candidates - 59 ;; listed when I want? - 60 (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-yasnippet)) - 61 (add-to-list 'completion-at-point-functions #'cape-dabbrev t)) - 62 - 63 ;; Org - 64 (defun kb/cape-capf-setup-org () - 65 (require 'org-roam) - 66 (if (org-roam-file-p) - 67 (org-roam--register-completion-functions-h) - 68 (let (result) - 69 (dolist (element (list - 70 (cape-super-capf #'cape-ispell #'cape-dabbrev) - 71 (cape-company-to-capf #'company-yasnippet)) - 72 result) - 73 (add-to-list 'completion-at-point-functions element))) - 74 )) - 75 - 76 ;; Eshell - 77 (defun kb/cape-capf-setup-eshell () - 78 (let ((result)) - 79 (dolist (element '(pcomplete-completions-at-point cape-file) result) - 80 (add-to-list 'completion-at-point-functions element)) - 81 )) - 82 - 83 ;; Git-commit - 84 (defun kb/cape-capf-setup-git-commit () - 85 (general-define-key - 86 :keymaps 'local - 87 :states 'insert - 88 "<tab>" 'completion-at-point) ; Keybinding for `completion-at-point' - 89 (let ((result)) - 90 (dolist (element '(cape-dabbrev cape-symbol) result) - 91 (add-to-list 'completion-at-point-functions element)))) - 92 - 93 ;; LaTeX - 94 (defun kb/cape-capf-setup-latex () - 95 (require 'company-auctex) - 96 (let ((result)) - 97 (dolist (element (list - 98 ;; First add `company-yasnippet' - 99 (cape-company-to-capf #'company-yasnippet) - 100 ;; Then add `cape-tex' - 101 #'cape-tex - 102 ;; Then add `company-auctex' in the order it adds its - 103 ;; backends. - 104 (cape-company-to-capf #'company-auctex-bibs) - 105 (cape-company-to-capf #'company-auctex-labels) - 106 (cape-company-to-capf - 107 (apply-partially #'company--multi-backend-adapter - 108 '(company-auctex-macros company-auctex-symbols company-auctex-environments)))) - 109 result) - 110 (add-to-list 'completion-at-point-functions element)))) - 111 - 112 - 113 ;; Sh - 114 (defun kb/cape-capf-setup-sh () - 115 (require 'company-shell) - 116 (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-shell))) - 117 :config - 118 ;; For pcomplete. For now these two advices are strongly recommended to - 119 ;; achieve a sane Eshell experience. See - 120 ;; https://github.com/minad/corfu#completing-with-corfu-in-the-shell-or-eshell - 121 - 122 ;; Silence the pcomplete capf, no errors or messages! - 123 (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) - 124 ;; Ensure that pcomplete does not write to the buffer and behaves as a pure - 125 ;; `completion-at-point-function'. - 126 (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)) ------------------------------------------------------------------------- - -Changelog - -• March 13, 2022 - - • Fixed typo. Added link to [[https://www.reddit.com/r/emacs/comments/td0nth/comment/i0hz6re/][u/JDRiverRun’s informative Reddit comment]]. - ------------------------------------------------------------------------- - -1 See [[https://kristofferbalintona.me/posts/202202270056/][Corfu, Kind-icon, and Corfu-doc]] to see the basic usage of corfu - and several accessory packages. [[https://kristofferbalintona.me/posts/202203130102/#fnref:1][↩︎]] - -2 tags-completion-at-point-function is a default - completion-at-point-function [[https://kristofferbalintona.me/posts/202203130102/#fnref:2][↩︎]] - -3 See its docstring to learn how to append to a list [[https://kristofferbalintona.me/posts/202203130102/#fnref:3][↩︎]] - -4 This feature is currently listed as experimental but, for the most - part, the results are as expected. If anything, rare edge cases are - the only points of missing functionality. [[https://kristofferbalintona.me/posts/202203130102/#fnref:4][↩︎]] - -Cross-references from other posts - -The posts below reference the current post: - -• [[https://kristofferbalintona.me/posts/202108102130/][Windows]] -• [[https://kristofferbalintona.me/posts/202112071230/][The "Great Books Idea" and liberal versus general education]] -• [[https://kristofferbalintona.me/posts/202202211546/][Vertico, Marginalia, All-the-icons-completion, and Orderless]] -• [[https://kristofferbalintona.me/posts/202202270056/][Corfu, Kind-icon, and Corfu-doc]] -• [[https://kristofferbalintona.me/posts/202206141852/][Citations in org-mode: Org-cite and Citar]] -• [[https://kristofferbalintona.me/posts/202301101610/][Think: A compelling introduction to philosophy by Simon Blackburn]] - ------------------------------------------------------------------------- - -The Text Completion and Minibuffer UI series - -This post is just one installation of the Text Completion and Minibuffer -UI series. Below are all the posts in this series: - -1 [[https://kristofferbalintona.me/posts/202202211546/][Vertico, Marginalia, All-the-icons-completion, and Orderless]] -2 [[https://kristofferbalintona.me/posts/202202270056/][Corfu, Kind-icon, and Corfu-doc]] -3 [[https://kristofferbalintona.me/posts/202203130102/][Cape]] this post! -4 [[https://kristofferbalintona.me/posts/202504050923/][Complement corfu, vertico, and completion-preview with prescient.el - sorting]] - -[[https://kristofferbalintona.me/][Home]] [[https://kristofferbalintona.me/index.xml][RSS]] [[https://kristofferbalintona.me/tags/][Tags]] [[https://kristofferbalintona.me/categories/][Categories]] [[https://kristofferbalintona.me/series/]]Series -** Consider Switching to Corfu from Company -URL: https://kristofferbalintona.me/posts/202202270056/ -Captured On:[2025-09-23 Tue 01:39] -[[https://kristofferbalintona.me/][Kristoffer Balintona]] -[[https://kristofferbalintona.me/posts/][Archive]] Submit - -Corfu, Kind-icon, and Corfu-doc - -Published Feb 27, 2022, last updated Mar 11, 2022 | 2261 words, 11 -minutes -[[https://kristofferbalintona.me/categories/guides/][Guides]] [[https://kristofferbalintona.me/tags/emacs/][Emacs]] -Table of Contents - -• [[https://kristofferbalintona.me/posts/202202270056/#synopsis][Synopsis]] -• [[https://kristofferbalintona.me/posts/202202270056/#corfu][Corfu]] - - • [[https://kristofferbalintona.me/posts/202202270056/#what-is-corfu-how-does-it-differ-from-company][What is corfu? How does it differ from company?]] - • [[https://kristofferbalintona.me/posts/202202270056/#basic][Basic]] - • [[https://kristofferbalintona.me/posts/202202270056/#working-with-the-recent-changes-to-corfu][Working with the recent changes to corfu]] - • [[https://kristofferbalintona.me/posts/202202270056/#further-configuration-in-minibuffers-and-with-lsp][Further configuration in minibuffers and with lsp]] - • [[https://kristofferbalintona.me/posts/202202270056/#end-product][End product]] - -• [[https://kristofferbalintona.me/posts/202202270056/#kind-icon][Kind-icon]] -• [[https://kristofferbalintona.me/posts/202202270056/#corfu-doc][Corfu-doc]] -• [[https://kristofferbalintona.me/posts/202202270056/#changelog][Changelog]] - -November 28, 2022 As is noted in the [[https://github.com/galeo/corfu-doc/issues/25][corfu-doc repository]], corfu-doc has -been deprecated by the built-in corfu-popupinfo corfu extension. -corfu-popupinfo’s functionality is roughly identical to corfu-doc’s, -though its interface and code is naturally more idiomatic to corfu. - -Synopsis - -I will be going over my personal Emacs' text completion (e.g. -company-mode and its accessories) configuration, which includes the -following packages: - -• [[https://github.com/minad/corfu][Corfu]] by Minad — simpler alternative to company-mode -• [[https://github.com/jdtsmith/kind-icon][Kind-icon]] by jdtsmith (u/JDRiverRun)— add icons to corfu popup (analog - to company-box-icons) -• [[https://github.com/galeo/corfu-doc][Corfu-doc]] by Galeo — add documentation popup for corfu candidates - (analog to company-box-doc) - -Note: I use straight.el for package management and general.el to set my -keybindings. Both of these packages have integration with use-package -which come in the form of the :straight and :general keywords, -respectively. - -Corfu - -What is corfu? How does it differ from company? - -Figure 1: The default corfu popup window. The GIF’s framerate is -low, which makes corfu appear less performant here than in actuality. - -Corfu is a text completion (e.g. completion-at-point, company-mode) -package. In my opinion, since its release, corfu has not gotten the -attention that it deserves. I prefer it to company for the following -reasons: - -1 It is easier to configure since corfu’s internals rely on the built-in - completion-at-point. This also means that, unlike company,[[https://kristofferbalintona.me/posts/202202270056/#fn:1][1]]… - - • any built-in invocation of completion-at-point or - completion-in-region leverages corfu, - • and any completion-style (e.g. orderless) can be used for filtering - candidates. - -2 Corfu has been more performant (i.e. fewer stutters, smoother cycling - of candidates) in my experience. -3 Corfu can support any company backend via cape-company-to-capf, - provided by the complementary cape package. Thus, packages like - company-yasnippet can be used with corfu easily (see the next post in - my Text completion and minibuffer UI series for more details and - examples.) - -Basic - -The following is a basic corfu configuration with my preferred keybinds: - - 1 (use-package corfu - 2 :general - 3 (:keymaps 'corfu-map - 4 :states 'insert - 5 "C-n" #'corfu-next - 6 "C-p" #'corfu-previous - 7 "<escape>" #'corfu-quit - 8 "<return>" #'corfu-insert - 9 "M-d" #'corfu-show-documentation - 10 "M-l" #'corfu-show-location) - 11 :config - 12 (corfu-global-mode)) - -These keybinds have C-n and C-p move through the candidates popup, -<return> choose the current candidate, and <escape> close the corfu -popup. Moreover, I have corfu’s documentation command -(corfu-show-documentation; shows the available documentation for the -current candidate, if any) bound to M-d, and corfu’s location command -(corfu-show-location) to go to the location of the current candidate to -M-l. - -Corfu offers a few variables to configure. You can take a look at each -docstring to see its function. Here are my preferences: - - 1 :custom - 2 (corfu-auto nil) ; Only use `corfu' when calling `completion-at-point' or - 3 ; `indent-for-tab-command' - 4 (corfu-auto-prefix 2) - 5 (corfu-auto-delay 0.25) - 6 - 7 (corfu-min-width 80) - 8 (corfu-max-width corfu-min-width) ; Always have the same width - 9 (corfu-count 14) - 10 (corfu-scroll-margin 4) - 11 (corfu-cycle nil) - 12 - 13 ;; `nil' means to ignore `corfu-separator' behavior, that is, use the older - 14 ;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using - 15 ;; `corfu-auto' = `t' workflow (in that case, make sure you also set up - 16 ;; `corfu-separator' and a keybind for `corfu-insert-separator', which my - 17 ;; configuration already has pre-prepared). Necessary for manual corfu usage with - 18 ;; orderless, otherwise first component is ignored, unless `corfu-separator' - 19 ;; is inserted. - 20 (corfu-quit-at-boundary nil) - 21 (corfu-preselect-first t) ; Preselect first candidate? - 22 - 23 ;; Other - 24 ;; NOTE 2022-02-05: In my actual configuration, I have this variable set to nil - 25 ;; since I use `corfu-doc', whose configuration comes later. But if you don't - 26 ;; use `corfu-doc', this might be helpful to you. - 27 (corfu-echo-documentation t) ; Show documentation in echo area? - 28 - -Additionally, the following two variables not under corfu but related to -completion-at-point will be useful to set: - - 1 ;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you - 2 ;; want to perform completion - 3 (tab-always-indent 'complete) - 4 (completion-cycle-threshold nil) ; Always show all candidates in popup menu - -Working with the recent changes to corfu - -On [[https://github.com/minad/corfu/commit/91feb66630eea6f36fee10576760c219896c1d05][February 7, 2022]], corfu introduced an important change[[https://kristofferbalintona.me/posts/202202270056/#fn:2][2]], particularly -the interaction between corfu and orderless. You can read more [[https://github.com/minad/corfu#orderless-completion][on their -README]], but, essentially, orderless now introduces the -corfu-insert-separator command that inserts the corfu-separator -character into the buffer. This character is what delimits orderless -components (see [[https://www.reddit.com/r/emacs/comments/t38kkh/comment/hytmcpg/][this Reddit comment]] for a more lengthy description of -this behavior.) A corfu workflow in which corfu-auto is set to t -leverages this change, for without it users could not realistically use -corfu with a multi-component completion-style like orderless. - -I do not use this workflow[[https://kristofferbalintona.me/posts/202202270056/#fn:3][3]], but if this behavior is desirable, you can -set corfu-separator to your orderless separator character to properly -delimit orderless components. I personally use the regular space -character. You can make the following modifications to your -configuration: - - 1 :general - 2 ;; NOTE 2022-02-28: `general-override-mode-map' is necessary to override local - 3 ;; binds to SPC in evil-mode's insert mode. May not be necessary if you don't use `evil' - 4 (:keymaps 'corfu-map - 5 :states 'insert - 6 "H-SPC" #'corfu-insert-separator ; I have a hyper key so this is an alternative keybind I use sometimes - 7 "SPC" #'corfu-insert-separator) - 8 :custom - 9 (corfu-quit-at-boundary 'separator) ; a non-nil value is necessary - 10 (corfu-separator ?\s) ; Use space - 11 (corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted - 12 (corfu-preview-current 'insert) ; Preview current candidate? - 13 :config - 14 ;; NOTE 2022-03-01: This allows for a more evil-esque way to have - 15 ;; `corfu-insert-separator' work with space in insert mode without resorting to - 16 ;; overriding keybindings with `general-override-mode-map'. See - 17 ;; https://github.com/minad/corfu/issues/12#issuecomment-869037519 - 18 ;; Alternatively, add advice without `general.el': - 19 ;; (advice-add 'corfu--setup :after 'evil-normalize-keymaps) - 20 ;; (advice-add 'corfu--teardown :after 'evil-normalize-keymaps) - 21 (general-add-advice '(corfu--setup corfu--teardown) :after 'evil-normalize-keymaps) - 22 (evil-make-overriding-map corfu-map) - -Further configuration in minibuffers and with lsp - -Corfu’s [[https://github.com/minad/corfu#completing-with-corfu-in-the-minibuffer][README]] provides a way to be able to use corfu completion in the -minibuffer: - - 1 ;; Enable Corfu more generally for every minibuffer, as long as no other - 2 ;; completion UI is active. If you use Mct or Vertico as your main minibuffer - 3 ;; completion UI. From - 4 ;; https://github.com/minad/corfu#completing-with-corfu-in-the-minibuffer - 5 (defun corfu-enable-always-in-minibuffer () - 6 "Enable Corfu in the minibuffer if Vertico/Mct are not active." - 7 (unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT - 8 (bound-and-true-p vertico--input)) - 9 (setq-local corfu-auto nil) ; Ensure auto completion is disabled - 10 (corfu-mode 1))) - 11 (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) - -This means that in commands like eval-expression, corfu is able to be -used (via <tab>) and provide completion. - -Figure 2: Using corfu in the minibuffer prompt for eval-expression. - -Figure 2: Using corfu in the minibuffer prompt for eval-expression. - -Additionally, for lsp-mode buffers, I have the following lines (this is -entirely optional and preferential): - - 1 :hook (lsp-completion-mode . kb/corfu-setup-lsp) ; Use corfu for lsp completion - 2 :custom - 3 (lsp-completion-provider :none) ; Use corfu instead the default for lsp completions - 4 :config - 5 ;; Setup lsp to use corfu for lsp completion - 6 (defun kb/corfu-setup-lsp () - 7 "Use orderless completion style with lsp-capf instead of the - 8 default lsp-passthrough." - 9 (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) - 10 '(orderless))) - -End product - -Putting it together, we end with my actual configuration: - - 1 (use-package corfu - 2 :hook (lsp-completion-mode . kb/corfu-setup-lsp) ; Use corfu for lsp completion - 3 :general - 4 (:keymaps 'corfu-map - 5 :states 'insert - 6 "C-n" #'corfu-next - 7 "C-p" #'corfu-previous - 8 "<escape>" #'corfu-quit - 9 "<return>" #'corfu-insert - 10 "H-SPC" #'corfu-insert-separator - 11 ;; "SPC" #'corfu-insert-separator ; Use when `corfu-quit-at-boundary' is non-nil - 12 "M-d" #'corfu-show-documentation - 13 "C-g" #'corfu-quit - 14 "M-l" #'corfu-show-location) - 15 :custom - 16 ;; Works with `indent-for-tab-command'. Make sure tab doesn't indent when you - 17 ;; want to perform completion - 18 (tab-always-indent 'complete) - 19 (completion-cycle-threshold nil) ; Always show candidates in menu - 20 - 21 (corfu-auto nil) - 22 (corfu-auto-prefix 2) - 23 (corfu-auto-delay 0.25) - 24 - 25 (corfu-min-width 80) - 26 (corfu-max-width corfu-min-width) ; Always have the same width - 27 (corfu-count 14) - 28 (corfu-scroll-margin 4) - 29 (corfu-cycle nil) - 30 - 31 ;; `nil' means to ignore `corfu-separator' behavior, that is, use the older - 32 ;; `corfu-quit-at-boundary' = nil behavior. Set this to separator if using - 33 ;; `corfu-auto' = `t' workflow (in that case, make sure you also set up - 34 ;; `corfu-separator' and a keybind for `corfu-insert-separator', which my - 35 ;; configuration already has pre-prepared). Necessary for manual corfu usage with - 36 ;; orderless, otherwise first component is ignored, unless `corfu-separator' - 37 ;; is inserted. - 38 (corfu-quit-at-boundary nil) - 39 (corfu-separator ?\s) ; Use space - 40 (corfu-quit-no-match 'separator) ; Don't quit if there is `corfu-separator' inserted - 41 (corfu-preview-current 'insert) ; Preview first candidate. Insert on input if only one - 42 (corfu-preselect-first t) ; Preselect first candidate? - 43 - 44 ;; Other - 45 (corfu-echo-documentation nil) ; Already use corfu-doc - 46 (lsp-completion-provider :none) ; Use corfu instead for lsp completions - 47 :init - 48 (corfu-global-mode) - 49 :config - 50 ;; NOTE 2022-03-01: This allows for a more evil-esque way to have - 51 ;; `corfu-insert-separator' work with space in insert mode without resorting to - 52 ;; overriding keybindings with `general-override-mode-map'. See - 53 ;; https://github.com/minad/corfu/issues/12#issuecomment-869037519 - 54 ;; Alternatively, add advice without `general.el': - 55 ;; (advice-add 'corfu--setup :after 'evil-normalize-keymaps) - 56 ;; (advice-add 'corfu--teardown :after 'evil-normalize-keymaps) - 57 (general-add-advice '(corfu--setup corfu--teardown) :after 'evil-normalize-keymaps) - 58 (evil-make-overriding-map corfu-map) - 59 - 60 ;; Enable Corfu more generally for every minibuffer, as long as no other - 61 ;; completion UI is active. If you use Mct or Vertico as your main minibuffer - 62 ;; completion UI. From - 63 ;; https://github.com/minad/corfu#completing-with-corfu-in-the-minibuffer - 64 (defun corfu-enable-always-in-minibuffer () - 65 "Enable Corfu in the minibuffer if Vertico/Mct are not active." - 66 (unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT - 67 (bound-and-true-p vertico--input)) - 68 (setq-local corfu-auto nil) ; Ensure auto completion is disabled - 69 (corfu-mode 1))) - 70 (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) - 71 - 72 ;; Setup lsp to use corfu for lsp completion - 73 (defun kb/corfu-setup-lsp () - 74 "Use orderless completion style with lsp-capf instead of the - 75 default lsp-passthrough." - 76 (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) - 77 '(orderless)))) - -Kind-icon - -Kind-icon is essentially company-box-icons for corfu. It adds icons to -the left margin of the corfu popup that represent the ‘function’ (e.g. -variable, method, file) of that candidate. - -Figure 3: Using corfu-doc in java-mode with completion candidates -provided by lsp-mode. - -Figure 3: Using corfu-doc in java-mode with completion candidates -provided by lsp-mode. - -The following is my configuration: - - 1 (use-package kind-icon - 2 :after corfu - 3 :custom - 4 (kind-icon-use-icons t) - 5 (kind-icon-default-face 'corfu-default) ; Have background color be the same as `corfu' face background - 6 (kind-icon-blend-background nil) ; Use midpoint color between foreground and background colors ("blended")? - 7 (kind-icon-blend-frac 0.08) - 8 - 9 ;; NOTE 2022-02-05: `kind-icon' depends `svg-lib' which creates a cache - 10 ;; directory that defaults to the `user-emacs-directory'. Here, I change that - 11 ;; directory to a location appropriate to `no-littering' conventions, a - 12 ;; package which moves directories of other packages to sane locations. - 13 (svg-lib-icons-dir (no-littering-expand-var-file-name "svg-lib/cache/")) ; Change cache dir - 14 :config - 15 (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter) ; Enable `kind-icon' - 16 - 17 ;; Add hook to reset cache so the icon colors match my theme - 18 ;; NOTE 2022-02-05: This is a hook which resets the cache whenever I switch - 19 ;; the theme using my custom defined command for switching themes. If I don't - 20 ;; do this, then the backgound color will remain the same, meaning it will not - 21 ;; match the background color corresponding to the current theme. Important - 22 ;; since I have a light theme and dark theme I switch between. This has no - 23 ;; function unless you use something similar - 24 (add-hook 'kb/themes-hooks #'(lambda () (interactive) (kind-icon-reset-cache)))) - -Corfu-doc - -Corfu-doc is basically company-quickhelp for corfu. It shows the -documentation of the selected candidate in an adjacent popup window. - -Figure 4: Using corfu-doc in a corfu popup. Called from a java file with -completion candidates provided by lsp-mode. - -Figure 4: Using corfu-doc in a corfu popup. Called from a java file with -completion candidates provided by lsp-mode. - -Here is a sample configuration[[https://kristofferbalintona.me/posts/202202270056/#fn:4][4]]: - - 1 (use-package corfu-doc - 2 ;; NOTE 2022-02-05: At the time of writing, `corfu-doc' is not yet on melpa - 3 :straight (corfu-doc :type git :host github :repo "galeo/corfu-doc") - 4 :after corfu - 5 :hook (corfu-mode . corfu-doc-mode) - 6 :general (:keymaps 'corfu-map - 7 ;; This is a manual toggle for the documentation popup. - 8 [remap corfu-show-documentation] #'corfu-doc-toggle ; Remap the default doc command - 9 ;; Scroll in the documentation window - 10 "M-n" #'corfu-doc-scroll-up - 11 "M-p" #'corfu-doc-scroll-down) - 12 :custom - 13 (corfu-doc-delay 0.5) - 14 (corfu-doc-max-width 70) - 15 (corfu-doc-max-height 20) - 16 - 17 ;; NOTE 2022-02-05: I've also set this in the `corfu' use-package to be - 18 ;; extra-safe that this is set when corfu-doc is loaded. I do not want - 19 ;; documentation shown in both the echo area and in the `corfu-doc' popup. - 20 (corfu-echo-documentation nil)) - -From my experience, corfu-doc is perfect for most. However, it should be -noted that for those who have a high [[https://wiki.archlinux.org/title/Xorg/Keyboard_configuration#Adjusting_typematic_delay_and_rate][repeat rate]][[https://kristofferbalintona.me/posts/202202270056/#fn:5][5]], rapidly scrolling -through candidates causes stuttering and/or lag. This is why I find -setting a keybind for corfu-doc-toggle to be useful. - ------------------------------------------------------------------------- - -Changelog - -• February 28, 2022 - - • Added link to Reddit comment in [[https://kristofferbalintona.me/posts/202202270056/#working-with-the-recent-changes-to-corfu][Working with the recent changes to - corfu]] section. Also update description of new corfu behavior. - • Added configuration for using corfu in the minibuffer. - • Listed more benefits to corfu, provided by u/JDRiverRun. - -• March 01, 2022 - - • Added link to relevant corfu GitHub Issue. - • Changed corfu configuration to avoid setting keybinds in - general-override-mode-map, suggested by a comment to this point. - • Added a note and GIF to corfu-doc section. - -• March 11, 2022 - - • Update to include new compatibility with corfu-insert-separator and - corfu-quit-at-boundary functionality. - ------------------------------------------------------------------------- - -1 Kudos to u/JDRiverRun, the current maintainer of kind-icon for - [[https://www.reddit.com/r/emacs/comments/t38kkh/comment/hyturrd/][providing a few benefits]] I didn’t originally list. [[https://kristofferbalintona.me/posts/202202270056/#fnref:1][↩︎]] - -2 This change was initially motivated by jdtsmith (u/JDRiverRun) and is - described in [[https://github.com/minad/corfu/issues/119][this GitHub issue]]. [[https://kristofferbalintona.me/posts/202202270056/#fnref:2][↩︎]] - -3 See [[https://github.com/minad/corfu/commit/b71465fa6b6588babc98a1ae7034c9a41e5eaca7][this commit]]. Also see this [[https://github.com/minad/corfu/issues/138][GitHub issue]] which reimplemented the - old corfu-quit-at-boundary functionality alongside the then new - corfu-insert-separator functionality. [[https://kristofferbalintona.me/posts/202202270056/#fnref:3][↩︎]] - -4 This is not exactly my configuration, but is quite close to it. [[https://kristofferbalintona.me/posts/202202270056/#fnref:4][↩︎]] - -5 I personally use a repeat rate of 37 ms with a delay rate of 225, set - by xset r rate 225 37. [[https://kristofferbalintona.me/posts/202202270056/#fnref:5][↩︎]] - -Cross-references from other posts - -The posts below reference the current post: - -• [[https://kristofferbalintona.me/posts/202203130102/][Cape]] - ------------------------------------------------------------------------- - -The Text Completion and Minibuffer UI series - -This post is just one installation of the Text Completion and Minibuffer -UI series. Below are all the posts in this series: - -1 [[https://kristofferbalintona.me/posts/202202211546/][Vertico, Marginalia, All-the-icons-completion, and Orderless]] -2 [[https://kristofferbalintona.me/posts/202202270056/][Corfu, Kind-icon, and Corfu-doc]] this post! -3 [[https://kristofferbalintona.me/posts/202203130102/][Cape]] -4 [[https://kristofferbalintona.me/posts/202504050923/][Complement corfu, vertico, and completion-preview with prescient.el - sorting]] - -[[https://kristofferbalintona.me/][Home]] [[https://kristofferbalintona.me/index.xml][RSS]] [[https://kristofferbalintona.me/tags/][Tags]] [[https://kristofferbalintona.me/categories/][Categories]] [[https://kristofferbalintona.me/series/]]Series -** Ensure You Have Prescient Set Up Now, Add to Corfu Later -URL: https://kristofferbalintona.me/posts/202504050923/ -Captured On:[2025-09-23 Tue 01:39] -[[https://kristofferbalintona.me/][Kristoffer Balintona]] -[[https://kristofferbalintona.me/posts/][Archive]] Submit - -Complement corfu, vertico, and completion-preview with prescient.el -sorting - -Published Apr 5, 2025, last updated Apr 22, 2025 | 1448 words, 7 minutes -[[https://kristofferbalintona.me/categories/tips-and-tricks/][Tips and Tricks]] [[https://kristofferbalintona.me/tags/emacs/][Emacs]] -April 07, 2025 With several commits made on April 6, 2025, vertico spun -off its sorting functionality into an extension: [[https://github.com/minad/vertico/blob/main/extensions/vertico-sort.el][vertico-sort]]. As -explained in vertico-sort.el’s commentary section, it includes a new -feature: when history-delete-duplicates is nil, all sorting functions -defined in vertico-sort.el now rank recently selected candidates above -frequently selected candidates. This approximates the sorting strategy -of prescient.el. I think I’ll stick with prescient.el sorting because I -like history-delete-duplicates set to non-nil, but try it out yourself! -April 22, 2025 Corfu now offers a similar functionality as vertic-sort -does for vertico with the new [[https://github.com/minad/corfu/blob/main/extensions/corfu-history.el][corfu-sort]] extension. Like vertico-sort, -corfu-sort requires history-delete-duplicates to be nil. - -[[https://github.com/radian-software/prescient.el][Prescient.el]] is a package that was popular during the era of ivy and -helm. Nowadays, I don’t see it mentioned much because of the very -popular corfu + vertico + marginalia + orderless combination which -enhance built-in Emacs completion and overtaken ivy and helm in terms of -popularity. (And it’s deserved! I use this combination, too.)[[https://kristofferbalintona.me/posts/202504050923/#fn:1][1]] But I’ve -found prescient.el a noticeable convenience that complements this set of -packages. - -The reason is prescient.el’s sorting. Roughly, we can think of -completion as having two halves: filtering and sorting. Filtering is -what completion-styles does: among the generated completion candidates, -which are shown to the user? Sorting is the order in which the filtered -candidates are shown. One might think that orderless, which is filters -candidates with its completion-style, also sorts candidates—but it -doesn’t. As explained in the [[https://github.com/oantolin/orderless?tab=readme-ov-file#prescient][orderless README]]: - - The [[https://github.com/radian-software/prescient.el][prescient.el]] library also provides matching of space-separated - components in any order. It offers a completion-style that can be - used with Emacs’ default completion UI, Mct, Vertico or with - Icomplete. Furthermore Ivy is supported. The components can be - matched literally, as regexps, as initialisms or in the flex style - (called “fuzzy” in prescient). Prescient does not offer the same - flexibility as Orderless with its style dispatchers. However in - addition to matching, Prescient supports sorting of candidates, - while Orderless leaves that up to the candidate source and the - completion UI. - -As such, in the corfu + vertico + marginalia + orderless world, vertico -(for minibuffer completions) and corfu (for in-inline completions) are -responsible for sorting candidates. - -The problem is this: although orderless brilliantly narrows down -candidates, both vertico and corfu have somewhat naive sorting -algorithms (see vertico-sort-function and corfu-sort-function). Vertico -offers vertico-sort-history-alpha and vertico-sort-history-length-alpha, -and corfu offers corfu-history-mode. In my limited experience, however, -I give prescient.el’s sorting algorithm an edge to both. (YMMV—feel free -to try them!) Oftentimes vertico and corfu will consistently show the -candidate you have in mind later in the list. This tends to be the case -when using certain common commands and certain -completion-at-point-functions: I would common seek out certain -candidates that orderless would place third or fourth in the list. - -Prescient.el almost entirely solved this issue for me. Prescient.el -offers a completion-style—this is its filtering functionality—but it -also offers sorting functionality. The sorting functionality is what’s -relevant here: - - When sorting, the last few candidates you selected are displayed - first, followed by the most frequently selected ones, and then the - remaining candidates are sorted by length. - -Prescient.el is the core package, and there are several [[https://github.com/radian-software/prescient.el?tab=readme-ov-file#usage][auxiliary -packages]] to integrate prescient with vertico, corfu, and others. We can -enable prescient’s sorting for vertico and corfu (but not its filtering; -I want to leave this to orderless) with something like the following: - - 1 ;; Core package - 2 (use-package prescient - 3 :custom - 4 ;; My settings for relevant user options: - 5 ;; (prescient-aggressive-file-save t) - 6 ;; (prescient-sort-length-enable nil) - 7 ;; (prescient-sort-full-matches-first t) - 8 ;; (prescient-history-length 200) - 9 ;; (prescient-frequency-decay 0.997) - 10 ;; (prescient-frequency-threshold 0.05) - 11 :config - 12 ;; Optional: persist prescient statistics to an on-disk cache - 13 ;; (`prescient-save-file') - 14 (prescient-persist-mode 1)) - 15 - 16 ;; Integration with corfu - 17 (use-package corfu-prescient - 18 ;; The :after keyword defers loading this package, meaning this package is - 19 ;; only loaded until something else wants something from it. If we want - 20 ;; `corfu-prescient-mode' to be enabled in the :config block, we need to - 21 ;; prevent deferral with the :demand keyword. In combination with our :after - 22 ;; block, the package is immediately loaded only after both corfu and - 23 ;; prescient are loaded. - 24 :demand t - 25 :after corfu prescient - 26 :custom - 27 ;; Sorting. These are the default values but I include them here to be - 28 ;; explicit. - 29 (corfu-prescient-enable-sorting t) - 30 (corfu-prescient-override-sorting nil) ; Don't override `display-sort-function' - 31 - 32 ;; Filtering - 33 (corfu-prescient-enable-filtering nil) ; We want orderless to do the filtering - 34 ;; See also `corfu-prescient-completion-styles', - 35 ;; `corfu-prescient-completion-category-overrides' and - 36 ;; `prescient--completion-recommended-overrides'. Those options apply only - 37 ;; when `corfu-prescient-enable-filtering' is non-nil. - 38 :config - 39 (corfu-prescient-mode 1)) - 40 - 41 ;; Integration with vertico - 42 (use-package vertico-prescient - 43 ;; The :after keyword defers loading this package, meaning this package is - 44 ;; only loaded until something else wants something from it. If we want - 45 ;; `vertico-prescient-mode' to be enabled in the :config block, we need to - 46 ;; prevent deferral with the :demand keyword. In combination with our :after - 47 ;; block, the package is immediately loaded only after both vertico and - 48 ;; prescient are loaded. - 49 :demand t - 50 :after vertico prescient - 51 :custom - 52 ;; Sorting. These are the default values but I include them here to be - 53 ;; explicit. - 54 (vertico-prescient-enable-sorting t) - 55 (vertico-prescient-override-sorting nil) ; Don't override `display-sort-function' - 56 - 57 ;; Filtering - 58 (vertico-prescient-enable-filtering nil) ; We want orderless to do the filtering - 59 ;; See also `vertico-prescient-completion-styles', - 60 ;; `vertico-prescient-completion-category-overrides', and - 61 ;; `prescient--completion-recommended-overrides'. Those options apply only - 62 ;; when when `vertico-prescient-enable-filtering' is non-nil. - 63 :config - 64 (vertico-prescient-mode 1)) - -The result is using orderless for completion filtering and prescient for -completion sorting everywhere—for in-line completions with corfu and -minibuffer completions with vertico. With this, among the candidates -filtered by orderless, the most recent and common ones will be bumped up -to the beginning. Try it out! - -Bonus: integration with completion-preview-mode - -Emacs 30.1 ships with the new completion-preview-mode. You can read -about it [[https://eshelyaron.com/posts/2023-11-17-completion-preview-in-emacs.html][in this blog post]] but also in the Emacs 30.1 news (i.e., C-u 30 -C-h n). Apparently, completion-preview-mode has its own sorting -function—which makes sense, since it isn’t hooked into either corfu nor -vertico (which we set up to use prescient above). The relevant user -option to modify its sorting is completion-preview-sort-function. So we -just have to make sure that function matches prescient’s. We can do that -like this: - - 1 (with-eval-after-load 'prescient - 2 ;; Have `completion-preview-mode' use prescient's sorting algorithm - 3 (setopt completion-preview-sort-function #'prescient-completion-sort)) - -But, if you also use corfu on top of completion-preview-mode, you’ll -still notice a discrepancy between corfu’s first candidate and -completion-preview’s candidate. They should be the same with the same -sorting algorithm but they’re not! I’m not 100%, but I’m fairly -confident that corfu-prescient keeps track of corfu-specific completion -usage, which is separate from non-corfu-specific use. Consequently, the -statistics used to sort candidates ends up different between corfu and -prescient-completion-sort. - -In any case, below is a simple fix that gets corfu and -completion-preview on the same page: - - 1 ;; We set completion-preview's sorting function - 2 ;; (`completion-preview-sort-function') to corfu's sorting function - 3 ;; (`corfu-sort-function'). - 4 (with-eval-after-load 'corfu - 5 (setopt completion-preview-sort-function corfu-sort-function)) - 6 - 7 ;; Alternative: for a solution that ensures `completion-preview-sort-function' - 8 ;; always matches `corfu-sort-function', we can use a variable watcher. This is - 9 ;; my preference. - 10 ;; - 11 ;; The below accounts for cases in which (i) the user has buffer-local values - 12 ;; for `corfu-sort-function' or (ii) the user changes the global value of - 13 ;; `corfu-sort-function' (e.g., from minor modes that disable/enable - 14 ;; functionality). It is a robust solution, and although I have not tested its - 15 ;; performance overhead, it should cause no problems. - 16 (add-variable-watcher 'corfu-sort-function - 17 (lambda (_symbol newval operation where) - 18 "Match the value of `completion-preview-sort-function' to `corfu-sort-function'. - 19 If `corfu-sort-function' is set buffer-locally, also set - 20 `completion-preview-sort-function' buffer-locally. Otherwise, change - 21 the default value of `completion-preview-sort-function' accordingly. - 22 - 23 This action only applies when the value of `corfu-sort-function' is - 24 set (i.e., OPERATION is \\='set). This excludes, e.g., let bindings." - 25 (when (equal operation 'set) - 26 (if where - 27 (with-current-buffer where - 28 (setq-local completion-preview-sort-function newval)) - 29 (setopt completion-preview-sort-function newval))))) - -Changelog - -• April 07, 2025 - - • Fixed several typos. - • Added clarity to several phrases. - • Added a note mentioning the new vertico-sort extension. - -• April 05, 2025 - - • Correction to explanation according to [[https://www.reddit.com/r/emacs/comments/1js6xvw/comment/mlkpge6/][r/JDRiverRun’s comment]]. - -• April 22, 2025 - - • Added a note mentioning the new corfu-history extension. - ------------------------------------------------------------------------- - -1 In my opinion, each package is much more modular than the helm or ivy - ecosystems while being more performant, integrated with existing Emacs - functionality, and just as useful, if not more. Though a bit dated, - I’ve previously written a big about how to configure this set of - packages: [[https://kristofferbalintona.me/posts/202202211546/][Vertico, Marginalia, All-the-icons-completion, and - Orderless]]. [[https://kristofferbalintona.me/posts/202504050923/#fnref:1][↩︎]] - ------------------------------------------------------------------------- - -The Text Completion and Minibuffer UI series - -This post is just one installation of the Text Completion and Minibuffer -UI series. Below are all the posts in this series: - -1 [[https://kristofferbalintona.me/posts/202202211546/][Vertico, Marginalia, All-the-icons-completion, and Orderless]] -2 [[https://kristofferbalintona.me/posts/202202270056/][Corfu, Kind-icon, and Corfu-doc]] -3 [[https://kristofferbalintona.me/posts/202203130102/][Cape]] -4 [[https://kristofferbalintona.me/posts/202504050923/][Complement corfu, vertico, and completion-preview with prescient.el - sorting]] this post! - -[[https://kristofferbalintona.me/][Home]] [[https://kristofferbalintona.me/index.xml][RSS]] [[https://kristofferbalintona.me/tags/][Tags]] [[https://kristofferbalintona.me/categories/][Categories]] [[https://kristofferbalintona.me/series/]]Series -** [[https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html][Difftastic diffing with Magit]] -URL: https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html -Captured On:[2025-09-07 Sun 12:38] - -[[https://github.com/Wilfred/difftastic][difftastic]] is a structural diff tool that compares files based on their syntax. So for example, if you conditionalize some statement, the diff would only show the addition of one if with its condition instead of showing one line added (the if (condition)) and the line with the statement being removed and re-added (because of indentation changes). In many cases, such structural diffs transport the meaning of a change much better than the typical line-based diffs. - -Of course, that comes with a price: difftastic has to understand the language's syntax (it currently supports [[https://difftastic.wilfred.me.uk/languages_supported.html][these languages]]) and computing a structural diff is a quite expensive operation. Also, there are certain kinds of changes where a line-based diff with changed-word highlighting gives better results, namely when the changed syntactic unit is a large blob with no sub-structure, e.g., a docstring or a comment. - -[[https://git-scm.com/][Git]] allows to use an external diff tool in many commands by setting the environment variable GIT_EXTERNAL_DIFF=<tool> and in the following I make use of that for being able to use difftastic for git show and git diff when operating on a git repository from inside Emacs using [[https://magit.vc/][Magit]]. Because the aforementioned downsides, I want that as opt-in behavior, i.e., separate commands. Also, Magit has some assumptions on how a git diff looks like which are not met by difftastic, e.g., difftastic prints line numbers and generates side-by-side diffs for changes which are not plain additions or deletions. - -So here we go. Let's first define a helper function which sets GIT_EXTERNAL_DIFF to difft (the difftastic binary), runs the given git command in -a new process asynchronously, and pops up a buffer with the result once the process finished. - - -(defun th/magit--with-difftastic (buffer command) - "Run COMMAND with GIT_EXTERNAL_DIFF=difft then show result in BUFFER." - (let ((process-environment - (cons (concat "GIT_EXTERNAL_DIFF=difft --width=" - (number-to-string (frame-width))) - process-environment))) - ;; Clear the result buffer (we might regenerate a diff, e.g., for - ;; the current changes in our working directory). - (with-current-buffer buffer - (setq buffer-read-only nil) - (erase-buffer)) - ;; Now spawn a process calling the git COMMAND. - (make-process - :name (buffer-name buffer) - :buffer buffer - :command command - ;; Don't query for running processes when emacs is quit. - :noquery t - ;; Show the result buffer once the process has finished. - :sentinel (lambda (proc event) - (when (eq (process-status proc) 'exit) - (with-current-buffer (process-buffer proc) - (goto-char (point-min)) - (ansi-color-apply-on-region (point-min) (point-max)) - (setq buffer-read-only t) - (view-mode) - (end-of-line) - ;; difftastic diffs are usually 2-column side-by-side, - ;; so ensure our window is wide enough. - (let ((width (current-column))) - (while (zerop (forward-line 1)) - (end-of-line) - (setq width (max (current-column) width))) - ;; Add column size of fringes - (setq width (+ width - (fringe-columns 'left) - (fringe-columns 'right))) - (goto-char (point-min)) - (pop-to-buffer - (current-buffer) - `(;; If the buffer is that wide that splitting the frame in - ;; two side-by-side windows would result in less than - ;; 80 columns left, ensure it's shown at the bottom. - ,(when (> 80 (- (frame-width) width)) - #'display-buffer-at-bottom) - (window-width - . ,(min width (frame-width)))))))))))) - -The crucial parts of that helper function are that we "wash" the result using ansi-color-apply-on-region so that the difftastic highlighting -using shell escape codes is transformed to emacs faces. Also, the needed width of the possibly wide side-by-side difftastic diff is computed and -tried to be accommodated for. - -Next, let's define our first command basically doing a git show for some revision which defaults to the commit or branch at point or queries -the user if there's none. - - -(defun th/magit-show-with-difftastic (rev) - "Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft." - (interactive - (list (or - ;; If REV is given, just use it. - (when (boundp 'rev) rev) - ;; If not invoked with prefix arg, try to guess the REV from - ;; point's position. - (and (not current-prefix-arg) - (or (magit-thing-at-point 'git-revision t) - (magit-branch-or-commit-at-point))) - ;; Otherwise, query the user. - (magit-read-branch-or-commit "Revision")))) - (if (not rev) - (error "No revision specified") - (th/magit--with-difftastic - (get-buffer-create (concat "*git show difftastic " rev "*")) - (list "git" "--no-pager" "show" "--ext-diff" rev)))) - -And here the second command which basically does a git diff. It tries to guess what one wants to diff, e.g., when point is on the Staged -changes section in a magit buffer, it will run git diff --cached to show a diff of all staged changes. If no context can be guessed, it'll query -the user for a range or commit for diffing. - - -(defun th/magit-diff-with-difftastic (arg) - "Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft." - (interactive - (list (or - ;; If RANGE is given, just use it. - (when (boundp 'range) range) - ;; If prefix arg is given, query the user. - (and current-prefix-arg - (magit-diff-read-range-or-commit "Range")) - ;; Otherwise, auto-guess based on position of point, e.g., based on - ;; if we are in the Staged or Unstaged section. - (pcase (magit-diff--dwim) - ('unmerged (error "unmerged is not yet implemented")) - ('unstaged nil) - ('staged "--cached") - (`(stash . ,value) (error "stash is not yet implemented")) - (`(commit . ,value) (format "%s^..%s" value value)) - ((and range (pred stringp)) range) - (_ (magit-diff-read-range-or-commit "Range/Commit")))))) - (let ((name (concat "*git diff difftastic" - (if arg (concat " " arg) "") - "*"))) - (th/magit--with-difftastic - (get-buffer-create name) - `("git" "--no-pager" "diff" "--ext-diff" ,@(when arg (list arg)))))) - -What's left is integrating these two new commands in Magit. For that purpose, I've created a new [[https://github.com/magit/transient][transient]] prefix for my personal commands. - - -(transient-define-prefix th/magit-aux-commands () - "My personal auxiliary magit commands." - ["Auxiliary commands" - ("d" "Difftastic Diff (dwim)" th/magit-diff-with-difftastic) - ("s" "Difftastic Show" th/magit-show-with-difftastic)]) - -I want my personal commands transient to be bound to # and be shown in the Magit dispatch transient (which is bound to ? in Magit status -buffers and C-x M-g in any Magit enabled buffer) after the Run (!) transient. - - -(transient-append-suffix 'magit-dispatch "!" - '("#" "My Magit Cmds" th/magit-aux-commands)) - -(define-key magit-status-mode-map (kbd "#") #'th/magit-aux-commands) - -And that's it! - -Finally, here's a screenshot showing how it looks like: - -Screenshot of Magit showing a difftastic diff -** Can I display org-mode attachments as inline images in my document? - Emacs Stack Exchange -[[https://emacs.stackexchange.com/questions/18404/can-i-display-org-mode-attachments-as-inline-images-in-my-document][Can I display org-mode attachments as inline images in my document? - Emacs Stack Exchange]] -Captured On: [2025-10-16 Thu 02:20] -** rougier/relative-date: Emacs package for formatting relative dates (dates difference) -[[https://github.com/rougier/relative-date][rougier/relative-date: Emacs package for formatting relative dates (dates difference)]] -Captured On: [2025-10-17 Fri 14:23] -** rougier/pdf-drop-mode: Get DOI from PDF files dropped onto a Emacs buffer -[[https://github.com/rougier/pdf-drop-mode][rougier/pdf-drop-mode: Get DOI from PDF files dropped onto a Emacs buffer]] -Captured On: [2025-10-17 Fri 14:23] -** rougier/emacs-svg-icon: An emacs library to create SVG icons on the fly -[[https://github.com/rougier/emacs-svg-icon][rougier/emacs-svg-icon: An emacs library to create SVG icons on the fly]] -Captured On: [2025-10-17 Fri 13:44] -** rougier/org-agenda-conflict: Mark conflicting items in the org-agenda -[[https://github.com/rougier/org-agenda-conflict][rougier/org-agenda-conflict: Mark conflicting items in the org-agenda]] -Captured On: [2025-10-17 Fri 13:38] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Holidays -(require 'cal-iso) -(require 'holidays) - -(defvar french-holidays nil - "French holidays") - -(setq french-holidays - `((holiday-fixed 1 1 "New year's Day") - (holiday-fixed 5 1 "Labour Day") - (holiday-fixed 5 8 "Victory in Europe Day") - (holiday-fixed 7 14 "Bastille day") - (holiday-fixed 8 15 "Assumption of Mary") - (holiday-fixed 11 11 "Armistice 1918") - (holiday-fixed 11 1 "All Saints' Day") - (holiday-fixed 12 25 "Christmas Day") - (holiday-easter-etc 0 "Easter Sunday") - (holiday-easter-etc 1 "Easter Monday") - (holiday-easter-etc 39 "Ascension Day") - (holiday-easter-etc 50 "Whit Monday") - (holiday-sexp - '(if (equal - (holiday-easter-etc 49) - (holiday-float 5 0 -1 nil)) - (car (car (holiday-float 6 0 1 nil))) - (car (car (holiday-float 5 0 -1 nil)))) - "Mother's Day"))) - -(setq calendar-holidays french-holidays ; French holidays - calendar-week-start-day 1 ; Week starts on Monday - calendar-mark-diary-entries-flag nil) ; Do not show diary entries - -; Mark today in calendar -(add-hook 'calendar-today-visible-hook #'calendar-mark-today) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:34] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Sorting strategy - -(setq org-agenda-sorting-strategy - '((agenda deadline-down scheduled-down todo-state-up time-up - habit-down priority-down category-keep) - (todo priority-down category-keep) - (tags timestamp-up priority-down category-keep) - (search category-keep))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:33] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Babel -(setq-default org-src-fontify-natively t ; Fontify code in code blocks. - org-adapt-indentation nil ; Adaptive indentation - org-src-tab-acts-natively t ; Tab acts as in source editing - org-confirm-babel-evaluate nil ; No confirmation before executing code - org-edit-src-content-indentation 0 ; No relative indentation for code blocks - org-fontify-whole-block-delimiter-line t) ; Fontify whole block -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:32] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Dashboard -Provide a mu4e-dashboard command that opens the mu4e dashboard on the left side. - -(require 'mu4e-dashboard) -(require 'svg-lib) - -(setq mu4e-dashboard-propagate-keymap nil) - -(defun mu4e-dashboard () - "Open the mu4e dashboard on the left side." - - (interactive) - (with-selected-window - (split-window (selected-window) -34 'left) - - (find-file (expand-file-name "mu4e-dashboard.org" user-emacs-directory)) - (mu4e-dashboard-mode) - (hl-line-mode) - (set-window-dedicated-p nil t) - (defvar svg-font-lock-keywords - `(("\\!\\([\\ 0-9]+\\)\\!" - (0 (list 'face nil 'display (svg-font-lock-tag (match-string 1))))))) - (defun svg-font-lock-tag (label) - (svg-lib-tag label nil - :stroke 0 :margin 1 :font-weight 'bold - :padding (max 0 (- 3 (length label))) - :foreground (face-foreground 'nano-popout-i) - :background (face-background 'nano-popout-i))) - (push 'display font-lock-extra-managed-props) - (font-lock-add-keywords nil svg-font-lock-keywords) - (font-lock-flush (point-min) (point-max)))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:32] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Colorization of recent emails -(defun my/mu4e-headers-colorize (item msg) - "Colorize item depending on whe msg was received" - - (let* ((recent (* 5 60)) ;; 5 minutes - (now (current-time)) - (unread (mu4e-message-field msg :unread)) - (date (mu4e-message-field msg :date)) - (delta (float-time (time-subtract now date)))) - (if (and (< delta recent) unread) - (propertize item 'face 'nano-salient) - item))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:31] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Bookmarks -(setq mu4e-bookmarks - '((:name "Unread" - :key ?u - :show-unread t - :query "flag:unread AND NOT flag:trashed") - - (:name "Inbox" - :key ?i - :show-unread t - :query "m:/inria/inbox or m:/univ/inbox or m:/gmail/inbox") - - (:name "Today" - :key ?t - :show-unread t - :query "date:today..now") - - (:name "Yesterday" - :key ?y - :show-unread t - :query "date:2d..today and not date:today..now") - - (:name "Last week" - :key ?w - :hide-unread t - :query "date:7d..now") - - (:name "Flagged" - :key ?f - :show-unread t - :query "flag:flagged") - - (:name "Sent" - :key ?s - :hide-unread t - :query "from:Nicolas.Rougier") - - (:name "Drafts" - :key ?d - :hide-unread t - :query "flag:draft"))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:30] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Write -See www.gnu.org/software/emacs/manual/html_node/message/Insertion-Variables.html - -(setq message-send-mail-function 'smtpmail-send-it - message-cite-reply-position 'below - message-citation-line-format "%N [%Y-%m-%d at %R] wrote:" - message-citation-line-function 'message-insert-formatted-citation-line - message-yank-prefix "> " - message-yank-cited-prefix "> " - message-yank-empty-prefix "> " - message-indentation-spaces 1 - message-kill-buffer-on-exit t - - mu4e-compose-format-flowed t - mu4e-compose-complete-only-personal t - mu4e-compose-complete-only-after "2021-01-01" ; Limite address auto-completion - mu4e-compose-dont-reply-to-self t - mu4e-compose-crypto-reply-policy 'sign-and-encrypt) - -Multi-language flyspell in compose mode - -(require 'flyspell) -(require 'guess-language) -(require 'flyspell-correct-popup) - -;; (bind-key "C-;" #'flyspell-popup-wrapper flyspell-mode-map) -(bind-key "C-;" #'flyspell-popup-correct flyspell-mode-map) - -;; Automatically detect language for Flyspell -(with-eval-after-load 'guess-language - (add-hook 'text-mode-hook #'guess-language-mode) - (setq guess-language-langcodes '((en . ("en_GB" "English")) - (fr . ("fr_FR" "French"))) - guess-language-languages '(en fr) - guess-language-min-paragraph-length 45)) - -(setq flyspell-generic-check-word-predicate 'mail-mode-flyspell-verify) -(defun my/mu4e-compose-hook () - "Settings for message composition." - - (auto-save-mode -1) - (turn-off-auto-fill) - (set-fill-column 79) - (setq flyspell-generic-check-word-predicate - 'mail-mode-flyspell-verify) - (flyspell-mode)) - -(add-hook 'mu4e-compose-mode-hook #'my/mu4e-compose-hook) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:30] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Encryption -See https://www.djcbsoftware.nl/code/mu/mu4e/Reading-messages.html - -(require 'epg-config) - -(setq epg-gpg-program "/usr/local/bin/gpg" ; What gpg program to use - epg-user-id "gpg_key_id" ; GnuPG ID of your default identity - mml2015-use 'epg ; The package used for PGP/MIME. - mml2015-encrypt-to-self t ; Add our own key ID to recipient list - mml2015-sign-with-sender t) ; Use message sender to find a key to sign with. - -;;(setq epa-file-cache-passphrase-for-symmetric-encryption nil) -;;(require 'epa-file) -;;(epa-file-enable) -;;(setq epa-file-select-keys nil) -(setq epa-pinentry-mode 'loopback) -(pinentry-start) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:29] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Vertico -Vertico provides a performant and minimalistic vertical completion UI based on the default completion system but aims to be highly flexible, extensible and modular. - -(require 'vertico) - -;; (setq completion-styles '(basic substring partial-completion flex)) - -(setq vertico-resize nil ; How to resize the Vertico minibuffer window. - vertico-count 8 ; Maximal number of candidates to show. - vertico-count-format nil) ; No prefix with number of entries - -(vertico-mode) -Tweaking settings - -(setq vertico-grid-separator - #(" | " 2 3 (display (space :width (1)) - face (:background "#ECEFF1"))) - - vertico-group-format - (concat #(" " 0 1 (face vertico-group-title)) - #(" " 0 1 (face vertico-group-separator)) - #(" %s " 0 4 (face vertico-group-title)) - #(" " 0 1 (face vertico-group-separator - display (space :align-to (- right (-1 . right-margin) (- +1))))))) - -(set-face-attribute 'vertico-group-separator nil - :strike-through t) -(set-face-attribute 'vertico-current nil - :inherit '(nano-strong nano-subtle)) -(set-face-attribute 'completions-first-difference nil - :inherit '(nano-default)) -Bind shift-tab for completion - -(bind-key "<backtab>" #'minibuffer-complete vertico-map) -Completion-at-point and completion-in-region (see https://github.com/minad/vertico#completion-at-point-and-completion-in-region) - -(setq completion-in-region-function - (lambda (&rest args) - (apply (if vertico-mode - #'consult-completion-in-region - #'completion--in-region) - args))) -Prefix the current candidate (See https://github.com/minad/vertico/wiki#prefix-current-candidate-with-arrow) - -(defun minibuffer-format-candidate (orig cand prefix suffix index _start) - (let ((prefix (if (= vertico--index index) - " " - " "))) - (funcall orig cand prefix suffix index _start))) - -(advice-add #'vertico--format-candidate - :around #'minibuffer-format-candidate) -See https://kristofferbalintona.me/posts/vertico-marginalia-all-the-icons-completion-and-orderless/#vertico - -(defun vertico--prompt-selection () - "Highlight the prompt" - - (let ((inhibit-modification-hooks t)) - (set-text-properties (minibuffer-prompt-end) (point-max) - '(face (nano-strong nano-salient))))) -See minad/vertico#145 - - -(defun minibuffer-vertico-setup () - - (setq truncate-lines t) - (setq completion-in-region-function - (if vertico-mode - #'consult-completion-in-region - #'completion--in-region))) - -(add-hook 'vertico-mode-hook #'minibuffer-vertico-setup) -(add-hook 'minibuffer-setup-hook #'minibuffer-vertico-setup) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:25] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Vertico -Vertico provides a performant and minimalistic vertical completion UI based on the default completion system but aims to be highly flexible, extensible and modular. - -(require 'vertico) - -;; (setq completion-styles '(basic substring partial-completion flex)) - -(setq vertico-resize nil ; How to resize the Vertico minibuffer window. - vertico-count 8 ; Maximal number of candidates to show. - vertico-count-format nil) ; No prefix with number of entries - -(vertico-mode) -Tweaking settings - -(setq vertico-grid-separator - #(" | " 2 3 (display (space :width (1)) - face (:background "#ECEFF1"))) - - vertico-group-format - (concat #(" " 0 1 (face vertico-group-title)) - #(" " 0 1 (face vertico-group-separator)) - #(" %s " 0 4 (face vertico-group-title)) - #(" " 0 1 (face vertico-group-separator - display (space :align-to (- right (-1 . right-margin) (- +1))))))) - -(set-face-attribute 'vertico-group-separator nil - :strike-through t) -(set-face-attribute 'vertico-current nil - :inherit '(nano-strong nano-subtle)) -(set-face-attribute 'completions-first-difference nil - :inherit '(nano-default)) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:25] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Consult -We replace some of emacs functions with their consult equivalent - -(require 'consult) - -(setq consult-preview-key nil) ; No live preview - -(bind-key "C-x C-r" #'consult-recent-file) -(bind-key "C-x h" #'consult-outline) -(bind-key "C-x b" #'consult-buffer) -(bind-key "C-c h" #'consult-history) -;; (bind-key "M-:" #'consult-complex-command) -For the consult-goto-line and consult-line commands, we define our owns with live preview (independently of the consult-preview-key) - -(defun my/consult-line () - "Consult line with live preview" - - (interactive) - (let ((consult-preview-key 'any) - (mini-frame-resize 'grow-only)) ;; !! Important - (consult-line))) - -(bind-key "C-s" #'my/consult-line) -(defun my/consult-goto-line () - "Consult goto line with live preview" - - (interactive) - (let ((consult-preview-key 'any)) - (consult-goto-line))) - -(bind-key "M-g g" #'my/consult-goto-line) -(bind-key "M-g M-g" #'my/consult-goto-line) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:25] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Corfu -Corfu enhances completion at point with a small completion popup. - -(require 'corfu) - -(setq corfu-cycle t ; Enable cycling for `corfu-next/previous' - corfu-auto t ; Enable auto completion - corfu-auto-delay 60.0 ; Delay before auto-completion shows up - corfu-separator ?\s ; Orderless field separator - corfu-quit-at-boundary nil ; Never quit at completion boundary - corfu-quit-no-match t ; Quit when no match - corfu-preview-current nil ; Disable current candidate preview - corfu-preselect-first nil ; Disable candidate preselection - corfu-on-exact-match nil ; Configure handling of exact matches - corfu-echo-documentation nil ; Disable documentation in the echo area - corfu-scroll-margin 5) ; Use scroll margin - -(global-corfu-mode) -A few more useful configurations… - -;; TAB cycle if there are only few candidates -(setq completion-cycle-threshold 3) - -;; Emacs 28: Hide commands in M-x which do not apply to the current mode. -;; Corfu commands are hidden, since they are not supposed to be used via M-x. -(setq read-extended-command-predicate - #'command-completion-default-include-p) - -;; Enable indentation+completion using the TAB key. -;; completion-at-point is often bound to M-TAB. -(setq tab-always-indent 'complete) - -;; Completion in source blocks -(require 'cape) - -(add-to-list 'completion-at-point-functions 'cape-elisp-symbol) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:24] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Parenthesis -Paren mode for highlighting matcing paranthesis - -(require 'paren) -;; (setq show-paren-style 'expression) -(setq show-paren-style 'parenthesis) -(setq show-paren-when-point-in-periphery t) -(setq show-paren-when-point-inside-paren nil) -(show-paren-mode) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:23] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Tabulations -No tabulation, ever. - -(setq-default indent-tabs-mode nil ; Stop using tabs to indent - tab-always-indent 'complete ; Indent first then try completions - tab-width 4) ; Smaller width for tab characters -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:23] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Clipboard -Allows system and Emacs clipboard to communicate smoothly (both ways) - -(setq-default select-enable-clipboard t) ; Merge system's and Emacs' clipboard -Make sure clipboard works properly in tty mode on OSX. - -(defun my/paste-from-osx () - (shell-command-to-string "pbpaste")) - -(defun my/copy-to-osx (text &optional push) - (let ((process-connection-type nil)) - (let ((proc (start-process "pbcopy" "*Messages*" "pbcopy"))) - (process-send-string proc text) - (process-send-eof proc)))) - -(when (and (not (display-graphic-p)) - (eq system-type 'darwin)) - (setq interprogram-cut-function #'my/copy-to-osx - interprogram-paste-function #'my/paste-from-osx)) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:21] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Scroll -Smoother scrolling. - -(setq-default scroll-conservatively 101 ; Avoid recentering when scrolling far - scroll-margin 2 ; Add a margin when scrolling vertically - recenter-positions '(5 bottom)) ; Set re-centering positions -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:21] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Mouse -Mouse behavior can be finely controlled using the mouse-avoidance-mode. - -(setq-default mouse-yank-at-point t) ; Yank at point rather than pointer -(mouse-avoidance-mode 'exile) ; Avoid collision of mouse with point -Mouse active in tty mode. - -(unless (display-graphic-p) - (xterm-mouse-mode 1) - (global-set-key (kbd "<mouse-4>") #'scroll-down-line) - (global-set-key (kbd "<mouse-5>") #'scroll-up-line)) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:21] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -A smarter fill/unfill command - -(defun my/fill-unfill () - "Like `fill-paragraph', but unfill if used twice." - - (interactive) - (let ((fill-column - (if (eq last-command #'my/fill-unfill) - (progn (setq this-command nil) - (point-max)) - fill-column))) - (call-interactively #'fill-paragraph))) - -(bind-key "M-q" #'my/fill-unfill) -;; (bind-key [remap fill-paragraph] #'my/fill-unfill) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:20] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -(setq-default use-short-answers t ; Replace yes/no prompts with y/n - confirm-nonexistent-file-or-buffer nil) ; Ok to visit non existent files -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:20] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Date related -A set of date related functions, mostly used for mail display. - -(defun my/date-day (date) - "Return DATE day of month (1-31)." - - (nth 3 (decode-time date))) -(defun my/date-month (date) - "Return DATE month number (1-12)." - - (nth 4 (decode-time date))) -(defun my/date-year (date) - "Return DATE year." - - (nth 5 (decode-time date))) -(defun my/date-equal (date1 date2) - "Check if DATE1 is equal to DATE2." - - (and (eq (my/date-day date1) - (my/date-day date2)) - (eq (my/date-month date1) - (my/date-month date2)) - (eq (my/date-year date1) - (my/date-year date2)))) -(defun my/date-inc (date &optional days months years) - "Return DATE + DAYS day & MONTH months & YEARS years" - - (let ((days (or days 0)) - (months (or months 0)) - (years (or years 0)) - (day (my/date-day date)) - (month (my/date-month date)) - (year (my/date-year date))) - (encode-time 0 0 0 (+ day days) (+ month months) (+ year years)))) -(defun my/date-dec (date &optional days months years) - "Return DATE - DAYS day & MONTH months & YEARS years" - - (let ((days (or days 0)) - (months (or months 0)) - (years (or years 0))) - (my/date-inc date (- days) (- months) (- years)))) -(defun my/date-today () - "Return today date." - - (current-time)) -(defun my/date-is-today (date) - "Check if DATE is today." - - (my/date-equal (current-time) date)) -(defun my/date-is-yesterday (date) - "Check if DATE is yesterday." - - (my/date-equal (my/date-dec (my/date-today) 1) date)) -(defun my/date-relative (date) - "Return a string with a relative date format." - - (let* ((now (current-time)) - (delta (float-time (time-subtract now date))) - (days (ceiling (/ (float-time (time-subtract now date)) (* 60 60 24))))) - (cond ((< delta (* 3 60)) "now") - ((< delta (* 60 60)) (format "%d minutes ago" (/ delta 60))) - ;; ((< delta (* 6 60 60)) (format "%d hours ago" (/ delta 3600))) - ((my/date-is-today date) (format-time-string "%H:%M" date)) - ((my/date-is-yesterday date) (format "Yesterday")) - ((< delta (* 4 24 60 60)) (format "%d days ago" (+ days 1))) - (t (format-time-string "%d %b %Y" date))))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:12] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -History -Remove text properties for kill ring entries (see https://emacs.stackexchange.com/questions/4187). This saves a lot of time when loading it. - -(defun unpropertize-kill-ring () - (setq kill-ring (mapcar 'substring-no-properties kill-ring))) - -(add-hook 'kill-emacs-hook 'unpropertize-kill-ring) -We save every possible history we can think of. - -(require 'savehist) - -(setq kill-ring-max 50 - history-length 50) - -(setq savehist-additional-variables - '(kill-ring - command-history - set-variable-value-history - custom-variable-history - query-replace-history - read-expression-history - minibuffer-history - read-char-history - face-name-history - bookmark-history - file-name-history)) - - (put 'minibuffer-history 'history-length 50) - (put 'file-name-history 'history-length 50) - (put 'set-variable-value-history 'history-length 25) - (put 'custom-variable-history 'history-length 25) - (put 'query-replace-history 'history-length 25) - (put 'read-expression-history 'history-length 25) - (put 'read-char-history 'history-length 25) - (put 'face-name-history 'history-length 25) - (put 'bookmark-history 'history-length 25) -No duplicates in history - -(setq history-delete-duplicates t) -Start history mode. - -(let (message-log-max) - (savehist-mode)) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:09] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -History -Remove text properties for kill ring entries (see https://emacs.stackexchange.com/questions/4187). This saves a lot of time when loading it. - -(defun unpropertize-kill-ring () - (setq kill-ring (mapcar 'substring-no-properties kill-ring))) - -(add-hook 'kill-emacs-hook 'unpropertize-kill-ring) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:09] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Recent files -50 Recents files with some exclusion (regex patterns). - -(require 'recentf) - -(setq recentf-max-menu-items 10 - recentf-max-saved-items 100 - recentf-exclude '("/Users/rougier/Documents/Mail.+" - "/Users/rougier/Documents/Notes.+" - )) - -(let (message-log-max) - (recentf-mode 1)) -History -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:09] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Recovery -If Emacs or the computer crashes, you can recover the files you were editing at the time of the crash from their auto-save files. To do this, start Emacs again and type the command M-x recover-session. Here, we parameterize how files are saved in the background. - -(setq auto-save-list-file-prefix ; Prefix for generating auto-save-list-file-name - (expand-file-name ".auto-save-list/.saves-" user-emacs-directory) - auto-save-default t ; Auto-save every buffer that visits a file - auto-save-timeout 20 ; Number of seconds between auto-save - auto-save-interval 200) ; Number of keystrokes between auto-saves -Backups -Emacs carefully copies the old contents to another file, called the “backup” file, before actually saving. Emacs makes a backup for a file only the first time the file is saved from a buffer. No matter how many times you subsequently save the file, its backup remains unchanged. However, if you kill the buffer and then visit the file again, a new backup file will be made. Here, we activate backup and parameterize the number of backups to keep. - -(setq backup-directory-alist ; File name patterns and backup directory names. - `(("." . ,(expand-file-name "backups" user-emacs-directory))) - make-backup-files t ; Backup of a file the first time it is saved. - vc-make-backup-files t ; No backup of files under version contr - backup-by-copying t ; Don't clobber symlinks - version-control t ; Version numbers for backup files - delete-old-versions t ; Delete excess backup files silently - kept-old-versions 6 ; Number of old versions to keep - kept-new-versions 9 ; Number of new versions to keep - delete-by-moving-to-trash t) ; Delete files to trash - -;; Back -(require 'vc-backup) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:08] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Path -Extending executable for recollq program - -(setenv "PATH" (concat (getenv "PATH") ":" "/Users/rougier/Applications/recoll.app/Contents/MacOS")) -(setq exec-path (append exec-path '("/Users/rougier/Applications/recoll.app/Contents/MacOS"))) - -(setenv "PATH" (concat (getenv "PATH") ":" "/Users/rougier/bin")) -(setq exec-path (append exec-path '("/Users/rougier/bin"))) -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:08] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE -Encoding -We tell emacs to use UTF-8 encoding as much as possible. - -(set-default-coding-systems 'utf-8) ; Default to utf-8 encoding -(prefer-coding-system 'utf-8) ; Add utf-8 at the front for automatic detection. -(set-terminal-coding-system 'utf-8) ; Set coding system of terminal output -(set-keyboard-coding-system 'utf-8) ; Set coding system for keyboard input on TERMINAL -(set-language-environment "English") ; Set up multilingual environment -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 13:07] -** dotemacs/dotemacs.org at master · rougier/dotemacs -#+BEGIN_QUOTE - inhibit-startup-echo-area-message t ; Disable initial echo message -#+END_QUOTE -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 12:58] -** rougier/reviewer-manifesto: A pledge for reviewers -[[https://github.com/rougier/reviewer-manifesto][rougier/reviewer-manifesto: A pledge for reviewers]] -Captured On: [2025-10-17 Fri 12:54] -** rougier/nano-calendar: An alternative calendar for Emacs -[[https://github.com/rougier/nano-calendar][rougier/nano-calendar: An alternative calendar for Emacs]] -Captured On: [2025-10-17 Fri 12:53] -** dotemacs/dotemacs.org at master · rougier/dotemacs -[[https://github.com/rougier/dotemacs/blob/master/dotemacs.org][dotemacs/dotemacs.org at master · rougier/dotemacs]] -Captured On: [2025-10-17 Fri 12:53] -** rougier/nano-tools: A set of tools for emacs that are not worth to be packaged. -[[https://github.com/rougier/nano-tools][rougier/nano-tools: A set of tools for emacs that are not worth to be packaged.]] -Captured On: [2025-10-17 Fri 12:52] -** rougier/nano-mu4e: Alternative layour for mu4e -[[https://github.com/rougier/nano-mu4e][rougier/nano-mu4e: Alternative layour for mu4e]] -Captured On: [2025-10-17 Fri 12:51] -** rougier/nano-theme: GNU Emacs / N Λ N O Theme -[[https://github.com/rougier/nano-theme][rougier/nano-theme: GNU Emacs / N Λ N O Theme]] -Captured On: [2025-10-17 Fri 12:51] -** rougier/nano-emacs: GNU Emacs / N Λ N O - Emacs made simple -#+BEGIN_QUOTE -GNU Emacs / N Λ N O is a set of configuration files for GNU Emacs such as to provide a nice and consistent look and feel as shown below. It is based on design principles I described in the article "On the design of text Editors" that is available on arXiv. The light theme is based on Material colors and the dark theme is based on Nord colors. - -The philosophy of nano emacs is to stick as much as possible to vanilla emacs without introducing too much dependencies (or none if possible) and to keep it modular enough. The idea is for users to copy the part they are interested in such as to include them in their own configuration. -#+END_QUOTE -[[https://github.com/rougier/nano-emacs][rougier/nano-emacs: GNU Emacs / N Λ N O - Emacs made simple]] -Captured On: [2025-10-17 Fri 11:59] -** rougier/mu4e-dashboard: A dashboard for mu4e (mu for emacs) -#+BEGIN_QUOTE -mu4e dashboard provides a new mu4e org link type that allows to execute various mu4e queries when clicked. Such links can be organised into a dashboard, by simply writing an org file as shown below: -#+END_QUOTE -[[https://github.com/rougier/mu4e-dashboard][rougier/mu4e-dashboard: A dashboard for mu4e (mu for emacs)]] -Captured On: [2025-10-17 Fri 11:56] -** rougier/mu4e-thread-folding: Functions for folding threads in mu4e headers view -#+BEGIN_QUOTE -mu4e-thread-folding.el is a small library to enable threads folding in mu4e. This works by using overlays with an invisible property and setting hooks at the right place. It is possible to configure colors to better highlight a thread and also to have a prefix string indicating if a thread is folded or not. Note that when a thread is folded, any unread child remains visible. -#+END_QUOTE -[[https://github.com/rougier/mu4e-thread-folding][rougier/mu4e-thread-folding: Functions for folding threads in mu4e headers view]] -Captured On: [2025-10-17 Fri 11:55] -** rougier/mu4e-dashboard: A dashboard for mu4e (mu for emacs) -[[https://github.com/rougier/mu4e-dashboard][rougier/mu4e-dashboard: A dashboard for mu4e (mu for emacs)]] -Captured On: [2025-10-17 Fri 11:48] -** beacoder/stock-tracker: Track stock price in Emacs -[[https://github.com/beacoder/stock-tracker][beacoder/stock-tracker: Track stock price in Emacs]] -Captured On: [2025-10-24 Fri 23:36] +** TODO [#D] BrainCurses Emacs Mastermind Game +[[https://sourceforge.net/projects/braincurses/][BrainCurses download | SourceForge.net]] +Captured On: [2025-09-26 Fri 00:38] +** TODO [#D] Emacs: org project should be yellow or green like todo +** TODO [#D] Emacs Signal Client +*** TODO investigate: net/signel.org · master · undefined · GitLab +[[https://gitlab.com/jaor/elibs/-/blob/master/net/signel.org][net/signel.org · master · undefined · GitLab]] +Captured On: [2025-05-29 Thu 04:24] +*** TODO investigate: signel, a barebones signal chat on top of signal-cli +[[https://jao.io/blog/signel.html][signel, a barebones signal chat on top of signal-cli]] +Captured On: [2025-05-29 Thu 04:23] +** TODO [#D] Add test document to test graphviz-dot-mode +** TODO [#D] Consider Replicating Some of This Auctex and PDF Tools Code +[[https://www.reddit.com/r/emacs/comments/cd6fe2/how_to_make_emacs_a_latex_ide/][How to make Emacs a Latex IDE? : r/emacs]] +Captured On: [2025-08-14 Thu 03:43] +** TODO [#D] Install ZOxide Integration into Emacs +[[https://gitlab.com/Vonfry/zoxide.el][Vonfry / zoxide.el · GitLab]] +Captured On: [2025-06-07 Sat 17:11] +** TODO [#D] manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer. +[[https://github.com/manateelazycat/awesome-tray][manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer.]] +Captured On: [2025-06-07 Sat 13:29] +** TODO [#D] ryuslash/mode-icons: Show icons instead of mode names |
