407 lines
14 KiB
EmacsLisp
407 lines
14 KiB
EmacsLisp
;;; 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 "📅 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-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))
|