Skip to content

ArtemSmaznov/dotfiles-doom-emacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Doom Emacs

Table of Contents

Introduction to the Config

Place your private configuration here! Remember, you do not need to run ‘doom sync’ after modifying this file!

;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-

Personal Info Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates and snippets.

(when (file-exists-p (expand-file-name "personal.el" doom-private-dir))
  (load! (expand-file-name "personal.el" doom-private-dir))
  )

Global Options

(setq
 doom-theme 'doom-gruvbox
 display-line-numbers-type 'relative
 scroll-margin 2
 window-divider-default-right-width 6 ;; set width of window dividers
 split-height-threshold nil ;; HACK diable to allow peep-dired to work (prefered 0)
 confirm-kill-emacs nil)

(xterm-mouse-mode 1)

Fonts

Doom exposes five (optional) variables for controlling fonts in Doom. Here are the three important ones:

  • ‘doom-font’ – standard monospace font that is used for most things in Emacs.
  • ‘doom-variable-pitch-font’ – variable font which is useful in some Emacs plugins.
  • ‘doom-big-font’ – used in doom-big-font-mode; useful for presentations.
  • ‘font-lock-comment-face’ – for comments.
  • ‘font-lock-keyword-face’ – for keywords with special significance like ‘setq’ in elisp.
  • ‘global-prettify-symbols-mode’ – change certain keywords to symbols, such as lambda!

They all accept either a font-spec, font string (“Input Mono-12”), or xlfd font string. You generally only need these two:

(setq
 doom-font                (font-spec :family "Hack Nerd Font" :size 16)
 doom-variable-pitch-font (font-spec :family "Hack Nerd Font")
 global-prettify-symbols-mode t)

(if (featurep :system 'macos) (setq
 doom-symbol-font         (font-spec :family "STIX Two Math")))

