diff --git a/users/profiles/emacs/config.el b/users/profiles/emacs/config.el index 17d8aa6af..30503ba8c 100644 --- a/users/profiles/emacs/config.el +++ b/users/profiles/emacs/config.el @@ -18,6 +18,9 @@ (setopt initial-major-mode 'fundamental-mode) ; default mode for the *scratch* buffer (setopt display-time-default-load-average nil) ; this information is useless for most +;; Allows us to call git commit / jj commit / split etc from a vterm inside emacs +(setq server-window 'switch-to-buffer) + ;; Automatically reread from disk if the underlying file changes (setopt auto-revert-avoid-polling t) ;; Some systems don't do file notifications well; see @@ -64,6 +67,10 @@ (setq custom-file "~/.emacs.d/custom.el") (load custom-file t t) +(setq find-program "@find-program@") +(setq shell-file-name "@shell-file-name@") +(setq explicit-shell-file-name shell-file-name) + ;; Enable winner mode after init (add-hook 'after-init-hook #'winner-mode) @@ -123,6 +130,17 @@ (setq display-line-numbers-type 'relative) (global-display-line-numbers-mode) +;; Disable them in certain major modes +(dolist (mode '(org-mode + eshell-mode + term-mode + shell-mode + vterm-mode + help-mode + compilation-mode)) + (add-hook (intern (concat (symbol-name mode) "-hook")) + (lambda () (display-line-numbers-mode 0)))) + ;; We won't set these, but they're good to know about ;; ;; (setopt indent-tabs-mode nil) @@ -162,9 +180,10 @@ (setopt display-time-interval 1) (display-time-mode) -(use-package emacs - :config - (load-theme 'modus-vivendi)) ; for light theme, use modus-operandi +;; Theme loading is handled by extras/catppuccin.el +;; (use-package emacs +;; :config +;; (load-theme 'modus-vivendi)) ; for light theme, use modus-operandi @extras@ diff --git a/users/profiles/emacs/default.nix b/users/profiles/emacs/default.nix index 9f6599760..ccb467dab 100644 --- a/users/profiles/emacs/default.nix +++ b/users/profiles/emacs/default.nix @@ -5,6 +5,8 @@ ... }: let emacsInit = pkgs.replaceVars ./config.el { + find-program = "${pkgs.findutils}/bin/find"; + shell-file-name = "${pkgs.bashInteractive}/bin/bash"; extras = lib.concatStringsSep "\n" (map (n: builtins.readFile ./extras/${n}) ( lib.mapAttrsToList (name: _: name) (lib.filterAttrs (_: type: type == "regular") (builtins.readDir ./extras)) )); @@ -19,37 +21,36 @@ in { ;; MASSIVE startup optimizations (setq gc-cons-threshold most-positive-fixnum gc-cons-percentage 0.6) - + ;; Prevent package.el loading packages prior to init.el (setq package-enable-at-startup nil) - + ;; Inhibit resizing frame (setq frame-inhibit-implied-resize t) - + ;; Disable file handler checking during startup (defvar default-file-name-handler-alist file-name-handler-alist) (setq file-name-handler-alist nil) - + ;; Prevent unwanted runtime compilation for native-comp (setq native-comp-deferred-compilation nil native-comp-async-report-warnings-errors 'silent) - + ;; Silence warnings (setq byte-compile-warnings '(not obsolete)) (setq warning-suppress-log-types '((comp) (bytecomp))) - + ;; UI optimizations (push '(menu-bar-lines . 0) default-frame-alist) (push '(tool-bar-lines . 0) default-frame-alist) (push '(vertical-scroll-bars) default-frame-alist) (setq frame-resize-pixelwise t) - + ;; Silence startup message (setq inhibit-startup-echo-area-message (user-login-name)) - + ;; Default frame configuration (setq default-frame-alist '((fullscreen . maximized) - (background-color . "#000000") (ns-appearance . dark) (ns-transparent-titlebar . t))) diff --git a/users/profiles/emacs/extras/base.el b/users/profiles/emacs/extras/base.el index 937484b0c..de0626fa3 100644 --- a/users/profiles/emacs/extras/base.el +++ b/users/profiles/emacs/extras/base.el @@ -68,7 +68,8 @@ (use-package embark :ensure t :defer t ; Load on first use - :bind (("C-c a" . embark-act)) ; bind this to an easy key to hit + :bind (("C-c a" . embark-act) ; bind this to an easy key to hit + ("C-c e" . embark-export)) :config ;; Add the option to run embark when using avy (defun bedrock/avy-action-embark (pt) @@ -180,11 +181,53 @@ ;; Modify search results en masse (use-package wgrep + :ensure t + :commands (wgrep-setup wgrep-change-to-wgrep-mode) + :init + ;; Set the key before wgrep loads + (setq wgrep-enable-key "C-c C-p") + (with-eval-after-load 'grep + (define-key grep-mode-map (kbd "C-c C-p") 'wgrep-change-to-wgrep-mode)) + :config + (setq wgrep-auto-save-buffer t) + (setq wgrep-change-readonly-file nil) + + ;; Helix-style keybindings in wgrep mode + (define-key wgrep-mode-map (kbd "C-c C-c") 'wgrep-finish-edit) + (define-key wgrep-mode-map (kbd "C-c C-k") 'wgrep-abort-changes) + (define-key wgrep-mode-map (kbd "C-x C-s") 'wgrep-finish-edit)) + +;; Setup wgrep for consult-grep/ripgrep results +(use-package grep + :ensure nil ; built-in + :config + (require 'wgrep nil t) + :hook ((grep-mode . wgrep-setup) + (ripgrep-search-mode . wgrep-setup))) + +;; Make consult-ripgrep results editable via embark and wgrep + +;; Deadgrep - better project search interface +(use-package deadgrep :ensure t :defer t + :commands (deadgrep) + :config + ;; Use project root by default + (setq deadgrep-project-root-function + (lambda () (project-root (project-current))))) + +;; wgrep support for deadgrep buffers (faster than deadgrep-edit-mode) +(use-package wgrep-deadgrep + :ensure t + :after wgrep :config - (setq wgrep-auto-save-buffer t)) + ;; Setup wgrep for deadgrep buffers + (add-hook 'deadgrep-finished-hook 'wgrep-deadgrep-setup)) +;; Add keybinding for entering wgrep mode in deadgrep buffers +(with-eval-after-load 'deadgrep + (define-key deadgrep-mode-map (kbd "C-c C-p") 'wgrep-change-to-wgrep-mode)) (use-package websocket :ensure t diff --git a/users/profiles/emacs/extras/catpuccin.el b/users/profiles/emacs/extras/catpuccin.el index 477d70376..1f81fd3c2 100644 --- a/users/profiles/emacs/extras/catpuccin.el +++ b/users/profiles/emacs/extras/catpuccin.el @@ -1,7 +1,7 @@ (use-package catppuccin-theme :ensure t + :demand t ; Load immediately, not deferred :init (setq catppuccin-flavor 'frappe) :config - (load-theme 'catppuccin :no-confirm) -) + (load-theme 'catppuccin :no-confirm)) diff --git a/users/profiles/emacs/extras/meow.el b/users/profiles/emacs/extras/meow.el index ee2c17552..52c845bb3 100644 --- a/users/profiles/emacs/extras/meow.el +++ b/users/profiles/emacs/extras/meow.el @@ -95,6 +95,19 @@ (forward-char 1) (delete-region (region-beginning) (region-end))))) +(defun meow-helix-toggle-comment () + "Toggle comment on current line or region, like Helix's C-c." + (interactive) + (if (region-active-p) + ;; Comment/uncomment region + (comment-or-uncomment-region (region-beginning) (region-end)) + ;; Comment/uncomment current line + (save-excursion + (beginning-of-line) + (set-mark (point)) + (end-of-line) + (comment-or-uncomment-region (region-beginning) (region-end))))) + (defvar meow-helix-ex-commands '(("w" . save-buffer) ("write" . save-buffer) @@ -405,6 +418,7 @@ (define-key map "B" 'ibuffer) (define-key map "s" 'save-buffer) (define-key map "S" 'save-some-buffers) + (define-key map "c" 'meow-helix-toggle-comment) ;; Comment toggle (define-key map "q" 'save-buffers-kill-emacs) (define-key map "Q" 'kill-emacs) (define-key map "w" 'meow-helix-window-mode) ;; Changed to enter window mode @@ -412,10 +426,9 @@ (define-key map "v" 'split-window-right) (define-key map "h" 'split-window-below) (define-key map "o" 'other-window) + (define-key map "/" 'meow-helix-project-search) ;; Project-wide search (define-key map "k" 'kill-current-buffer) (define-key map "K" 'kill-buffer) - (define-key map "r" 'query-replace) - (define-key map "R" 'query-replace-regexp) (define-key map "g" 'magit-status) (define-key map "p" 'project-switch-project) (define-key map "e" 'eval-expression) @@ -430,6 +443,41 @@ (interactive) (set-transient-map meow-helix-space-mode-map)) +;; Project-wide search and replace functions +(defun meow-helix-project-search () + "Search across the project using deadgrep. +Press C-c C-p to enter edit mode, C-c C-c to save changes." + (interactive) + (if (fboundp 'deadgrep) + (call-interactively 'deadgrep) + ;; Fallback to consult-ripgrep if available + (if (fboundp 'consult-ripgrep) + (progn + (consult-ripgrep) + (message "Press C-c e to export, then C-c C-p to edit")) + (call-interactively 'project-find-regexp)))) + + +(defun meow-helix-search-word-at-point () + "Search for the word at point across the project." + (interactive) + (let ((word (thing-at-point 'symbol t))) + (if word + (if (fboundp 'deadgrep) + (deadgrep word) + (if (fboundp 'consult-ripgrep) + (consult-ripgrep nil word) + (project-find-regexp word))) + (message "No word at point")))) + +(defun meow-helix-search-symbol-at-point () + "Search for symbol at point in current buffer. +With prefix arg, search across the project." + (interactive) + (if current-prefix-arg + (meow-helix-search-word-at-point) + (isearch-forward-symbol-at-point))) + (defvar meow-helix-window-mode-map (let ((map (make-sparse-keymap))) (define-key map "w" 'other-window) @@ -1163,7 +1211,7 @@ Returns (START-OPEN . END-CLOSE) positions or nil." '("?" . isearch-backward) '("n" . meow-helix-search-next) '("N" . meow-helix-search-prev) - '("*" . isearch-forward-symbol-at-point) + '("*" . meow-helix-search-symbol-at-point) '("m" . meow-helix-match-mode) '("%" . meow-helix-select-whole-file) ; Select whole file like Helix @@ -1173,6 +1221,7 @@ Returns (START-OPEN . END-CLOSE) positions or nil." '("c" . meow-change) ;; '("C" . meow-helix-duplicate) ; Replaced with cursor-below + '("M-c" . meow-helix-toggle-comment) ; Comment toggle (Alt-c, since C-c conflicts) '("d" . meow-helix-delete) ; Simple delete without extending selection '("D" . meow-kill-whole-line) @@ -1229,7 +1278,7 @@ Returns (START-OPEN . END-CLOSE) positions or nil." '("C-a" . meow-helix-select-all) '("C-b" . meow-helix-page-up) '("C-f" . meow-helix-page-down) - '("C-u" . meow-helix-half-page-up) + ;; '("C-u" . meow-helix-half-page-up) ; Commented out to restore C-u as universal argument '("C-d" . meow-helix-half-page-down) '("C-w" . meow-helix-window-mode) diff --git a/users/profiles/emacs/extras/modeline.el b/users/profiles/emacs/extras/modeline.el new file mode 100644 index 000000000..4e8705f0e --- /dev/null +++ b/users/profiles/emacs/extras/modeline.el @@ -0,0 +1,321 @@ +;;; modeline.el --- Custom mode-line configuration -*- lexical-binding: t; -*- + +;; A clean, minimal mode-line that works well in both TUI and GUI + +(defgroup custom-modeline nil + "Custom mode-line configuration." + :group 'mode-line) + +(defface custom-modeline-buffer-name + '((t :inherit mode-line-buffer-id :weight bold)) + "Face for buffer name in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-major-mode + '((t :inherit mode-line-emphasis)) + "Face for major mode in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-minor-mode + '((t :inherit mode-line)) + "Face for minor modes in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-info + '((t :inherit success)) + "Face for info in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-warning + '((t :inherit warning)) + "Face for warnings in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-error + '((t :inherit error)) + "Face for errors in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-modified + '((t :inherit error :weight bold)) + "Face for modified status in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-git-branch + '((t :inherit success)) + "Face for git branch in mode-line." + :group 'custom-modeline) + +(defface custom-modeline-separator + '((t :inherit shadow)) + "Face for separators in mode-line." + :group 'custom-modeline) + +;; Helper functions +(defun custom-modeline--separator () + "Return a separator string for mode-line." + " │ ") + +(defun custom-modeline--make-mouse-map (mouse function) + "Return a mouse map for MOUSE and FUNCTION." + (let ((map (make-sparse-keymap))) + (define-key map (vector 'mode-line mouse) function) + map)) + +;; Mode-line segments +(defvar-local custom-modeline-buffer-name + '(:eval + (propertize + (if buffer-file-name + (abbreviate-file-name buffer-file-name) + (buffer-name)) + 'face 'custom-modeline-buffer-name + 'help-echo (buffer-file-name) + 'mouse-face 'mode-line-highlight + 'local-map (custom-modeline--make-mouse-map 'mouse-1 'previous-buffer))) + "Mode-line segment for buffer name.") + +(defvar-local custom-modeline-modified + '(:eval + (when (and (buffer-modified-p) (buffer-file-name)) + (propertize " âœī¸" + 'face 'custom-modeline-modified + 'help-echo "Buffer modified"))) + "Mode-line segment for modified status.") + +(defvar-local custom-modeline-read-only + '(:eval + (when buffer-read-only + (propertize " 🔒" + 'face 'custom-modeline-warning + 'help-echo "Buffer is read-only"))) + "Mode-line segment for read-only status.") + +(defvar-local custom-modeline-position + '(:eval + (propertize + (format " %d:%d " + (line-number-at-pos) + (current-column)) + 'face 'mode-line + 'help-echo "Line:Column")) + "Mode-line segment for cursor position.") + +(defvar-local custom-modeline-region-info + '(:eval + (when (use-region-p) + (propertize + (format " [%d lines, %d chars] " + (count-lines (region-beginning) (region-end)) + (- (region-end) (region-beginning))) + 'face 'custom-modeline-info + 'help-echo "Region info"))) + "Mode-line segment for region information.") + +(defvar-local custom-modeline-major-mode + '(:eval + (propertize + (format " %s " (format-mode-line mode-name)) + 'face 'custom-modeline-major-mode + 'help-echo "Major mode\nmouse-1: Display major mode menu" + 'mouse-face 'mode-line-highlight + 'local-map (custom-modeline--make-mouse-map 'mouse-1 'mouse-major-mode-menu))) + "Mode-line segment for major mode.") + +;; VCS (jujutsu/git) support +(defun custom-modeline--jj-info () + "Get current jujutsu changeset info." + (when (file-exists-p ".jj") + (condition-case nil + (let* ((cmd "jj log -r@ -n1 --ignore-working-copy --no-graph --color never -T 'separate(\" \", change_id.shortest(8), bookmarks, description.first_line().substr(0, 30))'") + (output (string-trim (shell-command-to-string cmd))) + (parts (split-string output " " t)) + (change-id (car parts)) + (rest (cdr parts))) + (if rest + (format "đŸ“Ļ %s %s" change-id (string-join rest " ")) + (format "đŸ“Ļ %s" change-id))) + (error nil)))) + +(defun custom-modeline--git-branch () + "Get current git branch." + (when (and (fboundp 'vc-git-mode-line-string) + vc-mode + (string-match "Git[:-]\\(.+\\)" vc-mode)) + (format "đŸŒŋ %s" (match-string 1 vc-mode)))) + +(defvar-local custom-modeline-vcs + '(:eval + (let ((vcs-info + (cond + ;; Check jujutsu first (preferred) + ((custom-modeline--jj-info)) + ;; Fallback to git + ((custom-modeline--git-branch)) + ;; No VCS + (t nil)))) + (when vcs-info + (propertize + (format " %s" vcs-info) + 'face 'custom-modeline-git-branch + 'help-echo "Version control info")))) + "Mode-line segment for VCS (jujutsu or git).") + + +(defvar-local custom-modeline-encoding + '(:eval + (unless (string= (symbol-name buffer-file-coding-system) "utf-8-unix") + (propertize + (format " %s " (symbol-name buffer-file-coding-system)) + 'face 'custom-modeline-warning + 'help-echo "Buffer encoding"))) + "Mode-line segment for file encoding.") + +(defvar-local custom-modeline-eol + '(:eval + (let ((eol (coding-system-eol-type buffer-file-coding-system))) + (when (and (numberp eol) (> eol 0)) + (propertize + (cond + ((= eol 1) " CRLF ") + ((= eol 2) " CR ") + (t "")) + 'face 'custom-modeline-warning + 'help-echo "End-of-line style")))) + "Mode-line segment for end-of-line style.") + +(defvar-local custom-modeline-flycheck + '(:eval + (when (and (bound-and-true-p flycheck-mode) + (bound-and-true-p flycheck-current-errors)) + (let* ((errors (flycheck-count-errors flycheck-current-errors)) + (err (or (cdr (assq 'error errors)) 0)) + (warn (or (cdr (assq 'warning errors)) 0)) + (info (or (cdr (assq 'info errors)) 0))) + (propertize + (format " âš ī¸%d ❌%d â„šī¸%d " + warn err info) + 'face (cond + ((> err 0) 'custom-modeline-error) + ((> warn 0) 'custom-modeline-warning) + (t 'custom-modeline-info)))))) + "Mode-line segment for flycheck errors.") + +(defvar-local custom-modeline-lsp + '(:eval + (when (bound-and-true-p lsp-mode) + (propertize + " 🔗 LSP " + 'face 'custom-modeline-info + 'help-echo "Language Server Protocol active"))) + "Mode-line segment for LSP status.") + +;; Integration with meow/helix mode +(defvar-local custom-modeline-meow + '(:eval + (cond + ((bound-and-true-p meow-insert-mode) + (propertize " 📝 INSERT " 'face 'custom-modeline-info)) + ((bound-and-true-p meow-normal-mode) + (propertize " đŸŽ¯ NORMAL " 'face 'custom-modeline-major-mode)) + ((bound-and-true-p meow-motion-mode) + (propertize " 🏃 MOTION " 'face 'custom-modeline-warning)) + ((bound-and-true-p meow-keypad-mode) + (propertize " âŒ¨ī¸ KEYPAD " 'face 'custom-modeline-error)) + ((and (fboundp 'meow-helix-modeline-indicator) + (not (string= (meow-helix-modeline-indicator) ""))) + (propertize (concat " đŸ‘ī¸" (meow-helix-modeline-indicator) " ") 'face 'custom-modeline-info)) + (t ""))) + "Mode-line segment for meow mode.") + +(defvar-local custom-modeline-misc-info + '(:eval + (let ((misc (format-mode-line mode-line-misc-info))) + (unless (string= misc "") + (propertize misc 'face 'mode-line)))) + "Mode-line segment for misc info.") + +;; Construct the mode-line format +(defun custom-modeline-setup () + "Setup the custom mode-line." + (setq-default mode-line-format + '((:eval + (let* ((left (list + " " + custom-modeline-meow + custom-modeline-modified + custom-modeline-read-only + " " + custom-modeline-buffer-name + custom-modeline-position + custom-modeline-region-info)) + (center (list + custom-modeline-major-mode + custom-modeline-vcs)) + (right (list + custom-modeline-flycheck + custom-modeline-lsp + custom-modeline-encoding + custom-modeline-eol + custom-modeline-misc-info + " ")) + (available-width (- (window-width) + (length (format-mode-line left)) + (length (format-mode-line right)))) + (center-width (length (format-mode-line center))) + (center-padding (/ (- available-width center-width) 2))) + (append + left + (when (> center-padding 0) + (list (make-string center-padding ?\s))) + center + (when (> center-padding 0) + (list (make-string center-padding ?\s))) + right)))))) + +;; Additional customizations +(defun custom-modeline-adaptive-colors () + "Adapt mode-line colors based on terminal capabilities." + (when (not (display-graphic-p)) + ;; Simplify faces for terminal + (set-face-attribute 'mode-line nil + :box nil + :underline nil + :overline nil) + (set-face-attribute 'mode-line-inactive nil + :box nil + :underline nil + :overline nil))) + +;; Clean up default mode-line clutter +(defun custom-modeline-cleanup () + "Remove unnecessary elements from mode-line." + ;; Hide minor modes we don't need to see + (setq mode-line-modes + (let ((recursive-edit-help-echo "Recursive edit, type C-M-c to get out")) + (list (propertize "%[" 'help-echo recursive-edit-help-echo) + "(" + `(:propertize ("" mode-name) + help-echo "Major mode\n\ +mouse-1: Display major mode menu\n\ +mouse-2: Show help for major mode\n\ +mouse-3: Toggle minor modes" + mouse-face mode-line-highlight + local-map ,mode-line-major-mode-keymap) + '("" mode-line-process) + ")" + (propertize "%]" 'help-echo recursive-edit-help-echo))))) + +;; Initialize +(custom-modeline-setup) +(custom-modeline-adaptive-colors) +(custom-modeline-cleanup) + +;; Hook for terminal/GUI changes +(add-hook 'after-make-frame-functions + (lambda (frame) + (with-selected-frame frame + (custom-modeline-adaptive-colors)))) + +(provide 'modeline) +;;; modeline.el ends here \ No newline at end of file diff --git a/users/profiles/emacs/extras/org.el b/users/profiles/emacs/extras/org.el index 949de5d08..deb70f69d 100644 --- a/users/profiles/emacs/extras/org.el +++ b/users/profiles/emacs/extras/org.el @@ -83,6 +83,54 @@ (setq org-link-abbrev-alist '(("family_search" . "https://www.familysearch.org/tree/person/details/%s"))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Visual Enhancements for Terminal/TUI +;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Org-superstar for beautiful bullets in terminal +(use-package org-superstar + :ensure t + :hook (org-mode . org-superstar-mode) + :config + ;; Terminal-friendly bullets that work well in TUI + (setq org-superstar-headline-bullets-list '("◉" "○" "✸" "âœŋ" "✤" "✜" "◆" "â–ļ")) + (setq org-superstar-item-bullet-alist '((?* . 8902) (?+ . 8227) (?- . 8226))) + ;; Hide leading stars but keep indentation + (setq org-superstar-leading-bullet ?\s) + (setq org-superstar-leading-fallback ?\s) + ;; Make TODO keywords more visible + (setq org-superstar-special-todo-items t)) + +;; Rainbow delimiters for code blocks +(use-package rainbow-delimiters + :ensure t + :hook ((org-mode . rainbow-delimiters-mode) + (prog-mode . rainbow-delimiters-mode))) + +;; Org-modern for additional visual improvements (terminal-compatible) +(use-package org-modern + :ensure t + :hook ((org-mode . org-modern-mode) + (org-agenda-finalize . org-modern-agenda)) + :config + ;; Terminal-friendly settings + (setq org-modern-star '("◉" "○" "✸" "âœŋ" "✤" "✜" "◆" "â–ļ")) + (setq org-modern-list '((?* . 8902) (?+ . 8227) (?- . 8226))) + (setq org-modern-checkbox '((88 . "☑") (45 . "☐") (32 . "☐"))) + (setq org-modern-table-vertical 1) + (setq org-modern-table-horizontal 0.2) + (setq org-modern-block-fringe nil) ; Terminal doesn't have fringes + ;; Tag styling that works in terminal + (setq org-modern-tag t) + (setq org-modern-priority t) + ;; Timestamp styling + (setq org-modern-timestamp t) + (setq org-modern-statistics t) + ;; Fold ellipsis + (setq org-modern-fold-stars '(("â–ļ" . "â–ŧ")))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Phase 1: editing and exporting files @@ -91,11 +139,64 @@ (use-package org :hook ((org-mode . visual-line-mode) ; wrap lines at word breaks - (org-mode . flyspell-mode)) ; spell checking! + (org-mode . flyspell-mode) ; spell checking! + (org-mode . org-indent-mode) ; Clean indentation + (org-mode . (lambda () + (setq-local line-spacing 0.1) ; Slightly more line spacing + (variable-pitch-mode -1)))) ; Ensure monospace in terminal :bind (:map global-map - ("C-c l s" . org-store-link) ; Mnemonic: link → store - ("C-c l i" . org-insert-link-global)) ; Mnemonic: link → insert + ("C-c o s" . org-store-link) ; Mnemonic: link → store + ("C-c o i" . org-insert-link-global) ; Mnemonic: link → insert + ("C-c o a" . org-agenda) ; Quick agenda access + ("C-c o c" . org-capture)) ; Quick capture + :custom-face + ;; Terminal-friendly face customizations + (org-level-1 ((t (:height 1.2 :weight extra-bold :foreground "#51afef")))) + (org-level-2 ((t (:height 1.15 :weight bold :foreground "#c678dd")))) + (org-level-3 ((t (:height 1.1 :weight bold :foreground "#98be65")))) + (org-level-4 ((t (:height 1.05 :weight semi-bold :foreground "#da8548")))) + (org-level-5 ((t (:height 1.0 :weight semi-bold :foreground "#5699af")))) + (org-level-6 ((t (:height 1.0 :weight semi-bold :foreground "#a9a1e1")))) + (org-level-7 ((t (:height 1.0 :weight semi-bold :foreground "#46d9ff")))) + (org-level-8 ((t (:height 1.0 :weight semi-bold :foreground "#ff6c6b")))) + + ;; Block delimiters and code blocks + (org-block ((t (:background "#1e1e1e" :extend t)))) + (org-block-begin-line ((t (:foreground "#5c6370" :background "#1e1e1e" :extend t :slant italic)))) + (org-block-end-line ((t (:foreground "#5c6370" :background "#1e1e1e" :extend t :slant italic)))) + (org-code ((t (:foreground "#98be65" :background "#2e2e2e")))) + (org-verbatim ((t (:foreground "#da8548" :background "#2e2e2e")))) + + ;; Special keywords + (org-special-keyword ((t (:foreground "#5c6370" :slant italic)))) + (org-meta-line ((t (:foreground "#5c6370" :slant italic)))) + (org-drawer ((t (:foreground "#5c6370")))) + (org-property-value ((t (:foreground "#a9a1e1")))) + + ;; Links + (org-link ((t (:foreground "#51afef" :underline t)))) + (org-footnote ((t (:foreground "#c678dd" :underline t)))) + + ;; Dates and timestamps + (org-date ((t (:foreground "#a9a1e1" :underline nil)))) + (org-scheduled ((t (:foreground "#98be65")))) + (org-scheduled-today ((t (:foreground "#98be65" :weight bold)))) + (org-scheduled-previously ((t (:foreground "#ff6c6b")))) + (org-deadline-announce ((t (:foreground "#ff6c6b" :weight bold)))) + + ;; Tables + (org-table ((t (:foreground "#51afef")))) + (org-table-header ((t (:foreground "#51afef" :weight bold :underline t)))) + + ;; Tags + (org-tag ((t (:foreground "#5699af" :weight normal :slant italic)))) + + ;; Checkboxes + (org-checkbox ((t (:foreground "#51afef" :background nil :box nil)))) + (org-checkbox-statistics-todo ((t (:foreground "#c678dd" :weight bold)))) + (org-checkbox-statistics-done ((t (:foreground "#98be65" :weight bold)))) + :config (require 'oc-csl) ; citation support (add-to-list 'org-export-backends 'md) @@ -105,6 +206,38 @@ ;; Make exporting quotes better (setq org-export-with-smart-quotes t) + + ;; Visual settings for better appearance + (setq org-hide-emphasis-markers t) ; Hide markup characters + (setq org-pretty-entities t) ; Display UTF-8 characters + (setq org-ellipsis " â–ŧ ") ; Nicer ellipsis for folded content + (setq org-startup-indented t) ; Start with indented view + (setq org-hide-leading-stars t) ; Hide leading stars + (setq org-odd-levels-only nil) ; Use all levels + (setq org-adapt-indentation t) ; Adapt indentation + (setq org-src-fontify-natively t) ; Syntax highlighting in code blocks + (setq org-src-tab-acts-natively t) ; Tab works natively in code blocks + (setq org-edit-src-content-indentation 0) ; No extra indentation in src blocks + (setq org-fontify-quote-and-verse-blocks t) ; Fontify quote blocks + (setq org-fontify-whole-heading-line t) ; Extend background to full width + + ;; Better list bullets + (setq org-list-demote-modify-bullet '(("+" . "-") ("-" . "+") ("*" . "+"))) + + ;; Emphasis markers customization + (setq org-emphasis-alist + '(("*" (bold)) + ("/" (italic)) + ("_" (underline)) + ("=" org-verbatim verbatim) + ("~" org-code verbatim) + ("+" (:strike-through t)))) + + ;; Priority faces with nice colors + (setq org-priority-faces + '((?A . (:foreground "#ff6c6b" :weight bold)) + (?B . (:foreground "#da8548" :weight semi-bold)) + (?C . (:foreground "#98be65" :weight normal)))) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -121,11 +254,49 @@ ;; Instead of just two states (TODO, DONE) we set up a few different states ;; that a task can be in. (setq org-todo-keywords - '((sequence "TODO(t)" "WAITING(w@/!)" "STARTED(s!)" "|" "DONE(d!)" "OBSOLETE(o@)"))) + '((sequence + "TODO(t)" + "NEXT(n)" + "STARTED(s!)" + "WAITING(w@/!)" + "|" + "DONE(d!)" + "CANCELLED(c@)" + "OBSOLETE(o@)"))) + + ;; Beautiful TODO keyword faces with terminal-friendly colors + (setq org-todo-keyword-faces + '(("TODO" . (:foreground "#ff6c6b" :weight bold)) + ("NEXT" . (:foreground "#da8548" :weight bold)) + ("STARTED" . (:foreground "#46d9ff" :weight bold)) + ("WAITING" . (:foreground "#ecbe7b" :weight bold)) + ("DONE" . (:foreground "#98be65" :weight bold)) + ("CANCELLED" . (:foreground "#5c6370" :weight bold :strike-through t)) + ("OBSOLETE" . (:foreground "#5c6370" :slant italic)))) ;; Refile configuration (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file) + + ;; Agenda visual improvements + (setq org-agenda-breadcrumbs-separator " ❯ ") + (setq org-agenda-current-time-string "◀── now ─────────────────────────────────────────────────") + (setq org-agenda-time-grid + '((daily today require-timed) + (800 1000 1200 1400 1600 1800 2000) + " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")) + + ;; Compact and clean agenda view + (setq org-agenda-prefix-format + '((agenda . " %i %-12:c%?-12t% s") + (todo . " %i %-12:c") + (tags . " %i %-12:c") + (search . " %i %-12:c"))) + + ;; Better agenda display + (setq org-agenda-block-separator 9472) + (setq org-agenda-compact-blocks t) + (setq org-agenda-tags-column -80) (setq org-capture-templates '(("c" "Default Capture" entry (file "inbox.org") @@ -140,12 +311,12 @@ ("wr" "Work report" entry (file+headline "work.org" "Reports") "** TODO %?\n%U\n%i\n%a"))) - (setq org-agenda-custom-commands - '(("n" "Agenda and All Todos" - ((agenda) - (todo))) - ("w" "Work" agenda "" - ((org-agenda-files '("work.org"))))))) + (setq org-agenda-custom-commands + '(("n" "Agenda and All Todos" + ((agenda) + (todo))) + ("w" "Work" agenda "" + ((org-agenda-files '("work.org"))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; @@ -166,11 +337,11 @@ (window-height . fit-window-to-buffer)))) ;; Pretty web interface for org-roam -;(use-package org-roam-ui -; :ensure t -; :after org-roam -; :config -; (setq org-roam-ui-sync-theme t -; org-roam-ui-follow t -; org-roam-ui-update-on-save t -; org-roam-ui-open-on-start t)) + ;(use-package org-roam-ui + ; :ensure t + ; :after org-roam + ; :config + ; (setq org-roam-ui-sync-theme t + ; org-roam-ui-follow t + ; org-roam-ui-update-on-save t + ; org-roam-ui-open-on-start t)) diff --git a/users/profiles/emacs/extras/project.el b/users/profiles/emacs/extras/project.el deleted file mode 100644 index 1a038817e..000000000 --- a/users/profiles/emacs/extras/project.el +++ /dev/null @@ -1,14 +0,0 @@ -(defvar jae/opened-projects '() - "List of projects that have already been opened in this session.") - -(defun my-local-variables-hook () - (let ((project-root (project-root (project-current t)))) - (unless (member project-root jae/opened-projects) - (when (fboundp 'my/project-setup) - (my/project-setup) - ) - (add-to-list 'jae/opened-projects project-root)))) - -(add-hook 'hack-local-variables-hook #'my-local-variables-hook) - -(setq jae/opened-projects nil) diff --git a/users/profiles/emacs/extras/vcs.el b/users/profiles/emacs/extras/vcs.el new file mode 100644 index 000000000..2720f3751 --- /dev/null +++ b/users/profiles/emacs/extras/vcs.el @@ -0,0 +1,61 @@ +;; with-editor is required by git-commit +(use-package with-editor + :ensure t) + +(use-package git-commit + :ensure t + :config + ;; Recognize JJ temp files as commit messages too + (setq git-commit-filename-regexp + (concat git-commit-filename-regexp + "\\|/editor-.*\\.jjdescription\\'"))) + +;; Debug code - commented out after fixing project prompt issues +;; (defun my/debug-project-switch (orig-fun &rest args) +;; (message ">>> project-switch-project called with %S" args) +;; (backtrace) +;; (apply orig-fun args)) +;; (advice-add 'project-switch-project :around #'my/debug-project-switch) + +;; Advise project-current to return nil for commit buffers (preventing prompts) +(defun my/project-current-skip-commit-buffers (orig-fun &rest args) + "Skip project detection for commit buffers to prevent prompts." + (if (and buffer-file-name + (or (string-match-p "COMMIT_EDITMSG\\'" buffer-file-name) + (string-match-p "MERGE_MSG\\'" buffer-file-name) + (string-match-p "editor-.*\\.jjdescription\\'" buffer-file-name))) + ;; Return nil for commit buffers when MAYBE-PROMPT is non-nil + (if (car args) ; if maybe-prompt is true + nil ; return nil to prevent prompt + (funcall orig-fun nil)) ; otherwise call with nil to prevent prompt + ;; Normal operation for non-commit buffers + (apply orig-fun args))) + +(advice-add 'project-current :around #'my/project-current-skip-commit-buffers) + +(defun my/setup-commit-buffer-keys () + "Setup keybindings for both git and jj commit buffers." + ;; Ensure with-editor-mode is active + (when (fboundp 'with-editor-mode) + (with-editor-mode 1)) + ;; Setup standard commit keybindings + (local-set-key (kbd "C-c C-c") #'server-edit) + (local-set-key (kbd "C-c C-k") + (lambda () + (interactive) + (set-buffer-modified-p nil) + (server-edit-abort)))) + +;; Hook this to git-commit-mode, which now activates for both git and jj +(add-hook 'git-commit-setup-hook #'my/setup-commit-buffer-keys) + +;; Remove the old hooks that were trying to call git-commit-mode incorrectly +;; git-commit-mode is not a major mode, it's part of the setup process + +;; Clean up debug code - can be re-enabled if needed +;; (defun my/debug-mode-changes () +;; "Debug mode changes in commit buffers." +;; (when (and buffer-file-name +;; (string-match-p "\\(COMMIT_EDITMSG\\|MERGE_MSG\\|editor-.*\\.jjdescription\\)\\'" buffer-file-name)) +;; (message "DEBUG: after-change-major-mode-hook - mode is now %s for %s" major-mode buffer-file-name))) +;; (add-hook 'after-change-major-mode-hook #'my/debug-mode-changes) diff --git a/users/profiles/git.nix b/users/profiles/git.nix index fcc5d66ea..652b04376 100644 --- a/users/profiles/git.nix +++ b/users/profiles/git.nix @@ -23,7 +23,7 @@ in { extraConfig = { github.user = lib.mkDefault userinfo.githubUser; gitlab.user = lib.mkDefault userinfo.gitlabUser; - core.editor = "hx"; + core.editor = "emacsclient -a''"; push.default = "upstream"; pull.rebase = true; rebase.autoStash = true; diff --git a/users/profiles/jujutsu.nix b/users/profiles/jujutsu.nix index d7196e2cb..8d5c9d8b8 100644 --- a/users/profiles/jujutsu.nix +++ b/users/profiles/jujutsu.nix @@ -11,7 +11,7 @@ in { name = userinfo.fullName; }; ui = { - editor = "hx"; + editor = "emacsclient -a ''"; pager = "delta"; diff-formatter = ["difft" "--color=always" "$left" "$right"]; };