Simon's GNU Emacs Configuration

6131 Words 10 October 2023

This is my emacs configuration. Do with is as you will. This configuration is a work in progress, so anything can break at any point. You can also find this file in my dots.

PACKAGE MANAGEMENT

Configuration for Elpaca An Elisp Package Manager:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  (setq elpaca-core-date '(20240101))
   (defvar elpaca-installer-version 0.7)
   (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
   (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
   (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
   (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
                                 :ref nil :depth 1
                                 :files (:defaults "elpaca-test.el" (:exclude "extensions"))
                                 :build (:not elpaca--activate-package)))
   (let* ((repo  (expand-file-name "elpaca/" elpaca-repos-directory))
          (build (expand-file-name "elpaca/" elpaca-builds-directory))
          (order (cdr elpaca-order))
          (default-directory repo))
     (add-to-list 'load-path (if (file-exists-p build) build repo))
     (unless (file-exists-p repo)
       (make-directory repo t)
       (when (< emacs-major-version 28) (require 'subr-x))
       (condition-case-unless-debug err
           (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                    ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
                                                    ,@(when-let ((depth (plist-get order :depth)))
                                                        (list (format "--depth=%d" depth) "--no-single-branch"))
                                                    ,(plist-get order :repo) ,repo))))
                    ((zerop (call-process "git" nil buffer t "checkout"
                                          (or (plist-get order :ref) "--"))))
                    (emacs (concat invocation-directory invocation-name))
                    ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
                                          "--eval" "(byte-recompile-directory \".\" 0 'force)")))
                    ((require 'elpaca))
                    ((elpaca-generate-autoloads "elpaca" repo)))
               (progn (message "%s" (buffer-string)) (kill-buffer buffer))
             (error "%s" (with-current-buffer buffer (buffer-string))))
         ((error) (warn "%s" err) (delete-directory repo 'recursive))))
     (unless (require 'elpaca-autoloads nil t)
       (require 'elpaca)
       (elpaca-generate-autoloads "elpaca" repo)
       (load "./elpaca-autoloads")))
   (add-hook 'after-init-hook #'elpaca-process-queues)
   (elpaca `(,@elpaca-order))

GENERAL BEHAVIOUR

General settings

1
2
      (global-display-line-numbers-mode t)
      (setq display-line-numbers-type 'relative)

Undo Tree

1
2
3
4
5
  (use-package undo-tree
    :ensure t
    :init
    (global-undo-tree-mode)
    )

Consult

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  (use-package consult
    :ensure t
      :bind (
             ("C-s" . consult-line)          ;; Line search
             ("M-y" . consult-yank-pop)      ;; Yank pop
             ("C-x b" . consult-buffer)      ;; Buffer switch
             ("C-x 4 b" . consult-buffer-other-window)  ;; Buffer switch in other window
             ("C-x 5 b" . consult-buffer-other-frame)   ;; Buffer switch in other frame
             ("M-g g" . consult-goto-line)   ;; Go to line
             ("M-g M-g" . consult-goto-line) ;; Go to line
             ("M-g o" . consult-outline)     ;; Outline navigation
             ("M-g m" . consult-mark)        ;; Mark navigation
             ("M-g k" . consult-global-mark) ;; Global mark navigation
             ("M-s r" . consult-ripgrep)     ;; Ripgrep search
             ("M-s l" . consult-locate)      ;; Locate file
             ("M-s g" . consult-grep)        ;; Grep search
             )
      :init
      (setq register-preview-delay 0.5
            register-preview-function #'consult-register-format)
      :config
      ;; Optionally configure other settings here
      (consult-customize consult-theme
                         :preview-key '(:debounce 0.2 any))
      (consult-customize consult-buffer
                         consult-buffer-other-window
                         consult-buffer-other-frame
                         :preview-key "M-.")

      )

Hydra

Sticky bindings: Hydra

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  (use-package hydra
    :ensure t
    :config
    ;; Define a hydra for consult commands
    (defhydra hydra-consult (:color blue :hint nil)
      "
                    Consult Commands
                    ^Search^           ^Navigate^            ^Misc^
                    ^^^^^^^^——————————————————————————————————————————————
                    [_s_] Search Line  [_l_] Line            [_m_] Mark
                    [_r_] Ripgrep      [_b_] Buffer          [_h_] Outline
                    [_g_] Grep         [_f_] Buffer (other)  [_t_] Theme
                    [_o_] Locate       [_p_] Projectile      [_q_] Quit
        "
      ("s" consult-line)
      ("r" consult-ripgrep)
      ("g" consult-grep)
      ("o" consult-locate)
      ("l" consult-line)
      ("b" consult-buffer)
      ("f" consult-buffer-other-window)
      ("P" consult-projectile-switch-project)
      ("pf" consult-projectile-find-file)
      ("pp" consult-projectile-switch-project)
      ("pb" consult-projectile-switch-to-buffer)
      ("pq" projectile-run-command)
      ("m" consult-mark)
      ("h" consult-outline)
      ("t" consult-theme)
      ("q" nil :exit t))
    )

Projectile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  (use-package projectile
    :ensure t
    :init
    (projectile-mode +1)  ; Enable projectile globally
    :config
    (setq projectile-project-search-path '("~/Projects/"))  ; Adjust path as needed
    (setq projectile-completion-system 'default)
    (setq projectile-project-root-files-bottom-up (append projectile-project-root-files-bottom-up
                                                          '(".git")))

;; Define the Projectile Hydra
  (defhydra hydra-projectile (:color blue :hint nil)
    "
               Projectile Commands
               ^Files^         ^Buffers^        ^Project^
               ^^^^^^————————————————————————————————————————————————————————————————————
               [_f_] Find File [_b_] Switch to Buffer [_p_] Switch Project
               [_F_] Find File in Known Projects [_k_] Kill Project Buffers [_D_] Discover Projects
               [_r_] Recent File [_s_] Save Project Buffers [_i_] Invalidate Cache
               [_d_] Find Directory [_a_] Ag (Search) [_q_] Quit
    "
    ("f" projectile-find-file)
    ("F" projectile-find-file-in-known-projects)
    ("r" projectile-recentf)
    ("d" projectile-find-dir)
    ("b" projectile-switch-to-buffer)
    ("k" projectile-kill-buffers)
    ("s" projectile-save-project-buffers)
    ("p" projectile-switch-project)
    ("D" projectile-discover-projects-in-directory)
    ("i" projectile-invalidate-cache)
    ("a" projectile-ag)
    ("q" nil "quit" :color red :exit t)))

Evil Mode

I like vim key bindings: EVIL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        (elpaca elpaca-use-package
          (elpaca-use-package-mode)
          (setq elpaca-use-package-by-default t))
        (elpaca-wait)


        (use-package evil
          :init
          (setq evil-set-undo-system 'undo-tree)
          (setq evil-want-keybinding nil)
          (setq evil-want-integration t)
          (setq evil-vsplit-window-right t)
          (setq evil-split-window-below t)
          (setq evil-want-C-i-jump nil)
          :config
          (evil-mode 1)
          (define-key evil-normal-state-map (kbd "u") 'undo-tree-undo)
          (define-key evil-normal-state-map (kbd "U") 'undo-tree-redo))

  (use-package evil-collection
    :after evil
    :config
    (setq evil-collection-mode-list '(dashboard dired ibuffer))
    (evil-collection-init)
    (with-eval-after-load 'doomlike
      (evil-define-key 'normal 'global (kbd "SPC") 'doomlike/body)))

General

1
2
  (require 'ido)
  (ido-mode t)

Avy

Jumping around the buffer: avy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
  (use-package avy
    :ensure t
    :config
    (defhydra hydra-avy (:color blue :hint nil)
      "
                Avy Goto
                ^Char^   ^Line^   ^Word^  Description
                ^^^^^——————————————————————————————————————-
                [_c_]    [_l_]    [_w_]   Jump To
                [_C_]    [_L_]    [_W_]   Go To
      "
      ("c" avy-goto-char)
      ("l" avy-goto-line)
      ("w" avy-goto-word-0)
      ("C" avy-goto-char-2)
      ("L" avy-goto-line-above)
      ("W" avy-goto-word-1)
      ("q" nil :exit t)))

Ivy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(use-package ivy
  :ensure t
  :config
  (ivy-mode 1))

(use-package counsel
  :ensure t)

(use-package swiper
  :ensure t)

Tabs

Spell Checking

1
2
3
4
  (setq ispell-program-name "hunspell")
  (setq ispell-program-name (executable-find "hunspell"))
  (setq ispell-local-dictionary "en_US")
  (setq ispell-personal-dictionary "~/.config/dictionary/personal_dict")

External Programs

1
2
3
4
5
6
7
(use-package openwith
  :config
  (openwith-mode t)
  (setq openwith-associations '(("\\.pdf\\'" "zathura" (file))))

  ;; Open PDF files externally
  (add-to-list 'auto-mode-alist '("\\.pdf\\'" . openwith-open-pdf-externally)))

Neo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   (use-package neotree
     :ensure t
     :config
     (add-hook 'neo-after-create-hook
             (lambda (_)
               (setq-local display-line-numbers nil)))
     (defhydra hydra-neotree (:color blue :hint nil)

       "
              NeoTree Commands
              ^Navigation^   ^Actions^     ^Toggle^        ^Misc^
              ^^^^^^^^—————————————————————————————————————————
              [_n_] Next Line   [_C_] Create   [_t_] Tree      [_r_] Refresh
              [_p_] Prev Line   [_D_] Delete   [_h_] Hidden    [_s_] Change Root
              [_q_] Quit
       "
       ("n" neotree-next-line)
       ("p" neotree-previous-line)
       ("C" neotree-create-node)
       ("D" neotree-delete-node)
       ("t" neotree-toggle)
       ("h" neotree-hidden-file-toggle :which-key "Toggle Hidden Files")
       ("s" neotree-change-root)
       ("r" neotree-refresh)
       ("q" nil :exit t))
     )

Bookmarks

Doom Like Shortcuts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

    (defun my-doomlike-hint ()
      "Dynamically generate hint string for the doomlike Hydra."
      (concat
       "\n"
       "   ┌───────────────────┬───────────────────┬──────────────┬─────────────────────────┐ \n"
       "   │ g → GTD           │ b → Bookmark      │  c → Consult │                         │ \n"
       "   │ f → File          │ i → iBuffer       │  j → jump    │  n → Split Window       │ \n"
       "   │ o → Open          │ p → Projectile    │  r → Roam    │  t → Neotree            │ \n"
       "   │ T → Theme         │ x → Capture       │  g → GTD     │  . → Open (find-file)   │ \n"
       "   └───────────────────┴───────────────────┴──────────────┴─────────────────────────┘ \n"
       (when (or (eq major-mode 'LaTeX-mode)(eq major-mode 'org-mode)) "    l → LaTeX Command\n")))
    (defhydra doomlike (:color red :hint nil :exit t)
      "
      %s(my-doomlike-hint)"
      ("g" hydra-gtd/body :exit t)          ;; GTD Hydra
      ("c" hydra-consult/body :exit t)
      ("b" my/bookmark/body :exit t)
      ("j" hydra-avy/body :exit t)
      ("f" my/file-open/body :exit t)
      ("m" magit-status :exit t)
      ("o" my/open-buffer/body :exit t)
      ("p" hydra-projectile/body :exit t)
      ("t" hydra-neotree/body :exit t)
      ("i" ibuffer :exit t)
      ("l" (when (eq major-mode 'LaTeX-mode) (my/latex-hydra/body)) :exit t)
      ("n" my/split-window/body :exit t)
      ("r" my-org-roam-hydra/body :exit t)
      ("T" my/theme/body :exit t)
      ("x" org-capture :exit t)
      ("." find-file :exit t))
    ;; Bind doomlike hydra to SPC
  (with-eval-after-load 'evil
          (evil-define-key 'normal 'global (kbd "SPC") 'doomlike/body))

Hydras

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  (with-eval-after-load 'hydra
      (defhydra my/file-open (:color red :hint nil :exit t)
    "
  ^Open File^
  ——————————————
  _c_ -> Config.org"
    ("c" (find-file "~/.config/emacs/config.org")
    ))

  (defhydra my/bookmark (:color red :hint nil :exit t)
    "
  ^Bookmark^
  ——————————————
  _o_ -> Bookmark List
  _s_ -> Bookmark Set"
    ("o" bookmark-bmenu-list)
    ("s" bookmark-set))

  (defhydra my/split-window (:color red :hint nil :exit t)
    "
  ^Split Window^
  ——————————————
  _j_ -> Split Below
  _l_ -> Split Right"
    ("j" split-window-below)
    ("l" split-window-right))

  (defhydra my/theme (:color red :hint nil :exit t)
    "
  ^Theme Management^
  ——————————————
  _t_ -> Toggle Theme"
    ("t" toggle-solarized-theme))

  (defhydra my/open-buffer (:color red :hint nil :exit t)
    "
  ^Open Buffer^
  ——————————————
  _t_ -> neotree"
    ("t" (find-file "~/.config/emacs/config.org")
    )))

Window Movement

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    (use-package winum
      :ensure t
      :config
      ;; Automatically enable winum-mode
      (winum-mode))
  (global-set-key (kbd "M-0") 'winum-select-window-0)
  (global-set-key (kbd "M-1") 'winum-select-window-1)
  (global-set-key (kbd "M-2") 'winum-select-window-2)
  (global-set-key (kbd "M-3") 'winum-select-window-3)
  (global-set-key (kbd "M-4") 'winum-select-window-4)
  (global-set-key (kbd "M-5") 'winum-select-window-5)
  (global-set-key (kbd "M-6") 'winum-select-window-6)
  (global-set-key (kbd "M-7") 'winum-select-window-7)
  (global-set-key (kbd "M-8") 'winum-select-window-8)
  (global-set-key (kbd "M-9") 'winum-select-window-9)
  (global-set-key (kbd "M-l") 'evil-window-right)
  (global-set-key (kbd "M-h") 'evil-window-left)
  (global-set-key (kbd "M-k") 'evil-window-up)
  (global-set-key (kbd "M-j") 'evil-window-down)

Transient

1
  (elpaca transient)

Folding

1
2
3
(use-package origami
  :ensure t
  :hook (rust-mode . origami-mode))

IBuffer

1
;;; emacs/ibuffer/config.el -*- lexical-binding: t; -*-

Org Mode

Org mode configuration

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    ;; Enable automatic line wrapping in org mode
(setq org-src-preserve-indentation t)

(setq org-tags-column 80)
(setq org-indent-indentation-per-level 5)
(setq org-ellipsis "⤵")
(add-hook 'org-mode-hook
          (lambda ()
            (setq fill-column 80) ; Set the desired line width
            (turn-on-auto-fill)))

        ;; Configure visual line mode for org mode
        (add-hook 'org-mode-hook 'visual-line-mode)
        (add-hook 'org-mode-hook (lambda () (display-line-numbers-mode -1)))
          (add-to-list 'org-structure-template-alist
                       '("s" "#+NAME: ?\n#+BEGIN_SRC \n\n#+END_SRC"))
          (setq org-confirm-babel-evaluate nil)


          (defun org-icons ()
            "Beautify org mode keywords."
            (setq prettify-symbols-alist '(
                                           ("[#A]" . "")
                                           ("[#B]" . "")
                                           ("[#C]" . "")
                                           ("[ ]" . "")
                                           ("[X]" . "")
                                           ("[-]" . "")
                                           ("#+BEGIN_SRC" . "")
                                           ("#+END_SRC" . "")
                                           ("#+begin_src" . "")
                                           ("#+end_src" . "")
                                           (":ATTENDEES:" . "")
                                           (":AUTHORS:" . "")
                                           (":authors:" . "")
                                           (":PROPERTIES:" . "")
                                           ("#+OPTIONS" . "")
                                           (":END:" . "―")
                                           ("#+STARTUP:" . "")
                                           ("#+TITLE: " . "")
                                           ("#+title: " . "")
                                           ("#+RESULTS:" . "")
                                           ("#+DESCRIPTION:" . "")
                                           ("#+NAME:" . "")
                                           ("#+ROAM_TAGS:" . "")
                                           ("#+FILETAGS:" . "")
                                           (":FILETAGS:" . "")
                                           ("#+HTML_HEAD:" . "")
                                           ("#+hugo_custom_front_matter:" . "")
                                           ("#+AUTHOR:" . "")
                                           ("#+author:" . "")
                                           ("#+SUBTITLE:" . "")
                                           (":EFFORT:" . "")
                                           (":COMPLETED:" . "")
                                           (":SCHEDULED:" . "")
                                           (":URL:" . "")
                                           (":HEART:" . "")
                                           (":PHONE:" . "")
                                           (":EMAIL:" . "")
                                           (":ADDRESS:" . "")
                                           (":PERSON:" . "")
                                           ("ACADEMIC" . "")
                                           (":DEADLINE:" . "")))




            (prettify-symbols-mode))
          (add-hook 'org-mode-hook #'org-icons)

          (use-package org-superstar
            :config
            (setq org-superstar-special-todo-items t)
            (add-hook 'org-mode-hook (lambda ()
                                       (org-superstar-mode 1))))
          (defface my-ligature-face
            '((t (:family "Fira Code" :height 1.0))) ;; Replace "Fira Code" with the name of your desired font
            "Face for ligature"
            :group 'org-faces)

          (use-package org-appear
            :hook (org-mode . org-appear-mode))
          (setq org-startup-indented t
                org-pretty-entities t
                org-hide-emphasis-markers t
                org-startup-with-inline-images t
                org-image-actual-width '(300))
          (setq org-format-latex-options (plist-put org-format-latex-options :scale 2.0))
          ;; Configure org-mode to open links in the same buffer
          (setq org-return-follows-link t)


          (defun my-org-mode-keys ()
            (when (bound-and-true-p evil-local-mode)
              (evil-define-key 'normal org-mode-map (kbd "RET") 'org-open-at-point)))

          (add-hook 'org-mode-hook 'my-org-mode-keys)

          ;; Configure evil-mode to open links in the same buffer

      (setq org-structure-template-alist
        '(("c" . "comment\n")
          ("e" . "example\n")
          ("E" . "export")
          ("h" . "export html\n")
          ("l" . "export latex\n")
          ("q" . "quote\n")
          ("s" . "src")
          ("se" . "src emacs-lisp\n")
          ("v" . "verse\n")))
    (use-package org-sticky-header
      :ensure t
      :hook (org-mode . org-sticky-header-mode)
      :config
      (setq org-sticky-header-full-path 'full
            org-sticky-header-outline-path 'breadcrumb))
1
2
3
4
  (setq org-cite-activate-plain-format "\\cite{%l}")
  (add-to-list 'load-path "~/.config/emacs/site-lisp")
  (load "~/.config/emacs/site-lisp/org-pretty-table.el")
  (add-hook 'org-mode-hook #'org-pretty-table-mode)

Org-clock

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    (setq org-clock-clocktable-default-properties
          '(:maxlevel 3
            :scope file
            :block today
            :tstart "<today>"
            :tend "<tomorrow>"
            :stepskip0 t
            :step nil
            :step-width 1
            :properties ("CATEGORY")
            :fileskip0 nil
            :hidefiles t
            :emphasize nil
            :link t
            :narrow 40
            :indent nil
            :formula nil
            :timestamp nil
            :formula-formula nil
            :link-fmt "[[%s][%s]]"
            :block-fmt ""
            :properties-fmt ""
            :tags nil
            :tags-fmt nil
            :narrow-fmt ""
            :clock-summaries nil
            :fileskip0summary nil
            :compact nil
            :compact-fmt ""
            :sort nil
            :sort-fmt ""
            :show-properties nil
            :level nil
            :level-fmt ""
            :scope-agenda-text ""
            :table-line-above "--"
            :table-line-below "--"))

Babel

Install the org-contrib packge to get more languages

1
2
(use-package org-contrib
  :ensure t)
1
2
3
4
5
6
7
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)
     (shell . t)
     ))
  (setq org-confirm-babel-evaluate nil)

Darkroom mode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  (use-package darkroom
    :ensure t
    :config
    (setq darkroom-margins 0.2) ; Adjust the margin size to your preference
    (setq darkroom-fringes-outside-margins t) ; Set to t if you want fringes outside the margins
    (setq darkroom-mode-line 'light) ; Use 'light for a light mode-line
    (setq darkroom-text-scale-increase 2) ; Set to the desired text scale increase
    :bind
    ("<f12>" . darkroom-tentative-mode) ; Bind to a keybinding of your choice
    :hook
    (darkroom-mode . (lambda ()
                       (if darkroom-mode
                           (display-line-numbers-mode -1)
                         (display-line-numbers-mode 1)))
                   )
    )

Org Roam

Other Org Modes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
; (elpaca `(org-drill
;           :host github
;           :repo "SimonSekavcnik/org-drill"
;           :branch "fix-time-stamp-format"))
(elpaca `(org-drill :path "/home/simon/Projects/org-drill"))

(use-package org-drill
  :after org
  :ensure t
  :config
  ;; Enable org-drill when Org mode is loaded
  (with-eval-after-load 'org
    (require 'org-drill))

  ;; Customize org-drill settings
  (setq org-drill-spaced-repetition-algorithm 'sm2
        org-drill-add-random-noise-to-intervals-p t
        org-drill-adjust-intervals-for-early-and-late-repetitions-p t
        org-drill-maximum-items-per-session 30
        org-drill-learn-fraction 0.25
        org-drill-allow-visible-cloze-p t)

  ;; Customize org-drill faces (optional)
  (custom-set-faces
   '(org-drill-cloze-face ((t (:foreground "red" :weight bold))))
   '(org-drill-done-cloze-face ((t (:foreground "green" :weight bold))))))

Bibliography

LaTeX

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  (setq org-latex-to-pdf-process (list "latexmk -pdf %f"))
  (defun test/latex (a))

  (with-eval-after-load 'hydra

  (defhydra my/latex-hydra (:hint nil :exit t)
    "
    ^LaTeX Commands^
  —————————————————————
  _b_ → compile biber
  _c_ → cite (TAB to choose)
  _e_ → environment
  _f_ → preview fragment
  _F_ → preview document
  _l_ → compile latex
  _m_ → macro
  _v_ → view pdf
    "
    ("b" my/biber-compile :exit t)
    ("c" helm-bibtex :exit t)
    ("e" LaTeX-environment :exit t)
    ("f" preview-at-point :exit t)
    ("F" preview-document :exit t)
    ("l" my/latex-compile :exit t)
    ("m" TeX-insert-macro :exit t)
    ("v" my/latex-view :exit t)
    ))

AUCTex

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    (defun my/latex-mode-hook ()
      (advice-add #'TeX-command-master :before (lambda (&rest r) (save-buffer)))
      (push (list 'output-pdf "Zathura") TeX-view-program-selection))
(use-package auctex
  :ensure (auctex :pre-build (("./autogen.sh")
                               ("./configure" "--without-texmf-dir" "--with-lispdir=.")
                               ("make")))
  :mode (("\\.tex\\'" . LaTeX-mode))
      :defer t
      :hook
      (LaTeX-mode . my/latex-mode-hook)
      (LaTeX-mode . turn-on-prettify-symbols-mode)
      :bind (:map LaTeX-mode-map
                  ("C-c b" . my/vertico-bibtex))
      )
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex) ; Enable RefTeX with AUCTeX
  (setq reftex-plug-into-AUCTeX t)

References

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
            (use-package marginalia
              :ensure t
              :config
              (marginalia-mode 1))
            (defun my/bibtex-generate-autokey (&rest r)
                      (let* ((names (bibtex-autokey-get-names))
                             (year (bibtex-autokey-get-year))
                             (title (bibtex-autokey-get-title)))
                        (capitalize (format "%s%s" names year))))
            (advice-add #'bibtex-generate-autokey :around #'my/bibtex-generate-autokey)

            (defun my/vertico-bibtex--get-field (key candidate)
              "return the field matching KEY in CANDIDATE"
              (alist-get key (cdr candidate) nil nil #'string=))

            (defun vertico-bibtex--maybe-truncate (field len)
              (if field
                  (substring field 0 (min len (length field)))
                field))
            (defun my/vertico-bibtex--build-map (candidates)
              (mapcar
               (lambda (cand)
                 (let* ((key (my/vertico-bibtex--get-field "=key=" cand))
                        (title (vertico-bibtex--maybe-truncate
                                (my/vertico-bibtex--get-field "title" cand)
                                35))
                        (author (vertico-bibtex--maybe-truncate
                                 (aif (my/vertico-bibtex--get-field "author" cand)
                                      (string-replace " and " ", " it) it)
                                 40))
                        (book (my/vertico-bibtex--get-field "booktitle" cand))
                        (journal (my/vertico-bibtex--get-field "journal" cand)))
                   `(,key . (:title ,title :author ,author :journal ,(or journal book)))))
               candidates))
            (defun my/vertico-bibtex (&optional arg)
              "insert a bibtex citation at point using `completing-read`. if
            ARG is non-nil, refresh the bibtex-completion cache"
              (interactive "P")
              (when arg
                (bibtex-completion-clear-cache))
              (bibtex-completion-init)
              (let* ((candidates (bibtex-completion-candidates))
                     (map (my/vertico-bibtex--build-map candidates))
                     (keys (mapcar #'car map))
                     (completion-extra-properties
                      (list
                       :annotation-function
                       (lambda (key)
                         (let ((obj (alist-get key map nil nil #'string=)))
                           (marginalia--fields
                            ((plist-get obj :title) :width 35 :truncate 0.5 :face 'marginalia-string)
                            ((plist-get obj :author) :width 40 :truncate 0.5 :face 'marginalia-documentation)
                            ((plist-get obj :journal) :width 30 :truncate 0.5 :face 'marginalia-value))))))
                     (selection (completing-read "Insert citation: " keys)))
                (when selection
                  (insert selection))))

        (defun my/vertico-bibtex (&optional arg)
          "insert a bibtex citation at point using `completing-read`. if
        ARG is non-nil, refresh the bibtex-completion cache"
          (interactive "P")
          (when arg
            (bibtex-completion-clear-cache))
          (bibtex-completion-init)
          (let* ((candidates (bibtex-completion-candidates))
                 (map (my/vertico-bibtex--build-map candidates))
                 (keys (mapcar #'car map))
                 (completion-extra-properties
                  (list
                   :annotation-function
                   (lambda (key)
                     (let ((obj (alist-get key map nil nil #'string=)))
                       (marginalia--fields
                        ((plist-get obj :title) :width 35 :truncate 0.5 :face 'marginalia-string)
                        ((plist-get obj :author) :width 40 :truncate 0.5 :face 'marginalia-documentation)
                        ((plist-get obj :journal) :width 30 :truncate 0.5 :face 'marginalia-value))))))
                 (selection (completing-read "Insert citation: " keys)))
            (when selection
              (insert selection))))

    (defmacro aif (cnd then else)
      "anaphoric if from paul graham's on lisp. bind the result of CND
    to IT for use in the THEN and ELSE clauses"
      `(let ((it ,cnd))
         (if it ,then ,else)))

Ox

Mu4E

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  (use-package mu4e
    :ensure nil
    :init
    (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
    (require 'mu4e)
    :config
    (setq mu4e-change-filenames-when-moving t)
    (setq mu4e-update-interval (* 10 60))
    (setq mu4e-maildir "~/.mail/")
    (setq mu4e-attachment-dir "~/Downloads")
    (setq mu4e-update-interval 300)            ; Update interval (seconds)
    (setq mu4e-index-cleanup t)                ; Cleanup after indexing
    (setq mu4e-index-update-error-warning t)   ; Warnings during update
    (setq mu4e-index-update-in-background t)   ; Background update
    (setq mu4e-change-filenames-when-moving t) ; Needed for mbsync
    (setq mu4e-index-lazy-check nil)           ; Don't be lazy, index everything
    (setq mu4e-completing-read-function 'completing-read)
    (setq mu4e-contexts (list
                         (make-mu4e-context
                          :name "gmail"
                          :match-func (lambda (msg) (when msg
                                                      (string-prefix-p "/gmail" (mu4e-message-field msg :maildir))))
                          :vars `((msmtp-account . "jdm204-personal")
                                  (user-mail-address . "simon.sekavcnik@gmail.com")
                                  (mu4e-trash-folder . "/[Gmail]/Bin")
                                  (mu4e-sent-folder . "/[Gmail]/Sent Mail")
                                  (mu4e-drafts-folder . "/[Gmail]/Drafts")))
                         ))
    (setq
     mu4e-headers-unread-mark '("u" . "️")
     mu4e-headers-attach-mark '("a" . "")
     mu4e-headers-replied-mark '("r" . "")
     mu4e-headers-new-mark '("N" . "⭐")
     mu4e-headers-signed-mark '("S" . "✒️")
     mu4e-headers-trashed-mark '("T" . "❎️")
     mu4e-headers-draft-mark '("d" . "")
     mu4e-headers-flagged-mark '("f" . "🏴")
     mu4e-headers-list-mark '("l" . "️")
     mu4e-headers-seen-mark '("s" . "")
     mu4e-headers-encrypted-mark '("e" . "")))

Magit

Magit is a git client for emacs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  (use-package magit
    :ensure t
    :config
    (defhydra hydra-magit (:color blue :hint nil)
      "
                Magit Commands
      ^^^^^————————————————————————————————————————————
      [_s_]   Status      [_b_]   Blame
      [_c_]   Commit      [_l_]   Log
      [_d_]   Diff        [_p_]   Pull
      [_P_]   Push        [_f_]   Fetch
      [_q_]   Quit Hydra
      "
      ("s" magit-status)
      ("b" magit-blame)
      ("c" magit-commit)
      ("l" magit-log)
      ("d" magit-diff)
      ("p" magit-pull)
      ("P" magit-push)
      ("f" magit-fetch)
      ("q" nil :exit t))
    )

Keep Emacs Clean

1
2
3
4
5
6
7
8
9
(setq backup-directory-alist `(("." . ,(expand-file-name "/home/simon/.tmp/backups/" user-emacs-directory))))

;; auto-save-mode doesn't create the path automatically!
(make-directory (expand-file-name "/home/simon/.tmp/auto-saves/" user-emacs-directory) t)

(setq auto-save-list-file-prefix (expand-file-name "/home/simon/.tmp/auto-saves/sessions/" user-emacs-directory)
      auto-save-file-name-transforms `((".*" ,(expand-file-name "/home/simon/.tmp/auto-saves/" user-emacs-directory) t)))

(setq create-lockfiles nil)

Flyspell

1
(setq)

APPEARANCE

Theme

Using the Solaized theme

1
2
3
4

  (use-package solarized-theme
    :config
    (load-theme 'solarized-dark t)) ; Choose 'solarized-light' for light theme

A function to change the theme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  (defun toggle-solarized-theme ()
    "Toggle between 'solarized-dark' and 'solarized-light' themes."
    (interactive)
    (if (eq (car custom-enabled-themes) 'solarized-dark)
        (progn
          (disable-theme 'solarized-dark)
          (load-theme 'solarized-light t))
      (progn
        (disable-theme 'solarized-light)
        (load-theme 'solarized-dark t))))
1
(setq org-src-fontify-natively t)

Disable Menubar, Toolbars and Scrollbars

1
2
3
4
  (set-frame-font "Fira Code")
  (menu-bar-mode -1)
  (tool-bar-mode -1)
  (scroll-bar-mode -1)

Enable Ligatures

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  (use-package ligature
    :ensure t
    :config
    ;; Enable the www ligature in every possible major mode
    (ligature-set-ligatures 't '("www"))

    ;; Enable ligatures in programming modes
    (ligature-set-ligatures 'prog-mode '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\" "{-" "::"
                                         ":::" ":=" "!!" "!=" "!==" "-}" "----" "-->" "->" "->>"
                                         "-<" "-<<" "-~" "#{" "#[" "##" "###" "####" "#(" "#?" "#_"
                                         "#_(" ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*" "/**"
                                         "/=" "/==" "/>" "//" "///" "&&" "||" "||=" "|=" "|>" "^=" "$>"
                                         "++" "+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<="
                                         "=<<" "=/=" ">-" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<*"
                                         "<*>" "<|" "<|>" "<$" "<$>" "<!--" "<-" "<--" "<->" "<+"
                                         "<+>" "<=" "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<"
                                         "<~" "<~~" "</" "</>" "~@" "~-" "~>" "~~" "~~>" "%%"))

    (global-ligature-mode 't)
  )

Display Line Numbers and Truncate lines

1
2
  (global-display-line-numbers-mode 1)
  (global-visual-line-mode t)

Initial Screen

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  (use-package dashboard
    :ensure t
    :config
    (add-hook 'elpaca-after-init-hook #'dashboard-insert-startupify-lists)
    (add-hook 'elpaca-after-init-hook #'dashboard-initialize)
    (dashboard-setup-startup-hook))

  (setq dashboard-icon-type 'all-the-icons)
  ;; Set the title
  (setq dashboard-banner-logo-title "Simon's Emacs")
  ;; Set the banner
  ;;(setq dashboard-startup-banner [VALUE])
  ;; Value can be
  ;; - nil to display no banner
  ;; - 'official which displays the official emacs logo
  ;; - 'logo which displays an alternative emacs logo
  ;; - 1, 2 or 3 which displays one of the text banners
  ;; - "path/to/your/image.gif", "path/to/your/image.png" or "path/to/your/text.txt" which displays whatever gif/image/text you would prefer
  ;; - a cons of '("path/to/your/image.png" . "path/to/your/text.txt")

  ;; Content is not centered by default. To center, set
  (setq dashboard-center-content t)

  ;; To disable shortcut "jump" indicators for each section, set
  (setq dashboard-show-shortcuts nil)

MODES

Ledger

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
;; Ledger Mode Configuration
(use-package ledger-mode
  :ensure t
  :mode ("\\.ledger\\'" . ledger-mode)
  :config
  (setq ledger-clear-whole-transactions 1
        ledger-mode-should-check-version nil
        ledger-report-auto-refresh t)

  ;; Keybindings for Ledger mode
  (define-key ledger-mode-map (kbd "C-c C-a") 'ledger-add-transaction)
  (define-key ledger-mode-map (kbd "C-c C-c") 'ledger-toggle-current)
  (define-key ledger-mode-map (kbd "C-c C-r") 'ledger-reconcile)
  (define-key ledger-mode-map (kbd "C-c C-d") 'ledger-delete-current-transaction)
  (define-key ledger-mode-map (kbd "C-c C-e") 'ledger-edit-transaction)
  (define-key ledger-mode-map (kbd "C-c C-l") 'ledger-run-report)

  ;; Flycheck integration
  (add-hook 'ledger-mode-hook
            (lambda ()
              (flycheck-mode)
              (setq-local flycheck-checker 'ledger)))

  ;; Custom reports
  (setq ledger-reports
        '(("bal"    "%(binary) -f %(ledger-file) bal")
          ("reg"    "%(binary) -f %(ledger-file) reg")
          ("payee"  "%(binary) -f %(ledger-file) reg @%(payee)")
          ("account" "%(binary) -f %(ledger-file) reg %(account)")))

  ;; Function to run the custom reports
  (defun ledger-run-report (report-name)
    "Run a custom ledger report."
    (interactive (list (completing-read "Report: " (mapcar 'car ledger-reports))))
    (ledger-report report-name)))

PROGRAMING

Yasnipet

1
2
3
4
5
6
7
8
    (use-package yasnippet
      :ensure t
      :init
      (setq yas-snippet-dirs '("~/.config/emacs/snippets"))
      :config
      (yas-reload-all)
      (yas-global-mode 1)
      (add-hook 'org-mode-hook #'yas-minor-mode))

Python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
        (use-package python-mode
          :mode ("\\.py\\'" . python-mode)
          :interpreter ("python" . python-mode)
          :hook (python-mode . lsp-deferred)
          :config
          (setq python-indent-offset 4))

        ;; Install and configure LSP mode for code completion and linting
        (use-package lsp-mode
          :commands (lsp lsp-deferred)
          :hook
          (lsp-mode . lsp-enable-which-key-integration)
          (svelte-mode . lsp-deferred)
          :custom
          (lsp-diagnostics-provider :capf)
          (lsp-headerline-breadcrumb-enable t)
          (lsp-headerline-breadcrumb-segments '(project file symbols))
          (lsp-lens-enable nil)
          (lsp-disabled-clients '((python-mode . pyls)))
          :init
          (setq lsp-keymap-prefix "C-c l") ;; Or 'C-l', 's-l'
          :config
          )

          ;; Optional: Install and configure lsp-ui for additional features
        (use-package lsp-ui
          :hook (lsp-mode . lsp-ui-mode)
          :config
          (setq lsp-ui-doc-enable nil) ; Disable the documentation popup
          (setq lsp-ui-sideline-enable t) ; Show symbol information in the sideline
          (setq lsp-ui-sideline-show-hover t))

          ;; Optional: Install and configure flycheck for on-the-fly syntax checking
        (use-package flycheck
          :ensure t
          :hook (lsp-mode . flycheck-mode))

        (defun my-python-mode-keys ()
          (when (bound-and-true-p evil-local-mode)
            (evil-define-key 'normal python-mode-map (kbd "TAB") 'hs-toggle-hiding)))

      (use-package company
        :after lsp-mode
        :hook (lsp-mode . company-mode)
        :bind (:map company-active-map
               ("<tab>" . company-complete-selection))
              (:map lsp-mode-map
               ("<tab>" . company-indent-or-complete-common))
        :custom
        (company-minimum-prefix-length 1)
        (company-idle-delay 0.0))

      (use-package company-box
        :hook (company-mode . company-box-mode))

        (add-hook 'python-mode-hook 'my-python-mode-keys)
      (use-package pyvenv
        :config
        (pyvenv-mode 1))

  (use-package elpy
    :ensure t
    :init
    (elpy-enable)
    :config
    ;(setq elpy-formatter 'autopep8)
    ;(setq elpy-formatter 'yapf)
    )

Javascript

1
2
3
4
5
6
7
(add-hook 'js-mode-hook
          (lambda ()
            (setq js-indent-level 2)))

(add-hook 'js-jsx-mode-hook
          (lambda ()
            (setq js-jsx-indent-level 2)))

Rust

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  ;; Rust mode and dependencies
  (use-package ob-rust)
  (use-package rust-mode
    :mode "\\.rs\\'"
    :config
    ;; Set indentation to 4 spaces
    (setq rust-indent-offset 4)
    ;; Enable auto-formatting on save
    (add-hook 'rust-mode-hook
              (lambda () (add-hook 'before-save-hook 'rust-format-buffer nil t))))
  (add-hook 'rust-mode-hook 'eglot-ensure)

Docker

1
2
3
4
;; Dockerfile mode
(use-package dockerfile-mode
  :ensure t
  :mode ("Dockerfile\\'" . dockerfile-mode))
1
2
3
4
;; Docker-compose mode
(use-package docker-compose-mode
  :ensure t
  :mode ("docker-compose\\.yml\\'" . docker-compose-mode))
1
2
3
4
;; Docker-related settings
(use-package docker
  :ensure t
  :bind ("C-c d" . docker))

Jenkins

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  (use-package jenkinsfile-mode
    :ensure t
    :mode (("Jenkinsfile\\'" . jenkinsfile-mode))
    :config
    (setq jenkinsfile-mode-indent-offset 4)
    (add-hook 'jenkinsfile-mode-hook #'yas-minor-mode)
    (add-hook 'jenkinsfile-mode-hook
              (lambda ()
                (setq-local flycheck-command-wrapper-function
                            (lambda (command) (append '("sh") command))))
              ))

Golang

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  (use-package go-mode
    :ensure t
    :mode
    ("\\.go\\'" . go-mode)   ; Associate .go files with go-mode
    ("\\.proto\\'" . go-mode) ; Associate .proto files with go-mode
    :hook
    (go-mode . (lambda ()
                 (setq tab-width 2)
                 (setq indent-tabs-mode nil)
                 (add-hook 'before-save-hook 'gofmt-before-save nil t))))

  (use-package company-go
    :ensure t
    :hook
    (go-mode . (lambda ()
                 (set (make-local-variable 'company-backends) '(company-go))
                 (company-mode))))

Web

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  (use-package svelte-mode
    :ensure t
    :mode "\\.svelte\\'")

    (use-package typescript-mode
      :ensure t
      :mode "\\.ts\\'")
    (use-package emmet-mode
      :ensure t
      :hook
      (html-mode . emmet-mode)
      (css-mode . emmet-mode)
      (svelte-mode . emmet-mode)
      (web-mode . emmet-mode))
  (add-hook 'svelte-mode-hook #'yas-minor-mode)

CUSTOM BEHAVIOUR

ORG MODE

TUM BEAMER

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  (eval-after-load "ox-latex"

    ;; update the list of LaTeX classes and associated header (encoding, etc.)
    ;; and structure
    '(add-to-list 'org-latex-classes
                  `("beamer"
                    ,(concat "\\documentclass[presentation]{tumbeamer}\n"
                             "[DEFAULT-PACKAGES]"
                             "[PACKAGES]"
                             "[EXTRA]\n")
                    ("\\section{%s}" . "\\section*{%s}")
                    ("\\subsection{%s}" . "\\subsection*{%s}")
                    ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))))

GTD

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  (defhydra hydra-gtd (:color blue :hint nil)
    "
                    GTD Commands
                    ^Agenda^         ^Capture^                ^View^
                    ^^^^^^^^———————————————————————————————————————————————————
                    [_a_] Agenda     [_t_] Capture Task      [_v_] View All
                    [_t_] Todo       [_p_] Capture Project   [_g_] Go to
                    [_s_] Schedule   [_i_] Capture Inbox     [_q_] Quit
                                   [_S_] Capture Someday
    "
    ("a" org-agenda)
    ("t" org-todo)
    ("s" org-schedule)
    ("p" gtd/capture-project)
    ("S" gtd/capture-someday)
    ("i" gtd/capture-inbox)
    ("d" org-deadline)
    ("t" gtd/capture-task)
    ("r" org-refile)
    ("v" org-agenda-list)
    ("g" org-goto)
    ("q" nil :exit t))
1
2
3
  (setq org-todo-keywords
      '((sequence "INBOX(i)" "SCHED(s)" "NEXT(n)" "WAIT(w@/!)" "|" "DONE(d!)" "CANC(c)")
        (sequence "PLAN(p)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "DONE(d!)" "CANC(k@)")))

GTD Capture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
(defun gtd/capture-project ()
    "Capture a new project into an Org-Roam node and add the node to `org-agenda-files`."
    (interactive)
    ;; Use `org-roam-node-read` to either select an existing node or create a new one
    (let* ((node (org-roam-node-read))  ;; Prompt to select an existing node
           (file-path (org-roam-node-file node)))  ;; Get the file path of the selected node
      ;; If the file-path is nil or the file doesn't exist, create a new node file
      (unless (and file-path (file-exists-p file-path)) ;; Create the file manually using `org-roam-capture-`
        (setq file-path (expand-file-name
                         (concat (org-roam-node-slug node) ".org")
                         org-roam-directory))
        (unless (file-exists-p file-path)
          (with-temp-buffer
            (insert (format "#+title: %s\n#+FILETAGS: GTD\n" (org-roam-node-title node)))
            (write-file file-path))))
      ;; Use `org-roam-capture-` to capture the task in the selected or newly created node
      (org-roam-capture- :node node
                           :templates `(("t" "GTD TASK" plain
                                         "* INBOX %?\n  :PROPERTIES:\n  :CREATED: %U\n  :END:\n"
                                         :target (file+head ,file-path ""))))
        ;; Optionally add the file to `org-agenda-files`
        (message "Captured project task in %s" file-path)))

  (defun gtd/capture-task ()
    "Capture a new task in an existing Org-Roam GTD Node and add the file to `org-agenda-files`."
    (interactive)
    ;; Use `org-roam-node-find` to select an existing node, but capture the selected node instead of opening it
    (let* ((node (org-roam-node-read nil #'gtd/org-roam-gtd-only nil nil nil))  ;; Prompt to select an existing GTD-tagged node
           (file-path (org-roam-node-file node)))  ;; Get the file path of the selected node
      ;; Use `org-roam-capture-` to capture the task in the selected node
      (org-roam-capture- :node node
                         :templates '((
                                       "i" "Inbox Task" plain
                                       "\n** INBOX %^{Task Title}\n:PROPERTIES:\n:CREATED: %U\n:END:\n%?"
                                       :if-new (file+head+olp file-path "" ("Tasks")))))
      (message "Trying to add to the org agenda files")
      (gtd/add-to-org-agenda-files file-path)))

    (defun gtd/schedule ()
      "Prompt the user to pick a schedule date or skip it."
      (let ((schedule (org-read-date nil nil nil "Schedule: ")))
        (if (string-empty-p schedule)
            ""
          (format ":SCHEDULED: <%s>" schedule))))

    (defun gtd/deadline ()
      "Prompt the user to pick a schedule date or skip it."
      (let ((schedule (org-read-date nil nil nil "Schedule: ")))
        (if (string-empty-p schedule)
            ""
          (format ":DEADLINE: <%s>" schedule))))

    (defun gtd/capture-inbox ()
      "Capture a new task as a heading in the Org-roam inbox file."
      (interactive)
      (org-roam-capture- :node (org-roam-node-create)
                         :templates '(("i" "Inbox Task" plain "\n* NEXT %^{Task Title}\n:PROPERTIES:\n:CREATED: %U\n%(gtd/schedule)\n%(gtd/deadline)\n:END:\n%?"
                                       :target (file+head "inbox.org" "")))))

    (defun gtd/capture-someday ()
      "Capture a new task as a heading in the Org-roam someday file."
      (interactive)
      (org-roam-capture- :node (org-roam-node-create)
                         :templates '(("s" "Someday Task" plain "\n* WAITING %^{Task Title}\n:PROPERTIES:\n:CREATED: %U\n:END:\n%?"
                                       :target (file+head "someday.org" "")))))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
      (defun gtd/add-to-org-agenda-files (file)
        "Append FILE to `org-agenda-files` if it's not already present."
        (interactive "fSelect file to add to `org-agenda-files`: ")
        (let ((expanded-file (expand-file-name file)))
          (unless (member expanded-file org-agenda-files)
            (setq org-agenda-files (append org-agenda-files (list expanded-file)))
            (message "Added %s to `org-agenda-files`" expanded-file))))

  (defun gtd/org-roam-gtd-only (node)
      (let ((tags (org-roam-node-tags node)))
          (member "GTD" tags)))

Copy done tasks to dailies

After a task was completed it is copied to the TODAY dailies file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  (defun gtd/org-copy-to-daily ()
    (interactive)
    (let ((org-refile-keel t) ;; Set this to nil to delete the original!
          (org-roam-dailies-capture-templates
           '(("t" "tasks" entry "%?"
              :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+title:%<%Y-%m-%d>\n" ("Completed Tasks")))))
          (org-after-refile-insert-hook #'save-buffer)
          today-file
          pos)
      (save-window-excursion
        (org-roam-dailies--capture (current-time) t)
        (setq today-file (buffer-file-name))
        (setq pos (point)))
      (unless (equal (file-truename today-file)
                     (file-truename (buffer-file-name)))
        (org-refile nil nil (list "Completed Tasks" today-file nil pos)))))

  (add-to-list 'org-after-todo-state-change-hook
               (lambda ()
                 (when (equal org-state "DONE")
                   (gtd/org-copy-to-daily))))

Org Agenda

Prefix

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(defun my/org-agenda-get-roam-title ()
  "Return the SHORT_TITLE property or the Org-roam title of the file corresponding to the current agenda item."
  (let* ((marker (org-get-at-bol 'org-hd-marker))
         (file (buffer-file-name marker))
         (short-title (if file
                          (with-current-buffer (org-get-agenda-file-buffer file)
                            (org-with-wide-buffer
                             (goto-char (point-min))
                             (when (re-search-forward "^#\\+SHORT_TITLE:[ \t]*\\(.*\\)$" nil t)
                               (match-string-no-properties 1)))))))
    (if (and short-title (not (string-empty-p short-title)))
        short-title
      (if file
          (my/org-roam-get-title file)
        ""))))


  (defun my/org-roam-get-title (file)
    "Get the title from the Org-roam FILE.
  If the title is not found, return the base name of the file."
    (with-temp-buffer
      (insert-file-contents file)
      (goto-char (point-min))
      (if (re-search-forward "^#\\+title: \\(.*\\)$" nil t)
          (match-string 1)
        (file-name-base file))))

  (setq org-agenda-prefix-format
        '((agenda . " %i %-12(my/org-agenda-get-roam-title) %?-12t% s")
          (todo . " %i %-12(my/org-agenda-get-roam-title) ")
          (tags . " %i %-12(my/org-agenda-get-roam-title) ")
          (search . " %i %-12(my/org-agenda-get-roam-title) ")))

Views

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(setq org-agenda-hide-tags-regexp "\\(GTD\\|INBOX\\|PROJECT\\)")

(defun gtd/skip-if-next ()
  "Skip entries that are in the NEXT state."
  (let ((subtree-end (save-excursion (org-end-of-subtree t))))
    (if (string= (org-get-todo-state) "NEXT")
        subtree-end
      nil)))

(setq org-agenda-custom-commands
      '(("g" "Custom Day Agenda"
         ((agenda ""
                  ((org-agenda-span 'day)
                   (org-deadline-warning-days 0)
                   (org-agenda-use-time-grid t)
                   (org-agenda-overriding-header "My Custom Day Agenda")
                   (org-agenda-time-grid '((daily today remove-match)
                                           (0800 1000 1200 1400 1600 1800)
                                           "......" "----------------"))))
          (todo "NEXT"
                ((org-agenda-overriding-header "Next Tasks")
                 (org-agenda-sorting-strategy '(priority-down))))
          (alltodo ""
                   ((org-agenda-overriding-header "All Tasks")
                    (org-agenda-skip-function
                     '(org-agenda-skip-entry-if 'regexp ":PROJECT:" 'todo '("NEXT" "SCHED")))))
          (alltodo ""
                   ((org-agenda-overriding-header "All Active Projects")
                    (org-agenda-skip-function
                     '(org-agenda-skip-entry-if 'notregexp ":PROJECT:"))))))))
(with-eval-after-load 'org-agenda
  ;; Remap j and k to move down and up
  (define-key org-agenda-mode-map (kbd "j") 'org-agenda-next-line)
  (define-key org-agenda-mode-map (kbd "k") 'org-agenda-previous-line)

  ;; Remap l and h to move forward and backward (day-wise)
  (define-key org-agenda-mode-map (kbd "l") 'org-agenda-later)
  (define-key org-agenda-mode-map (kbd "h") 'org-agenda-earlier))

Notification

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(use-package org-alert
  :ensure t
  :custom (alert-default-style 'notifications)
  :config
  (setq org-alert-interval 10
	org-alert-notify-cutoff 10
	org-alert-notify-after-event-cutoff 10)
  ;(setq org-alert-time-match-string
  ;    "\\(?:SCHED\\|DEADLINE\\):.*?<.*?\\([0-9]\\{2\\}:[0-9]\\{2\\}\\).*>")
  )

GERMAN

1
2
(use-package anki-editor
  :ensure t)