#+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 (or (string= (system-name) "fsociety")
(string= (system-name) "heisenberg"))
(add-to-list 'load-path "~/.guix-profile/share/emacs/site-lisp")
(guix-emacs-autoload-packages))
#+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
# ** MacOS
# If it's a mac, load from homebrew path
# #+begin_src emacs-lisp
# (when (equal system-type 'darwin)
# (use-package mu4e
# :load-path "/opt/homebrew/share/emacs/site-lisp/mu/mu4e/"))
# #+end_src
#+begin_src emacs-lisp
(when (equal system-type 'darwin)
(use-package mu4e
:load-path "/opt/homebrew/share/emacs/site-lisp/mu/mu4e/"))
(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)
(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