summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-03 18:01:24 -0600
committerCraig Jennings <c@cjennings.net>2025-11-03 18:01:24 -0600
commitc0c4b176ace7910cbc1a71b5ec473873b6d821be (patch)
treebbd78f1449630a9a6a5db8ad025205df5dc824a9
parent9e75fea0cf16c72dfd490b0dcb726e6c7bd023bb (diff)
feat: Complete modeline overhaul with custom segments and interactive features
Replaced mood-line with a custom, minimal modeline using only built-in Emacs functionality to avoid native compilation issues. **Architecture:** - Named segment system using defvar-local for easy reordering - Emacs 30 built-in right-alignment (mode-line-format-right-align) - All segments marked as risky-local-variable for proper evaluation **Features:** - Color-coded buffer names (green=writeable, red=read-only, gold=overwrite) - VC branch with git symbol (U+E0A0) and state-based coloring - Position format: L:line C:col - Help-echo tooltips on all segments - Mouse click handlers for interactive actions - String truncation in narrow windows (< 100 chars) - Active-window-only display for branch and misc-info **Interactive Actions:** - Buffer name: mouse-1 = prev-buffer, mouse-3 = next-buffer - Major mode: mouse-1 = describe-mode - Git branch: mouse-1 = vc-diff, mouse-3 = vc-root-diff **Bug Fixes:** - Disabled async native compilation to prevent "Selecting deleted buffer" errors - Fixed difftastic loading by changing :demand to :defer - Abstracted buffer status colors to user-constants.el for reuse Inspired by Prot's modeline design patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--NOTES.org59
-rw-r--r--early-init.el16
-rw-r--r--modules/modeline-config.el170
-rw-r--r--modules/ui-config.el9
-rw-r--r--modules/user-constants.el9
-rw-r--r--modules/vc-config.el5
6 files changed, 239 insertions, 29 deletions
diff --git a/NOTES.org b/NOTES.org
index d048710f..0e7b92a2 100644
--- a/NOTES.org
+++ b/NOTES.org
@@ -123,3 +123,62 @@ Note: This file evolves as we learn best practices.
- Defined four-question Q&A framework
- Established seven core principles for session creation
- Documented in =docs/sessions/create-session.org=
+
+** 2025-11-03: Method 2 Quick Wins Session
+- Completed 6 tasks across Methods 1, 2, and 3
+- Focus: Low-risk improvements aligned with V2MOM Methods
+
+*** Key Technical Learnings
+
+**** Difftastic vs Ediff - Complementary Tools
+- **Difftastic**: Read-only structural diffs for git (language-aware)
+- **Ediff**: Interactive side-by-side editing for buffer comparison
+- **Discovery**: User had ediff at C-c D but C-; b D used basic diff
+- **Solution**: Hybrid approach - ediff for buffers, difftastic for git
+- **Lesson**: Don't assume one replaces the other; they serve different purposes
+
+**** Org-mode Visual Emphasis
+- =org-fontify-emphasized-text= controls bold/italic rendering
+- =org-hide-emphasis-markers= hides markup characters (* / _)
+- =org-appear= shows markers only when cursor is on emphasized text
+- **Pattern**: Hide markers globally, reveal contextually with org-appear
+
+*** User Workflow Preferences
+
+**** Task Selection Strategy
+- User prefers quick wins when energy is lower
+- Appreciates effort estimates BEFORE committing to work
+- Will defer mission-critical changes (e.g., Company → Corfu)
+- Quote: "now I'm hungry for more" after successful quick wins
+
+**** Communication Patterns
+- User sometimes needs clarification iterations ("whoops. I wasn't clear again")
+- Always confirm understanding before major changes
+- User values knowing keybindings for new features
+- Appreciates when tasks are already complete (just need verification)
+
+**** V2MOM Task Management
+- Tasks organized by 6 Methods with progress tracking (e.g., 7/13, 3/5)
+- User maintains detailed completion notes in todo.org
+- Each task includes context, rationale, and completion details
+- Weekly triage scheduled to keep active todos < 20
+
+*** Development Patterns
+
+**** Git Workflow
+- Always push to BOTH remotes: origin (cjennings.net) AND github
+- Commit messages include Claude Code attribution
+- User reviews git status and diffs before committing
+
+**** Configuration Management
+- User runs Emacs 30.2 with use-package
+- Prefers editing existing files over creating new ones
+- Values code reduction (mood-line: -40 lines, ediff upgrade: -12 lines)
+
+*** Tasks Completed
+1. Switched doom-modeline → mood-line (Method 2)
+2. Removed deprecated tree-sitter package (Method 2)
+3. Fixed cj/goto-git-gutter-diff-hunks void-function (Method 1)
+4. Cleaned up go-ts-mode-map declarations (Method 1)
+5. Upgraded buffer diff to ediff + added difftastic for git (Method 3)
+6. Added org-appear and enabled emphasis rendering (Method 2)
diff --git a/early-init.el b/early-init.el
index 79ff7816..6fa3e0b2 100644
--- a/early-init.el
+++ b/early-init.el
@@ -51,15 +51,19 @@
(setq debug-on-error t) ;; default nil. turn on to debug issues only.
(setq debug-on-quit t) ;; debug on C-g (breaking out of hangs/freezes)
-(add-hook 'emacs-startup-hook
- (lambda ()
- (setq debug-on-error nil)
- (setq debug-on-quit nil)))
+;; (add-hook 'emacs-startup-hook
+;; (lambda ()
+;; (setq debug-on-error nil)
+;; (setq debug-on-quit nil)))
;; ------------------------------ Bug Workarounds ------------------------------
-;; Prevent org-element from being natively compiled again by adding the line
-(setq native-comp-jit-compilation-deny-list '(".*org-element.*"))
+;; Disable async native compilation to prevent "Selecting deleted buffer" errors
+;; This is a known issue in Emacs 30.x where async compilation buffers get
+;; deleted before the compilation process completes. Synchronous compilation
+;; is slower initially but avoids these race conditions.
+(setq native-comp-deferred-compilation nil) ;; Disable async/deferred compilation
+(setq native-comp-async-report-warnings-errors nil) ;; Silence async warnings
;; --------------------------- Warning Notifications ---------------------------
diff --git a/modules/modeline-config.el b/modules/modeline-config.el
index af0c3524..1c98e965 100644
--- a/modules/modeline-config.el
+++ b/modules/modeline-config.el
@@ -3,29 +3,169 @@
;;; Commentary:
-;; Minimal modeline configuration using mood-line.
-
-;; mood-line is a lightweight, minimal modeline inspired by doom-modeline
-;; but with much better performance and simpler configuration.
+;; Simple, minimal modeline using only built-in Emacs functionality.
+;; No external packages = no buffer issues, no native-comp errors.
;; Features:
-;; - Buffer status and modification indicators
-;; - Major mode display
+;; - Buffer status (modified, read-only)
+;; - Buffer name
+;; - Major mode
;; - Version control status
-;; - Flycheck/Flymake status
-;; - Cursor position and buffer percentage
-;; - Anzu and multiple-cursors counters
-;; - No dependencies
-;; - Minimal performance overhead
+;; - Line and column position
+;; - Buffer percentage
;;; Code:
-;; -------------------------------- mood-line ----------------------------------
+;; Use buffer status colors from user-constants
+(require 'user-constants)
+
+;; -------------------------- Modeline Configuration --------------------------
+
+;; Use Emacs 30's built-in right-alignment
+(setq mode-line-right-align-edge 'right-margin)
+
+;; String truncation length for narrow windows
+(defcustom cj/modeline-string-truncate-length 12
+ "String length after which truncation happens in narrow windows."
+ :type 'natnum
+ :group 'modeline)
+
+;; -------------------------- Helper Functions ---------------------------------
+
+(defun cj/modeline-window-narrow-p ()
+ "Return non-nil if window is narrow (less than 100 chars wide)."
+ (< (window-total-width) 100))
+
+(defun cj/modeline-string-truncate-p (str)
+ "Return non-nil if STR should be truncated."
+ (and (stringp str)
+ (not (string-empty-p str))
+ (cj/modeline-window-narrow-p)
+ (> (length str) cj/modeline-string-truncate-length)
+ (not (one-window-p :no-minibuffer))))
+
+(defun cj/modeline-string-cut-middle (str)
+ "Truncate STR in the middle if appropriate, else return STR.
+Example: 'my-very-long-name.el' → 'my-ver...me.el'"
+ (if (cj/modeline-string-truncate-p str)
+ (let ((half (floor cj/modeline-string-truncate-length 2)))
+ (concat (substring str 0 half) "..." (substring str (- half))))
+ str))
+
+;; -------------------------- Modeline Segments --------------------------------
+
+(defvar-local cj/modeline-buffer-name
+ '(:eval (let* ((state (cond
+ (buffer-read-only 'read-only)
+ (overwrite-mode 'overwrite)
+ (t 'normal)))
+ (color (alist-get state cj/buffer-status-colors))
+ (name (buffer-name))
+ (truncated-name (cj/modeline-string-cut-middle name)))
+ (propertize truncated-name
+ 'face `(:foreground ,color)
+ 'mouse-face 'mode-line-highlight
+ 'help-echo (concat
+ name "\n"
+ (or (buffer-file-name)
+ (format "No file. Directory: %s" default-directory)))
+ 'local-map (let ((map (make-sparse-keymap)))
+ (define-key map [mode-line mouse-1] 'previous-buffer)
+ (define-key map [mode-line mouse-3] 'next-buffer)
+ map))))
+ "Buffer name colored by read-only/read-write status.
+Green = writeable, Red = read-only, Gold = overwrite.
+Truncates in narrow windows. Click: mouse-1 = prev buffer, mouse-3 = next buffer.")
+
+(defvar-local cj/modeline-position
+ '(:eval (format "L:%d C:%d" (line-number-at-pos) (current-column)))
+ "Line and column position as L:line C:col.")
+
+(defvar cj/modeline-vc-faces
+ '((added . vc-locally-added-state)
+ (edited . vc-edited-state)
+ (removed . vc-removed-state)
+ (missing . vc-missing-state)
+ (conflict . vc-conflict-state)
+ (locked . vc-locked-state)
+ (up-to-date . vc-up-to-date-state))
+ "VC state to face mapping.")
+
+(defvar-local cj/modeline-vc-branch
+ '(:eval (when (mode-line-window-selected-p) ; Only show in active window
+ (when-let* ((file (or buffer-file-name default-directory))
+ (backend (vc-backend file)))
+ (when-let* ((branch (vc-working-revision file backend)))
+ ;; For Git, try to get symbolic branch name
+ (when (eq backend 'Git)
+ (require 'vc-git)
+ (when-let* ((symbolic (vc-git--symbolic-ref file)))
+ (setq branch symbolic)))
+ ;; Get VC state for face
+ (let* ((state (vc-state file backend))
+ (face (alist-get state cj/modeline-vc-faces 'vc-up-to-date-state))
+ (truncated-branch (cj/modeline-string-cut-middle branch)))
+ (concat
+ (propertize (char-to-string #xE0A0) 'face 'shadow) ; Git branch symbol
+ " "
+ (propertize truncated-branch
+ 'face face
+ 'mouse-face 'mode-line-highlight
+ 'help-echo (format "Branch: %s\nState: %s\nmouse-1: vc-diff\nmouse-3: vc-root-diff" branch state)
+ 'local-map (let ((map (make-sparse-keymap)))
+ (define-key map [mode-line mouse-1] 'vc-diff)
+ (define-key map [mode-line mouse-3] 'vc-root-diff)
+ map))))))))
+ "Git branch with symbol and colored by VC state.
+Shows only in active window. Truncates in narrow windows.
+Click: mouse-1 = vc-diff, mouse-3 = vc-root-diff.")
+
+(defvar-local cj/modeline-major-mode
+ '(:eval (let ((mode-str (format-mode-line mode-name)) ; Convert to string
+ (mode-sym major-mode))
+ (propertize mode-str
+ 'mouse-face 'mode-line-highlight
+ 'help-echo (if-let* ((parent (get mode-sym 'derived-mode-parent)))
+ (format "Major mode: %s\nDerived from: %s\nmouse-1: describe-mode" mode-sym parent)
+ (format "Major mode: %s\nmouse-1: describe-mode" mode-sym))
+ 'local-map (let ((map (make-sparse-keymap)))
+ (define-key map [mode-line mouse-1] 'describe-mode)
+ map))))
+ "Major mode name only (no minor modes).
+Click: mouse-1 = describe-mode.")
+
+(defvar-local cj/modeline-misc-info
+ '(:eval (when (mode-line-window-selected-p)
+ mode-line-misc-info))
+ "Misc info (chime notifications, etc).
+Shows only in active window.")
+
+;; -------------------------- Modeline Assembly --------------------------------
-(use-package mood-line
- :config
- (mood-line-mode))
+(setq-default mode-line-format
+ '("%e" ; Error message if out of memory
+ ;; LEFT SIDE
+ " "
+ cj/modeline-major-mode
+ " "
+ cj/modeline-buffer-name
+ " "
+ cj/modeline-position
+ ;; RIGHT SIDE (using Emacs 30 built-in right-align)
+ ;; Order: leftmost to rightmost as they appear in the list
+ mode-line-format-right-align
+ cj/modeline-vc-branch
+ " "
+ cj/modeline-misc-info))
+;; Mark all segments as risky-local-variable (required for :eval forms)
+(dolist (construct '(cj/modeline-buffer-name
+ cj/modeline-position
+ cj/modeline-vc-branch
+ cj/modeline-vc-faces
+ cj/modeline-major-mode
+ cj/modeline-misc-info))
+ (put construct 'risky-local-variable t))
(provide 'modeline-config)
;;; modeline-config.el ends here
diff --git a/modules/ui-config.el b/modules/ui-config.el
index 91dbaf31..837d2169 100644
--- a/modules/ui-config.el
+++ b/modules/ui-config.el
@@ -36,11 +36,8 @@
"Opacity level for Emacs frames when `cj/enable-transparency' is non-nil.
100 = fully opaque, 0 = fully transparent.")
-(defconst cj/cursor-colors
- '((read-only . "#f06a3f") ; red – buffer is read-only
- (overwrite . "#c48702") ; gold – overwrite mode
- (normal . "#64aa0f")) ; green – insert & read/write
- "Alist mapping cursor states to their colors.")
+;; Use buffer status colors from user-constants
+(require 'user-constants)
;; ----------------------------- System UI Settings ----------------------------
@@ -104,7 +101,7 @@ When `cj/enable-transparency' is nil, reset alpha to fully opaque."
(buffer-read-only 'read-only)
(overwrite-mode 'overwrite)
(t 'normal)))
- (color (alist-get state cj/cursor-colors)))
+ (color (alist-get state cj/buffer-status-colors)))
(unless (and (string= color cj/-cursor-last-color)
(string= (buffer-name) cj/-cursor-last-buffer))
(set-cursor-color color)
diff --git a/modules/user-constants.el b/modules/user-constants.el
index bcb34bcc..ba52cec2 100644
--- a/modules/user-constants.el
+++ b/modules/user-constants.el
@@ -38,6 +38,15 @@ Example: (setq cj/debug-modules '(org-agenda mail))
(defvar user-mail-address "c@cjennings.net"
"The user's email address.")
+;; ---------------------------- Buffer Status Colors ---------------------------
+
+(defconst cj/buffer-status-colors
+ '((read-only . "#f06a3f") ; red – buffer is read-only
+ (overwrite . "#c48702") ; gold – overwrite mode
+ (normal . "#64aa0f")) ; green – insert & read/write
+ "Alist mapping buffer states to their colors.
+Used by cursor color, modeline, and other UI elements.")
+
;; ------------------------ Directory And File Constants -----------------------
;; DIRECTORIES
diff --git a/modules/vc-config.el b/modules/vc-config.el
index b9b61c29..141f6e17 100644
--- a/modules/vc-config.el
+++ b/modules/vc-config.el
@@ -125,10 +125,11 @@ interactive selection to jump to any changed line in the buffer."
;; -------------------------------- Difftastic ---------------------------------
;; Structural diffs for better git change visualization
+;; Requires: difft binary (installed via pacman -S difftastic)
(use-package difftastic
- :demand t
- :after magit
+ :defer t
+ :commands (difftastic-magit-diff difftastic-magit-show)
:bind (:map magit-blame-read-only-mode-map
("D" . difftastic-magit-show)
("S" . difftastic-magit-show))