;;; 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)) ;; Inherit SSH_AUTH_SOCK cuz emacs daint (use-package exec-path-from-shell :ensure t :config (exec-path-from-shell-initialize) (exec-path-from-shell-copy-envs '("SSH_AUTH_SOCK" "SSH_AGENT_PID"))) (setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" vc-ignore-dir-regexp tramp-file-name-regexp)) ;; Disable projectile with remote files (defun opal/maybe-disable-projectile () "Disable projectile-mode for remote files." (when (file-remote-p default-directory) (projectile-mode -1))) (add-hook 'find-file-hook #'opal/maybe-disable-projectile) ;; Set name & e-mail (setq user-full-name "opal" user-mail-address "ry.orlando@proton.me") (setq vterm-shell "/bin/zsh") ;; 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 shell-mode-hook eshell-mode-hook)) (add-hook mode (lambda () (display-line-numbers-mode 0)))) ;; Font Config (setq doom-font (font-spec :family "Victor Mono" :size 22 :weight 'medium) doom-variable-pitch-font (font-spec :family "Victor Mono" :size 22 :weight 'medium :slant 'italic)) (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 heading sizes only (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 :height (cdr face))) ;; Ensure that all agenda items inherit default (custom-set-faces '(org-agenda-date-today ((t (:inherit default :weight bold)))) '(org-agenda-date ((t (:inherit default)))) '(org-agenda-date-weekend ((t (:inherit default :weight bold)))) '(org-agenda-done ((t (:inherit default :strike-through t)))) '(org-agenda-dimmed-todo-face ((t (:inherit default)))) '(org-agenda-structure ((t (:inherit default)))) '(org-scheduled ((t (:inherit default)))) '(org-scheduled-today ((t (:inherit default)))) '(org-scheduled-previously ((t (:inherit default)))) '(org-upcoming-deadline ((t (:inherit default)))) '(org-deadline-announce ((t (:inherit default)))) '(org-time-grid ((t (:inherit default)))))) (defun opal/org-mode-setup () (org-indent-mode) (visual-line-mode 1) (variable-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)))) (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 "πŸ“… Today’s 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-firefox() "Open the current Elfeed entry link in firefox." (interactive) (let ((link (elfeed-entry-link (elfeed-search-selected :single)))) (if link (let ((browse-url-generic-program "firefox")) (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) ;; Attempt to fix slow Tramp issues (remove-hook 'evil-insert-state-exit-hook #'doom-modeline-update-buffer-file-name) (remove-hook 'find-file-hook #'doom-modeline-update-buffer-file-name) (remove-hook 'find-file-hook 'forge-bug-reference-setup) (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))