Skip to content

Latest commit

 

History

History
1465 lines (1277 loc) · 44.8 KB

README.org

File metadata and controls

1465 lines (1277 loc) · 44.8 KB

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))

(doom-big-font-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 "writing/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
  ;;           ;; '("j" "Journal"      entry (file+olp+datetree +org-capture-journal-file) "* %U %?\n%i\n%a"   :prepend t)
  ;;           '("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
  :init
  (setq
   org-journal-dir (expand-file-name "writing/" org-directory))

  :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 "todo.org" org-directory)
                           :files (,(expand-file-name "todo.org" org-directory)
                                   ,(expand-file-name "archive.org" org-directory)))

                          (:calendar-id "work" :select-tags ("calwrk")
                           :inbox ,(expand-file-name "todo.org" org-directory)
                           :files (,(expand-file-name "todo.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
            '("yes" "no")
            '("on" "off")
            '("start" "end")))

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.