#+TITLE: Emacs Configuration #+PROPERTY: header-args :tangle init.el #+auto_tangle: t #+OPTIONS: num:nil toc:nil * Table of contents :TOC: - [[#setting-up-packages][Setting up Packages]] - [[#setup-for-guixsd-machines][Setup for GuixSD machines]] - [[#define-and-install-packages][Define and install packages]] - [[#ui-settings][UI Settings]] - [[#basic-ui][Basic UI]] - [[#dashboard][Dashboard]] - [[#theme--modeline][Theme & modeline]] - [[#ivy][Ivy]] - [[#helpful][Helpful]] - [[#dired][Dired]] - [[#keys][Keys]] - [[#all-the-iconsel][All-the-icons.el]] - [[#terminals][Terminals]] - [[#vterm][Vterm]] - [[#eshell][Eshell]] - [[#custom][Custom]] - [[#chatgpt][ChatGPT]] - [[#random-functions][Random functions]] - [[#key-bindings][Key-bindings]] - [[#org-mode-configuration][Org-mode Configuration]] - [[#org-make-toc][org-make-toc]] - [[#themes][Themes]] - [[#settings][Settings]] - [[#babel][Babel]] - [[#markdown][Markdown]] - [[#theme][Theme]] - [[#settings-1][Settings]] - [[#programming][Programming]] - [[#essentials][Essentials]] - [[#emacs-lisp][Emacs lisp]] - [[#lsp-mode][LSP-Mode]] - [[#python][Python]] - [[#json][JSON]] - [[#javascript][Javascript]] - [[#elfeed][Elfeed]] - [[#feeds][Feeds]] - [[#settings-2][Settings]] - [[#pdf][PDF]] - [[#telega][Telega]] - [[#mu4e--email-configuration][mu4e | Email Configuration]] - [[#exwm][EXWM]] * Setting up Packages ** Setup for GuixSD machines We check the ~$HOSTNAME~, if it's one of my devices running GuixSD. we use ~guix-emacs-autoload-packages~ to load emacs packages installed using guix if ~t~ + If you are running GuixSD, replace the following hostnames ~fsociety~ or ~heisenberg~ with your own ~$HOSTNAME~ #+begin_src emacs-lisp ;; When guix t, load emacs packages (when (require 'guix t) (add-to-list 'load-path "~/.guix-profile/share/emacs/site-lisp") (guix-emacs-autoload-packages)) (with-eval-after-load 'geiser-guile (add-to-list 'geiser-guile-load-path "~/dotfiles/.config/guix")) ;; Personal Information (setq user-full-name "Thanos Apollo") (setq user-mail-address "public@thanosapollo.com") (setq copyright-names-regexp (format "%s <%s>" user-full-name user-mail-address)) #+end_src ** Define and install packages *** List of required packages Request the following packages: #+begin_src emacs-lisp (defconst my-package-list '(org-snooze all-the-icons all-the-icons-dired all-the-icons-ivy-rich dap-mode toc-org general doom-themes doom-modeline counsel which-key ivy ivy-rich helpful org org-modern visual-fill-column rainbow-delimiters flycheck lsp-mode lsp-ui json-mode rjsx-mode typescript-mode python-mode pyvenv company company-box magit elfeed elfeed-goodies paredit corfu monkeytype sudo-edit exwm exwm-mff exwm-firefox-core consult alsamixer simple-httpd circe eshell-syntax-highlighting pdf-tools org-superstar mastodon dashboard org-auto-tangle)) #+end_src *** Installation & activation Set our ~package-archives~, and install our packages #+begin_src emacs-lisp (setq package-archives '(("melpa" . "https://melpa.org/packages/") ("org" . "https://orgmode.org/elpa/") ("elpa" . "https://elpa.gnu.org/packages/"))) (package-initialize) (unless package-archive-contents (package-refresh-contents)) (defvar my-missing-packages '() "List populated at each startup. Contains the list of packages that need to be installed.") (dolist (p my-package-list) (when (not (package-installed-p p)) (add-to-list 'my-missing-packages p))) (when my-missing-packages (message "Emacs is now refreshing its package database...") (package-refresh-contents) ;; Install the missing packages (dolist (p my-missing-packages) (message "Installing `%s' .." p) (package-install p)) (setq my-missing-packages '())) (unless (package-installed-p 'use-package) (package-install 'use-package)) #+end_src * UI Settings ** Basic UI Fonts and basic appearance settings #+begin_src emacs-lisp (setq inhibit-startup-message nil) ;; Transparency (set-frame-parameter (selected-frame) 'alpha '(90 90)) (add-to-list 'default-frame-alist '(alpha 90 90)) (add-hook 'dired-mode-hook 'all-the-icons-dired-mode) (scroll-bar-mode -1) (tool-bar-mode -1) (tooltip-mode -1) (set-fringe-mode 10) (menu-bar-mode -1) (blink-cursor-mode -1) (menu-bar--visual-line-mode-enable) (setq visible-bell t) (column-number-mode) (global-display-line-numbers-mode 0) (menu-bar--display-line-numbers-mode-relative) ;;Disable line numbers for some modes (dolist (mode '(pdf-view-mode-hook org-mode-hook term-mode-hook shell-mode-hook eshell-mode-hook vterm-mode-hook elfeed)) (add-hook mode (lambda () (display-line-numbers-mode 0)))) (defvar apollo/default-font-size 140) (set-face-attribute 'default nil :font "JetBrains Mono" :height apollo/default-font-size) (set-face-attribute 'fixed-pitch nil :font "JetBrains Mono" :height apollo/default-font-size) (set-face-attribute 'variable-pitch nil :font "JetBrains Mono" :height apollo/default-font-size :weight 'regular) #+end_src ** Dashboard #+begin_src emacs-lisp (require 'dashboard) (require 'all-the-icons) (dashboard-setup-startup-hook) (setq dashboard-items '((recents . 5) (bookmarks . 5))) (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")) dashboard-item-names '(("Recent Files:" . "Recent Files:") ("Bookmarks:" . "Study:"))) ;; Set the banner (setq dashboard-startup-banner "~/dotfiles/pictures/medicine/plague-doctor-s.png") ;; Set the title (setq dashboard-banner-logo-title "Is that a flying flower?") ;; (setq dashboard-init-info "Bring me your sick and wounded!") (setq dashboard-set-init-info t dashboard-center-content t dashboard-set-navigator t dashboard-set-heading-icons t dashboard-set-file-icons t dashboard-show-shortcuts nil dashboard-set-footer t dashboard-footer-messages '("Welcome to the Church Of Emacs") dashboard-footer-icon (all-the-icons-octicon "broadcast" :height 1.15 :v-adjust -0.05 :face 'font-lock-keyword-face)) #+end_src ** Theme & modeline #+begin_src emacs-lisp (load-theme 'doom-ayu-dark t) (doom-modeline-mode 1) (setq doom-modeline-height 35) #+end_src ** Ivy #+begin_src emacs-lisp (use-package ivy :diminish :bind (("C-s" . swiper) :map ivy-minibuffer-map ("TAB" . ivy-alt-done) ("C-l" . ivy-alt-done) ("C-j" . ivy-next-line) ("C-k" . ivy-previous-line) :map ivy-switch-buffer-map ("C-k" . ivy-previous-line) ("C-l" . ivy-done) ("C-d" . ivy-switch-buffer-kill) :map ivy-reverse-i-search-map ("C-k" . ivy-previous-line) ("C-d" . ivy-reverse-i-search-kill)) :config (ivy-mode 1) (setq ivy-use-selectable-prompt t)) (ivy-rich-mode 1) (all-the-icons-ivy-rich-mode 1) #+end_src ** Helpful #+begin_src emacs-lisp (use-package helpful :custom (counsel-describe-function-function #'helpful-callable) (counsel-describe-variable-function #'helpful-variable) :bind ([remap describe-function] . counsel-describe-function) ([remap describe-command] . helpful-command) ([remap describe-variable] . counsel-describe-variable) ([remap describe-key] . helpful-key)) #+end_src * Dired ** Keys #+begin_src emacs-lisp (define-key dired-mode-map "b" 'dired-up-directory) #+end_src ** All-the-icons.el #+begin_src emacs-lisp ;;; all-the-icons-dired.el --- Shows icons for each file in dired mode -*- lexical-binding: t; -*- ;;; Code: (require 'cl-lib) (require 'dired) (require 'all-the-icons) (defface all-the-icons-dired-dir-face '((((background dark)) :foreground "white") (((background light)) :foreground "black")) "Face for the directory icon" :group 'all-the-icons-faces) (defcustom all-the-icons-dired-v-adjust 0.01 "The default vertical adjustment of the icon in the dired buffer." :group 'all-the-icons :type 'number) (defvar all-the-icons-dired-mode) (defun all-the-icons-dired--add-overlay (pos string) "Add overlay to display STRING at POS." (let ((ov (make-overlay (1- pos) pos))) (overlay-put ov 'all-the-icons-dired-overlay t) (overlay-put ov 'after-string string))) (defun all-the-icons-dired--overlays-in (beg end) "Get all all-the-icons-dired overlays between BEG to END." (cl-remove-if-not (lambda (ov) (overlay-get ov 'all-the-icons-dired-overlay)) (overlays-in beg end))) (defun all-the-icons-dired--overlays-at (pos) "Get all-the-icons-dired overlays at POS." (apply #'all-the-icons-dired--overlays-in `(,pos ,pos))) (defun all-the-icons-dired--remove-all-overlays () "Remove all `all-the-icons-dired' overlays." (save-restriction (widen) (mapc #'delete-overlay (all-the-icons-dired--overlays-in (point-min) (point-max))))) (defun all-the-icons-dired--refresh () "Display the icons of files in a dired buffer." (all-the-icons-dired--remove-all-overlays) (save-excursion (goto-char (point-min)) (while (not (eobp)) (when (dired-move-to-filename nil) (let ((file (dired-get-filename 'relative 'noerror))) (when file (let ((icon (if (file-directory-p file) (all-the-icons-icon-for-dir file :face 'all-the-icons-dired-dir-face :v-adjust all-the-icons-dired-v-adjust) (all-the-icons-icon-for-file file :v-adjust all-the-icons-dired-v-adjust)))) (if (member file '("." "..")) (all-the-icons-dired--add-overlay (point) " \t") (all-the-icons-dired--add-overlay (point) (concat icon "\t"))))))) (forward-line 1)))) (defun all-the-icons-dired--refresh-advice (fn &rest args) "Advice function for FN with ARGS." (apply fn args) (when all-the-icons-dired-mode (all-the-icons-dired--refresh))) (defun all-the-icons-dired--setup () "Setup `all-the-icons-dired'." (when (derived-mode-p 'dired-mode) (setq-local tab-width 1) (advice-add 'dired-readin :around #'all-the-icons-dired--refresh-advice) (advice-add 'dired-revert :around #'all-the-icons-dired--refresh-advice) (advice-add 'dired-internal-do-deletions :around #'all-the-icons-dired--refresh-advice) (advice-add 'dired-insert-subdir :around #'all-the-icons-dired--refresh-advice) (advice-add 'dired-do-kill-lines :around #'all-the-icons-dired--refresh-advice) (with-eval-after-load 'dired-narrow (advice-add 'dired-narrow--internal :around #'all-the-icons-dired--refresh-advice)) (all-the-icons-dired--refresh))) (defun all-the-icons-dired--teardown () "Functions used as advice when redisplaying buffer." (advice-remove 'dired-readin #'all-the-icons-dired--refresh-advice) (advice-remove 'dired-revert #'all-the-icons-dired--refresh-advice) (advice-remove 'dired-internal-do-deletions #'all-the-icons-dired--refresh-advice) (advice-remove 'dired-narrow--internal #'all-the-icons-dired--refresh-advice) (advice-remove 'dired-insert-subdir #'all-the-icons-dired--refresh-advice) (advice-remove 'dired-do-kill-lines #'all-the-icons-dired--refresh-advice) (all-the-icons-dired--remove-all-overlays)) ;;;###autoload (define-minor-mode all-the-icons-dired-mode "Display all-the-icons icon for each files in a dired buffer." :lighter " all-the-icons-dired-mode" (when (and (derived-mode-p 'dired-mode) (display-graphic-p)) (if all-the-icons-dired-mode (all-the-icons-dired--setup) (all-the-icons-dired--teardown)))) #+end_src Hook with ~dired-mode~ #+begin_src emacs-lisp (add-hook 'dired-mode-hook 'all-the-icons-dired-mode) #+end_src * Terminals ** Vterm #+begin_src emacs-lisp (use-package vterm :ensure nil :config ;; Check if it's one of my guix systems to load from ;; ~/.guix-profile. Otherwise we use /bin/zsh. (if (or (string= (system-name) "fsociety") (string= (system-name) "heisenberg")) (setq vterm-shell "~/.guix-profile/bin/zsh") (setq vterm-shell "/bin/zsh"))) #+end_src ** Eshell #+begin_src emacs-lisp (use-package eshell :ensure nil :bind (("C-c e" . 'eshell)) :config (defvar eshell-path-env (getenv "~/.local/bin"))) (defun with-face (str &rest face-plist) (propertize str 'face face-plist)) (defun apollo-eshell-prompt () (let ((winter-blue "#3F3B6C") (white-summer "#E5E5CB") (green-night "#03C988") (orange-summer "#FFB100") (green-summer "#A3BB98") (summer-sea "#2192FF") (black "#000000")) (concat (with-face (concat "[" user-login-name) :foreground orange-summer :background black) (with-face "@" :foreground orange-summer :background black) (with-face (concat system-name "]\n") :foreground orange-summer :background black) (with-face (concat "|" (eshell/pwd) ) :foreground "#F0E9D2" :background winter-blue) (with-face (format-time-string " | %H:%M" (current-time)) :background winter-blue :foreground "#888") (with-face "\n -> ")))) ;; (setq eshell-prompt-function 'apollo-eshell-prompt) ;; (setq eshell-highlight-prompt t) #+end_src * Custom ** ChatGPT #+begin_src emacs-lisp (require 'seq) (eval-when-compile (require 'cl-lib) (require 'subr-x) (require 'env) (require 'json)) (defgroup chatgpt nil "ChatGPT frontend." :group 'convenience :prefix "chatgpt-") (defcustom chatgpt-max-tokens 300 "Upper limit on the number of tokens the API will return." :type 'integer) (defvar chatgpt-buffer "*ChatGPT*" "Title of the buffer used to store the results of an OpenAI API query.") (define-error 'chatgpt-error "An error related to the ChatGPT emacs package") (define-error 'chatgpt-parsing-error "An error caused by a failure to parse an OpenAI API Response") (defmacro chatgpt-show-results-buffer-if-active () "Show the results in other window if necessary." `(if (and (not ;; visible (get-buffer-window chatgpt-buffer)) (called-interactively-p 'interactive)) (lambda (&optional buf) (ignore buf) (with-current-buffer buf (view-mode t)) (switch-to-buffer-other-window chatgpt-buffer)) #'identity)) ;;;###autoload (defun chatgpt-prompt (prompt callback) "Query OpenAI with PROMPT calling the CALLBACK function on the resulting buffer. Returns buffer containing the text from this query" (interactive (list (read-string "Prompt ChatGPT with: ") (lambda (buf) (with-current-buffer buf (view-mode t)) (switch-to-buffer-other-window chatgpt-buffer)))) (chatgpt--query-open-api prompt (lambda (results) (with-current-buffer (get-buffer-create chatgpt-buffer) ;; Erase contents of buffer after receiving response (read-only-mode -1) (erase-buffer) (insert results) ;; Return the chatgpt output buffer for non interactive usage (funcall callback (current-buffer)))))) ;;;###autoload (defun chatgpt-fix-region (BEG END) "Takes a region BEG to END asks ChatGPT to explain whats wrong with it. It then displays the answer in the `chatgpt-buffer'." (interactive "r") (let ((current-code (buffer-substring BEG END))) (chatgpt-prompt (chatgpt--append-to-prompt current-code "Why doesn't this code work?") (chatgpt-show-results-buffer-if-active)))) ;;;###autoload (defun chatgpt-explain-region (BEG END) "Takes a region BEG to END asks ChatGPT what it does. The answer in the displays in `chatgpt-buffer'." (interactive "r") (let ((current-code (buffer-substring BEG END))) (chatgpt-prompt (chatgpt--append-to-prompt current-code "What does this code do?") (chatgpt-show-results-buffer-if-active)))) ;;;###autoload (defun chatgpt-gen-tests-for-region (BEG END) "Takes a region BEG to END asks ChatGPT to write a test for it. It then displays the answer in the `chatgpt-buffer'." (interactive "r") (let ((current-code (buffer-substring BEG END))) (chatgpt-prompt (chatgpt--append-to-prompt current-code "Write me a tests for this code") (chatgpt-show-results-buffer-if-active)))) ;; TODO currently just says what changed but doesn't wanna show the code it's self ;; (defun chatgpt-optimize-region (BEG END) ;; "Takes a region BEG to END asks ChatGPT to optimize it for speed. ;; It then displays the answer in the `chatgpt-buffer'." ;; (interactive "r") ;; (let ((current-code (buffer-substring BEG END))) ;; (chatgpt-prompt (chatgpt--append-to-prompt ;; current-code ;; "Refactor this code for speed and tell me what you changed and why it's faster") ;; (chatgpt-show-results-buffer-if-active)))) ;;;###autoload (defun chatgpt-refactor-region (BEG END) "Takes a region BEG to END asks ChatGPT refactor it. It then displays the answer in the `chatgpt-buffer'." (interactive "r") (let ((current-code (buffer-substring BEG END))) (chatgpt-prompt (chatgpt--append-to-prompt current-code "Refactor this code and tell me what you changed") (chatgpt-show-results-buffer-if-active)))) ;;;###autoload (defun chatgpt-prompt-region (BEG END) "Prompt ChatGPT with the region BEG END. It then displays the results in a separate buffer `chatgpt-buffer'." (interactive "r") (chatgpt-prompt (buffer-substring BEG END) ;; Show the results if not already being viewed (chatgpt-show-results-buffer-if-active))) ;;;###autoload (defun chatgpt-prompt-region-and-replace (BEG END) "Replace region from BEG to END with the response from the ChatGPT API. The region is BEG and until END" (interactive "r") (let ((og-buf (current-buffer))) (chatgpt-prompt (buffer-substring BEG END) (lambda (buf) (save-excursion (with-current-buffer og-buf (delete-region BEG END) (goto-char BEG) (insert (with-current-buffer buf (buffer-string))))))))) (defun chatgpt--append-to-prompt (prompt comment-str) "Append the string COMMENT-STR extra information to a PROMPT as a comment." (concat prompt "\n" comment-start " " comment-str)) (defun chatgpt--extract-text-from-query (query-result) "Extract the resulting text from a given OpenAI response QUERY-RESULT." (condition-case err (thread-last query-result (assoc-default 'choices) seq-first (assoc-default 'text) string-trim) (error (signal 'chatgpt-parsing-error err)))) (defun chatgpt--parse-response (status callback) "Ignoring STATUS and parse the response executing the CALLBACK function on the resulting string." (ignore status) ;; All this is ran inside the buffer containing the response (goto-char 0) (re-search-forward "^$") (funcall callback (chatgpt--extract-text-from-query (json-read)))) (defun chatgpt--query-open-api (prompt callback) "Send a string PROMPT to OpenAI API and pass the resulting buffer to CALLBACK. The environment variable OPENAI_API_KEY is used as your API key You can register an account here https://beta.openai.com/docs/introduction/key-concepts" (let* ((api-key (getenv secret-api)) (url-request-method (encode-coding-string "POST" 'us-ascii)) (url-request-extra-headers `(("Content-Type" . "application/json") ("Authorization" . ,(format "Bearer %s" api-key)))) (url-request-data (json-encode `(("model" . "text-davinci-003") ("prompt" . ,prompt) ("max_tokens" . ,chatgpt-max-tokens) ("temperature" . 0))))) (cl-assert (not (string= "" api-key)) t "Current contents of the environmental variable OPENAI_API_KEY are '%s' which is not an appropriate OpenAI token please ensure you have the correctly set the OPENAI_API_KEY variable" api-key) (url-retrieve "https://api.openai.com/v1/completions" 'chatgpt--parse-response (list callback)))) #+end_src ** Random functions #+begin_src emacs-lisp (defun apollo/html-boostrap-boilerplate () "Insert html boilerplate with boostrap link." (interactive) (insert " My Title