(custom-set-faces!
  '(font-lock-comment-face :slant italic)
  '(font-lock-keyword-face :slant italic))

(use-package! doom-themes
  :config
  (setq
   doom-themes-enable-bold t
   doom-themes-enable-italic t))

(use-package! emojify
  :defer t
  :hook (after-init . global-emojify-mode))

Core

Dashboard

Doom Dashboard

(setq
 fancy-splash-image (expand-file-name "doom-logo.png" doom-user-dir)
 +doom-dashboard-menu-sections '(
                                 ("Reload last session" :icon
                                  (nerd-icons-octicon "nf-oct-history" :face 'doom-dashboard-menu-title)
                                  :when
                                  (cond
                                   ((modulep! :ui workspaces)
                                    (file-exists-p
                                     (expand-file-name persp-auto-save-fname persp-save-dir)))
                                   ((require 'desktop nil t)
                                    (file-exists-p
                                     (desktop-full-file-name))))
                                  :action doom/quickload-session)
                                 ("Recently opened files" :icon
                                  (nerd-icons-faicon "nf-fa-file_text" :face 'doom-dashboard-menu-title)
                                  :action recentf-open-files)
                                 ("Open org-agenda" :icon
                                  (nerd-icons-octicon "nf-oct-calendar" :face 'doom-dashboard-menu-title)
                                  :when
                                  (fboundp 'org-agenda)
                                  :action org-agenda)
                                 ("Open project" :icon
                                  (nerd-icons-octicon "nf-oct-briefcase" :face 'doom-dashboard-menu-title)
                                  :action projectile-switch-project)
                                 ("Jump to bookmark" :icon
                                  (nerd-icons-octicon "nf-oct-bookmark" :face 'doom-dashboard-menu-title)
                                  :action bookmark-jump)
                                 ("Open private configuration" :icon
                                  (nerd-icons-octicon "nf-oct-tools" :face 'doom-dashboard-menu-title)
                                  :when
                                  (file-directory-p doom-user-dir)
                                  :action doom/open-private-config)
                                 ("Open documentation" :icon
                                  (nerd-icons-octicon "nf-oct-book" :face 'doom-dashboard-menu-title)
                                  :action doom/help)))

Emacs Dashboard

Modeline

(use-package! doom-modeline
  :config
  (remove-hook 'doom-modeline-mode-hook #'size-indication-mode) ; filesize in modeline
  (setq
   doom-modeline-height 32
   doom-modeline-column-zero-based nil
   doom-modeline-indent-info t
   doom-modeline-major-mode-icon t))

Dired

(use-package! dired
  :commands (dired dired-jump)
  :config
  (map! :map dired-mode-map
        :desc "Go to parent directory"      :n "h" #'dired-up-directory
        :desc "Go to directory / Open file" :n "l" #'dired-find-file
        :desc "Create symlink"              :n "S" #'dired-do-symlink
        :desc "Create new file"             :n "-" #'dired-create-empty-file))

Toggle hidden files in Dired

(use-package! dired-hide-dotfiles
  :after dired
  :hook (dired-mode . dired-hide-dotfiles-mode)

  :config
  (map! :map dired-mode-map
        :desc "Hide dot files"   :n "zo" (lambda () (interactive) (dired-hide-dotfiles-mode 0))
        :desc "Show dot files"   :n "zm" (lambda () (interactive) (dired-hide-dotfiles-mode 1))
        :desc "Toggle dot files" :n "za" #'dired-hide-dotfiles-mode
        :desc "Toggle dot files" :n "z." #'dired-hide-dotfiles-mode))

Peep

(use-package! peep-dired
  :after dired
  :config
  (add-hook 'peep-dired-hook 'evil-normalize-keymaps)
  (map! :map dired-mode-map
        :desc "Preview a file" :n "p" #'peep-dired

        :map peep-dired-mode-map
        :desc "Preview next file"        :n "k" #'peep-dired-prev-file
        :desc "Preview prev file"        :n "j" #'peep-dired-next-file
        :desc "Scroll preview pane up"   :n "C-k" #'peep-dired-scroll-page-up
        :desc "Scroll preview pane down" :n "C-j" #'peep-dired-scroll-page-down)

  (setq
   peep-dired-cleanup-eagerly nil
   peep-dired-cleanup-on-disable t))

Evil Mode

(use-package! evil
  :config
  (map! :map evil-insert-state-map
        :i "<C-h>" #'evil-delete-backward-char-and-join)

  (setq evil-cross-lines t))

Which Key

(use-package! which-key
  :init
  (setq which-key-idle-delay 0.4))

XDG

(use-package! xdg)

Smart Parens

(use-package! smartparens-mode
  :ensure smartparens
  ;; :defer t
  :hook (js-mode . smartparens-strict-mode)

  :config
  (require 'smartparens-config))

Org Mode

Org

(use-package! org
  :commands org-capture-goto-target
  :init
  (setq
   org-directory (if (featurep :system 'macos) "~/Documents/notes/"
                   (expand-file-name "notes/" (xdg-user-dir "DOCUMENTS")))
   org-agenda-files          (list org-directory)
   org-default-notes-file    (expand-file-name "notes.org" org-directory)
   +org-capture-journal-file (expand-file-name "journal.org" org-directory)
   org-archive-location      (expand-file-name "archive.org::datetree/" org-directory) ;; can also use "archive.org::datetrea/* %s"
   org-id-locations-file     (expand-file-name ".orgids" org-directory))

  :hook (org-mode . (lambda ()
                      (make-local-variable 'display-line-numbers)
                      (visual-line-mode -1)
                      (setq display-line-numbers 'visual)))

  :config
  (map! :mode org-mode
        :localleader
        :n "B" #'org-babel-tangle)

  (map! :map org-mode-map
        :desc "Move line(s) up"        :nv "<M-up>"    #'drag-stuff-up
        :desc "Move line(s) down"      :nv "<M-down>"  #'drag-stuff-down
        :desc "Move line(s) left"      :nv "<M-left>"  #'drag-stuff-left
        :desc "Move line(s) right"     :nv "<M-right>" #'drag-stuff-right
        :desc "Go to prev visual line" :n  "<up>"      #'evil-previous-visual-line
        :desc "Go to next visual line" :n  "<down>"    #'evil-next-visual-line)

  ;; (map! :map org-mode-map
  ;;       :n "<M-h>" #'org-table-previous-field
  ;;       :n "<M-j>" #'org-table-next-row
  ;;       :n "<M-k>" #'org-table-previous-row
  ;;       :n "<M-l>" #'org-table-next-field)

  ;; ----- org-capture -----------------------------------------------------
  (pushnew! org-capture-templates
            '("w" "Work todo" entry    (file+headline "work.org"    "Inbox") "* TODO %?\n%i\n%a" :prepend t)
            '("h" "Housing todo" entry (file+headline "housing.org" "Inbox") "* TODO %?\n%i\n%a" :prepend t))

  ;; ----- headings --------------------------------------------------------
  (setq org-ellipsis ""
        org-log-into-drawer t
        org-log-done 'time
        org-hide-emphasis-markers t)

  ;; ----- fonts -----------------------------------------------------------
  ;; font sizes for each header level in Org mode.
  (custom-set-faces
   '(org-level-1 ((t (:inherit outline-1 :height 1.2))))
   '(org-level-2 ((t (:inherit outline-2 :height 1.1))))
   '(org-level-3 ((t (:inherit outline-3 :height 1.0))))
   '(org-level-4 ((t (:inherit outline-4 :height 1.0))))
   '(org-level-5 ((t (:inherit outline-5 :height 1.0)))))

  ;; ----- org-special -----------------------------------------------------
  (setq org-src-window-setup 'other-frame)

  ;; ----- org-refile ------------------------------------------------------
  (setq org-refile-targets '((org-agenda-files :maxlevel . 1)))
  (advice-add 'org-refile :after 'org-save-all-org-buffers)

  ;; ----- org-archive -----------------------------------------------------
  (setq org-archive-subtree-add-inherited-tags t)

  ;; ----- org-publish -----------------------------------------------------
  (setq org-publish-project-alist
        '(("github.io"
           :base-directory "~/projects/git/artemsmaznov.github.io/org"
           :base-extension "org"
           :publishing-directory "~/projects/git/artemsmaznov.github.io"
           :recursive t
           :publishing-function org-html-publish-to-html
           :headline-levels 4
           :auto-preamble t
           :exclude "header.org")))

  ;; ----- Search Engines --------------------------------------------------
  ;; e.g. [[arch-wiki:emacs][Emacs Page]]
  (setq org-link-abbrev-alist
        '(("arch-wiki" . "https://wiki.archlinux.org/title/")
          ("pacman"    . "https://archlinux.org/packages/?name=")
          ("aur"       . "https://aur.archlinux.org/packages/")
          ("github"    . "https://github.com/")
          ("google"    . "http://www.google.com/search?q=")
          ("brave"     . "https://search.brave.com/search?q=")
          ("wiki"      . "https://en.wikipedia.org/wiki/")
          ))

  ;; ----- org-clock -------------------------------------------------------
  (map! :mode org-mode
        :localleader
        :prefix "c"
        :n "p" #'org-clock-display)

  (setq org-clock-persist 'history
        org-clock-idle-time nil)

  (org-clock-persistence-insinuate)

  ;; ----- Org templates ---------------------------------------------------
  (require 'org-tempo)

  ;; extra languages for src blocks
  (pushnew! org-structure-template-alist
            '("el" . "src emacs-lisp")
            '("js" . "src javascript")
            '("lu" . "src lua")
            '("py" . "src python")
            '("sh" . "src shell")
            '("ya" . "src yaml"))

  ;; extra org structure templates
  (pushnew! org-src-lang-modes
            '("conf-unix" . conf-unix)
            '("toml"      . conf-toml)))

Org Journal

(use-package! org-journal
  :after org
  :config
  (setq org-journal-hide-entries-p nil
        org-journal-search-results-order-by :desc
        org-journal-enable-encryption nil
        org-journal-encrypt-journal nil))

Roam

(use-package! org-roam
  :after org
  :config
  (setq org-roam-directory org-directory))

Org Agenda

(use-package! org-agenda
  :after org
  :defer t
  :config
  (map! :map org-agenda-mode-map
        :m "D"   #'org-agenda-day-view
        :m "W"   #'org-agenda-week-view
        :m "M"   #'org-agenda-month-view ;; doesn't work
        :m "T"   #'org-agenda-fortnight-view
        :m "C-." #'org-agenda-goto-today
        :m "C-h" #'org-agenda-earlier
        :m "C-l" #'org-agenda-later)

  (setq org-agenda-start-with-log-mode t
        org-agenda-start-day nil
        org-agenda-span 'week
        org-agenda-start-on-weekday 1
        org-deadline-warning-days 14))

Org Bullets

(use-package! org-superstar
  :after org
  :defer t
  :hook (org-mode . org-superstar-mode))

Org Auto Tangle

Put at the header of the Org document to enable auto tangle on save for it

#+auto_tangle: t
(use-package! org-auto-tangle
  :after org
  :defer t
  :hook (org-mode . org-auto-tangle-mode)
  :config
  (setq org-auto-tangle-babel-safelist
        '("README.org"
          "SHELLS.org"
          "local.org")))

Ox

We need ox-man for “Org eXporting” to manpage format.

(after! org
  (use-package ox-man))

Office Tools

Email

(setq rmail-spool-directory "/var/spool/mail/artem")

AuthInfo

Setting up ~/.authinfo.gpg with credentials

machine smtp.gmail.com login example@gmail.com password eXaMpLePaSsWoRd port 465

mu4e

  • Arch Linux: $ pacman -S isync $ paru -S mu

A custom variable containing an email address string needs to be defined for each context

(defvar my/email/main "example@gmail.com" "My primary email address")
(use-package! mu4e
  :defer t
  :init
  (setq
   doom-modeline-mu4e t)

  ;; start mu4e in the background so it auto-syncs emails
  (if (executable-find "mu")
      (mu4e t))

  :config
  (map! :map mu4e-view-mode-map
        :n "m" #'mu4e-view-mark-for-something
        :n "M" #'mu4e-view-mark-for-move
        :n "t" #'mu4e-view-mark-subthread
        :n "T" #'mu4e-view-mark-thread

        :map mu4e-headers-mode-map
        :n "m" #'mu4e-headers-mark-for-something
        :n "M" #'mu4e-headers-mark-for-move
        :n "t" #'mu4e-headers-mark-subthread
        :n "T" #'mu4e-headers-mark-thread)

  (setq
   mu4e-update-interval (* 5 60) ;; auto-sync interval in seconds
   mu4e-confirm-quit t

   mu4e-get-mail-command "true"
   mu4e-maildir-shortcuts
   '(("/inbox"   . ?i)
     ("/drafts"  . ?d)
     ("/sent"    . ?s)
     ("/archive" . ?a)
     ("/trash"   . ?t))

   +mu4e-header--maildir-colors
   '(("/sent"   . all-the-icons-dgreen)
     ("/drafts" . all-the-icons-yellow)
     ("/trash"  . all-the-icons-red))

   ;; headers - view listing the emails
   mu4e-split-view 'vertical
   mu4e-headers-visible-columns 170
   mu4e-headers-time-format "%l:%M:%S %p"
   mu4e-headers-date-format "%e %b %Y"
   mu4e-headers-long-date-format "%a, %e %B %Y, %l:%M:%S %p"

   ;; colum layout for mail list
   mu4e-headers-fields
   '((:account-stripe . 1)
     (:flags          . 7)
     (:human-date     . 12)
     (:from-or-to     . 25)
     (:thread-subject . nil))

   ;; message
   ;; mu4e-view-date-format "%c"
   ;; mu4e-date-format-long "%c"
   message-kill-buffer-on-exit t ;; don't keep message buffers

   ;; composing
   mu4e-compose-format-flowed t ;; use html formatting for outgoing emails
   mu4e-compose-dont-reply-to-self t

   ;; contexts
   user-full-name "Artem Smaznov"
   mu4e-context-policy 'pick-first
   mu4e-compose-context-policy 'ask-if-none
   ;; mu4e-index-cleanup nil ;; don't need to run cleanup after indexing for gmail
   ;; mu4e-index-lazy-check t ;; because gmail uses labels as folders we can use lazy check since messages don't really "move"
   mu4e-contexts
   `(
     ,(make-mu4e-context
       :name "spool"
       ;; :vars `((user-mail-address  . ,my/email/artem))
       )))

  ;; modeline
  (setq
   ;; mu4e-alert-interesting-mail-query "flag:unread AND NOT flag:trashed AND NOT maildir:\"/[Gmail]/All Mail\""
   mu4e-display-update-status-in-modeline t))
;; (use-package! mu4e
;;   :defer t
;;   :init
;;   (setq
;;    doom-modeline-mu4e t)
;;   :config
;;   (map! :map mu4e-view-mode-map
;;         :n "m" #'mu4e-view-mark-for-something
;;         :n "M" #'mu4e-view-mark-for-move
;;         :n "t" #'mu4e-view-mark-subthread
;;         :n "T" #'mu4e-view-mark-thread

;;         :map mu4e-headers-mode-map
;;         :n "m" #'mu4e-headers-mark-for-something
;;         :n "M" #'mu4e-headers-mark-for-move
;;         :n "t" #'mu4e-headers-mark-subthread
;;         :n "T" #'mu4e-headers-mark-thread)

;;   (setq
;;    mu4e-get-mail-command (concat "mbsync -a -c " (xdg-config-home) "/isync/mbsyncrc" )
;;    mu4e-update-interval (* 15 60) ;; auto-sync interval in seconds
;;    mu4e-maildir-shortcuts
;;    '(("/Inbox"             . ?i)
;;      ("/Work"              . ?w)
;;      ("/[Gmail]/Important" . ?I)
;;      ("/[Gmail]/Sent Mail" . ?s)
;;      ("/[Gmail]/Drafts"    . ?d)
;;      ("/[Gmail]/All Mail"  . ?a)
;;      ("/[Gmail]/Trash"     . ?t))
;;    +mu4e-header--maildir-colors
;;    '(("/Inbox"      . all-the-icons-yellow)
;;      ("/Work"       . all-the-icons-red)
;;      ("[Gmail]"     . all-the-icons-dgreen)))

;;   ;; headers - view listing the emails
;;   (setq
;;    mu4e-split-view 'vertical
;;    mu4e-headers-visible-columns 170
;;    mu4e-headers-time-format "%l:%M:%S %p"
;;    mu4e-headers-date-format "%e %b %Y"
;;    mu4e-headers-long-date-format "%a, %e %B %Y, %l:%M:%S %p"
;;    ;; colum layout for mail list
;;    mu4e-headers-fields
;;    '((:account-stripe . 1)
;;      (:flags          . 7)
;;      (:human-date     . 12)
;;      (:from-or-to     . 25)
;;      (:thread-subject . nil)))

;;   ;; message
;;   (setq
;;    ;; mu4e-view-date-format "%c"
;;    ;; mu4e-date-format-long "%c"
;;    message-kill-buffer-on-exit t) ;; don't keep message buffers

;;   ;; composing
;;   (setq
;;    mu4e-compose-format-flowed t ;; use html formatting for outgoing emails
;;    mu4e-compose-dont-reply-to-self t)

;;   ;; contexts
;;   (setq
;;    user-full-name "Artem Smaznov"
;;    mu4e-context-policy 'pick-first
;;    mu4e-compose-context-policy 'ask-if-none
;;    ;; mu4e-index-cleanup nil ;; don't need to run cleanup after indexing for gmail
;;    ;; mu4e-index-lazy-check t ;; because gmail uses labels as folders we can use lazy check since messages don't really "move"
;;    mu4e-contexts
;;    `(
;;      ;; ,(make-mu4e-context
;;      ;;   :name "Artem"
;;      ;;   :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to my/email/artem)))
;;      ;;   :vars `((smtpmail-smtp-server  . "smtp.gmail.com")
;;      ;;           (smtpmail-smtp-service . 465)
;;      ;;           (smtpmail-stream-type  . ssl)
;;      ;;           (user-mail-address     . ,my/email/artem)
;;      ;;           (mu4e-drafts-folder    . "/[Gmail]/Drafts")
;;      ;;           (mu4e-sent-folder      . "/[Gmail]/Sent Mail")
;;      ;;           (mu4e-refile-folder    . "/[Gmail]/All Mail")
;;      ;;           (mu4e-trash-folder     . "/[Gmail]/Trash")))
;;      ,(make-mu4e-context
;;        :name "Main"
;;        :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg :to my/email/main)))
;;        ;; :match-func (lambda (msg) (when msg (string-prefix-p "/Main" (mu4e-message-field msg :maildir))))
;;        :vars `((user-mail-address  . ,my/email/main)
;;                (mu4e-drafts-folder . "/[Gmail]/Drafts")
;;                (mu4e-sent-folder   . "/[Gmail]/Sent Mail")
;;                (mu4e-refile-folder . "/[Gmail]/All Mail")
;;                (mu4e-trash-folder  . "/[Gmail]/Trash")))))

;;      ;; start mu4e in the background so it auto-syncs emails
;;      (mu4e t)

;;      ;; modeline
;;      (setq
;;       mu4e-alert-interesting-mail-query "flag:unread AND NOT flag:trashed AND NOT maildir:\"/[Gmail]/All Mail\""
;;       mu4e-display-update-status-in-modeline t))

Authentication

Function used by mbsync for authentication with the email server

(defun my/lookup-password (&rest keys)
  (let ((result (apply #'auth-source-search keys)))
    (if result
        (funcall (plist-get (car result) :secret))
        nil)))

Calendar

CalFW

(use-package! calfw
  :defer t
  :init
  (map! :leader
        :prefix "o"
        :desc "Calendar" :e "c" #'cfw:open-org-calendar)
  :config
  (map! :map cfw:calendar-mode-map
        :m "C-j" #'cfw:navi-next-month-command
        :m "C-k" #'cfw:navi-previous-month-command
        :m "C-." #'cfw:navi-goto-today-command
        :m "0"   #'cfw:navi-goto-week-begin-command
        :m "gd"  #'cfw:org-goto-date
        :m "zd"  #'cfw:change-view-day
        :m "zw"  #'cfw:change-view-week
        :m "zm"  #'cfw:change-view-month
        :m "zt"  #'cfw:change-view-two-weeks
        :m "T"   #'cfw:change-view-two-weeks) ;; not active due to evil-snipe and evil-find-char

  (setq
   calendar-week-start-day 1
   calendar-date-style 'european))

iCalendar

(use-package! icalendar
  :defer t
  :config
  (setq
   org-icalendar-use-scheduled '(event-if-todo event-if-not-todo todo-start)
   org-icalendar-use-deadline '(event-if-todo-not-done)))

CalDAV sync

(use-package! org-caldav
  :after calfw
  :config
  (map! :map cfw:calendar-mode-map
        :localleader
        :desc "Sync with server" :n "S" #'org-caldav-sync)

  (setq
   org-caldav-url (concat "https://" my/nextcloud/url "/remote.php/dav/calendars/" my/username)
   org-caldav-delete-calendar-entries 'always
   org-caldav-delete-org-entries 'ask
   org-caldav-show-sync-results nil
   org-caldav-save-directory (expand-file-name ".caldav/" org-directory)
   org-caldav-backup-file (expand-file-name "backup.org" org-caldav-save-directory)
   org-caldav-location-newline-replacement ","
   org-caldav-exclude-tags '("nocal")
   org-caldav-calendars `((:calendar-id "personal" :select-tags ("calgnr")
                           :inbox ,(expand-file-name "todo.org" org-directory)
                           :files (,(expand-file-name "todo.org" org-directory)
                                   ,(expand-file-name "archive.org" org-directory)))

                          (:calendar-id "housing" :select-tags ("calhsn")
                           :inbox ,(expand-file-name "housing.org" org-directory)
                           :files (,(expand-file-name "housing.org" org-directory)
                                   ,(expand-file-name "archive.org" org-directory)))

                          (:calendar-id "work" :select-tags ("calwrk")
                           :inbox ,(expand-file-name "work.org" org-directory)
                           :files (,(expand-file-name "work.org" org-directory)
                                   ,(expand-file-name "archive.org" org-directory))))))

Ledger

(use-package! ledger-mode
  :defer t
  :config
  (map! :map ledger-mode-map
        :localleader
        :e "c" #'ledger-mode-clean-buffer)

  (setq ledger-default-date-format "%Y-%m-%d"))

Presentations

(use-package! org-tree-slide
  :after org
  :defer t
  :init
  (map! :map org-mode-map
        :leader
        :prefix "t"
        :desc "Presentation" :e "p" #'org-tree-slide-mode)

  :hook ((org-tree-slide-play . my/presentation-start)
         (org-tree-slide-stop . my/presentation-end))

  :config
  (map! :map org-tree-slide-mode-map
        "C-h"   #'org-tree-slide-move-previous-tree
        "C-l"   #'org-tree-slide-move-next-tree
        "C-SPC" #'org-tree-slide-content)

  (setq
   org-tree-slide-activate-message "Presentation started!"
   org-tree-slide-deactivate-message "Presentation finished!"
   org-tree-slide-slide-in-effect t
   org-tree-slide-header t
   org-tree-slide-breadcrumbs " > "
   org-image-actual-width nil))
(defun my/presentation-start ()
  (writeroom-mode 1)
  (display-line-numbers-mode 0)
  (org-display-inline-images) ;; Can also use org-startup-with-inline-images
  )

(defun my/presentation-end ()
  (writeroom-mode 0)
  (display-line-numbers-mode 1)
  )

Development Tools

Git

Magit

(use-package! magit
  :defer t
  :config
  (setq
   magit-repository-directories `((,(xdg-config-home) . 1)
                                  ("~/.local/bin" . 0)
                                  ("~/projects" . 5))

   magit-revision-show-gravatars t ;; enable gravatars
   ;; magit-display-buffer-function 'magit-display-buffer-traditional ;; open magit in a side window

   ;; enable granular diff-highlights for all hunks
   ;; change to t if performance is bad
   magit-diff-refine-hunk 'all
   magit-repolist-column-flag-alist ' ((magit-untracked-files . "?")
                                       (magit-unstaged-files . "!")
                                       (magit-staged-files . "+"))
   magit-repolist-columns ' (("" 10 magit-repolist-column-branch ((:right-align t)))
                             ("B<U" 3 magit-repolist-column-unpulled-from-upstream ((:right-align t) (:sort <)))
                             ("B>U" 3 magit-repolist-column-unpushed-to-upstream ((:right-align t) (:sort <)))
                             ("F" 3 magit-repolist-column-flags nil)
                             ("Name" 25 magit-repolist-column-ident nil)
                             ("Version" 25 magit-repolist-column-version ((:sort magit-repolist-version<)))
                             ("Path" 99 magit-repolist-column-path nil))

   magit-submodule-list-columns ' (("Path" 40 magit-modulelist-column-path nil)
                                   ("Version" 25 magit-repolist-column-version
                                    ((:sort magit-repolist-version<)))
                                   ("Branch" 20 magit-repolist-column-branch nil)
                                   ("B<U" 3 magit-repolist-column-unpulled-from-upstream
                                    ((:right-align t)
                                     (:sort <)))
                                   ("B>U" 3 magit-repolist-column-unpushed-to-upstream
                                    ((:right-align t)
                                     (:sort <)))
                                   ("B<P" 3 magit-repolist-column-unpulled-from-pushremote
                                    ((:right-align t)
                                     (:sort <)))
                                   ("B>P" 3 magit-repolist-column-unpushed-to-pushremote
                                    ((:right-align t)
                                     (:sort <)))
                                   ("B" 3 magit-repolist-column-branches
                                    ((:right-align t)
                                     (:sort <)))
                                   ("S" 3 magit-repolist-column-stashes
                                    ((:right-align t)
                                     (:sort <))))))

Forge

(use-package! forge
  :after magit
  :defer t

  :init
  (if (featurep :system 'macos)
      (setq doom-modeline-github nil)
      (setq doom-modeline-github t))

  :config
  (setq
   forge-pull-notifications nil
   forge-repository-list-columns '(("Owner" 20 t nil owner nil)
                                   ("N" 1 t nil sparse-p nil)
                                   ("S" 1 t nil selective-p nil)
                                   ("Name" 50 t nil name nil)
                                   ("Worktree" 99 t nil worktree nil))))

Code Review

(use-package! code-review
  :after magit
  :defer t
  :config
  (map! :map magit-mode-map
        "R" #'code-review-forge-pr-at-point

        :map forge-topic-mode-map
        "R" #'code-review-forge-pr-at-point))

Todos

(use-package! magit-todos
  :after magit
  :defer t
  :hook
  (magit-mode . magit-todos-mode)
  :config
  (pushnew! magit-todos-exclude-globs
            "Basemark*/"
            "Brave*/"
            "Code*/"
            "Cypress/"
            "GIMP/"
            "KDE/"
            "Nextcloud/"
            "chromium/"
            "coc/"
            "discord/"
            "glib*/"
            "google*/"
            "google-chrome/"
            "kde*/"
            "torbrowser*/"
            "unity*/"
            "vivaldi*/"
            "{emacs,doom}/"))

Ansible

(use-package! ansible
  :defer t
  :hook
  (yaml-mode . (lambda ()
                 (if (s-contains? "ansible" (file-name-directory buffer-file-name) t)
                     (ansible 1)))))

Projectile

(use-package! projectile
  :defer t
  :init
  (setq projectile-switch-project-action #'projectile-dired)
  (when (file-directory-p "~/projects")
    (setq projectile-project-search-path '("~/projects")))
  :config
  (map! :leader
        :prefix "p"
        :desc "Run project"        :e "A" #'projectile-run-project
        :desc "Project substitute" :e "R" #'projectile-replace-regexp))

Treemacs

(use-package! lsp-treemacs
  :defer t
  :commands lsp-treemacs-errors-list)

Completion

(use-package! company
  :defer t
  :config
  (map! :after lsp-mode
        :map lsp-mode-map
        :i "<tab>" #'company-indent-or-complete-common)

  (setq
   company-idle-delay 0.5
   company-tooltip-idle-delay 2
   company-minimum-prefix-length 1))

Ivy

(use-package! lsp-ivy
  :defer t
  :commands lsp-ivy-workspace-symbol)

Rotate Text

To enable a set of items to cycle through globally, add the following to your configuration

(use-package! rotate-text
  :defer t
  :config
  (pushnew! rotate-text-words
            '("on" "off")
            '("yes" "no")))

Indent Guides

(use-package! highlight-indent-guides
  :defer t
  :config
  (setq highlight-indent-guides-method 'fill))

Rainbow Mode

Highlight colors in file

(use-package! rainbow-mode
  :defer t
  :init
  (map! :leader
        :prefix "t"
        :desc "Colors" :e "c" #'rainbow-mode))

LSP

LSP Mode

(use-package! lsp-mode
  :defer t
  :commands (lsp lsp-deferred)
  :hook
  (rjsx-mode    . lsp-deferred)
  (python-mode  . lsp-deferred)
  (feature-mode . lsp-deferred)
  (vimrc-mode   . lsp-deferred)
  (groovy-mode  . lsp-deferred))

See this for LSP UI elements and their respective variables

(use-package! lsp-ui
  :defer t
  :commands lsp-ui-mode
  :hook
  (lsp-mode . lsp-ui-mode)

  :config
  (setq
   ;; 1. Symbol highlighting
   lsp-enable-symbol-highlighting t

   ;; 2. `lsp-ui-doc` hover dialogs
   lsp-ui-doc-enable t
   lsp-ui-doc-delay 0.75
   lsp-ui-doc-show-with-cursor t
   lsp-ui-doc-show-with-mouse nil

   lsp-ui-doc-position 'top
   lsp-ui-doc-alignment 'window
   lsp-ui-doc-header t
   lsp-ui-doc-border "gray"

   ;; 3. Lenses
   lsp-lens-enable t

   ;; 4. Headerline
   lsp-headerline-breadcrumb-enable t

   ;; 5. Sideline code actions
   lsp-ui-sideline-enable t
   lsp-ui-sideline-show-code-actions t

   ;; 6. Sideline hover symbols
   lsp-ui-sideline-show-hover nil

   ;; 7. Modeline code actions
   lsp-modeline-code-actions-enable t

   ;; 8. Flycheck / flymake
   ;; lsp-diagnostics-provider :auto

   ;; 9. Sideline diagnostics
   lsp-ui-sideline-show-diagnostics t

   ;; 10. Eldoc
   lsp-eldoc-enable-hover t

   ;; 11. Modeline diagnostics statistics
   lsp-modeline-diagnostics-enable t

   ;; 12. Signarure help
   ;; lsp-signature-auto-activate '(:on-trigger-char :on-server-request)

   ;; 13.
   lsp-signature-render-documentation t

   ;; 14. Completion
   ;; lsp-completion-provider :capf

   ;; 15. Completion item detail
   lsp-completion-show-detail t

   ;; 16. Completion item kind
   lsp-completion-show-kind t))

JavaScript

;; (use-package! rjsx-mode
;;   :ensure t
;;   :mode
;;   "\\.js\\'"
;; )

Python

  • macOS: $ brew install pyright
  • Arch Linux: $ pacman -S pyright

Groovy

(use-package! groovy-mode
  :defer t
  :mode
  "/Jenkinsfile.*\\'"
  "\\.pipe\\'"
  "\\.PIPE\\'"

  :hook
  (groovy-mode . (lambda () (rainbow-delimiters-mode 1)))

  :config
  (setq groovy-indent-offset 2))

Yaml

(use-package! yaml-mode
  :defer t
  :hook
  (yaml-mode . (lambda ()
                   (spell-fu-mode -1))))

Json

(use-package! jsonc-mode
  :defer t
  :mode
  "\\.jsonc\\'"
)

Conf

(use-package! conf-mode
  :defer t
  :mode
  "\\.automount\\'"
  "\\.mount\\'"
  "\\.path\\'"
  "\\.service\\'"
  "\\.slice\\'"
  "\\.socket\\'"
  "\\.target\\'"
  "\\.timer\\'"
  )

Crontab

(use-package! crontab-mode
  :defer t
  ;; :mode
  ;; "cron\\(.d\\)?"
  )

Jinja

(use-package! jinja2-mode
  :defer t
  :hook
  (jinja2-mode . (lambda ()
                   (spell-fu-mode -1))))

Cucumber

(use-package! feature-mode
  :defer t
  :mode
  "\\.feature\\'"

  :config
  (setq
   feature-default-language "en"
   ;; feature-step-search-path "features/../**/*step*/*.js"
   ))

Vim

Enable syntax highlighting for .vim files

(use-package! vimrc-mode
  :defer t
  :mode
  "\\.vim\\(rc\\)?\\'"
  "\\.vifm\\'"

  :config
  (setq evil-shift-width 2))

Logs

(use-package! syslog-mode
  :defer t
  :mode
  "\\.log"
  "\\.[0-9]+\\'"

  :hook
  (syslog-mode . (lambda ()
                   (make-local-variable 'display-line-numbers-type)
                   (setq display-line-numbers-type t)
                   (display-line-numbers-mode 1))))

RSS

Elfeed

(use-package! elfeed
  :defer t
  :init
  (map! :leader
        :prefix "o"
        :desc "RSS News" :e "n" #'elfeed)

  :config
  (map! :mode elfeed-search-mode
        :desc "Remove Selected" :n "D" #'my/elfeed-search-remove-selected

        :mode (elfeed-search-mode elfeed-show-mode)
        :localleader
        :desc "Show starred" :n "s" #'my/elfeed-show-starred
        :desc "Toggle logs"  :n "l" #'elfeed-goodies/toggle-logs
        :desc "Update"       :n "u" #'elfeed-update)

  (elfeed-set-timeout 36000)
  (setq
   elfeed-log-level 'info
   elfeed-goodies/log-window-position 'left
   elfeed-goodies/wide-threshold 0.3
   elfeed-goodies/show-mode-padding 1
   elfeed-goodies/entry-pane-size 0.5
   elfeed-goodies/feed-source-column-width 20
   elfeed-use-curl t
   elfeed-search-date-format '("%d-%m-%Y" 10 :left)
   elfeed-search-filter "@1-month-ago +unread")

  (defun my/elfeed-show-starred ()
    "Show all starred feeds"
    (interactive)
    (elfeed-search-set-filter "+star"))

  (defun my/elfeed-db-remove-entry (id)
    "Removes elfeed entry for given ID"
    (avl-tree-delete elfeed-db-index id)
    (remhash id elfeed-db-entries))

  (defun my/elfeed-search-remove-selected ()
    "Remove selected entries from elfeed database"
    (interactive)
    (let* ((entries (elfeed-search-selected))
           (count (length entries)))
      (when (y-or-n-p (format "Delete %d entires?" count))
        (cl-loop for entry in entries
                 do (my/elfeed-db-remove-entry (elfeed-entry-id entry)))))
    (elfeed-search-update--force)))

Elfeed Org

(use-package! elfeed-org
  :after elfeed
  :config
  (setq
   rmh-elfeed-org-files (list (expand-file-name "rss.org" org-directory))
   rmh-elfeed-org-tree-id "elfeed"
   rmh-elfeed-org-ignore-tag "ignore"))

Elfeed Protocol

(use-package! elfeed-protocol
  :after elfeed elfeed-org
  :config
  (defadvice elfeed (after configure-elfeed-protocol-feeds activate)
    "Make elfeed-org autotags rules works with elfeed-protocol."
    (setq
     elfeed-protocol-feeds (list
                            (list (concat "owncloud+https://" my/username "@" my/nextcloud/url)
                                  :use-authinfo t
                                  :autotags  elfeed-feeds)))
    (elfeed-update))

  (setq
   elfeed-protocol-enabled-protocols '(owncloud)
   elfeed-protocol-owncloud-fetch-category-as-tag nil
   elfeed-protocol-owncloud-update-with-modified-time t
   elfeed-protocol-owncloud-star-tag 'star)
  (elfeed-protocol-enable))

Spell Checking

  • macOS: $ brew install aspell
  • Arch Linux: $ pacman -S aspell aspell-en aspell-ru
(use-package! spell-fu
  :defer t
  :hook
  (spell-fu-mode
   . (lambda ()
       ;; extra languages
       (spell-fu-dictionary-add (spell-fu-get-ispell-dictionary "ru"))
       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "ru"
                                 (expand-file-name
                                  "dict/ru.pws" (xdg-data-home))))

       ;; extra personal dictionaries
       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "people"
                                 (expand-file-name
                                  "dict/en.people.pws" (xdg-data-home))))

       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "places"
                                 (expand-file-name
                                  "dict/en.places.pws" (xdg-data-home))))

       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "brands"
                                 (expand-file-name
                                  "dict/en.brands.pws" (xdg-data-home))))

       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "finance"
                                 (expand-file-name
                                  "dict/en.finance.pws" (xdg-data-home))))

       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "dev"
                                 (expand-file-name
                                  "dict/en.dev.pws" (xdg-data-home))))

       (spell-fu-dictionary-add (spell-fu-get-personal-dictionary
                                 "work"
                                 (expand-file-name
                                  "dict/en.work.pws" (xdg-data-home))))))

  :config
  (setq
   spell-fu-word-delimit-camel-case t
   spell-fu-idle-delay 0.25
   ispell-personal-dictionary (expand-file-name
                               "dict/en.pws" (xdg-data-home))))

Global Keybindings

(map! :leader
      ;; buffer/bookmark
      :prefix "b"
      :desc "List bookmarks"                          :e "L" #'list-bookmarks
      :desc "Save current bookmarks to bookmark file" :e "w" #'bookmark-save
      ;; :desc "Clone indirect buffer other window" "c" #'clone-indirect-buffer-other-window

      ;; insert
      :prefix "i"
      :desc "Toilet pagga" :e "t" (cmd! (evil-ex "R!toilet -f pagga "))

      ;; <localleader>
      :prefix "m"
      :desc "Justify text"    :e "j" #'set-justification

      ;; toggle
      :prefix "t"
      :desc "Toggle auto fill mode" :e "a" #'auto-fill-mode
      :desc "Toggle scroll bars"    :e "S" #'scroll-bar-mode
      :desc "Fill column indicator" :e "|" #'global-display-fill-column-indicator-mode

      ;; workspace
      :prefix "TAB"
      :desc "Move workspace left"  :e "<" #'+workspace/swap-left
      :desc "Move workspace right" :e ">" #'+workspace/swap-right)

Local Overwrite

Load custom configuration overwrites from and external file

(when (file-exists-p (expand-file-name "local.el" doom-private-dir))
  (load! (expand-file-name "local.el" doom-private-dir))
  )

Additional Notes

Whenever you reconfigure a package, make sure to wrap your config in an `after!’ block, otherwise Doom’s defaults may override your settings. E.g.

(after! PACKAGE
    (setq x y))

The exceptions to this rule:

  • Setting file/directory variables (like `org-directory’)
  • Setting variables which explicitly tell you to set them before their package is loaded (see ‘C-h v VARIABLE’ to look up their documentation).
  • Setting doom variables (which start with ‘doom-’ or ‘+’).

Here are some additional functions/macros that will help you configure Doom.

  • `load!’ for loading external *.el files relative to this one
  • `use-package!’ for configuring packages
  • `after!’ for running code after a package has loaded
  • `add-load-path!’ for adding directories to the `load-path’, relative to

this file. Emacs searches the `load-path’ when you load packages with `require’ or `use-package’.

  • `map!’ for binding new keys

To get information about any of these functions/macros, move the cursor over the highlighted symbol at press ‘K’ (non-evil users must press ‘C-c c k’). This will open documentation for it, including demos of how they are used. Alternatively, use `C-h o’ to look up a symbol (functions, variables, faces, etc).

You can also try ‘gd’ (or ‘C-c c d’) to jump to their definition and see how they are implemented.