;;(mapc #'package-install '(ledger-mode transpose-frame dictionary paredit expand-region 0x0 pinentry rainbow-delimiters clhs emms ebdb markdown-mode magit captain exwm go-translate plz jabber)) ;; Code to contribute (with-eval-after-load 'message (defun message-add-openpgp-header () "Add OpenPGP header to point to public key. Header will be constructed as specified in `message-openpgp-header'. Consider adding this function to `message-header-setup-hook'" ;; See https://tools.ietf.org/html/draft-josefsson-openpgp-mailnews-header (when (and message-openpgp-header (or (nth 0 message-openpgp-header) (nth 1 message-openpgp-header) (nth 2 message-openpgp-header))) (save-excursion (message-add-header (with-temp-buffer (insert "OpenPGP: ") ;; add ID (let (need-sep) (when (nth 0 message-openpgp-header) (insert "id=" (nth 0 message-openpgp-header)) (setq need-sep t)) ;; add URL (when (nth 1 message-openpgp-header) (when need-sep (insert "; ")) (insert "url=\"" (nth 1 message-openpgp-header) "\"") (setq need-sep t)) ;; add preference (when (nth 2 message-openpgp-header) (when need-sep (insert "; ")) (insert "preference=" (nth 2 message-openpgp-header)))) ;; insert header (buffer-string)))) (message-sort-headers)))) ;; My auxiliary functions ;;; Macros (defmacro dointerval (spec &rest body) "A dotimes starting at MIN." (declare (indent 1) (debug dolist)) (let ((var (nth 0 spec)) (start (nth 1 spec)) (end (nth 2 spec)) (counter (make-symbol "counter"))) `(let ((,counter ,start)) (while (<= ,counter ,end) (let ((,var ,counter)) ,@body) (setq ,counter (1+ ,counter)))))) ;;; ELisp Functions (defun american-number-string-to-number (string) (string-to-number (string-replace "," "" string))) (defun random-interval (min max) "Get a random number between [MIN; MAX[." (+ min (random (- max min)))) (defun current-line-empty-p () "Check if current line is an empty line." (save-excursion (beginning-of-line) (looking-at-p "[[:blank:]]*$"))) ;; load-path (add-to-list 'load-path (expand-file-name "~/.emacs.d/my-libs")) (add-to-list 'load-path "/usr/share/emacs/site-lisp") ;; load my elisp standard stuff (load "my-lisp") ;; unbind key (keymap-global-unset "C-x g") ;;; emacs related functions (defun emacs-eval-init-file () (interactive) (let ((buf-name "init.el")) (unless (get-buffer buf-name) (find-file (expand-file-name "~/.emacs.d/init.el")) (previous-buffer)) (set-buffer buf-name) (eval-buffer))) (keymap-global-set "C-x M-e" 'emacs-eval-init-file) (defun my-move-beginning-of-line (arg) (interactive "^p") (or arg (setq arg 1)) (cond ((eql (point) (save-excursion (re-search-backward "^\\([[:blank:]]+\\)" nil 't) (match-end 1))) (move-beginning-of-line arg)) ('t (back-to-indentation)))) (keymap-global-set "C-a" 'my-move-beginning-of-line) (defun my-split-window-below () (interactive) (split-window-below) (set-window-buffer (next-window) (other-buffer)) (other-window 1)) (keymap-global-set "C-x 2" 'my-split-window-below) (defun my-split-window-right () (interactive) (split-window-right) (set-window-buffer (next-window) (other-buffer)) (other-window 1)) (keymap-global-set "C-x 3" 'my-split-window-right) (defun list-all-buffers (&optional files-only) "Display a list of names of existing buffers. The list is displayed in a buffer named `*Buffer List*'. Non-null optional arg FILES-ONLY means mention only file buffers. For more information, see the function `buffer-menu'." (interactive "P") (display-buffer (list-buffers-noselect files-only (buffer-list)))) (keymap-global-set "C-x C-b" 'list-all-buffers) (defun list-all-functions () "Lisp all current functions" (interactive) (apropos-command "." nil) (other-window 1) (read-only-mode -1) (goto-char (point-min)) (kill-line 2) (flush-lines "^ " nil nil 't) (re-search-forward "^\\([[:alnum:]/\\+\\*\\?!@#\\$%&_=-]+\\).*$") (replace-match "\\1") ;; (replace-match "\\1") ;; (replace-regexp ;; "^\\([[:alnum:]/\\+\\*\\?!@#\\$%&_=-]+\\).*$" "\\1" nil ;; (use-region-beginning) (use-region-end) nil (use-region-noncontiguous-p)) (save-excursion (let ((sort-fold-case 't)) (sort-lines nil (point-min) (point-max)))) (read-only-mode 1)) (keymap-global-set "ESC M-." 'xref-find-apropos) (keymap-global-set "C-h C-a" 'apropos-function) (keymap-global-set "C-h M-a" 'apropos-variable) (keymap-global-set "C-h M-i" 'info-apropos) ;;; my other functions (defun play-alarm (file) "Play a sound alarm FILE." (shell-command (concat "ffplay -v 0 -nodisp -volume 100 -autoexit " (expand-file-name file) " &>/dev/null"))) (defun quack () "Hear a duck's quack." (interactive) (play-alarm "~/.local/share/sound/duck-quack.mp3") (message "quack...")) (defun coin () "Hear a coin drop." (interactive) (play-alarm "~/.local/share/sound/coin.mp3")) (defun plink () "Hear a retro game sound." (interactive) (play-alarm "~/.local/share/sound/game-retro-beep.mp3")) (defun bip () "Hear a cool beep sound." (interactive) (play-alarm "~/.local/share/sound/error-deepin.mp3")) (defun birds () "Hear singing birds sound." (interactive) (play-alarm "~/.local/share/sound/little-birds-in-tree.opus")) (defun heart () "Hear a heart machine sound." (interactive) (play-alarm "~/.local/share/sound/beep-heart-machine.mp3")) (defun rice-minutes (secs) "Rice alarm." (interactive "nMinutes for the rice cooking: ") (run-with-timer (* 60 secs) 0 (lambda () (dotimes (i 3) (quack) (sit-for 1)) (message "Alarm done."))) (message "Rice alarm set.")) (defun years-old (year) (interactive "nBirth year: ") (message "%d" (- (nth 5 (decode-time)) year))) (defun password-prompt () "Open a prompt, in the minibuffer, for passwords. Good when using M-x shell." (interactive) (comint-send-invisible) (clear-this-command-keys)) (keymap-global-set "C-c p" 'password-prompt) (defun lesspass (site username counter length symbol-type) "Use lpcli on EWW." (interactive (list (read-string "Site: " (progn (string-match "https?://\\(.*\\)/+?" (plist-get eww-data :url)) (match-string 1 (plist-get eww-data :url)))) (read-string "Username: " (getenv "LESSPASS_USER")) (number-to-string (read-number "Counter: " 1)) (number-to-string (read-number "Length: " 16)) (read-string "Symbol's type: " "ulds"))) (insert (shell-command-to-string (concat "printf " (read-passwd "Password: ") " | lpcli " site " ${LESSPASS_USER} -p" symbol-type "c" counter "n" length " | grep -v -E 'Options:|Enter Password' | tr -dc '[:print:]'")))) (defalias 'lp 'lesspass) (defun connect () "Connect to services." (interactive) (jabber-connect-all) (dolist (buf '("Newnet" "Libera.Chat")) (when (get-buffer buf) (set-buffer buf) (erc-process-input-line "/reconnect\n")))) (keymap-global-set "C-x g c" 'connect) (defun disconnect () "Disconnect from services." (interactive) ;; gnus ;; (gnus-run-hooks 'gnus-exit-gnus-hook) ;; (gnus-save-newsrc-file) ;; (gnus-close-backends) ;; (gnus-clear-system) ;; jabber (jabber-disconnect) ;; irc (weri-disable) (dolist (buf '("Newnet" "Libera.Chat")) (when (get-buffer buf) (set-buffer buf) (erc-quit-server nil)))) (keymap-global-set "C-x g d" 'disconnect) (defun suspend-pc () "Suspend the PC." (interactive) (emms-stop) (disconnect) (shell-command "sleep 0.25; mem-suspend")) (keymap-global-set "C-x C-z" 'suspend-pc) (defun my-save-buffers-kill-emacs () "Save buffers and kill Emacs daemon." (interactive) (when (y-or-n-p "Do you want to kill Emacs client and Emacs daemon? ") (save-buffers-kill-emacs))) (keymap-global-set "C-x C-c" 'my-save-buffers-kill-emacs) (keymap-global-set "C-x M-c" 'save-buffers-kill-terminal) ;; help with M-x ;; (icomplete-mode) ;; compiling (setq compile-command "make -j $(nproc --all) ") (setq compilation-scroll-output 't) ;; escape sequences ;;; compile with colors (no raw escape sequences) (require 'ansi-color) (add-hook 'compilation-filter-hook 'ansi-color-compilation-filter) ;;; fix raw escape sequences in shell (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on) ;; emacs global keybindings (keymap-global-set "C-w" 'backward-kill-word) (keymap-global-set "M-DEL" 'kill-region) (keymap-global-set "C-x %" 'shrink-window) (keymap-global-set "C-x M-u" (lambda () (interactive) (package-upgrade-all nil))) (keymap-global-set "C-_" 'undo-only) (keymap-global-set "C-c ;" 'comment-region) (keymap-global-set "C-c e" 'end-of-buffer) (keymap-global-set "C-c b" 'beginning-of-buffer) (keymap-global-set "ESC M-%" 'query-replace-regexp) ;; emacs default mode's hook (setq-default major-mode 'text-mode) ;; deleting trailing whitespace (add-hook 'before-save-hook 'delete-trailing-whitespace) ;; some emacs options (setq make-backup-files nil) (setq gc-cons-treshold 20000000) (setq sentence-end-double-space nil) (setq fill-column 72) ;; dired with integration to gnus ;; (for example, attaching multiple files) (add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) ;; LISP ;;; lisp show parenthesis (show-paren-mode) ;;; lisp pair up parenthesis (electric-pair-mode) ;;; lisp color parenthesis automatic (package rainbow-delimiters) (add-hook 'lisp-mode-hook 'rainbow-delimiters-mode) (add-hook 'eldoc-mode-hook 'rainbow-delimiters-mode) ;;; lisp with paredit (with-eval-after-load 'paredit ;; (keymap-set paredit-mode-map "C-c M-q" 'slime-close-all-parens-in-sexp) (keymap-set paredit-mode-map "ESC DEL" 'paredit-kill-region) (keymap-set paredit-mode-map "C-c M-<" 'paredit-forward-barf-sexp) (keymap-set paredit-mode-map "C-c M->" 'paredit-forward-slurp-sexp) (keymap-set paredit-mode-map "C-c M-s" 'paredit-splice-sexp-killing-backward)) (add-hook 'lisp-mode-hook 'enable-paredit-mode) (add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) ;;; Common Lisp (setq inferior-lisp-program "sbcl") ;;; (load (expand-file-name "~/quicklisp/slime-helper.el")) ;;; (require 'slime) ;;; (slime-setup) (setq clhs-root "file:///usr/share/doc/clhs/HyperSpec/") (with-eval-after-load 'lisp-mode (keymap-set lisp-mode-map "C-h C-s" 'clhs-doc)) ;;; Kilo Lisp (defun run-klisp () "Run LISP Club Language REPL." (interactive) (cd "~/me/programming/desktop/lisp/interpreter/klisp/klisp.new/") (if (not (comint-check-proc "*inferior-kilolisp*")) (let ((cmdlist (split-string-shell-command "kl")) (inferior-lisp-buffer "*inferior-kilolisp*")) (set-buffer (apply (function make-comint) "inferior-kilolisp" (car cmdlist) nil (cdr cmdlist))))) (pop-to-buffer "*inferior-kilolisp*" display-comint-buffer-action)) ;;; Scheme (setq scheme-program-name "/usr/bin/guile") ;; (setq scheme-program-name "/usr/bin/mit-scheme") ;; (setq scheme-program-name "gsi -:t") ;; gambit ;; EWW (setq browse-url-browser-function 'eww-browse-url) (setq eww-search-prefix "https://duckduckgo.com/?q=") (setq url-cookie-untrusted-urls '(".")) (setq url-cookie-trusted-urls '("lists.gnupg.org" "labs.parabola.nu")) (setq url-user-agent (shell-command-to-string "yt-dlp --dump-user-agent 2>/dev/null")) (setq eww-download-directory (expand-file-name "~/Downloads")) (setq shr-discard-aria-hidden 't) (setq shr-max-width 179) ;; show eww using all of my screen width (setq webjump-sites '(("GNU Project FTP Archive" . [mirrors "https://ftp.gnu.org/pub/gnu/" "https://ftpmirror.gnu.org"]) ("GNU Project Website" . "www.gnu.org") ("Emacs Website" . "www.gnu.org/software/emacs/emacs.html") ("Savannah Emacs page" . "savannah.gnu.org/projects/emacs") ("Emacs Lisp List" . "www.damtp.cam.ac.uk/user/eglen/emacs/ell.html") ("Savannah" . [simple-query "git.savannah.gnu.org/cgit" "https://git.savannah.gnu.org/cgit/?q=" #1=""]) ("Emacs Wiki" . [simple-query "www.emacswiki.org" "www.emacswiki.org/cgi-bin/wiki/" #1=""]) ("DuckDuckGo" . [simple-query "duckduckgo.com" "duckduckgo.com/?q=" #1#]) ("Google Groups" . [simple-query "groups.google.com" "groups.google.com/groups?q=" #1#]) ("Yahoo" . [simple-query "www.yahoo.com" "search.yahoo.com/search?p=" #1#]) ("Yahoo: Reference" . "www.yahoo.com/Reference/") ("Qwant Lite" . [simple-query "lite.qwant.com" "https://lite.qwant.com/?q=" #1#]) ("Mojeek" . [simple-query "www.mojeek.com" "https://www.mojeek.com/search?q=" #1#]) ("Google" . [simple-query "www.google.com" "http://www.google.com/search?q=" #1#]) ("Marginalia" . [simple-query "old-search.marginalia.nu" "https://old-search.marginalia.nu/search?recent=default&profile=corpo&searchTitle=default&adtech=default&js=default&query=" #1#]) ("Wikipedia" . [simple-query "wikipedia.org" "https://en.wikipedia.org/wiki/Special:Search?go=Go&search=" #1#]) ;; ("National Weather Service" . webjump-to-iwin) ("Usenet FAQs" . "www.faqs.org/faqs/") ("RTFM Usenet FAQs by Group" . "ftp://rtfm.mit.edu/pub/usenet-by-group/") ("RTFM Usenet FAQs by Hierarchy" . "ftp://rtfm.mit.edu/pub/usenet-by-hierarchy/") ("X Consortium Archive" . "ftp.x.org") ("Association for Computing Machinery" . "www.acm.org") ("Computer Professionals for Social Responsibility" . "www.cpsr.org") ("Electronic Frontier Foundation" . "www.eff.org") ("IEEE Computer Society" . "www.computer.org") ("Risks Digest" . webjump-to-risks) ("Supplemental Web site list for webjump" . "www.neilvandyke.org/webjump/") ("!Bangs" . "https://duckduckgo.com/bang_lite.html") ("Priberam" . [simple-query "dicionario.priberam.org" "https://dicionario.priberam.org/" #1#]) ("Chambers" . [simple-query "chambers.co.uk/search/" "https://chambers.co.uk/search/?title=21st&query=" #1#]) ("Xiph" . [simple-query "dir.xiph.org" "http://dir.xiph.org/search?q=" #1#]) ("Archwiki" . [simple-query "wiki.archlinux.org" "https://wiki.archlinux.org/index.php?search=" #1#]) ("Parabolawiki" . [simple-query "wiki.parabola.nu" "https://wiki.parabola.nu/index.php?search=" #1#]) ("AUR" . [simple-query "aur.archlinux.org" "https://aur.archlinux.org/packages?O=0&K=" #1#]) ("Archlinux Packages" . [simple-query "archlinux.org/packages/" "https://archlinux.org/packages/?q=" #1#]) ("Parabola Packages" . [simple-query "www.parabola.nu/packages/" "https://www.parabola.nu/packages/?q=" #1#]) ("Artix Packages" . [simple-query "gitea.artixlinux.org" "https://gitea.artixlinux.org/packages?language=&sort=recentupdate&q=" #1#]) ("PKGBUILD Arch" . [simple-query "archlinux.org/packages/" "https://gitlab.archlinux.org/archlinux/packaging/packages/" #1="/-/raw/main/PKGBUILD"]) ("PKGBUILD Artix" . [simple-query "archlinux.org/packages/" "https://gitea.artixlinux.org/packages/" #1="/raw/branch/master/PKGBUILD"]) ("PKGBUILD AUR" . [simple-query "aur.archlinux.org" "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=" #1=""]))) (keymap-global-set "C-x w w" 'eww) (keymap-global-set "C-x w q" 'webjump) (with-eval-after-load 'eww (keymap-set eww-buffers-mode-map "k" 'eww-buffer-kill)) ;; package manager (require 'package) (with-eval-after-load 'package (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/"))) ;; unicode ;; (set-selection-coding-system 'utf-8) ;; ;;; (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) ;; (set-language-environment "UTF-8") ;; prefer utf-8 for language settings ;; (prefer-coding-system 'utf-8) ;; (set-default-coding-systems 'utf-8) ;; (setq sendmail-coding-system 'utf-8) ;; ispell (with-eval-after-load 'ispell ;; (setenv "LANG" "pt_PT.UTF-8") (setq ispell-program-name "/usr/bin/hunspell") (setq ispell-dictionary "en_GB,pt_PT") (ispell-set-spellchecker-params) (ispell-hunspell-add-multi-dic "en_GB,pt_PT") (setq ispell-personal-dictionary (expand-file-name "~/.hunspell_personal")) (setq ispell-alternate-dictionary "/usr/share/dict/british")) (keymap-global-set "C-c s" nil) (keymap-global-set "C-c s i" 'ispell-message) ;; languagetool (require 'languagetool) (setq languagetool-java-arguments '("-Dfile.encoding=UTF-8" "-cp" "/usr/share/languagetool:/usr/share/java/languagetool/*") languagetool-console-command "org.languagetool.commandline.Main" languagetool-console-arguments '("--languagemodel" "~/.local/share/languagetool/ngram/") languagetool-server-command "org.languagetool.server.HTTPServer") (defun languagetool () "Interactively check a region or buffer for spelling errors using languagetool" (interactive) (if (region-active-p) (languagetool-console-check (region-beginning) (region-end)) (languagetool-console-check (point-min) (point-max))) (languagetool-correct-buffer)) (keymap-global-set "C-c s l" 'languagetool) (keymap-global-set "C-c s q" 'languagetool-clear-suggestions) ;; time european (setq calendar-time-display-form '(24-hours ":" minutes (if time-zone " (") time-zone (if time-zone ")"))) (setq display-time-24hr-format 't) ;; display time (display-time-mode) ;; switch command mode (require 'ido) (ido-mode 't) ;; winner-mode (setq winner-dont-bind-my-keys 't) (winner-mode 1) (keymap-global-set "C-x w " 'winner-undo) (keymap-global-set "C-x w " 'winner-redo) ;; windmove (keymap-global-set "C-c " 'windmove-up) (keymap-global-set "C-c " 'windmove-left) (keymap-global-set "C-c " 'windmove-down) (keymap-global-set "C-c " 'windmove-right) ;; C language ;;; identation for C (setq-default indent-tabs-mode nil) ;; spaces instead of tabs (setq c-basic-offset 4) ;;; gdb gud (setq gdb-many-windows 't) ;; info (setq Info-hide-note-references 'hide) ;; org mode (setq org-export-with-toc nil) ;; abbrev (setq abbrev-file-name (expand-file-name "~/.emacs.d/abbrev_defs")) (setq save-abbrevs 'silently) (add-hook 'erc-mode-hook 'abbrev-mode) (add-hook 'jabber-chat-mode-hook 'abbrev-mode) (defun abbrev-save () (interactive) (write-abbrev-file (expand-file-name "~/.emacs.d/abbrev_defs")) (message "Abbreviations saved.")) (keymap-global-set "C-x a s" 'abbrev-save) (keymap-global-set "C-x a TAB" 'unexpand-abbrev) ;; ledger (setq ledger-default-commodity 'EUR) ;; gnus ;;; load secret data (load "my-secret") ;; general message config (setq message-kill-buffer-on-exit 't) ;; C-c C-c kills the buffer ;; general newgroups config (setq gnus-check-bogus-newsgroups 't) ;; delete bogus newsgroups at gnus startup (setq gnus-subscribe-newsgroup-method 'gnus-subscribe-interactively) ;;; open gnus in topics mode (add-hook 'gnus-group-mode-hook 'gnus-topic-mode) ;;; lines with time (setq gnus-summary-line-format "%U%R%z%I %4L %(%[%-25,25D %-23,23f%]%) %s ") ;;; order threads by most recent date (setq gnus-thread-sort-functions '((not gnus-thread-sort-by-number))) ;; show certain message headers (setq gnus-visible-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^[BGF]?C[Cc]:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Mail-Followup-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-From:\\|^Archived-At:\\|^List-Unsubscribe:\\|^.*Spam.*:\\|^X-.*Complain.*:\\|^X-.*Abuse.*:\\|^OpenPGP:\\|^X-.*Key.*:\\|^X-.*PGP.*:") ;; show all headers of articles in spam newsgroup (defun gnus-spam-article-show-all-headers () (interactive) (when (equal gnus-newsgroup-name "spam") (gnus-summary-show-all-headers))) (setq gnus-select-article-hook '(gnus-spam-article-show-all-headers)) ;; hide summary lines of articles that have scores lower than this (setq gnus-summary-expunge-below -9) (load "my-select-methods.el") (setq gnus-message-archive-group '((if (message-news-p) "list" "sent"))) (setq nndraft-directory (expand-file-name "~/.mail/drafts")) (setq message-directory (expand-file-name "~/.mail")) (setq gnus-directory (expand-file-name "~/.emacs.d/news")) (setq nnfolder-directory (expand-file-name "~/.mail/archive")) (setq nnfolder-active-file (expand-file-name "~/.mail/archive/active")) (setq gnus-server-alist '(("archive" nnfolder "archive" (nnfolder-directory "~/.mail/archive") (nnfolder-active-file "~/.mail/archive/active") (nnfolder-get-new-mail nil) (nnfolder-inhibit-expiry t)))) (setq mail-default-directory (expand-file-name "~/.mail")) (setq smtpmail-queue-dir (expand-file-name "~/.mail/.queued-mail")) (setq nnimap-inbox "INBOX") ;; remove my email from wide-replies (setq message-dont-reply-to-names message-alternative-emails) ;; mailing list (setq message-subscribed-address-file (expand-file-name "~/.mailinglists")) ;; (setq message-subscribed-address-functions ;; '(gnus-find-subscribed-addresses)) ;; (setq gnus-group-name-charset-group-alist '((".*" . utf-8))) ;;; mailcap (eval-after-load 'mailcap (mailcap-parse-mailcaps)) ;;; gnus ssh tilde smtp (require 'smtpmail) (defun tilde-ssh-smtpmail () (let* ((smtpmail-smtp-server "localhost") (smtpmail-smtp-service 2525) (smtpmail-stream-type nil) (smtp-local-domain "tilde.institute") (smtp-sendto-domain "tilde.institute") (sendmail (start-process "ssh-smtp" nil "/usr/bin/ssh" "-L" "2525:localhost:25" "institute" "cat")) (ready-p nil) (sendmail-filter (lambda (p s) (if (string-match "tunnel is ready" s) (setq ready-p 't) (message "Received from SSH connection: %s" s))))) (unwind-protect (progn (set-process-filter sendmail sendmail-filter) (process-send-string sendmail "tunnel is ready\n") (while (not ready-p) (accept-process-output sendmail)) (message "") (smtpmail-send-it)) (process-send-eof sendmail) ;; ask it nicely first (kill-process sendmail)))) ;; if that does nothing, inhume it forcefully (defun smtp-tilde-ssh () (setq send-mail-function 'tilde-ssh-smtpmail) (setq message-send-mail-function 'tilde-ssh-smtpmail)) (defun smtp-tilde-club () (setq send-mail-function 'smtpmail-send-it message-send-mail-function 'smtpmail-send-it smtpmail-smtp-server "smtp.tilde.club" smtpmail-smtp-service 587 smtpmail-stream-type 'starttls)) (defun smtp-yandex-native () (setq send-mail-function 'smtpmail-send-it message-send-mail-function 'smtpmail-send-it smtpmail-smtp-server "smtp.yandex.com" smtpmail-smtp-service 465 smtpmail-stream-type 'ssl)) (defun smtp-msmtp () (setq message-sendmail-f-is-evil 't) (setq message-sendmail-extra-arguments '("--read-envelope-from")) (setq message-send-mail-function 'message-send-mail-with-sendmail) (setq sendmail-program "/usr/bin/msmtp")) ;; gnus search (setq gnus-search-default-engines '((nnimap . gnus-search-imap) (nnmaildir . gnus-search-find-grep) (nnselect . gnus-search-nnselect))) ;;;; functions (defun my-email-set-field (search-tag insert-line) "Set email SEARCH-TAG header line as this INSERT-LINE." (save-excursion (goto-char (point-min)) (re-search-forward (concat "^" search-tag ":.*$") nil) (replace-match insert-line))) (defun my-email-set-fields (email name gcc) "Set From and Gcc header's fields." (my-email-set-field "From" (concat "From: " (if name name user-full-name) " <" email ">")) (my-email-set-field "Gcc" (concat "Gcc: " gcc)) ;;; set OpenPGP header and mml tag (let ((sign-p (mml-can-sign-p email))) (cond ((and (mml-secure-is-encrypted-p) sign-p) (my-message-add-openpgp-header)) (sign-p (my-mml-secure-message-sign)) ('t (my-mml-unsecure-message))))) (defun my-email-get-from-address () "Get current From email address from the email compose buffer." (let ((from (mail-fetch-field "from"))) (string-match ".*<\\(.*\\)>$" from) (match-string 1 from))) (defun message-loop-email (&optional email-config-list nth-pos) "Email loop and set through all of the EMAIL-CONFIG-LIST items." (interactive) (or email-config-list (setq email-config-list (cons (list user-mail-address user-full-name "sent") my-other-email-addresses))) (or nth-pos (setq nth-pos 0)) (let ((current-address (my-email-get-from-address)) (next-field (nth (1+ nth-pos) email-config-list))) (cond ((length= (cdr email-config-list) nth-pos) (my-email-set-fields (caar email-config-list) (cadar email-config-list) (caddar email-config-list))) ((equal current-address (car (nth nth-pos email-config-list))) (my-email-set-fields (car next-field) (cadr next-field) (caddr next-field))) ('t (message-loop-email email-config-list (1+ nth-pos)))))) (with-eval-after-load 'message (keymap-set message-mode-map "" 'message-loop-email) (keymap-set message-mode-map "" (lambda () (interactive) (message-loop-email my-mobile-email-addresses)))) (defun message-to-public-p () "Says if I am sending an email message to the public (mailinglist or netnews)." (cond ((message-news-p) 't) ((member (my-email-get-from-address) my-public-addresses) 't) ('t nil))) (defun sending-email () (let ((from-address (my-email-get-from-address))) (cond ((equal from-address user-mail-address-tilde) (smtp-tilde-club)) ((member from-address (mapcar #'car my-mobile-email-addresses)) (smtp-yandex-native)) ('t (smtp-msmtp))))) (add-hook 'message-send-hook 'sending-email) ;;; mml-attach-file goes to the end of the message (setq mml-attach-file-at-the-end 't) ;;; gnus pgp (setq mml-secure-openpgp-signers nil ;; we want to be able to read the emails we wrote. mml-secure-openpgp-encrypt-to-self 't ;; mml choose correct key for signing/ciphering mml-secure-openpgp-sign-with-sender 't ;; always verify signatures mm-verify-option 'always ;; show result of pgp verification gnus-buttonized-mime-types '("multipart/signed" "multipart/encrypted" "application/pgp-keys") mm-discouraged-alternatives '("image/.*")) (setq gnus-message-replysign 't) (setq gnus-message-replyencrypt 't) (setq gnus-message-replysignencrypted 't) (keymap-global-set "C-c ESC k" 'epa-list-keys) ;;; enable epa-mail-mode (add-hook 'message-setup-hook 'epa-mail-mode) (defun pgp-keyid (email) "Get PGP keyid (short format without \"0x\") of EMAIL. Return NIL if there is no keyid for EMAIL." (let ((keyid (string-trim-right (shell-command-to-string (concat "keyid " email))))) (cond ((string-empty-p keyid) nil) ('t keyid)))) (defun mml-can-sign-p (email) "Say whether if message can be signed." (cond ((message-to-public-p) nil) ((pgp-keyid email) 't))) (defun mml-secure-tag-p () "Say whether the <#secure tag is present anywhere in the buffer." (save-excursion (goto-char (point-min)) (message-goto-body) (re-search-forward "<#secure .*>$" nil 't))) ;; openpgp header (defun my-message-add-openpgp-header (&optional email-address) "Set the openpgp message header." (mail-fetch-field "openpgp" nil nil nil 't) (let* ((my-email-address (or email-address (my-email-get-from-address))) (pgp-keyid (pgp-keyid my-email-address)) (pgp-keyurl (cond ((equal my-email-address user-mail-address-tilde) my-wkd-tilde-url) ('t (string-trim-right (shell-command-to-string (concat "gpg-wks-client --print-wkd-url " my-email-address)))))) (message-openpgp-header (list (concat "0x" pgp-keyid) pgp-keyurl "signencrypt"))) (when (mml-can-sign-p my-email-address) (message-add-openpgp-header)))) ;;(add-hook 'message-header-setup-hook 'my-message-add-openpgp-header) (defun my-mml-unsecure-message () "My remove security related MML tags from message." (interactive) (mail-fetch-field "openpgp" nil nil nil 't) (mml-unsecure-message)) ;;; attempt to sign all non-news mails I'll be sending. (defun my-mml-secure-message-sign (&optional prefix email-address) "Attempt to set MML signing message tag." (interactive "p") (let* ((my-email-address (or email-address (my-email-get-from-address))) (pgp-keyid (pgp-keyid my-email-address))) (when (mml-can-sign-p my-email-address) (my-message-add-openpgp-header) (cond ((eql prefix 4) (mml-secure-message-sign-pgp)) ('t (mml-secure-message-sign-pgpmime)))))) ;;; gnus pgp keybindings (defun my-mml-secure-message-sign-encrypt (&optional prefix email-address) "Add MML tag to encrypt and sign the entire message." (interactive "p") (let* ((my-email-address (or email-address (my-email-get-from-address))) (pgp-keyid (pgp-keyid my-email-address))) (when (mml-can-sign-p my-email-address) (my-message-add-openpgp-header) (cond ((eql prefix 4) (mml-secure-message-encrypt-pgp)) ('t (mml-secure-message-encrypt-pgpmime))) (my-email-set-field "Gcc" "Gcc: crypto-sent")))) (add-hook 'message-setup-hook 'my-mml-secure-message-sign) ;; needs the below hook to properly place the mml secure tag (add-hook 'gnus-message-setup-hook 'my-mml-secure-message-sign) (with-eval-after-load 'message (keymap-set mml-mode-map "C-c RET C-n" 'my-mml-unsecure-message) (keymap-set mml-mode-map "C-c RET C-s" 'my-mml-secure-message-sign) (keymap-set mml-mode-map "C-c RET C-e" 'my-mml-secure-message-sign-encrypt)) ;;; insert a GDPR notice (defun message-insert-gdpr-notice () (if (and (not (message-to-public-p)) (not (mml-secure-is-encrypted-p))) (save-excursion (goto-char (point-max)) (when (re-search-backward message-signature-separator nil t) (forward-line -1) (move-end-of-line nil)) (when (not (current-line-empty-p)) (insert "\n")) (insert "\n\n") (insert-file-contents (expand-file-name "~/.europe-gdpr"))))) ;; cannot be message-send-mail-hook . It is too late. (add-hook 'message-send-hook 'message-insert-gdpr-notice -90) ;;; gnus activate yubikey on sending (defun yubikey-ask () (when 't (eval-expression ;; the below line is a fix of (shell-command-to-string command) (with-output-to-string (with-current-buffer standard-output (async-shell-command (concat "echo hello | gpg -e -r " my-secure-keyid " | gpg -d &> /dev/null") "*gpg*"))))) 't) ;; cannot be message-send-mail-hook (add-hook 'message-send-hook 'yubikey-ask -10) (defun yubikey-pin () (interactive) (yubikey-ask) (switch-to-buffer-other-window "*gpg*")) (defalias 'y 'yubikey-pin) ;; gnus mail other people certificate PEM certificates (setq smime-certificate-directory (expand-file-name "~/.certs/mail")) ;;; gnus rss (setq nnrss-directory (expand-file-name "~/.emacs.d/gnus/rss")) ;;; gnus in M-x mail (setq mail-user-agent 'gnus-user-agent) ;;; read mail in Gnus (setq read-mail-command 'gnus) ;;; gnus atom feeds (require 'mm-url) (defadvice mm-url-insert (after DE-convert-atom-to-rss () ) "Converts atom to RSS by calling xsltproc." (when (re-search-forward "xmlns=\"http://www.w3.org/.*/Atom\"" nil 't) (goto-char (point-min)) (message "Converting Atom to RSS... ") (call-process-region (point-min) (point-max) "xsltproc" 't 't nil (expand-file-name "~/.emacs.d/gnus/atom2rss.xsl") "-") (goto-char (point-min)) (message "Converting Atom to RSS... done"))) (ad-activate 'mm-url-insert) ;;; gnus shortcut (defun my-gnus () "My way of opening Gnus." (interactive) (tab-bar-new-tab-to 1) (tab-rename "gnus") (gnus)) (keymap-global-set "C-x g g" 'my-gnus) ;; gpg pinentry ;;; trying to setup the more secure loopback with pinentry on Emacs. Unsuccessful. ;; (setq epg-pinentry-mode 'loopback) (when (and (not (bound-and-true-p pinentry--server-process)) (equal (getenv "GPG_TTY") "/dev/tty1")) (pinentry-start)) (defun pinentry-restart () (interactive) (shell-command "gpg-reload") (pinentry-stop) (pinentry-start)) (defalias 'pr 'pinentry-restart) (keymap-global-set "C-x g r" 'pinentry-restart) ;; get mail function (defun fetch-mail () (interactive) (eval-expression ;; the below line is a fix of (shell-command-to-string command) (with-output-to-string (with-current-buffer standard-output ;; (async-shell-command "mpop") (async-shell-command "fdm fetch")) )) (switch-to-buffer-other-window "*Async Shell Command*")) (keymap-global-set "C-x g f" 'fetch-mail) ;; tramp ;; (customize-set-variable 'tramp-default-method "ssh") (setq ido-work-directory-list-ignore-regexps '("/ssh:" "/-:")) ;; footnotes (autoload 'footnote-mode "footnote" nil 't) (setq footnote-section-tag "" footnote-spaced-footnotes nil) (require 'footnote) (add-hook 'message-mode-hook 'footnote-mode) (setq footnote-index-starting-number 0) (with-eval-after-load 'footnote (defun footnote--insert-footnote (arg) "Insert a footnote numbered ARG, at (point)." (push-mark) (let ((ptr (footnote--insert-numbered-footnote arg 't))) (footnote--goto-char-point-max) (if (footnote--goto-first) (save-restriction (when footnote-narrow-to-footnotes-when-editing (footnote--narrow-to-footnotes)) (footnote-goto-footnote (1- arg)) ; evil, FIXME (less evil now) ;; (message "Inserting footnote %d" arg) (or (eq arg footnote-index-starting-number) (when (re-search-forward (if footnote-spaced-footnotes "\n\n" (concat "\n" (footnote--current-regexp))) nil 't) (beginning-of-line) 't) (footnote--goto-char-point-max) (footnote--goto-first))) (unless (looking-at "^$") (insert "\n")) (when (eobp) (insert "\n")) (unless (equal footnote-section-tag "") (insert footnote-section-tag "\n"))) (let ((text (footnote--insert-numbered-footnote arg nil))) (footnote--insert-markers arg text ptr)))) (defun footnote--make-hole () "Make room in the alist for a new footnote at point. Return the footnote number to use." (save-excursion (let (rc) (dolist (alist-elem footnote--markers-alist) (when (<= (point) (caddr alist-elem)) (unless rc (setq rc (car alist-elem))) (save-excursion (message "Renumbering from %s to %s" (footnote--index-to-string (car alist-elem)) (footnote--index-to-string (1+ (car alist-elem)))) (footnote--renumber (1+ (car alist-elem)) alist-elem)))) (or rc (1+ (or (caar (last footnote--markers-alist)) (1- footnote-index-starting-number))))))) (defun footnote-renumber-footnotes () "Renumber footnotes, starting from `footnote-index-starting-number'." (interactive "*") (save-excursion (let ((i footnote-index-starting-number)) (dolist (alist-elem footnote--markers-alist) (footnote--renumber i alist-elem) (setq i (1+ i))))))) ;; ebdb (require 'ebdb-gnus) (require 'ebdb-message) (setq ebdb-record-self "36b79c52-1bbf-432a-9c80-c84e056fbcd1") ;; managesieve (autoload 'sieve-mode "sieve-mode") (setq auto-mode-alist (cons '("\\.s\\(v\\|iv\\|ieve\\)\\'" . sieve-mode) auto-mode-alist)) ;; erc (setq erc-user-full-name user-full-name) (setq erc-session-client-certificate 't) (setq erc-track-enable-keybindings nil) (setq erc-track-exclude-server-buffer 't) (setq erc-hide-list '("PART" "QUIT")) ;; hide irc part and quit messages (join allowed) (setq erc-kill-buffer-on-part 't) ;;; erc modules (setq erc-modules '(log autoaway netsplit fill button match track completion readonly networks ring noncommands irccontrols move-to-prompt stamp menu list)) ;;; prevent bot functions from displaying text of ignored nicks ;; (defun erc-bot-messages-ignore (string) ;; (when ;; (and ;; (or ;; (string-match ;; "^<[][:alnum:]`|^_{}[-]+> \\[Sed\\] <\\([][:alnum:]`|^_{}[-]+\\)> .*" ;; string) ;; (string-match ;; "^<[][:alnum:]`|^_{}[-]+> \\[Karma\\] .+ now has [0-9]+ karma ([0-9]+ from \\([][:alnum:]`|^_{}[-]+\\))" ;; string) ;; (string-match ;; "^<[][:alnum:]`|^_{}[-]+> \\([][:alnum:]`|^_{}[-]+\\): <[][:alnum:]`|^_{}[-]+> .*" ;; string)) ;; (member (match-string 1 string) erc-ignore-list)) ;; (setq erc-insert-this nil))) ;; (add-hook 'erc-insert-pre-hook 'erc-bot-messages-ignore) ;;; erc auto-away (setq erc-auto-set-away 't erc-autoaway-use-emacs-idle 't erc-auto-discard-away 't erc-autoaway-idle-seconds 210 erc-autoaway-message "...") ;;; erc logging (setq erc-log-channels-directory (expand-file-name "~/log")) ;;; erc custom function ;;;; voice and devoice nick(s) (defun erc-cmd-VOICE (&rest people) "Add the voice mode to users(s) given in PEOPLE." (when (> (length people) 0) (erc-server-send (concat "MODE " (erc-default-target) " +" (make-string (length people) ?v) " " (mapconcat #'identity people " "))) 't)) (defun erc-cmd-DEVOICE (&rest people) "Remove the voice setting from user(s) given in PEOPLE." (when (> (length people) 0) (erc-server-send (concat "MODE " (erc-default-target) " -" (make-string (length people) ?v) " " (mapconcat #'identity people " "))) 't)) ;;;; query nick (defun erc-query-channel-nick (nick) "Query a nick of the current channel." (interactive (list (completing-read "Nick: " erc-channel-users))) (erc-cmd-QUERY nick)) (with-eval-after-load 'erc (keymap-set erc-mode-map "C-c q" 'erc-query-channel-nick)) ;;;; always send a final point at the end (defun erc-insert-final-point (input-struct) (let* ((input-string (cl-struct-slot-value 'erc-input 'string input-struct)) (last-char (string-to-char (substring input-string -1))) (first-char (string-to-char (substring input-string 0 1)))) (cond ((or (eq last-char ?.) (eq last-char ?!) (eq last-char ??) (eq last-char ?,) (eq last-char ?\;) (eq last-char ?:) (eq first-char ?,) (eq first-char ?/) (string-match "^s/" input-string)) 't) ((string-match " $" input-string) (setf (cl-struct-slot-value 'erc-input 'string input-struct) (substring input-string 0 -2))) ((or ;; smiley (string-match "[:;%8>xX]\\{1\\}[`',]?-?[)(/\\ZzXxDPpSsbO]\\{1,4\\}$" input-string) ;; URLs (string-match "\\(?:http\\|ftp\\)s?://.+?[^ )]+$" input-string) ;; other (eq last-char ?^)) (setf (cl-struct-slot-value 'erc-input 'string input-struct) (concat input-string " ."))) ('t (setf (cl-struct-slot-value 'erc-input 'string input-struct) (concat input-string ".")))))) (add-hook 'erc-pre-send-functions 'erc-insert-final-point) ;;;; fix the double correction issue (defun my-erc-send-current-line () "Parse current line and send it to IRC." (interactive) (let ((now (current-time))) (if (or (not erc-accidental-paste-threshold-seconds) (time-less-p erc-accidental-paste-threshold-seconds (time-subtract now erc-last-input-time))) (save-restriction (widen) (if-let* ((str (erc-user-input)) (msg (run-hook-with-args-until-success 'erc--check-prompt-input-functions str (split-string str erc--input-line-delim-regexp)))) (when (stringp msg) (erc-error msg)) (let ((inhibit-read-only 't) (old-buf (current-buffer))) (progn ; unprogn this during next major surgery (erc-set-active-buffer (current-buffer)) ;; Kill the input and the prompt (delete-region (erc-beg-of-input-line) (erc-end-of-input-line)) (unwind-protect (erc-send-input str 'skip-ws-chk) ;; Fix the buffer if the command didn't kill it (when (buffer-live-p old-buf) (with-current-buffer old-buf (save-restriction (widen) (goto-char (point-max)) (when (processp erc-server-process) (set-marker (process-mark erc-server-process) (point))) (set-marker erc-insert-marker (point)) (let ((buffer-modified (buffer-modified-p))) (erc-display-prompt) (set-buffer-modified-p buffer-modified)))))) ;; Only when last hook has been run... (run-hook-with-args 'erc-send-completed-hook str))) (setq erc-last-input-time now))) (switch-to-buffer "*ERC Accidental Paste Overflow*") (lwarn 'erc :warning "You seem to have accidentally pasted some text!")))) (with-eval-after-load 'erc (keymap-set erc-mode-map "RET" 'my-erc-send-current-line)) ;;; other erc functions (load "my-common-system") ;; (load "my-quack-system") (load "my-erc-coins-system") (load "my-weri") (defun erc-rejoin-channel () "Rejoin current IRC channel." (interactive) (erc-join-channel (erc-channel-name (buffer-name)) nil)) (with-eval-after-load 'erc (keymap-set erc-mode-map "C-c j" 'erc-rejoin-channel)) ;;;; newnet windows setup (defun newnet-set-windows-up (&optional server nick) (interactive) (defun my-switch-to-buffer (buffer) (when (bufferp (get-buffer buffer)) (switch-to-buffer buffer))) (run-with-timer 1 nil `(lambda () (when (and (or (null ,server) (string-search "newnet.net" ,server)) (equal (cdadr (tab-bar--current-tab)) "newnet")) (delete-other-windows) (my-switch-to-buffer "#meta") (my-split-window-right) (my-switch-to-buffer "#lisp") (my-split-window-below) (my-switch-to-buffer "#club") (other-window 1) ;; (my-split-window-below) ;; (my-switch-to-buffer "#bots") ;; (windmove-up) (balance-windows))))) (add-hook 'erc-after-connect 'newnet-set-windows-up) ;; jabber (require 'jabber) ;;; disable roster keybindings menu (setq jabber-roster-show-bindings nil) ;;; make fsm output debug buffer disappear (setq fsm-debug nil) ;; (add-to-list 'load-path (expand-file-name "~/me/programming/desktop/emacs-jabber/lisp/")) ;; (add-to-list 'load-path (expand-file-name "~/me/programming/desktop/emacs-jabber/")) ;; (load (expand-file-name "~/me/programming/desktop/emacs-jabber/jabber-autoloads")) ;;; upload files (M-x jabber-httpupload-send-file) (require 'jabber-httpupload) (with-eval-after-load 'jabber-muc (keymap-set jabber-chat-mode-map "C-c C-n" 'jabber-muc-names) (keymap-set jabber-chat-mode-map "C-c C-t" 'jabber-muc-set-topic) (keymap-set jabber-chat-mode-map "C-c C-p" 'jabber-muc-leave) (keymap-set jabber-chat-mode-map "C-c q" 'jabber-muc-private)) (with-eval-after-load 'jabber-httpupload (keymap-set jabber-chat-mode-map "C-c a" 'jabber-httpupload-send-file)) ;; (unless (package-installed-p 'jabber) ;; (require 'package-vc) ;; (add-to-list 'package-vc-selected-packages ;; '(jabber ;; :url "https://codeberg.org/emacs-jabber/emacs-jabber" ;; :branch "production" ;; :lisp-dir "lisp" ;; :doc "README.org")) ;; ;; Change the path below to the location of your local jabber.el repository. ;; (package-vc-install-from-checkout (expand-file-name "~/me/programming/desktop/emacs-jabber/") "jabber")) (defun my-jabber () "My way of opening jabber.el." (interactive) (tab-bar-new-tab-to 2) (tab-rename "xmpp") (jabber-connect-all) (run-with-timer 5 0 (lambda () (jabber-switch-to-roster-buffer) (my-split-window-below) (jabber-activity-switch-to) (windmove-up)))) (keymap-global-set "C-x g j" 'my-jabber) ;; emms (require 'emms-info-exiftool) ;; metadata (emms-all) (setq emms-player-list '(emms-player-vlc emms-player-alsaplayer)) ;; need pacman -S perl-image-exiftool below (setq emms-info-functions '(emms-info-exiftool emms-info-metaflac emms-info-native)) (setq emms-source-file-default-directory (expand-file-name "~/me/files/audio/music")) (setq emms-volume-change-function 'emms-volume-pulse-change) (defun pulseaudio-sinks () (let ((output (shell-command-to-string "pactl list short sinks")) (last-match-index 0) (result nil)) (while (setq last-match-index (and (string-match "^\\([0-9]+\\)" output last-match-index) (1+ (string-match "^\\([0-9]+\\)" output last-match-index)))) (push (string-to-number (match-string 1 output)) result)) (reverse result))) ;;; music playing (defun my-play-music (dir message) "Play my music." (interactive) (emms-play-m3u-playlist (expand-file-name dir)) (emms-playlist-sort-by-random) (emms-playlist-sort-by-random) (emms-random) (emms-playlist-sort-by-random) (message message)) (defun chill () "Play my Chillhop music." (interactive) (my-play-music "~/me/files/audio/playlist/chillhop.m3u" "Sit back and Chill...")) (defun classical () "Play my Classical music." (interactive) (my-play-music "~/me/files/audio/playlist/classical.m3u" "Enjoy the art...")) (defun emms-insert-show () "Insert playing music into buffer." (interactive) (insert (emms-show))) ;;;; volume mode timeout (setq emms-volume-mode-timeout 6) ;;;; emms keybindings (keymap-global-set "C-x p +" 'emms-volume-mode-plus) (keymap-global-set "C-x p -" 'emms-volume-mode-minus) (keymap-global-set "C-x p " 'chill) (keymap-global-set "C-x p " 'emms-stop) (keymap-global-set "C-x p " 'emms-next) (keymap-global-set "C-x p " 'emms-previous) (keymap-global-set "C-x p SPC" 'emms-pause) (keymap-global-set "C-x p r" 'emms-random) (keymap-global-set "C-x p t" 'emms-toggle-random-playlist) (keymap-global-set "C-x p ." 'emms-show) (keymap-global-set "C-x p i" 'emms-insert-show) ;;; set audio output (defun speakers-set (number) "Set audio speakers with a sink NUMBER." (interactive "NSink number: ") (shell-command (concat "pactl set-default-sink " (number-to-string number))) (setq emms-volume-pulse-sink number)) (defun speakers-set0 () "Set audio speakers as the first sink number." (interactive) (speakers-set (car (pulseaudio-sinks))) (message "Speakers output set.")) (defun speakers-set1 () "Set audio speakers as second sink number." (interactive) (speakers-set (cadr (pulseaudio-sinks))) (message "Headphones output set.")) (defun mic-mute () "Mute default mic." (interactive) (shell-command (concat "pactl set-source-mute " (string-trim-right (shell-command-to-string "pactl get-default-source")) " 1")) (message "Mic muted.")) (defun mic-unmute () "Unmute default mic." (interactive) (shell-command (concat "pactl set-source-mute " (string-trim-right (shell-command-to-string "pactl get-default-source")) " 0")) (message "Mic unmuted.")) (keymap-global-set "C-x g m" 'mic-mute) (keymap-global-set "C-x g u" 'mic-unmute) ;; calendar (defun my-calendar-exit () (interactive) (calendar-exit 't)) (keymap-set calendar-mode-map "q" 'my-calendar-exit) ;;; holidays ;;;; disable following holidays (setq holiday-hebrew-holidays nil holiday-islamic-holidays nil holiday-bahai-holidays nil holiday-oriental-holidays nil) ;;;; disable some (setq holiday-general-holidays '((holiday-fixed 1 1 "New Year's Day") (holiday-fixed 2 14 "Valentine's Day") (holiday-fixed 3 17 "St. Patrick's Day") (holiday-fixed 4 1 "April Fools' Day"))) ;;;; portugal holidays (national and religious) (setq holiday-portugal-holidays '((holiday-easter-etc -47 "Carnaval") (holiday-fixed 3 19 "Dia do Pai") (holiday-easter-etc -2 "Sexta-feira Santa") (holiday-fixed 4 25 "Dia da Liberdade") (holiday-fixed 5 1 "Dia do Trabalhador") (holiday-float 5 0 1 "Dia da Mãe") (holiday-easter-etc 60 "Corpo de Deus") (holiday-fixed 6 10 "Dia de Portugal") (holiday-fixed 8 15 "Assunção de Nossa Senhora") (holiday-fixed 10 5 "Implantação da República") (holiday-fixed 11 1 "Todos os Santos") (holiday-fixed 12 1 "Restauração da Independência") (holiday-fixed 12 8 "Imaculada Conceição"))) ;;;; fun days (setq holiday-fun-days '((holiday-fixed 2 7 "Dia de Enviar um Postal a um/a Amigo/a") (holiday-float 2 1 2 "Dia de Limpar o teu Computador") (holiday-fixed 2 22 "Dia de Single Tasking") (holiday-fixed 3 1 "Dia Mundial do Elogio") (holiday-fixed 3 3 "Dia de Eu Quero que Sejas Feliz") (holiday-fixed 3 14 "Dia do PI") (holiday-fixed 3 19 "Dia de Vamos Rir") (holiday-fixed 3 25 "Dia do Waffle") (holiday-fixed 4 23 "Dia do/a Amante") (holiday-fixed 5 6 "Dia da Bebida") (holiday-fixed 5 9 "Dia da Europa") (holiday-fixed 5 28 "Dia da Hamburga") (holiday-fixed 6 4 "Dia de Abraçar o Teu Gato/a") (holiday-fixed 6 17 "Dia de Comer os Teus Vegetais") (holiday-fixed 7 12 "Dia da Simplicidade") (holiday-float 7 0 3 "Dia do Gelado") (holiday-fixed 7 24 "Dia do/a Primo/a") (holiday-fixed 7 26 "Dia do/a Tia/o") (holiday-fixed 7 29 "Dia da Lasanha") (holiday-float 8 5 1 "Dia Internacional da Cerveja") (holiday-float 8 0 1 "Dia da Irmã") (holiday-fixed 8 9 "Dia do/a Amante de Livros") (holiday-fixed 8 11 "Dia do/a Filho/a") (holiday-fixed 8 15 "Dia do Relaxamento") (holiday-fixed 9 5 "Dia da Pizza de Queijo") (holiday-fixed 9 6 "Dia de Ler um Livro") (holiday-float 10 1 3 "Dia Nacional de Limpar o Desktop Virtual") (holiday-fixed 10 27 "Dia da Cerveja Americana") (holiday-fixed 10 29 "Dia da Internet") (holiday-fixed 11 1 "Dia do/a Autor(a)") (holiday-float 11 4 1 "Dia de Homens Fazem o Jantar") (holiday-fixed 11 21 "Dia Mundial do Olá") (holiday-fixed 11 29 "Dia do Cartão Eletrónico") (holiday-fixed 12 9 "Dia do Cartão de Natal") (holiday-fixed 12 20 "Dia da Sangria"))) ;;;; United Nations days (setq holiday-un-days '((holiday-fixed 1 24 "International Day of Education") (holiday-fixed 3 5 "International Day for Disarmament and Non-Proliferation Awareness") (holiday-fixed 3 20 "International Day of Happiness") (holiday-fixed 3 30 "International Day of Zero Waste") (holiday-fixed 4 5 "International Day of Conscience") (holiday-fixed 4 23 "World Book and Copyright Day") (holiday-fixed 5 5 "World Portuguese Language Day") (holiday-fixed 5 16 "International Day of Living Together in Peace") (holiday-fixed 5 21 "International Tea Day") (holiday-fixed 5 31 "World No-Tobacco Day") (holiday-fixed 6 21 "International Day of Yoga") (holiday-fixed 7 30 "International Day of Friendship") (holiday-fixed 9 15 "International Day of Democracy") (holiday-fixed 9 21 "International Day of Peace") (holiday-fixed 9 28 "International Day for Universal Access to Information") (holiday-fixed 10 2 "International Day of Non-Violence") (holiday-fixed 10 10 "World Mental Health Day") (holiday-fixed 10 16 "World Food Day") (holiday-fixed 12 10 "Human Rights Day") (holiday-fixed 12 12 "International Day of Neutrality"))) (setq holiday-other-holidays (append holiday-portugal-holidays holiday-un-days holiday-fun-days)) (setq calendar-mark-holidays-flag 't) ;; enable holidays in calendar (setq calendar-date-style 'european) ;; diary (setq calendar-mark-diary-entries-flag 't) ;; enable diary in calendar (setq diary-comment-start ";") ;; set comments for diary files ;;; enable include facility in diary files (add-hook 'diary-list-entries-hook 'diary-include-other-diary-files) (add-hook 'diary-mark-entries-hook 'diary-mark-included-diary-files) ;;; cal function (defun cal () (interactive) (calendar) (kill-buffer "diary")) (keymap-global-set "C-x c" 'cal) (defun today () (interactive) (shell-command "date")) (keymap-global-set "C-x M-t" 'today) ;; remember (setq remember-notes-initial-major-mode 'text-mode) ;; po (autoload 'po-mode "po-mode" "Major mode for translators to edit PO files" 't) (setq auto-mode-alist (cons '("\\.po\\'\\|\\.po\\." . po-mode) auto-mode-alist)) (require 'po) (require 'po-mode) (autoload 'po-find-file-coding-system "po-compat") (modify-coding-system-alist 'file "\\.po\\'\\|\\.po\\." 'po-find-file-coding-system) ;;; autofill untranslated with msgid (setq po-auto-edit-with-msgid 't) ;;; po file preprocessing (defun po-update-this-file () (interactive) (shell-command "po-update") nil) (add-hook 'po-mode-hook 'po-update-this-file) (with-eval-after-load 'po (defun po-replace-project-id-version () "Update the Project-Id-Version in the PO file header." (save-excursion (goto-char (point-min)) (if (re-search-forward "^\"Project-Id-Version:.*" nil 't) (let* ((buffer-read-only po-read-only)) (replace-match (concat "\"Project-Id-Version: " (shell-command-to-string "version") "\\n\"") 't 't)))) ;; Return nil to indicate that the buffer has not yet been saved. nil) (add-hook 'po-mode-hook 'po-replace-project-id-version)) ;;; substitution (defun subst-string (data-var) (mapc (lambda (list-pair) (goto-char (point-min)) ;; (replace-string (car list-pair) (cdr list-pair)) (search-forward (car list-pair)) (replace-match (cdr list-pair))) data-var) (goto-char (point-max))) (defun subst-bing () (interactive) (subst-string subst-data-bing)) (setq subst-data-bing '(("senha" . "frase-secreta") ("frase secreta" . "frase-secreta") ("frases secretas" . "frases-secretas") ("SSH" . "ssh") ("o cache" . "a cache") ("um cache" . "uma cache") ("não alfa" . "não alfabéticos") ("mouse" . "rato") ("soquete" . "socket") ("agente GPG" . "gpg-agent") ("agente gpg" . "gpg-agent") ("escutando" . "ouvindo") ("o diretório" . "a pasta") ("diretório" . "pasta") ("diretoria" . "pasta") ("manipulador" . "handler") ("arquivo" . "ficheiro") ("do usuário" . "de utilizador") ("usuário" . "utilizador") ("placas" . "cartões") ("placa" . "cartão") ("não." . "nº") ("cadeia de caracteres" . "string") ("criptografia" . "cifragem") ("cifragem" . "cifração") ("encripta" . "cifra") ("desencripta" . "decifra") ("administrador" . "Admin") ("modo de lote" . "modo batch") ("excluí" . "apaga") ("exclui" . "apaga") ("somente" . "só") ("(s/n)" . "(s/N)") ("e-mail" . "email") ("e armadura" . "a blindagem") ("armadura" . "blindagem") ("compactaç" . "compress") ("propriedade" . "ownership") ("listagens de assinatura" . "listagens de assinaturas") ("autoassina" . "auto-assina") ("tecla" . "chave") ("conjunto de chaves" . "porta-chaves") ("chaveiro" . "porta-chaves") ("agente" . "agent") ("mescla" . "fundi") ("detect" . "detet") ("insir" . "introduz") ("digite" . "introduza") ("cartão inteligente" . "smartcard") ("Você realmente quer" . "De certeza que você deseja") ("Você realmente" . "De certeza que você") ("você realmente quer" . "de certeza que você deseja") ("você realmente" . "de certeza que você") ("um banco de dados" . "uma base de dados") ("o banco de dados" . "a base de dados") ("Realmente" . "De certeza que deseja") ("primária" . "principal") ("primário" . "principal") ("o ID" . "a ID") ("um ID" . "uma ID") ("um documento de identificação com foto" . "uma ID fotográfica") ("o documento de identificação com foto" . "a ID fotográfica") ("portanto" . "por isso") ("resumo" . "digest") ("descriptograf" . "decifr") ("preterid" . "depreciad") ("acessar" . "aceder") ("essa" . "esta") ("esse" . "este") ("Observação:" . "Nota:") ("cadeia" . "corrente") ("assunto" . "entidade") ("gravar" . "escrever") ("registr" . "regist") ("carimbo de data/hora" . "timestamp") ("expressão S" . "S-expression") ("banco" . "base") ("sinalizador" . "flag") ("solicitação" . "pedido") ("ruim" . "inválido") ("incorret" . "inválid") ("incorrect" . "inválid") ("geral" . "genérico") ("decodificação" . "descodificação"))) (defun po-mark-whole-subedit-buffer () (interactive) (goto-char (point-min)) (push-mark (1- (point-max)) nil 't)) ;;; send email (setq po-default-commit-message "po: Update Portuguese Translation.") (defun po-git-commit (msg) "My function to git commit before sending a patch." (shell-command "rm 0001-*.patch") (shell-command (concat "git add " (file-name-nondirectory buffer-file-name))) (shell-command (concat "git commit " (when (yes-or-no-p "Amend commit? ") "--amend ") (when (yes-or-no-p "Signoff commit? ") "-s ") "-m \"" msg "\"")) (shell-command "git format-patch -k HEAD~1")) (defun po-send-address () (let ((gnupg "GnuPG , Werner Koch ")) (cond ((or (string-search "/gnupg/gnupg/" default-directory) (string-search "/gnupg/1-libgpg-error/" default-directory)) gnupg) ('t (po-guess-team-address))))) (defun my-po-send-mail () "My function to start composing a letter, and send git PO patch file." (interactive) (shell-command (concat "po-prod " (file-name-nondirectory buffer-file-name))) (let ((string-msg (read-string "Commit message: " po-default-commit-message)) (user-mail-address user-mail-address-translator)) (po-git-commit string-msg) (compose-mail-other-window (po-send-address) string-msg nil) (my-message-add-openpgp-header user-mail-address)) (message-goto-body) (insert "\n\n") (mml-attach-file (shell-command-to-string "printf ${PWD}/0001-*.patch") "text/x-patch" (concat (shell-command-to-string "printf $(version | awk 'NF{NF-=1};1')") " patch.")) (message-goto-body) (open-line 2) (my-mml-secure-message-sign) (message "You may start writing the email...")) (with-eval-after-load 'po-mode (keymap-set po-subedit-mode-map "C-x h" 'po-mark-whole-subedit-buffer) (keymap-set po-subedit-mode-map "" 'subst-bing) (keymap-set po-subedit-mode-map "C-c s" nil) (keymap-set po-subedit-mode-map "C-c s i" 'ispell-message) (keymap-set po-mode-map "M" 'my-po-send-mail)) ;; go-translate (require 'go-translate) (setq gt-langs '(pt en)) (setq gt-preset-translators `((normal . ,(gt-translator :taker (gt-taker :text 'word :pick 'paragraph :prompt nil) :engines (gt-libre-engine) :render (gt-buffer-render))) (po . ,(gt-translator :taker (gt-taker :text 'word :pick 'paragraph :prompt nil) :engines (gt-bing-engine) :render (gt-insert-render :type 'replace :then (lambda (&rest rest) (run-with-timer 0.5 0 'subst-bing))))))) (keymap-global-set "C-c t" 'gt-do-translate) (keymap-global-set "C-c T" 'gt-do-setup) ;;; automatically translate only untranslated entries (with-eval-after-load 'po (defun po-translate () (po-mark-whole-subedit-buffer) (gt-do-translate)) (defun my-po-edit-msgstr (&optional no-translation) "Use another window to edit the current msgstr." (interactive "p") (po-find-span-of-entry) (defvar current-po-entry-type po-entry-type) ;; added this line (defvar current-po-translate-p (eq 1 no-translation)) ;; added this line (po-edit-string (if (and po-auto-edit-with-msgid (eq po-entry-type 'untranslated)) (po-get-msgid) (po-get-msgstr-form)) 'msgstr 't)) (keymap-set po-mode-map "C-m" 'my-po-edit-msgstr) (defun po-translate-untranslated () (when (and current-po-translate-p (eq current-po-entry-type 'untranslated)) (po-translate))) (add-hook 'po-subedit-mode-hook 'po-translate-untranslated)) ;; emerge (setq emerge-combine-versions-template "%a%b") ;; expand-region (keymap-global-set "C-c C-SPC" 'er/expand-region) (keymap-global-set "C-c DEL" 'er/contract-region) ;; dictd (setq dictionary-server "localhost") (keymap-global-set "C-c d" 'dictionary-search) ;; auto-capitalization (with-eval-after-load 'captain (defun captain--default-predicate () "The default predicate for determining whether the captain should work." 't)) ;; (defun auto-capitalization-total () ;; (defvar captain-predicate (lambda () 't))) ;; (add-hook 'erc-mode-hook 'auto-capitalization-total) ;; (add-hook 'jabber-chat-mode-hook 'auto-capitalization-total) ;; (add-hook 'message-mode-hook 'auto-capitalization-total) (add-hook 'erc-mode-hook 'captain-mode) (add-hook 'jabber-chat-mode-hook 'captain-mode) (add-hook 'message-mode-hook 'captain-mode) (defun fancy-mode () "Set all the fancy modes on a buffer." (interactive) (auto-fill-mode) (abbrev-mode) (captain-mode) (defvar captain-predicate (lambda () 't))) (defun battery () "Get the percentage of battery charge." (interactive) (message "%d" (/ (* (string-to-number (shell-command-to-string "cat /sys/class/power_supply/BAT1/charge_now")) 100) (string-to-number (shell-command-to-string "cat /sys/class/power_supply/BAT1/charge_full"))))) ;; other keybindings (keymap-global-set "C-x t t" 'tab-bar-mode) (keymap-global-set "C-x 7" 'flop-frame) (keymap-global-set "C-x 8" 'flip-frame) (keymap-global-set "C-x 9" 'transpose-frame) (keymap-global-set "C-x M-s" 'scratch-buffer) ;; other aliases (defalias 'u 'uncomment-region) (defalias 'sh 'shell) ;; EXWM (require 'exwm) ;;; Set the initial workspace number. (setq exwm-workspace-number 4) ;;; Make class name the buffer name. (add-hook 'exwm-update-class-hook (lambda () (exwm-workspace-rename-buffer exwm-class-name))) ;;; my exwm functions (defun my-exwm-input--fake-key (event &optional buffer) "Fake a key event equivalent to Emacs event EVENT at the current buffer. Optionally choose a buffer." (let* ((keysyms (xcb:keysyms:event->keysyms exwm--connection event)) (target-buffer (or buffer (window-buffer (selected-window)))) keycode id) (when (= 0 (caar keysyms)) (user-error "[EXWM] Invalid key: %s" (single-key-description event))) (setq keycode (xcb:keysyms:keysym->keycode exwm--connection (caar keysyms))) (when (/= 0 keycode) (setq id (exwm--buffer->id target-buffer)) (exwm--log "id=#x%x event=%s keycode" id event keycode) (dolist (class '(xcb:KeyPress xcb:KeyRelease)) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id :event-mask xcb:EventMask:NoEvent :event (xcb:marshal (make-instance class :detail keycode :time xcb:Time:CurrentTime :root exwm--root :event id :child 0 :root-x 0 :root-y 0 :event-x 0 :event-y 0 :state (cdar keysyms) :same-screen 1) exwm--connection))))) (xcb:flush exwm--connection))) (defun exwm-send-shortcut (buffer key-event) "Activate shortcut to window." (let ((window (get-buffer buffer))) (when window (set-buffer window) ;; (run-with-timer 0.3 ;; 0 ;; `(lambda () ;; (my-exwm-input--fake-key ,key-event ,window))) (my-exwm-input--fake-key key-event window) ;; (run-with-timer 0.5 0 `(lambda () (pop-to-buffer ,cur))) ))) ;;; ;;; NOTE: use (read-event) or (read-key) ;;; (defun teams-toggle-mute () "Toggle mute in micro$oft teams." (interactive) (exwm-send-shortcut "firefox" 33554445)) (keymap-global-set "C-x g t" 'teams-toggle-mute) (defun teams-toggle-hand () "Toggle mute in micro$oft teams." (interactive) (exwm-send-shortcut "firefox" 33554443)) (keymap-global-set "C-x g h" 'teams-toggle-hand) (defun obs-stream-stop () "Stop stream in OBS Studio." (interactive) (exwm-send-shortcut "obs" 8388729) (message "OBS streaming stopped!")) (defun obs-record-stop () "Stop record in OBS Studio." (interactive) (exwm-send-shortcut "obs" 8388712) (message "OBS recording stopped!")) (defun obs-stream-start () "Start stream in OBS Studio." (interactive) (exwm-send-shortcut "obs" 8388725)) (defun obs-record-start () "Start record in OBS Studio." (interactive) (exwm-send-shortcut "obs" 8388714)) ;; Global keybindings. (setq exwm-input-global-keys `(([?\s-y] . obs-stream-stop) ([?\s-h] . obs-record-stop) ([?\s-u] . obs-stream-start) ([?\s-j] . obs-record-start) ([?\s-r] . exwm-reset) ;; Reset (to line-mode). ([?\s-w] . exwm-workspace-swap) ;; Swap workspace. ([?\s-m] . exwm-workspace-move-window) ;; Move window. ([?\s-&] . (lambda (cmd) ;; Launch application. (interactive (list (read-shell-command "$ "))) (start-process-shell-command cmd nil cmd))) ;; s-Number: Switch to certain workspace. ,@(mapcar (lambda (i) `(,(kbd (format "s-%d" i)) . (lambda () (interactive) (exwm-workspace-switch-create ,i)))) (number-sequence 0 9)) ([?\s-s] . (lambda () (interactive) (shell-command "screenshot-save /tmp/screenshot.png") (let ((file (read-file-name "Save PNG as: " "~/Downloads/"))) (if (string-empty-p file) (message "/tmp/screenshot.png saved.") (shell-command (concat "mv /tmp/screenshot.png " file)) (message (concat file " saved.")))))) ([?\s-i] . (lambda () (interactive) (let ((cmd "icecat")) (start-process-shell-command cmd nil cmd)))) ([?\s-f] . (lambda () (interactive) (let ((cmd "firefox")) (start-process-shell-command cmd nil cmd)))) ([?\s-p] . (lambda () (interactive) (let ((cmd "java -jar ~/me/programming/desktop/gov-apps/plugin-autenticacao/plugin-autenticacao-gov.jar") (procname "autenticacao-gov-plugin")) (start-process-shell-command procname nil cmd)))) ([?\s-a] . (lambda () (interactive) (let ((cmd "env QT_QPA_PLATFORMTHEME=gtk3 eidguiV2") (procname "autenticacao-gov")) (start-process-shell-command procname nil cmd)))) ([?\s-o] . (lambda () (interactive) (let ((cmd "libreoffice") (procname "libreoffice")) (start-process-shell-command procname nil cmd)))) ([?\s-t] . (lambda () (interactive) (let ((cmd "obs") (procname "obs")) (start-process-shell-command procname nil cmd)))))) ;; key shortcuts (add-hook 'exwm-manage-finish-hook (lambda () (cond ((and exwm-class-name (or (equal exwm-class-name "icecat") (equal exwm-class-name "iceweasel") (equal exwm-class-name "firefox"))) (exwm-input-set-local-simulation-keys '(([?\C-b] . [left]) ([?\C-f] . [right]) ([?\C-p] . [up]) ([?\C-n] . [down]) ([?\C-a] . [home]) ([?\C-e] . [end]) ([?\M-v] . [prior]) ([?\C-v] . [next]) ([?\C-d] . [delete]) ([?\M-b] . [M-left]) ([?\M-f] . [M-right]) ([?\C-k] . [C-w]) ([?\M-w] . [C-c]) ([?\C-w] . [C-x]) ([?\C-y] . [C-v]) ([?\C-s] . [C-f])))) ('t (exwm-input-set-local-simulation-keys nil))))) ;;; Enable EXWM ;;(exwm-enable) ;; disable the too much stuff for graphical enviroment (tool-bar-mode -1) (menu-bar-no-scroll-bar) (menu-bar-mode -1) (tab-bar-mode (if (getenv "DISPLAY") -1 nil)) (and (fboundp 'fringe-mode) (fringe-mode '(0 . 0))) ;; other X stuff (defun x-refresh () (interactive) (shell-command "xrefresh; which keyboard-x-activate") (message "X refreshed.")) (keymap-global-set "C-x x x" 'x-refresh) ;; abacus training (defun abacus-exercise (dir-str msg) (interactive) (cd dir-str) (kill-buffer "*Messages*") (load (concat dir-str "my.el")) (message msg) (switch-to-buffer "*Messages*") (text-scale-increase 5) (read-only-mode -1) (erase-buffer)) (keymap-global-set "C-x 0" (lambda () (interactive) (abacus-exercise "~/abacus/0-reading/" "Reading exercise..."))) (keymap-global-set "C-x 1" (lambda () (interactive) (abacus-exercise "~/abacus/1-writing/" "Writing exercise..."))) (keymap-global-set "C-x 2" (lambda () (interactive) (abacus-exercise "~/abacus/2-yama/" "Yama exercise..."))) ;; always enable some disable commands (with M-x enable-command) (put 'list-timers 'disabled nil) ;; final message (message "GNU Emacs configuration loaded.") ;;; end of emacs configuration (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. ;; '(mml-secure-key-preferences nil) '(package-selected-packages '(go-translate languagetool plz clhs mediawiki exwm fsm srv jabber captain magit pinentry markdown-mode ebdb emms transpose-frame rainbow-delimiters paredit ledger-mode expand-region dictionary 0x0))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(erc-fool-face ((t (:strike-through t :slant italic)))) '(magit-blame-dimmed ((t (:inherit magit-dimmed)))) '(magit-diff-added ((t (:extend t :background "#335533" :foreground "black")))) '(magit-diff-added-highlight ((t (:extend t :background "#336633" :foreground "black")))) '(magit-diff-base ((t (:extend t :background "#555522" :foreground "black")))) '(magit-diff-base-highlight ((t (:extend t :background "#666622" :foreground "black")))) '(magit-diff-removed ((t (:extend t :background "#553333" :foreground "black")))) '(magit-diff-removed-highlight ((t (:extend t :background "#663333" :foreground "black")))) '(magit-dimmed ((t (:slant italic)))) '(magit-hash ((t (:slant italic)))) '(magit-reflog-checkout ((t (:background "white" :foreground "blue")))))