Starting point

" )) (defun apollo/center-buffer () "Centers/Uncenters selected buffer" (interactive) (if visual-fill-column-center-text (setq visual-fill-column-center-text nil) (setq visual-fill-column-center-text t)) (visual-fill-column-mode 1) (message "General's task completed!")) (defun apollo/rofi-switch-window () "Navigate X11 buffers using rofi." (interactive) (start-process-shell-command "rofi" nil "rofi -show window")) (defun apollo/run-in-background (command) "Run COMMAND in the background." (let ((command-parts (split-string command "[ ]+"))) (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts))))) (defun rofi () "Run Rofi." (interactive) (apollo/run-in-background "rofi -show drun")) (defun apollo/volume-increase () "Increase Volume." (interactive) (start-process-shell-command "amixer" nil "amixer sset Master 5%+")) (defun apollo/volume-decrease () "Decrease Volume." (interactive) (start-process-shell-command "amixer" nil "amixer sset Master 5%-")) (defun apollo/restore-wallpaper () "Set NAME as wallpaper." (interactive) (start-process-shell-command "feh" nil "feh --bg-scale ~/dotfiles/wallpaper.png")) (defun apollo/emacs-keys () "Swap caps with ctrl." (interactive) (start-process-shell-command "setxkbmap" nil "setxkbmap us -option ctrl:swapcaps")) (defun apollo/greek-keyboard () "Swap caps with ctrl." (interactive) (start-process-shell-command "setxkbmap" nil "setxkbmap gr")) (defun apollo/exwm-update-class () (exwm-workspace-rename-buffer exwm-class-name)) (defun eshell-new() "Open a new instance of eshell." (interactive) (eshell 'N)) (defun make-mini-geiser () (interactive) (split-window-below 60) (geiser nil)) (defun start-polybar () "Check which system is running, start polybar accordingly." (interactive) (if (string= (system-name) "fsociety") (start-process-shell-command "polybar" nil "polybar main & polybar second") (start-process-shell-command "polybar" nil "polybar main"))) #+end_src ** Key-bindings #+begin_src emacs-lisp (global-set-key (kbd "") 'keyboard-escape-quit) #+end_src *** General #+begin_src emacs-lisp ;; my general's leader key! (defconst general-key "C-c g") (general-create-definer general-does :prefix general-key) ;; Basic functions (general-define-key "C-d" 'kill-region "C-k" 'copy-region-as-kill "C-c v" 'multi-vterm-next "C-x 9" 'make-mini-buffer "C-c l e" 'apollo/emacs-keys ;;Change keyboard layouts/language "C-c l g" 'apollo/greek-keyboard "C-κ" 'apollo/emacs-keys "C-x C-b" 'ibuffer) ;; My Generals does: (general-does "t" 'counsel-load-theme "l" 'display-line-numbers-mode "v" 'multi-vterm-next "e" 'eshell "i" 'circe "c" 'apollo/center-buffer "m" 'mu4e "f" 'elfeed "C-t" 'telega) ;;pdf-tools (general-define-key :keymaps 'pdf-view-mode-map "C-c d" 'pdf-view-midnight-minor-mode "C-s" 'isearch-forward "C-o" 'pdf-isearch-occur) ;;vterm (general-does :keymaps 'vterm-mode-map "n" 'multi-vterm-next "o" 'multi-vterm "p" 'multi-vterm-prev) (general-does :keymaps 'eshell-mode-map "n" 'eshell-new) (general-does :keymaps 'org-mode-map "3" 'org-insert-image-size-300) #+end_src *** Hydra #+begin_src emacs-lisp (defhydra hydra-zoom (global-map "s-z") "zoom" ("=" text-scale-increase "in") ("-" text-scale-decrease "out")) #+end_src * Org-mode Configuration ** org-make-toc #+begin_src emacs-lisp (add-hook 'org-mode-hook 'org-make-toc-mode) #+end_src ** Themes *** Dracula #+begin_src emacs-lisp (defun apollo/org-theme-dracula () "Enable Dracula theme for Org headers." (interactive) (dolist (face '((org-level-1 1.7 "#8be9fd" extra-bold) (org-level-2 1.6 "#bd93f9" extra-bold) (org-level-3 1.5 "#50fa7b" bold) (org-level-4 1.4 "#ff79c6" semi-bold) (org-level-5 1.3 "#9aedfe" normal) (org-level-6 1.2 "#caa9fa" normal) (org-level-7 1.1 "#5af78e" normal) (org-level-8 1.0 "#ff92d0" normal))) (set-face-attribute (nth 0 face) nil :font "JetBrains Mono" :weight (nth 3 face) :height (nth 1 face) :foreground (nth 2 face))) (set-face-attribute 'org-table nil :font "JetBrains Mono" :weight 'normal :height 1.0 :foreground "#bfafdf")) #+end_src *** Darkone #+begin_src emacs-lisp (defun apollo/org-theme-darkone () "Enable Darkone theme for Org headers." (interactive) (dolist (face '((org-level-1 1.70 "#51afef" bold) (org-level-2 1.55 "#7FBCD2" bold) (org-level-3 1.40 "#da8548" bold) (org-level-4 1.20 "#da8548" semi-bold) (org-level-5 1.20 "#5699af" normal) (org-level-6 1.20 "#a9a1e1" normal) (org-level-7 1.10 "#46d9ff" normal) (org-level-8 1.00 "#ff6c6b" normal))) (set-face-attribute (nth 0 face) nil :font "Jetbrains Mono" :weight (nth 3 face) :height (nth 1 face) :foreground (nth 2 face))) (set-face-attribute 'org-table nil :font "Jetbrains Mono" :weight 'normal :height 1.0 :foreground "#A66CFF")) #+end_src ** Settings #+begin_src emacs-lisp (setq org-directory "~/org/" org-agenda-files '("~/org/agenda.org") org-default-notes-file (expand-file-name "notes.org" org-directory) org-ellipsis " ▼ " org-log-done 'time org-hide-emphasis-markers nil ;;change to t to hide emphasis markers org-table-convert-region-max-lines 20000 org-agenda-start-log-mode t org-log-done 'time org-log-into-drawer t org-indent-mode t org-todo-keywords ;; This overwrites the default Doom org-todo-keywords '((sequence "TODO(t)" ;; A task that is ready to be tackled "BLOG(b)" ;; Blog writing assignments "GYM(g)" ;; Things to accomplish at the gym "WAIT(w)" ;; Something is holding up this task "|" ;; The pipe necessary to separate "active" states and "inactive" states "DONE(d)" ;; Task has been completed "CANCELLED(c)" )) org-superstar-headline-bullets-list '("◉" "●" "○" "●" "○" "●" "◆") org-superstar-itembullet-alist '((?+ . ?➤) (?- . ?✦))) ;; changes +/- symbols in item lists) ;; Hooks (add-hook 'org-mode-hook 'apollo/org-theme-dracula) (add-hook 'org-mode-hook 'org-superstar-mode) (add-hook 'org-mode-hook 'flyspell-mode) (add-hook 'org-mode-hook 'toc-org-mode) #+end_src ** Babel #+begin_src emacs-lisp (defadvice org-edit-src-code (around set-buffer-file-name activate compile) (let ((file-name (buffer-file-name))) ;; (1) ad-do-it ;; (2) (setq buffer-file-name file-name))) ;; (3) (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (python . t))) (add-to-list 'org-structure-template-alist '("sh" . "src shell")) (add-to-list 'org-structure-template-alist '("b" . "src bash")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) (add-to-list 'org-structure-template-alist '("py" . "src python")) ;;Auto tangle (add-hook 'org-mode-hook 'org-auto-tangle-mode) #+end_src * Markdown ** Theme #+begin_src emacs-lisp (defun apollo/markdown-theme () (interactive) (dolist (face '(markdown-header-face-1 :height 2.0)))) #+end_src ** Settings #+begin_src emacs-lisp (setq markdown-header-scaling t) (use-package markdown-mode :ensure t :mode (".md" . gfm-mode) :init (setq markdown-command "multimarkdown")) #+end_src * Programming ** Essentials #+begin_src emacs-lisp (electric-pair-mode 1) (global-flycheck-mode) (use-package company :after lsp-mode :hook (lsp-mode . company-mode) :bind (:map company-active-map ("" . company-complete-selection)) (:map lsp-mode-map ("" . company-indent-or-complete-common)) :custom (company-minimum-prefix-length 1) (company-idle-delay 0.0)) (use-package dap-mode :custom (lsp-enable-dap-auto-configure nil) :config (dap-ui-mode 1)) (use-package company-box :hook (company-mode . company-box-mode)) #+end_src *** Magit #+begin_src emacs-lisp (use-package magit :custom (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) #+end_src ** Emacs lisp #+begin_src emacs-lisp (setq tab-always-indent 'complete) (add-to-list 'completion-styles 'initials t) (add-hook 'emacs-lisp-mode-hook #'rainbow-delimiters-mode) (add-hook 'scheme-mode-hook #'rainbow-delimiters-mode) #+end_src ** LSP-Mode #+begin_src emacs-lisp (defun apollo/lsp-mode-setup () (setq lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols)) (lsp-headerline-breadcrumb-mode)) (use-package lsp-mode :commands (lsp lsp-deferred) :hook (lsp-mode . apollo/lsp-mode-setup) :init (setq lsp-keymap-prefix "C-c l") ;; Or 'C-l', 's-l' :config (lsp-enable-which-key-integration t) (setq lsp-pyls-server-command "~/.local/bin/pylsp")) (use-package lsp-ui :hook (lsp-mode . lsp-ui-mode) :custom (lsp-ui-doc-position 'bottom)) #+end_src ** Python #+begin_src emacs-lisp (use-package python-mode :ensure t :mode ".py" :hook (python-mode . lsp-deferred) :custom (dap-python-debugger 'debugpy) :config (require 'dap-python)) (use-package pyvenv :config (pyvenv-mode 1)) #+end_src ** JSON #+begin_src emacs-lisp (use-package json-mode :mode ".json" :hook (json-mode . lsp-deferred)) #+end_src ** Javascript #+begin_src emacs-lisp (use-package rjsx-mode :mode ".js" :hook (rjsx-mode . lsp-deferred) :config (defadvice js-jsx-indent-line (after js-jsx-indent-line-after-hack activate) "Workaround sgml-mode and follow airbnb component style." (save-excursion (beginning-of-line) (if (looking-at-p "^ +\/?> *$") (delete-char sgml-basic-offset))))) #+end_src * Elfeed ** Feeds #+begin_src emacs-lisp (require 'elfeed) (require 'elfeed-goodies) (setq elfeed-feeds (quote ( ("https://hackaday.com/blog/feed/" hackaday linux) ("https://opensource.com/feed" opensource linux) ("https://linux.softpedia.com/backend.xml" softpedia linux) ("https://www.thelancet.com/rssfeed/lancet_current.xml" Lancet lancet) ("https://www.thelancet.com/rssfeed/lancet_online.xml" LancetOnline lancet) ("https://www.thelancet.com/rssfeed/lanmic_online.xml" LancetOnline Microbiology) ("https://www.techrepublic.com/rssfeeds/topic/open-source/" techrepublic linux) ("https://www.thanosapollo.com/public/feed.xml" Thanos) ("https://protesilaos.com/news.xml" Protesilaos News) ("https://protesilaos.com/codelog.xml" Proetesilaos Coding) ("https://stallman.org/rss/rss.xml" Stallman news) ))) #+end_src ** Settings #+begin_src emacs-lisp (setq elfeed-goodies/entry-pane-size 0.5) #+end_src * PDF #+begin_src emacs-lisp ;;Add pdf-isearch-minor-mode hook, otherwise isearch will be buggy ;;Darkmode hook, cause I don't want color or light in my life, I'm a vampire. (add-hook 'pdf-view-mode-hook 'pdf-isearch-minor-mode) (add-hook 'pdf-view-mode-hook 'pdf-view-midnight-minor-mode) (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode)) #+end_src * Telega In ~GuixSD~ systems I have telegram installed via guix, and it's loaded via ~guix-emacs-autoload-packages~ In macOS, I have installed it through homebrew. #+begin_src emacs-lisp (when (equal system-type 'darwin) (use-package telega :load-path "~/telega.el" :commands (telega) :defer t :config (setq telega-server-libs-prefix "/opt/homebrew/Cellar/tdlib/HEAD-3d0140b"))) #+end_src * mu4e | Email Configuration #+begin_src emacs-lisp ;; In macOS, load mu4e through homebrew (when (equal system-type 'darwin) (use-package mu4e :load-path "/opt/homebrew/share/emacs/site-lisp/mu/mu4e/")) ;; Check if we have mu4e available ;; if t load mu4e settings (when (require 'mu4e t) (setq mu4e-update-interval (* 10 60)) (setq mu4e-get-mail-command "mbsync -a") (setq mu4e-maildir-list "~/Mail/Inbox") (defun set-mu4e-context (context-name full-name mail-address signature server) "Return a mu4e context named CONTEXT-NAME with :match-func matching folder name CONTEXT-NAME in Maildir. The context's `user-mail-address', `user-full-name' and `mu4e-compose-signature'`smtpmail-smpt-server' is set to MAIL-ADDRESS FULL-NAME SIGNATURE and SERVER respectively. Special folders are set to context specific folders." (let ((dir-name (concat "/" context-name))) (make-mu4e-context :name context-name ;; we match based on the maildir of the message :match-func `(lambda (msg) (when msg (string-match-p ,(concat "^" dir-name) (mu4e-message-field msg :maildir)))) :vars `((user-mail-address . ,mail-address) (user-full-name . ,full-name) (mu4e-sent-folder . ,(concat dir-name "/Sent")) (mu4e-drafts-folder . ,(concat dir-name "/Drafts")) (mu4e-trash-folder . ,(concat dir-name "/Trash")) (mu4e-trash-folder . ,(concat dir-name "/Starred")) (mu4e-refile-folder . ,(concat dir-name "/Archive")) (smtpmail-smtp-service . ,465) (smtpmail-smtp-server . ,server) (mu4e-compose-signature . ,signature))))) ;;Fixing duplicate UID errors when using mbsync and mu4e (setq mu4e-change-filenames-when-moving t) (setq mu4e-maildir-shortcuts '(("/Fastmail/Inbox" . ?i) ("/Fastmail/Sent" . ?s) ;; ("/Gmail/Trash" . ?t) ;; ("/Gmail/Drafts" . ?d) ;; ("/Gmail/[Gmail]/All Mail" . ?a )) (setq mu4e-contexts `(, (set-mu4e-context "Fastmail" "Thanos Apollo" "thanos@thanosapollo.com" "Thanos\nhttps://thanosapollo.com/public" "smtp.fastmail.com"))) ;; (setq smtpmail-smtp-service 465 ;; smtpmail-stream-type 'ssl ;; smtpmail-smtp-server "smtp.fastmail.com") (setq message-send-mail-function 'smtpmail-send-it smtpmail-stream-type 'ssl)) #+end_src # ** Settings & Context setup # #+begin_src emacs-lisp # (require 'mu4e) # (setq mu4e-update-interval (* 10 60)) # (setq mu4e-get-mail-command "mbsync -a") # (setq mu4e-maildir-list "~/Mail/Inbox") # (defun set-mu4e-context (context-name full-name mail-address signature server) # "Return a mu4e context named CONTEXT-NAME with :match-func matching # folder name CONTEXT-NAME in Maildir. The context's `user-mail-address', # `user-full-name' and `mu4e-compose-signature'`smtpmail-smpt-server' is set to MAIL-ADDRESS # FULL-NAME SIGNATURE and SERVER respectively. # Special folders are set to context specific folders." # (let ((dir-name (concat "/" context-name))) # (make-mu4e-context # :name context-name # ;; we match based on the maildir of the message # :match-func # `(lambda (msg) # (when msg # (string-match-p # ,(concat "^" dir-name) # (mu4e-message-field msg :maildir)))) # :vars # `((user-mail-address . ,mail-address) # (user-full-name . ,full-name) # (mu4e-sent-folder . ,(concat dir-name "/Sent")) # (mu4e-drafts-folder . ,(concat dir-name "/Drafts")) # (mu4e-trash-folder . ,(concat dir-name "/Trash")) # (mu4e-trash-folder . ,(concat dir-name "/Starred")) # (mu4e-refile-folder . ,(concat dir-name "/Archive")) # (smtpmail-smtp-service . ,465) # (smtpmail-smtp-server . ,server) # (mu4e-compose-signature . ,signature))))) # ;;Fixing duplicate UID errors when using mbsync and mu4e # (setq mu4e-change-filenames-when-moving t) # ;; Messaging # (setq message-send-mail-function 'smtpmail-send-it # smtpmail-stream-type 'ssl) # #+end_src # ** Shortcuts # #+begin_src emacs-lisp # (setq mu4e-maildir-shortcuts # '(("/Fastmail/Inbox" . ?i) # ("/Fastmail/Sent" . ?s) # )) # #+end_src # ** Contexts # #+begin_src emacs-lisp # (setq mu4e-contexts # `(, (set-mu4e-context # "Fastmail" "Thanos Apollo" # "thanos@thanosapollo.com" "Thanos\nhttps://thanosapollo.com/public" # "smtp.fastmail.com"))) # #+end_src * EXWM EXWM configuration is tangled with ~.exwm~ + In GuixSD ~emacs-exwm~ package will look for ~~/.exwm~ #+begin_src emacs-lisp :tangle ../.exwm.el (defun apollo/exwm-init-hook () "Do this upon start." (if (string= (system-name) "fsociety") ;; Check if it's my desktop, otherwise display battery (display-battery-mode 0) (display-battery-mode 1)) (setq display-time-day-and-date t) (display-time-mode 1) (exwm-firefox-holyK-mode 1) ;;Launch apps that will run in the background ;; (apollo/run-in-background "blueman-applet") (apollo/run-in-background "picom") ;; (apollo/run-in-background "nm-applet") (apollo/emacs-keys) (apollo/restore-wallpaper) ) (defun apollo/exwm-update-class () (exwm-workspace-rename-buffer exwm-class-name)) (setq exwm-workspace-number 6) ;; When window "class" updates, use it to set the buffer name (add-hook 'exwm-update-class-hook #'apollo/exwm-update-class) ;;When EXWM starts up, run this hook (add-hook 'exwm-init-hook #'apollo/exwm-init-hook) (start-process-shell-command "xrandr" nil "xrandr --output DisplayPort-0 --primary --mode 2560x1440 --pos 1930x0 --rotate normal --output DisplayPort-1 --off --output DisplayPort-2 --off --output HDMI-A-0 --mode 1920x1080 --pos 0x0 --rotate normal") ;; Set the screen resolution (require 'exwm-randr) (setq exwm-randr-workspace-monitor-plist '(0 "DP-1" 2 "HDMI-A-0")) (add-hook 'exwm-randr-screen-change-hook (lambda () (start-process-shell-command "xrandr" nil "xrandr --output DisplayPort-2 --primary --mode 2560x1440 --pos 1920x0 --rotate normal --output HDMI-A-0 --mode 1920x1080 --pos 0x0 --rotate normal" ))) (exwm-randr-enable) ;; Load the system tray before exwm-init (require 'exwm-systemtray) (exwm-systemtray-enable) ;; These keys should always pass through to Emacs (setq exwm-input-prefix-keys '(?\C-x ?\C-u ?\C-h ?\C-w ?\M-x ?\M-` ?\M-& ?\M-: ?\s-d ?\C-\M-j ;; Buffer list ?\C-\ ?\C-k ?\C-y ?\C-n ?\C-p ?\C-f ?\C-b ?\C-s ?\M-f ?\M-b ?\M-< ?\M->)) ;; Ctrl+Q will enable the next key to be sent directly (define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key) ;; Set up global key bindings. These always work, no matter the input state! ;; Keep in mind that changing this list after EXWM initializes has no effect. (setq exwm-input-global-keys `( ([?\s-r] . exwm-reset) ;; ;; Launch applications via shell command ;; ([?\s-&] . (lambda (command) ;; (interactive (list (read-shell-command "$ "))) ;; (start-process-shell-command command nil command))) ;; Switch workspace ([?\s-e] . exwm-workspace-switch) ([?\s-`] . (lambda () (interactive) (exwm-workspace-switch-create 0))) ([?\s-2] . (lambda () (interactive) (exwm-workspace-switch-create 1))) ([?\s-3] . (lambda () (interactive) (exwm-workspace-switch-create 2))) ([?\s-4] . (lambda () (interactive) (exwm-workspace-switch-create 3))) ([?\s-5] . (lambda () (interactive) (exwm-workspace-switch-create 4))) ,@(mapcar (lambda (i) `(,(kbd (format "s-%d" i)) . (lambda () (interactive) (exwm-workspace-switch-create ,i)))) (number-sequence 0 9)))) (defun exwm-enlarge-horizontally-50 () (interactive) (exwm-layout-enlarge-window-horizontally 50)) (defun exwm-shrink-horizontally-50 () "Shrink window horizontally by 10" (interactive) (exwm-layout-shrink-window-horizontally 50)) (defun exwm-shrink-vertically-50 () "Shrink window by 50." (interactive) (exwm-layout-shrink-window 50)) (defun exwm-enlarge-vertically-50 () "Enlarge window by 50." (interactive) (exwm-layout-enlarge-window 50)) (exwm-input-set-key (kbd "C-c d") 'dmenu) (exwm-input-set-key (kbd "s-") 'rofi) (exwm-input-set-key (kbd "s-Q") 'kill-emacs) (exwm-input-set-key (kbd "") 'apollo/volume-increase) (exwm-input-set-key (kbd "") 'apollo/volume-decrease) (exwm-input-set-key (kbd "s-0") 'apollo/emacs-keys) (exwm-input-set-key (kbd "s-9") 'apollo/greek-keyboard) (exwm-input-set-key (kbd "C-c C-") 'exwm-shrink-horizontally-50) (exwm-input-set-key (kbd "C-c C-") 'exwm-enlarge-horizontally-50) (exwm-input-set-key (kbd "C-c C-") 'exwm-shrink-vertically-50) (exwm-input-set-key (kbd "C-c C-") 'exwm-enlarge-vertically-50) (exwm-input-set-key (kbd "C-c C-e") 'exwm-layout-toggle-fullscreen) (exwm-enable) #+end_src