Files
opalfiles/.config/doom/config.el
2025-10-14 19:55:55 -07:00

407 lines
14 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;;; config.el -*- lexical-binding: t; -*-
;; Clean up starting screen
;; (setq fancy-splash-image "~/.config/doom/splash.png")
(remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-shortmenu)
(remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-banner)
(remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-footer)
(remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-loaded)
(add-hook! '+doom-dashboard-mode-hook (hide-mode-line-mode 1) (hl-line-mode -1))
(setq-hook! '+doom-dashboard-mode-hook evil-normal-state-cursor (list nil))
;; Set name & e-mail
(setq user-full-name "opal"
user-mail-address "ry.orlando@proton.me")
;; Theme & background color
(load-theme 'doom-gruvbox t)
;;(set-face-background 'default "#E8D8B0")
;; Lockfiles
(setq create-lockfiles nil)
;; Remove line numbers from specific modes
(dolist (mode '(org-mode-hook
term-mode-hook
shell-mode-hook
eshell-mode-hook))
(add-hook mode (lambda () (display-line-numbers-mode 0))))
;; Font configuration
(set-face-attribute 'default nil :font "Monospace" :height 170)
(set-face-attribute 'fixed-pitch nil :font "Monospace" :height 1.0)
(set-face-attribute 'variable-pitch nil :font "ETBembo" :height 1.0)
(defun opal/org-font-setup ()
;; Replace list hyphen with dot
(font-lock-add-keywords 'org-mode
'(("^ *\\([-]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1)
""))))))
;; Set faces for heading levels
(dolist (face '((org-level-1 . 1.2)
(org-level-2 . 1.1)
(org-level-3 . 1.05)
(org-level-4 . 1.0)
(org-level-5 . 1.1)
(org-level-6 . 1.1)
(org-level-7 . 1.1)
(org-level-8 . 1.1)))
(set-face-attribute (car face) nil :font "ETBembo" :weight 'regular :height (cdr face)))
;; Set properties and scheduling faces to inherit variable-pitch and be smaller
;; Comment out these lines because mixed-pitch-mode will handle these.
(set-face-attribute 'org-property-value nil :inherit 'variable-pitch :height 150)
(set-face-attribute 'org-special-keyword nil :inherit 'variable-pitch :height 150)
(set-face-attribute 'org-scheduled-today nil :inherit 'variable-pitch :height 150)
(set-face-attribute 'org-drawer nil :inherit 'variable-pitch :height 150)
(set-face-attribute 'org-date nil :inherit 'variable-pitch :height 150)
;; Ensure that all agenda items use fixed-pitch font
(custom-set-faces
'(org-agenda-date-today ((t (:inherit fixed-pitch :weight bold))))
'(org-agenda-date ((t (:inherit fixed-pitch))))
'(org-agenda-date-weekend ((t (:inherit fixed-pitch :weight bold))))
'(org-agenda-done ((t (:inherit fixed-pitch :strike-through t))))
'(org-agenda-dimmed-todo-face ((t (:inherit fixed-pitch))))
'(org-agenda-structure ((t (:inherit fixed-pitch))))
'(org-scheduled ((t (:inherit fixed-pitch))))
'(org-scheduled-today ((t (:inherit fixed-pitch))))
'(org-scheduled-previously ((t (:inherit fixed-pitch))))
'(org-upcoming-deadline ((t (:inherit fixed-pitch))))
'(org-deadline-announce ((t (:inherit fixed-pitch))))
'(org-time-grid ((t (:inherit fixed-pitch)))))
)
(defun opal/org-mode-setup ()
(org-indent-mode)
(visual-line-mode 1)
(mixed-pitch-mode 1))
(use-package org
:commands (org-capture org-agenda)
:hook (org-mode . opal/org-mode-setup)
:config
(setq org-ellipsis "")
(setq org-agenda-start-with-log-mode t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-startup-folded t)
(setq org-indent-indentation-per-level 2)
(setq org-hide-emphasis-markers t)
(setq org-agenda-files (directory-files-recursively "~/sync/org/agenda/" "\\.org$"))
(setq org-agenda-todo-ignore-scheduled 'future)
(require 'org-habit)
(add-to-list 'org-modules 'org-habit)
(setq org-habit-graph-column 60)
(setq org-todo-keywords
'((sequence "ALRT(a)" "EASY(e)" "PROG(p)" "NEXT(n)"
"TODO(t)" "WAIT(w)" "|" "DONE(d)")))
(setq org-todo-keyword-faces
'(("ALRT" . (:foreground "Red" :weight bold))
("TODO" . (:foreground "Orange" :weight bold))
("WAIT" . (:foreground "SlateBlue" :weight bold))
("PROG" . (:foreground "DodgerBlue" :weight bold))
("NEXT" . (:foreground "Purple" :weight bold))
("EASY" . (:foreground "MediumSeaGreen" :weight bold))
("DONE" . (:foreground "ForestGreen" :weight bold))))
(add-hook 'org-mode-hook #'org-make-toc-mode)
(setq org-agenda-custom-commands
'(("d" "GTD Dashboard"
;; Begin list of blocks:
(
(tags "+TODO=\"ALRT\""
((org-agenda-overriding-header "🚨 Urgent / Interruptions")))
(tags "+TODO=\"EASY\""
((org-agenda-overriding-header "🍃 Quick Tasks")))
(tags "+TODO=\"PROG\""
((org-agenda-overriding-header "🚧 In Progress")))
(tags "+TODO=\"NEXT\""
((org-agenda-overriding-header "▶ Next Actions")))
(tags "+TODO=\"WAIT\""
((org-agenda-overriding-header "⏳ Pending / Blocked")))
(tags "+TODO=\"TODO\""
((org-agenda-overriding-header "📝 Backlog / To Plan")))
(tags "+TODO=\"DONE\"+CLOSED>=\"<-3d>\""
((org-agenda-overriding-header "✅ Recently Done")))
(agenda ""
((org-agenda-span 1)
(org-agenda-start-day "0d")
(org-agenda-show-all-dates t)
(org-agenda-overriding-header "📅 Todays Schedule")))
(agenda ""
((org-agenda-span 3)
(org-agenda-start-day "+1d")
(org-agenda-overriding-header "📆 Next 3 Days")
(org-agenda-time-grid nil)))
(alltodo ""
((org-agenda-overriding-header "📋 All Tasks")))
) ; End of block-list
) ; End of the "d" command entry
)) ; End of defcustom list
;; Create capture templates
(setq org-capture-templates
`(("t" "Tasks")
("tw" "Work Task" entry (file+headline "~/sync/org/agenda/work.org" "Inbox")
"* TODO %?\n %U\n %i" :empty-lines 1)
("tp" "Personal Task" entry (file+headline "~/sync/org/agenda/personal.org" "Inbox")
"* TODO %?\n %U\n %i" :empty-lines 1)))
;; Tell Org to stop indenting inside of org source blocks.
(setq org-edit-src-content-indentation 0)
;; Set org agenda dir
(setq org-directory "~/sync/org/agenda")
(setq org-agenda-include-diary t)
(setq diary-file "~/sync/org/diary")
(require 'org-tempo)
(dolist (template '(("sh" . "src shell")
("el" . "src emacs-lisp")
("cl" . "src lisp")
("sql" . "src sql")
("py" . "src python")))
(add-to-list 'org-structure-template-alist template))
;; Init org font setup
(opal/org-font-setup))
(use-package org-bullets
:after org
:hook (org-mode . org-bullets-mode)
:custom
(org-bullets-bullet-list '("" "" "" "" "" "" "")))
(defun opal/org-mode-visual-fill ()
(setq visual-fill-column-width 150
visual-fill-column-center-text t)
(visual-fill-column-mode 1))
(use-package visual-fill-column
:hook (org-mode . opal/org-mode-visual-fill))
(use-package! ob
:after org
:config
;; Enable support for these code block languages
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(sql . t)))
;; Support correct font/highlighting for conf-unix blocks
(push '("conf-unix" . conf-unix) org-src-lang-modes))
;; Playing youtube videos via mpv/yt-dlp/emms
(defun opal/play-yt-url-at-point ()
"Play YT link under point with EMMS/MPV/yt-dlp."
(interactive)
(let ((url (thing-at-point 'url t)))
(if url
(emms-play-url url)
(message "No URL found at point."))))
(global-set-key (kbd "C-c y") 'opal/play-yt-url-at-point)
;; Elfeed
(defun opal/elfeed-open-in-eww()
"Open the current Elfeed entry link in eww."
(interactive)
(let ((link (elfeed-entry-link (elfeed-search-selected :single))))
(if link
(eww-browse-url link)
(message "No link to open."))))
(defun opal/elfeed-open-in-librewolf ()
"Open the current Elfeed entry link in LibreWolf."
(interactive)
(let ((link (elfeed-entry-link (elfeed-search-selected :single))))
(if link
(let ((browse-url-generic-program "librewolf"))
(browse-url-generic link))
(message "No link to open."))))
(defun opal/elfeed-mark-all-read ()
"Marks all feeds in *elfeed-search* as read."
(interactive)
(elfeed-untag elfeed-search-entries 'unread)
(elfeed-search-update :force)) ; redraw
(use-package elfeed
:bind
(("C-c e" . elfeed))
:config
;; Ensure faces are set after elfeed has loaded
(set-face-attribute 'elfeed-search-filter-face nil :inherit 'variable-pitch :height 200)
;; Monospace fonts are necessary for text/column alignment in the *elfeed-search* buffer
(set-face-attribute 'elfeed-search-title-face nil :inherit 'fixed-pitch :height 200)
(set-face-attribute 'elfeed-search-feed-face nil :inherit 'fixed-pitch :height 200)
;; Set variable-pitch face for article fonts in the *elfeed-entry* buffer
(add-hook 'elfeed-show-mode-hook
(lambda () (buffer-face-set 'variable-pitch)))
;; Set default search filter
(setq elfeed-search-filter "@2-weeks-ago +unread")
;; Update feeds automatically when entering Elfeed search mode
(add-hook 'elfeed-search-mode-hook 'elfeed-update)
;; Evil keybindings for Elfeed
(evil-define-key 'normal elfeed-search-mode-map
"e" 'opal/elfeed-open-in-eww ;; Open link in eww
"o" 'opal/elfeed-open-in-librewolf ;; Open link in eww
"r" 'opal/elfeed-mark-all-read ;; Marks all feeds as read
"gr" 'elfeed-update ;; Refresh feeds
"q" 'quit-window)) ;; Quit Elfeed
(use-package elfeed-org
:ensure t
:config
(setq rmh-elfeed-org-files '("~/sync/org/elfeed/feeds.org")))
(after! osm
(set-popup-rule! "^\\*osm\\*" :ignore t))
;; eww
;; extra frontends to use:
;; https://eddrit.com
;; https://eu.safereddit.com
;; https://redlib.catsarch.com/
(defun opal/eww-reddit-redirect (url)
"Redirect reddit.com (with or without www) to a privacy frontend."
(replace-regexp-in-string "^https://\\(www\\.\\)?reddit\\.com" "https://old.reddit.com" url))
(defun opal/eww-x-redirect (url)
"Redirect x.com (with or without www) to a privacy frontend."
(replace-regexp-in-string "^https://\\(www\\.\\)?x\\.com" "https://xcancel.com" url))
(defun opal/eww-twitter-redirect (url)
"Redirect twitter.com (with or without www) to a privacy frontend."
(replace-regexp-in-string "^https://\\(www\\.\\)?twitter\\.com" "https://xcancel.com" url))
(global-set-key (kbd "C-c w") #'eww)
(global-set-key (kbd "C-c f") #'elfeed-goodies/show-link-hint)
(defun +opal--eww-fix-fonts-h ()
"Fix fonts, wrapping, and scaling for EWW buffers."
(visual-line-mode 1)
(setq-local face-remapping-alist
'((default variable-pitch default)
(shr-text variable-pitch default)
(shr-link variable-pitch default)
(shr-strong variable-pitch default)
(fixed-pitch fixed-pitch default)
(variable-pitch variable-pitch default)))
(text-scale-set 1))
(use-package! eww
:init
(setq browse-url-browser-function #'eww-browse-url
eww-auto-rename-buffer 'title
reddit-proxy "https://eddrit.com"
eww-url-transformers '(eww-remove-tracking
opal/eww-reddit-redirect
opal/eww-twitter-redirect
opal/eww-x-redirect)
shr-use-fonts nil
shr-fill-text nil)
(after! eww
(set-popup-rule! "^\\*eww" :ignore t))
:hook (eww-mode . +opal--eww-fix-fonts-h))
;; Since I sometimes write notes in two languages, guess-language will allow for multiple language
;; spelling checks to be done in a single buffer.
(use-package guess-language
:defer t
:init (add-hook 'text-mode-hook #'guess-language-mode)
:config
(setq guess-language-langcodes '((en . ("en_US" "English"))
(pt_BR . ("pt_BR" "Brazilian Portuguese")))
guess-language-languages '(en pt_BR)
guess-language-min-paragraph-length 45)
:diminish guess-language-mode)
(defun opal/nov-mode-setup ()
"Configure fonts and layout for nov.el."
(variable-pitch-mode 1)
(visual-line-mode 1)
(setq visual-fill-column-width 150
visual-fill-column-center-text t
line-spacing 0.2)
(visual-fill-column-mode 1))
(use-package! nov
:mode ("\\.epub\\'" . nov-mode)
:hook (nov-mode . opal/nov-mode-setup))
(setq +lookup-dictionary-prefer-offline t)
(setq dictionary-server "dict.org")
(setq browse-url-generic-program "librewolf")
(setq browse-url-browser-function 'browse-url-generic)
(use-package! tramp
:init
;; TRAMP optimizations
;; See: https://coredumped.dev/2025/06/18/making-tramp-go-brrrr
;; Set core TRAMP behavior before it loads
(setq remote-file-name-inhibit-locks t
remote-file-name-inhibit-auto-save-visited t
tramp-use-scp-direct-remote-copying t
tramp-copy-size-limit (* 1024 1024) ;; 1MB
tramp-verbose 2
magit-tramp-pipe-stty-settings 'pty)
:config
;; Enable async remote process copying
(connection-local-set-profile-variables
'remote-direct-async-process
'((tramp-direct-async-process . t)))
(connection-local-set-profiles
'(:application tramp :protocol "scp")
'remote-direct-async-process)
;; Prevent compile mode from disabling SSH ControlMaster
(with-eval-after-load 'compile
(remove-hook 'compilation-mode-hook
#'tramp-compile-disable-ssh-controlmaster-options)))
(use-package emms
:after emms-player-mpd
:config
(require 'emms-setup)
(require 'emms-player-mpd)
(emms-all)
(emms-default-players)
(setq emms-player-list '(emms-player-mpd)
emms-player-mpd-server-name "127.0.0.1"
emms-player-mpd-server-port "6660"
emms-player-mpd-music-directory "~/music")
(add-to-list 'emms-info-functions 'emms-info-mpd)
(emms-player-mpd-connect))