Files
memex/9_system/emacs.org

70 KiB
Raw Blame History

Emacs

DONE early-init.el

For straight.el to pick up before package.el

  (setq package-enable-at-startup nil)

DONE [7/7] .emacs

  • State "DONE" from "DONE" [2025-05-30 Fri 15:00]
  • State "DONE" from "DONE" [2025-05-30 Fri 15:00]
  • State "DONE" from "DONE" [2025-05-30 Fri 15:00]
  • State "DONE" from "DONE" [2024-07-19 Fri 16:07]
  • State "DONE" from "DONE" [2024-07-19 Fri 16:07]
  • State "DONE" from "DONE" [2024-07-19 Fri 14:40]
  • State "DONE" from "DONE" [2024-07-19 Fri 14:40]
  • State "DONE" from "DONE" [2024-07-09 Tue 12:11]
  • State "DONE" from "TODO" [2023-07-02 Sun 15:02]
DONE Front matter
  ;;; .emacs --- Global settings
  ;;; Commentary:
  ;;; Code:

  ;; -*- lexical-binding: t; -*-
DONE Garbage collector - increase threshold to 500 MB to ease startup
  (setq gc-cons-threshold (* 500 1024 1024))
DONE [3/3] Package.el
CNCL List package archives and initialize them (package.el)
  (require 'package)
  (setq package-archives '(
    			 ("gnu" . "https://elpa.gnu.org/packages/")
    			 ("melpa" . "https://melpa.org/packages/")
    			 ("nongnu" . "https://elpa.nongnu.org/nongnu/")
    			 )
        )
  (setq package-install-upgrade-built-in t)
  (setq package-check-signature "allow-unsigned")
  (gnu-elpa-keyring-update)		
  (package-initialize)
  (package-refresh-contents)
CNCL Install use-package (package.el)
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package)
    )

  (eval-when-compile  (require 'use-package)) ;; allow byte-compile while using use-package
CNCL Make sure Org is installed (package.el)
  (unless (package-installed-p 'org)
    (package-install 'org)
    )
DONE [3/3] Straight.el
DONE Bootstrap Straight.el and install use-package
  (setq straight-repository-branch "develop") ;; Using develop branch temporarily to fix the org-roam-dailies issue. From https://github.com/org-roam/org-roam/issues/2361#issuecomment-1671601796

   (eval-and-compile
     (defvar bootstrap-version)
     (let ((bootstrap-file
            (expand-file-name "straight/repos/straight.el/bootstrap.el"
               		   (or (bound-and-true-p straight-base-dir)
               		       user-emacs-directory)))
           (bootstrap-version 7))
       (unless (file-exists-p bootstrap-file)
         (with-current-buffer
             (url-retrieve-synchronously  "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies)
           (goto-char (point-max))
           (eval-print-last-sexp)))
       (load bootstrap-file nil 'nomessage))
     
     (straight-use-package 'use-package)
     )
t
DONE Integrate use-package and straight
  (setq straight-use-package-by-default t)
DONE Make sure Org is installed (straight.el)

Freezing Org@9.5.5 fixes the issue with org-roam resulting in 'Wrong type argument: integer-or-marker-p, nil'

 (unless (file-directory-p "~/.emacs.d/straight/versions") (make-directory (concat user-emacs-directory "straight/versions")))
  					; This goes in ~/.emacs.d/straight/versions/default.el 
  ;;  (("org" . "8ef6205a560cd3a92f8c5f8fe34953b80121c2cb")) ; org@9.5.5
  ;;  (("org" . "5890aca3d29e593640b728308096a052998355b1")) ; org@9.6.7
  :gamma
  :tangle ~/.emacs.d/straight/versions/default.el
  (("org-roam" . "d4c606078752ac7c1c8a492a042564f4294a23a6"))
  (use-package org)
DONE Tangle emacs.org
  (require 'ob-tangle)
  
  ;; Specify the input file and the output directory
  (defvar config-org-file "~/org/6_system/emacs.org")
  (defvar config-el-file "~/.emacs.d/config.el")
  (defvar org-use-property-inheritance t)

  ;; Tangle emacs.org into config.el and load config.el
  (org-babel-tangle-file config-org-file)
  (load-file config-el-file)
DONE Garbage collector - decrease threshold to 5 MB
  (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold (* 5 1024 1024))))
DONE End matter
  (provide '.emacs)
  ;;; .emacs ends here

TODO [8/11] config.el and custom.el

This Emacs configuration file is a fork of Sri Ramkswamy's and Sacha Chusa's settings. I am sure there is much more to learn from them as I go. Worth revisiting.

DONE Front matter

  ;;; Package --- Summary
  ;;; Commentary:
  ;;; Code:

  ;; -*- lexical-binding: t; -*-
  ;;; Package --- Summary
  ;;; Commentary:
  ;;; Code:

  ;; -*- lexical-binding: t; -*-

DONE [9/9] Startup and general configurations

  • State "DONE" from "DONE" [2024-07-20 Sat 11:55]
  • State "DONE" from "TODO" [2024-07-19 Fri 15:10]
  • State "DONE" from "TODO" [2024-07-10 Wed 10:45]

DONE Run Emacs as a server

  (require 'server)
  (unless (server-running-p) (server-start))
  (defvar server-max-buffers 100)

DONE Custom file

  (setq custom-file (expand-file-name "custom.el" user-emacs-directory))
  (when (file-exists-p custom-file) (load custom-file))

DONE use-package

  • State "DONE" from "CNCL" [2024-07-16 Tue 18:02]

"A use-package declaration for simplifying your .emacs"

  (require 'use-package)
;;  (require 'bind-key)
    ;;  (require 'use-package-ensure)
    ;;  (setq use-package-always-ensure t) ; Ensure use-package installs all packages by default. Use :ensure nil to override.
    ;;  (package-install-selected-packages)

CNCL Quelpa

  (unless (package-installed-p 'quelpa)
    (with-temp-buffer
      (url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
      (eval-buffer)
      (quelpa-self-upgrade)))

DONE System information

  • State "DONE" from "TODO" [2023-08-28 Mon 18:46]
  • State "DONE" from "DONE" [2023-08-28 Mon 18:43]
  • State "DONE" from "NEXT" [2023-08-03 Thu 13:03]

I took this from Sacha's settings. This allows for tweaking configuations according to platform. I intend to use more of this more as I develop Emacs configs across platforms.

  (defvar my-laptop-p (equal (system-name) "lilitop"))
  (defvar my-server-p (and (equal (system-name) "localhost") (equal user-login-name "root")))
  (defvar my-phone-p (not (null (getenv "ANDROID_ROOT")))
    "If non-nil, GNU Emacs is running on Termux.")
  (when my-phone-p (defvar gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
  (global-auto-revert-mode)  ; simplifies syncing

DONE Persistent history

  (savehist-mode)

TODO Backup and versioning

  (use-package magit
    :ensure t
    )

DONE Personal information

  (setq user-full-name "Amr Gharbeia")
  (defvar email-address "amr@gharbeia.net")
  (defvar calendar-latitude 39.0)
  (defvar calendar-longitude -77.1)
  (defvar calendar-location-name "Washington, DC")
  (defvar calendar-time-zone -300)
  (defvar calendar-standard-time-zone-name "EST")
  (defvar calendar-daylight-time-zone-name "EDT")

TODO [2/3] Advanced Features

TODO [0/2] Text

TODO [0/1] Case
TODO Convert DOuble capitals to single capitals
  • State "DONE" from "TODO" [2024-06-27 Thu 13:02]
  • State "DONE" from "NEXT" [2023-08-09 Wed 13:51]
  (defun my/dcaps-to-scaps ()
    "Convert word in DOuble CApitals to Single Capitals."
    (interactive)
    (and (= ?w (char-syntax (char-before)))
         (save-excursion
           (and (if (called-interactively-p)
                    (skip-syntax-backward "w")
                  (= -3 (skip-syntax-backward "w"))
                  )
                (let (case-fold-search)
                  (looking-at "\\b[[:upper:]]\\{2\\}[[:lower:]]")
                  )
              (capitalize-word 1)
              )
           )
         )
    )

Then, lets define a minor mode for it to be activated.

  (define-minor-mode my-dubcaps-mode
    "Toggle 'my-dubcaps-mode' and convert words in DOuble CApitals to Single Capitals as you type."
    :init-value nil
    :lighter (" DC")
    (if my-dubcaps-mode
        (add-hook 'post-self-insert-hook #'my/dcaps-to-scaps nil 'local)
      (remove-hook 'post-self-insert-hook #'my/dcaps-to-scaps 'local)))

Finally, lets add a hook so that it is on for all the text files Emacs opens.

  (add-hook 'text-mode-hook #'my-dubcaps-mode)

Also, since we add a minor mode string (it might be useful sometimes), currently I prefer to diminish it.

  (defun my/diminish-dubcaps ()
    (interactive)
    (diminish 'my-dubcaps-mode ""))
  (add-hook 'my-dubcaps-mode-hook 'my/diminish-dubcaps)
TODO Text Mode [0/1]
TODO Outline Mode [0/1]
TODO [4/10] Org Mode
  • State "DONE" from "TODO" [2024-02-28 Wed 16:49]
DONE Basic setup
  (use-package org
    :config
    (defvar org-outline-path-complete-in-steps nil)
    :bind (("C-c l" . org-store-link)
  	 ("C-c a" . org-agenda)
  	 ("C-c c" . org-capture)
  	 :map org-mode-map)
    )
  (defvar org-directory (concat (getenv "HOME") "/org/"))
TODO [5/6] Looks
  • State "DONE" from "TODO" [2024-07-19 Fri 15:58]
  • State "DONE" from "TODO" [2024-07-16 Tue 21:51]
DONE Basic
  (defvar org-pretty-entities t) ; Improve org mode looks
  (defvar org-hide-emphasis-markers t) ; Hide emphasis markup
  (defvar org-num-mode nil)
  (defvar org-startup-folded 'shw2levels)
DONE Indentation of headers
  • State "DONE" from "TODO" [2025-06-22 Sun 13:44]
  • State "DONE" from "TODO" [2024-07-16 Tue 21:27]
  • State "DONE" from [2023-08-28 Mon 18:17]
  (defvar org-startup-indented t) ; Indent org heirarchy
  (defvar org-adapt-indentation t)
  (defvar org-hide-leading-stars t) ; Minimal Outline
  (defvar org-odd-levels-only nil)
DONE Indentation of lists
  • State "DONE" from "TODO" [2025-06-22 Sun 13:45]
  • State "DONE" from [2024-02-11 Sun 13:15]
  (setq org-list-demote-modify-bullet t)
DONE Org-modern
  • State "DONE" from "TODO" [2025-06-22 Sun 13:45]
  • State "DONE" from "TODO" [2024-07-16 Tue 21:27]
  • State "DONE" from "TODO" [2024-06-27 Thu 13:06]
  (use-package org-modern
    :ensure t
    :config
        ;; Choose some fonts
    (set-face-attribute 'default nil :family "sans-serif")
    (set-face-attribute 'variable-pitch nil :family "sans-serif")
    (set-face-attribute 'org-modern-symbol nil :family "Iosevka")

    ;; Edit settings
    (defvar org-auto-align-tags nil)
    (defvar org-tags-column 0)
    (defvar org-catch-invisible-edits 'show-and-error)
    (defvar org-special-ctrl-a/e t)
    (defvar org-insert-heading-respect-content t)

     ;; Org styling, hide markup etc.
     (defvar org-hide-emphasis-markers t)
     (defvar org-pretty-entities t)

     ;; Agenda styling
     (defvar org-agenda-tags-column 0)
     (defvar org-agenda-block-separator ?─)
     (defvar org-agenda-time-grid
       '((daily today require-timed)
         (800 1000 1200 1400 1600 1800 2000)
         " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"))
     (defvar org-agenda-current-time-string
       "◀── now ─────────────────────────────────────────────────")

    ;; Ellipsis styling
     (defvar org-ellipsis "…")
     (set-face-attribute 'org-ellipsis nil :inherit 'default :box nil)
    
     (global-org-modern-mode)
    )
DONE Highlight Sourcecode Syntax
  • State "DONE" from "TODO" [2025-06-22 Sun 13:46]
  (setq org-src-fontify-natively t)
  (setq org-src-tab-acts-natively t)
TODO Images
  • State "DONE" from "TODO" [2024-07-19 Fri 15:34]
  (setq org-startup-with-inline-images t)
  (setq org-image-actual-width '(300))
TODO [4/5] Agenda
DONE Basic agenda settings
  • State "DONE" from "TODO" [2025-06-22 Sun 13:57]
  • State "DONE" from "TODO" [2024-07-19 Fri 15:52]
   (setq org-deadline-warning-days 7)
   (setq org-agenda-skip-additional-timestamps-same-entry t)
   (setq org-agenda-span 'fortnight)
   (setq org-agenda-tags-column 'auto)
   (setq org-agenda-skip-scheduled-if-deadline-is-shown t)
DONE Agenda files
  • State "DONE" from "TODO" [2025-06-22 Sun 13:57]
  (setq org-agenda-files (list
  			(concat org-directory "/0_inbox/inbox.org")
  			(concat org-directory "/0_inbox/org-gtd-tasks.org")
  			)
        )
DONE Better agenda views
  • State "DONE" from "TODO" [2025-06-22 Sun 13:58]
  (use-package org-super-agenda)
TODO [4/6] To-do
  • State "DONE" from "TODO" [2024-07-19 Fri 15:53]
DONE Basic todo
  • State "DONE" from "TODO" [2024-07-19 Fri 15:53]
  (setq org-todo-keywords
          '(
            (sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
            (sequence "WAIT(w@/!)" "|" "CNCL(c@)")
            )
          )

  (setq org-todo-keyword-faces
          '(
            ("TODO" :foreground "red" :weight bold)
            ("NEXT" :foreground "red" :weight bold)
            ("WAIT" :foreground "yellow" :weight bold)
            ("DONE" :foreground "green" :weight bold)
            ("CNCL" :foreground "blue" :weight bold)
            )
          )

  (setq org-enforce-todo-dependencies t)
  (setq org-tags-exclude-from-inheritance '("crypt" "!private"))
DONE Switch entry to 'DONE' when all subentries are done
  (defun org-summary-todo (n-done n-not-done)
    "Switch entry to 'DONE' when all subentries are done, to 'TODO' otherwise.
  Uses N-DONE and N-NOT-DONE"
    (let (org-log-done org-log-states)   ; turn off logging
      (org-todo (if (= n-not-done 0) "DONE" "TODO")
                )
        )
    )

    (add-hook 'org-after-todo-statistics-hook #'org-summary-todo)
DONE Getting Things Done (GTD)
  • State "DONE" from "TODO" [2025-06-22 Sun 13:58]

I am now relying on org-gtd to create a GTD workflow:

  1. everything comes into ~/org/inbox.org
  2. Items are clarified with textual context, then with including:

    • a horizon
    • tags

    Items are then goes into one of the following buckets:

    • a single action
    • a project
    • an action within an existing project
    • a sometime/maybe
    • a habit
    • a knowledge/reference item
    • discarded as trash
  3. The above categories are all now headers in ~/org-gtd-tasks.org, but should each have their own file in the future.
  4. All actions are states

    • TODO (instead of NEXT. Will decide if I will use next per the orthdoxy)
    • WAIT
    • DONE
    • CNCL

    I also used to have MAYBE and STARTED tags. Maybe to avoid having a different pool for it (GTD is old, relies on paper and is therefore sequential. Computers solved this problem). STARTED was the tag for the things I am doing, because my NEXT (TODO) list is huge at the moment, mainly because of decades of backlog.

  5. Reference is ~/org/library.org. I am beginning to think I might split this further as it grows. My main ~/library is massive, obviously.
  6. Calendar is still half connected to org-mode and GTD. Need to find a way to connect across devices. org-caldav looks promising.
  (use-package org-gtd
    :defer t
    :init (setq org-gtd-update-ack "3.0.0")
    :after org
    :config
    ;; Keeping these two settings on instead of enabling (org-gtd-mode) until this issue is resolved https://github.om/Trevoke/org-gtd.el/issues/198
    (setq org-edna-use-inheritance t)
    (org-edna-mode)
    ;;    (org-gtd-mode)
    :bind (
  	 ("C-c d c" . org-gtd-capture)
  	 ("C-c d e" . org-gtd-engage)
  	 ("C-c d p" . org-gtd-process-inbox)
  	 :map org-gtd-clarify-map
  	 ("C-c c" . org-gtd-organize)
  	 )
    )
  (defvar org-gtd-directory org-directory)
  (defvar org-gtd-organize-hooks '(org-gtd-set-area-of-focus org-set-tags-command))
  (defvar org-gtd-organize-hooks '(org-gtd-set-area-of-focus))
  (defvar org-gtd-areas-of-focus '(
  				 "Atoms"
  				 "Bits"
  				 "Cells"
  				 "Flags"
  				 "Business"
  				 "Wealth"
  				 "Learning"
  				 "Skills"
  				 "Privacy"
  				 "Archive"
  				 "Library"
  				 "Writing"
  				 "Health"
  				 "Home"
  				 "Family"
  				 "Social"
  				 "Egypt"
  				 )
    )
  (defvar org-gtd-clarify-show-horizons 'right)
DONE Logging
  (setq org-log-into-drawer "LOGBOOK")
TODO Clocking work in drawer
  • State "DONE" from "NEXT" [2023-08-03 Thu 13:16]
  (setq org-clock-into-drawer t)
TODO Habits
  • State "DONE" from "TODO" [2024-07-16 Tue 21:36]
  • State "DONE" from "TODO" [2023-07-31 Mon 14:33]
  (setq org-habit-graph-column 80)
  (setq org-habit-show-habits-only-for-today nil)
DONE [3/3] Reifle
  • State "DONE" from "TODO" [2025-06-22 Sun 13:59]
DONE org-refile targets
  • State "DONE" from "TODO" [2025-06-22 Sun 13:59]
  • State "DONE" from "TODO" [2024-07-16 Tue 21:38]
  • State "DONE" from "TODO" [2023-07-07 Fri 16:51]

Allow refiling to agenda files, nine headers deep, either in current buffer or in agenda files.

  (setq org-refile-targets '((nil :maxlevel . 9)
                             (org-agenda-files :maxlevel . 9)
                             )
        )
DONE Set type of refile targets completion
  • State "DONE" from "TODO" [2025-06-22 Sun 13:59]
  • State "DONE" from "TODO" [2023-07-07 Fri 16:50]

This setting is related to the completion of refile targets. If set to `t`, you build the path in steps by selecting one note at a time. This might be useful with deep hierarchies, but can be slow. When set to `nil`, you can enter the path directly, and Org-Mode uses a Helm-like interface to auto-complete the path. This can be faster, but possibly more difficult with deep hierarchies.

  (setq org-outline-path-complete-in-steps nil)
DONE Allow refiling to new parents created on the go after confirmation
  • State "DONE" from "TODO" [2023-07-07 Fri 16:50]
  (setq org-refile-allow-creating-parent-nodes 'confirm)
TODO [1/2] Capture
  • State "DONE" from "DONE" [2024-07-19 Fri 15:50]
  • State "DONE" from "DONE" [2023-08-17 Thu 14:06]
  • State "DONE" from "DONE" [2023-08-11 Fri 14:16]
  • State "DONE" from "TODO" [2023-07-05 Wed 16:51]
  (defvar org-default-notes-file (concat org-directory "/0_inbox/inbox.org"))
DONE [4/4] Org-protocol
  • State "DONE" from "DONE" [2024-07-19 Fri 15:49]
  • State "DONE" from "TODO" [2023-07-05 Wed 13:21]
DONE Linux configuration

For GNU/Linux setup, put this in ~/.local/share/applications/org-protocol.desktop or in /usr/share/applications to set up system-wide.

[Desktop Entry]
Name=org-protocol
Comment=Intercept calls from emacsclient to trigger custom actions
Categories=Other;
Keywords=org-protocol;
Icon=emacs
Type=Application
Exec=emacsclient -- %u
Terminal=false
StartupWMClass=Emacs
MimeType=x-scheme-handler/org-protocol;

then update the cache database of MIME types handled by desktop files:

update-desktop-database ~/.local/share/applications/
DONE Basic configuration
  (require 'org-protocol)
  (setq org-protocol-default-buffer-for-file-links "*scratch*") ; fixes 'no buffers remain to edit error for org-protocol capturer
DONE Org-protocol templates

And finally, here are the capture templates for org-protocol captures.

  (defvar org-capture-templates '(
                                ("p" "Protocol"
                                 entry
                                 (file "0_inbox/inbox.org")
                                 "* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?"
                                 )
                                ("L" "Protocol Link"
                                 entry
                                 (file "0_inbox/inbox.org")
                                 "* %? [[%:link][%:description]]\n:PROPERTIES:\n:TITLE: %:description\n:URI: %:link\n:CREATED: %U\n:END:"
                                 :prepend nil
                                 :empty-lines 1
                                 :created t
                                 :kill-buffer t
                                 )
                                )
        )
  (setq org-protocol-default-template-key "L")
DONE Convert Orgzly captures to org-protocol captures standard
  • State "DONE" from "TODO" [2023-07-10 Mon 11:52]

This will create clickable titles, create "TITLE", " URL", and "CREATED" properties

  (defun my/org-convert-orgzly-to-org-protocol ()
    "Reformat Orgzly bookmark at point to org-protocol bookmark."
    (interactive)
    (when (org-at-heading-p)
      (let ((headline (nth 4 (org-heading-components))))
        ;; Find and store the link. Delete the link line.
        (search-forward-regexp "^https?://\\S-*" nil t)
        (let ((link (match-string 0)))
          (beginning-of-line)
          (kill-line)
          ;; Delete any trailing blank spaces
          (org-back-to-heading)
          (end-of-line)
          (when (not (org-on-heading-p))
            (delete-char 1)
            )
          ;; Set new headline
          (goto-char (org-entry-beginning-position))
          (org-edit-headline (format "[[%s][%s]]" link headline))
          ;; Set new properties
          (org-set-property "TITLE" headline)
          (org-set-property "URI" link)
          (message "Reformatted Orgzly bookmark at point to org-protocol bookmark")
          )
        )
      )
    )
TODO org-roam-capture templates
  • State "DONE" from "TODO" [2023-08-19 Sat 18:17]
  (setq org-roam-capture-templates
        '(
          ("L" "link" plain
           (function org-roam--capture-get-point)
           "%?"
           :file-name "web/%<%Y-%m-%dT%H%M%S>.org"
           :head "#+TITLE: ${title}\n#+CREATED: %<%Y-%m-%dT%H%M%S>"
           :immediate-finish t
           :unnarrowed t
           )

          ("h" "hugo post" plain
           "%?"
           :target (file+head "posts/${slug}.org"
                              "#+TITLE: ${title}\n#+DATE: %U\n#+HUGO_BASE_DIR: ~/gharbeia.net\n#+HUGO_SECTION: ./posts\n#+HUGO_AUTO_SET_LASTMOD: t\n#+HUGO_TAGS: article\n#+HUGO_DRAFT: true\n")
           :immediate-finish t
           :unnarrowed t
           )

          ("p" "person" plain
           "%?"
           :if-new (file+head "people/${slug}.org"
                             "#+TITLE: ${title}")
          :immediate-finish t
          :unnarrowed t
          )
         )
       )
  (setq org-roam-dailies-capture-templates
        '(
          ("d" "daily" plain
           ""
          :target ("file+heaed %<%Y-%m-%d>.org"  "#+title: %<%Y-%m-%d>\n\n")
          :immediate-finish t
          )
         )
       )
TODO [1/5] Org-roam
TODO Basic org-roam setup
  • State "DONE" from "TODO" [2024-07-16 Tue 21:30]
  • State "DONE" from "TODO" [2023-07-05 Wed 17:11]
(use-package org-roam
  :init (setq org-roam-v2-ack t) ;; Acknowledge V2 upgrade
  :after org
  :config
  (org-roam-db-autosync-enable)
  (require 'org-roam-dailies)
  :bind  (
   ("C-c n f" . org-roam-node-find)
   ("C-c n g" . org-roam-graph)
   ("C-c n r" . org-roam-node-random)
   ("C-c n h" . org-roam-node-convert-headline)
   ("C-c n i" . org-roam-node-insert)
   ("C-c n o" . org-id-get-create)
   ("C-c n t" . org-roam-tag-add)
   ("C-c n a" . org-roam-alias-add)
   ("C-c n l" . org-roam-buffer-display-dedicated)
   )
  )
  (use-package org-roam
;    :straight (:files (:defaults "extensions/*"))
    :init (setq org-roam-v2-ack t) ;; Acknowledge V2 upgrade
    :after org
    :config
    (org-roam-db-autosync-enable)
    (require 'org-roam-dailies)
    (setq org-roam-mode-sections
  	(list #'org-roam-backlinks-section
                #'org-roam-reflinks-section
                #'org-roam-unlinked-references-section
                )
  	)
    (add-to-list 'display-buffer-alist
                 '("\\*org-roam\\*"
                   (display-buffer-in-side-window)
                   (side . right)
                   (slot . 0)
                   (window-width . 0.33)
                   (window-parameters . ((no-other-window . t)
                                         (no-delete-other-windows . t)))))
    :bind  (
  	  ("C-c n f" . org-roam-node-find)
  	  ("C-c n g" . org-roam-graph)
  	  ("C-c n r" . org-roam-node-random)
  	  ("C-c n h" . org-roam-node-convert-headline)
  	  ("C-c n i" . org-roam-node-insert)
  	  ("C-c n o" . org-id-get-create)
  	  ("C-c n t" . org-roam-tag-add)
  	  ("C-c n a" . org-roam-alias-add)
  	  ("C-c n l" . org-roam-buffer-display-dedicated)
  	  )
    )
  (setq org-roam-directory (concat org-directory "/1_thinking"))
  (setq org-roam-dailies-directory (concat org-directory "/0_inbox/daily"))
  (use-package sqlite3)
  (require 'sqlite3)
DONE Include subdirectories in org-roam
  • State "DONE" from "TODO" [2024-07-19 Fri 16:45]
  • State "DONE" from "TODO" [2023-07-06 Thu 12:54]
  (setq org-roam-file-exclude-regexp "^[.][.]?/")
TODO Configure what display in org-roam-buffer
  • State "DONE" from "TODO" [2024-07-19 Fri 16:47]

Note that computing unlinked references may be slow, and has not been added in by default.

  (setq org-roam-mode-sections
        (list #'org-roam-backlinks-section
              #'org-roam-reflinks-section
              #'org-roam-unlinked-references-section 
              )
        )
TODO Filter org-roam nodes find by tag
  • State "DONE" from "TODO" [2023-08-19 Sat 18:13]
  (defun my/org-roam-node-has-tag (node tag)
    "Filter function to check if the given NODE has the specified TAG."
    (member tag (org-roam-node-tags node))
    )

  (defun my/org-roam-node-find-by-tag ()
    "Find and open an Org-roam node based on a specified tag."
    (interactive)
    (let ((tag (read-string "Enter tag: ")))
      (org-roam-node-find nil nil (lambda (node) (my/org-roam-node-has-tag node tag))))
    )
TODO [0/3] Move org header to org-roam-daily
TODO OpenAI
  (defun my/org-move-entry-to-daily-notes ()
    "Move the current org-mode headline to the daily notes file based on its :CREATED: property."
    (interactive)
    (let*
        (
         (created-prop (org-entry-get nil "CREATED"))
         (created-date (when created-prop
                         (org-parse-time-string created-prop)))
         (year (nth 5 created-date)) ; Extract year (6th element)
         (month (nth 4 created-date)) ; Extract month (5th element)
         (day (nth 3 created-date)) ; Extract day (4th element)
         (target-date (format "%04d-%02d-%02d" year month day)) ; Format date string
         (target-file (org-roam-dailies-goto target-date)) 
         )
      (when target-file
        (org-cut-subtree)
        (find-file target-file)
        (goto-char (point-max))
        (unless (bolp) (newline))
        (org-paste-subtree)
        )
      )
  )
    (defun my/org-move-entry-to-daily-notes ()
      "Move the current org-mode headline to the daily notes file based on its :CREATED: property."
      (interactive)
      (let*
        (
         (created-prop (org-entry-get nil "CREATED"))
         (created-date (when created-prop
                         (org-parse-time-string created-prop)))
         (year (nth 5 created-date)) ; Extract year (6th element)
         (month (nth 4 created-date)) ; Extract month (5th element)
         (day (nth 3 created-date)) ; Extract day (4th element)
         (target-date (format "%04d-%02d-%02d" year month day)) ; Format date string
         ;(target-date "2024-01-01")
         (target-file (concat org-roam-dailies-directory "/" target-date ".org"))
         (find-file target-file)
         )
      )
      (when target-file
        (org-cut-subtree)
        (find-file target-file)
        (org-id-get-create)
  ;; #+title: target-date
        (goto-char (point-max))
        (unless (bolp) (newline))
        (org-paste-subtree)
        )
      )
TODO Modified rose Refile to org-roam-dailies

Arrived to from this conversation

Here's a breakdown of the functions and their roles:

  1. org-roam-dailiescapture
  (defun org-roam-dailies--capture (time &optional goto keys)
  "Capture an entry in a daily-note for TIME, creating it if necessary.
When GOTO is non-nil, go the note without creating an entry.

ELisp programs can set KEYS to a string associated with a template.
In this case, interactive selection will be bypassed."
  (let ((org-roam-directory (expand-file-name org-roam-dailies-directory org-roam-directory))
        (org-roam-dailies-directory "./"))
    (org-roam-capture- :goto (when goto '(4))
                       :keys keys
                       :node (org-roam-node-create)
                       :templates org-roam-dailies-capture-templates
                       :props (list :override-default-time time)))
  (when goto (run-hooks 'org-roam-dailies-find-file-hook)))
  1. `my/refile`: This function refiles a single headline by finding the file, reverting the buffer, and replacing fuzzy links with roam: links.
      ;;; lisp/refile.el -*- lexical-binding: t; -*-
    (defun my/refile ()
        "Refiles a headline (and its subtree) with a CREATED property to its corresponding daily."
      (interactive)
      (revert-buffer t t)
      ;; replace fuzzy links with roam: links (exclude non-fuzzy links, ie. links with `:')
      (while (re-search-forward "\\[\\[\\([^:]+?\\)\\]\\]" nil t)
        (replace-match "[[roam:\\1]]" nil nil))
  ;    (org-roam-link-replace-all) ;; TODO create blank page if non-existent
      ;; remove blank lines because i think they are ugly
      (while (re-search-forward "\n+" nil t)
        (replace-match "\n" nil nil))
      (let ((entries (org-map-entries #'my/refile--inbox-headline nil 'file)))
        (message (format "Refiled %d headline(s)" (seq-count #'identity entries)))
        )
      )
  1. `my/refileinbox-headline`: This function refiles a headline at the current point by deleting the CREATED property and capturing the headline using org-capture.
  (defun my/refile--inbox-headline ()
    "Refile headline at POINT."
    (setq org-map-continue-from (point))
    (if-let (capture-template (my/refile--get-template))
        (my/refile--capture capture-template)
      (my/refile--to-node)))
  1. `my/refilecapture`: This function runs org-capture on an inbox heading and inserts the result into the buffer.
  (defun my/refile--capture (capture-template)
    "Run 'org-capture' on inbox heading using CAPTURE-TEMPLATE."
    ;; (org-entry-delete nil "CREATED")
    (let ((keys (car capture-template))
          (heading (cdr capture-template))
          (entry (org-no-properties (org-get-entry))))
      (org-capture nil keys)
      (insert heading "\n" entry))
    (org-capture-finalize)
    (org-cut-subtree)
    )
  1. `my/refileget-template`: This function parses the capture template prefix from the heading and returns a cons cell containing the keys and heading.
  (defun my/refile--get-template ()
    "Parse capture template prefix from heading."
    (when-let* ((raw-heading (org-no-properties (org-get-heading)))
                (match (string-match "@\\(\\w+\\) \\(.+\\)$" raw-heading))
                (keys (match-string-no-properties 1 raw-heading))
                (heading (match-string-no-properties 2 raw-heading)))
      (cons keys heading))
    )
  1. `my/refileto-node`: This function refiles a headline to an Org-roam node.
  (defun my/refile--to-node ()
    "Refiles non-capture headings to org-roam node."
    (if-let ((to (+org/entry-get-delete "TO")))
        (my/refile--org-roam-node (org-roam-node-from-title-or-alias to))
      (my/refile--to-daily)))
  1. `my/refileto-daily`: This function refiles a headline to a daily node based on its CREATED property.
  (defun my/refile--to-daily ()
    "Refile headline at POINT to the associated daily node based on its `CREATED' property."
    (when-let* ((created (org-entry-get nil "CREATED"))
                (time (org-time-string-to-time created))
                (daily-node (my/refile--get-daily-node time)))
      (org-entry-delete nil "CREATED")
      (my/refile--org-roam-node daily-node)))
  1. `my/refileget-daily-node`: This function returns the Org-roam node for a given time.
  (defun my/refile--get-daily-node (time)
    "Return org-roam node for TIME."
    (save-window-excursion
      (org-roam-dailies--capture time t)
      (org-roam-node-at-point)))
  1. `my/refileorg-roam-node`: This function refiles a node to an Org-roam node. The `my/refileorg-roam-node` function is quite long and complex, but it seems to be responsible for refiling a node to an Org-roam node. It takes several arguments, including the node to refile, and uses several org-roam functions to perform the refiling.
  (defun my/refile--org-roam-node (node)
    "Refile NODE at point to an Org-roam node.
  If region is active, then use it instead of the node at point.
  Implementation of `org-roam-refile' from org-roam PR #2388."
    (interactive
     (list (org-roam-node-read nil nil nil 'require-match)))
    (let* ((regionp (org-region-active-p))
           (region-start (and regionp (region-beginning)))
           (region-end (and regionp (region-end)))
           (file (org-roam-node-file node))
           (nbuf (or (find-buffer-visiting file)
                     (find-file-noselect file)))
           level reversed)
      (if (equal (org-roam-node-at-point) node)
          (user-error "Target is the same as current node")
        (if regionp
            (progn
              (org-kill-new (buffer-substring region-start region-end))
              (org-save-markers-in-region region-start region-end))
          (progn
            (if (org-before-first-heading-p)
                (org-roam-demote-entire-buffer))
            (org-copy-subtree 1 nil t)))
        (with-current-buffer nbuf
          (org-with-wide-buffer
           (goto-char (org-roam-node-point node))
           (setq level (org-get-valid-level (funcall outline-level) 1)
                 reversed (org-notes-order-reversed-p))
           (goto-char
            (if reversed
                (or (outline-next-heading) (point-max))
              (or (save-excursion (org-get-next-sibling))
                  (org-end-of-subtree t t)
                  (point-max))))
           (unless (bolp) (newline))
           (org-paste-subtree level nil nil t)
           (and org-auto-align-tags
                (let ((org-loop-over-headlines-in-active-region nil))
                  (org-align-tags)))
           (when (fboundp 'deactivate-mark) (deactivate-mark))))
        (if regionp
            (delete-region (point) (+ (point) (- region-end region-start)))
          (org-preserve-local-variables
           (delete-region
            (and (org-back-to-heading t) (point))
            (min (1+ (buffer-size)) (org-end-of-subtree t t) (point)))))
        ;; If the buffer end-up empty after the refile, kill it and delete its
        ;; associated file.
        (when (eq (buffer-size) 0)
          (if (buffer-file-name)
              (delete-file (buffer-file-name)))
          (set-buffer-modified-p nil)
          ;; If this was done during capture, abort the capture process.
          (when (and org-capture-mode
                     (buffer-base-buffer (current-buffer)))
            (org-capture-kill))
          (kill-buffer (current-buffer))))))
  (defun +org/entry-get-delete (entry)
  (prog1 (org-entry-get nil entry) (org-entry-delete nil entry)))
TODO Automatically copy (or move) completed tasks to dailies
  (defun my/org-roam-copy-todo-to-today ()
    (interactive)
    (let ((org-refile-keep 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" ("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)))

      ;; Only refile if the target file is different than the current file
      (unless (equal (file-truename today-file)
                     (file-truename (buffer-file-name)))
        (org-refile nil nil (list "Tasks" today-file nil pos)))))

  (add-to-list 'org-after-todo-state-change-hook
               (lambda ()
                 (when (equal org-state "DONE")
                   (my/org-roam-copy-todo-to-today))))
DONE Exporting [1/1]
  • State "DONE" from "TODO" [2025-03-24 Mon 17:31]

From Sriramkswamy:

Org has a powerful exporting feature. Lets select the various formats to export and also mention how exactly we need it to export to LaTeX with syntax highlighting. I have also taken a good looking CSS configuration from Zhitao Gong and I use it for exporting by putting it in the same folder as my org file and adding #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="org.css"/> to the top of my org file.

  (setq org-export-with-smart-quotes t)
  (setq org-export-backends '(beamer html latex md))
DONE Export to EPUB
  • State "DONE" from [2025-03-24 Mon 17:31]
  (use-package ox-epub
    )
DONE org-attach
  (defvar org-attach-id-dir (concat org-directory "/library"))
DONE Enable shell scripting support in org-babel
  (defvar org-babel-do-load-languages  'org-babel-load-languages  '((shell . t)))
TODO Insert org-mode links from clipboard
  • State "DONE" from "TODO" [2023-08-18 Fri 13:10]
  (use-package org-cliplink
    :bind
    (("C-x p i" . org-cliplink))
    )
TODO Deft
  (use-package deft
    :commands (deft)
    :init
    (defvar deft-extensions '("org"))
    (defvar deft-recursive nil)
    (defvar deft-use-filename-as-title t)
    :config
    (defvar deft-directory org-directory)
    (defvar deft-recursive t)
    (defvar deft-strip-summary-regexp ":PROPERTIES:\n\\(.+\n\\)+:END:\n")
    (defvar deft-use-filename-as-title t)
    :bind  ("C-c n d" . deft)
    )

DONE [3/3] Shell

  • State "DONE" from "TODO" [2024-07-19 Fri 15:34]
  • State "DONE" from "TODO" [2024-07-09 Tue 17:11]
DONE Bash completion
  • State "DONE" from "TODO" [2024-07-16 Tue 20:35]
  • State "DONE" from "TODO" [2023-08-18 Fri 13:02]
  (use-package bash-completion
    :config
    (require 'bash-completion)
    (bash-completion-setup)
    )
(defvar shell-dynamic-complete-functions t)
DONE [3/3] Eshell
CNCL Add programmable bash completion to Emacs shell-mode
  • State "DONE" from "TODO" [2024-02-28 Wed 16:30]
  (require 'bash-completion)
  (add-hook 'eshell-mode-hook
          (lambda ()
            (add-hook 'completion-at-point-functions
                      'bash-completion-capf-nonexclusive nil t
                      )
            )
          )
CNCL Use colors in eshell
  • State "DONE" from "TODO" [2023-08-28 Mon 18:56]
  (use-package xterm-color
    :commands (xterm-color-filter)
    )

  (use-package eshell
    :after xterm-color
    :config
    (define-key eshell-hist-mode-map (kbd "M-r") #'consult-history)
    (add-hook 'eshell-mode-hook
              (lambda ()
                (setenv "TERM" "xterm-256color")))
    (add-hook 'eshell-before-prompt-hook (setq xterm-color-preserve-properties t))
    (add-to-list 'eshell-preoutput-filter-functions 'xterm-color-filter)
    (setq eshell-output-filter-functions
          (remove 'eshell-handle-ansi-color eshell-output-filter-functions)
          )
    )
CNCL Eshell completion
  (add-hook 'eshell-mode-hook
          (lambda ()
            (add-hook 'completion-at-point-functions
                      'bash-completion-capf-nonexclusive nil t)))
CNCL Emulate A Terminal (EAT)
  • State "CNCL" from "TODO" [2024-04-01 Mon 15:52]
    Moved to shell and eshell. Eat is not in repositories currently.
  • State "DONE" from [2023-08-30 Wed 20:43]
  (use-package eat
    :config
    ;; For `eat-eshell-mode'.
    (add-hook 'eshell-load-hook #'eat-eshell-mode)

    ;; For `eat-eshell-visual-command-mode'.
    (add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode)
    )

DONE [2/2] Saving Emacs Sessions

  • State "DONE" from "TODO" [2024-07-19 Fri 15:08]
  • State "DONE" from "DONE" [2024-02-28 Wed 16:26]
DONE Close frame when done
  • State "DONE" from "NEXT" [2023-08-03 Thu 13:21]

When a server buffer is done, the current window (frame) should be closed. This is useful in scenarios where Emacs is used as an external editor (for instance, from a version control system). When you're done editing, the frame closes automatically. If this is the only frame, Emacs will exit.

  (add-hook 'server-done-hook (lambda () (delete-frame)))
DONE Save desktop session
  (desktop-save-mode t)

TODO [8/21] Reading and Writing

  • State "DONE" from "TODO" [2023-07-06 Thu 12:32]

DONE Move correctly over camelCased words

  • State "DONE" from "TODO" [2024-07-19 Fri 16:04]
  (subword-mode)

DONE Understand the more common sentence with double space

  • State "DONE" from "TODO" [2024-07-19 Fri 16:04]
  (setq sentence-end-double-space nil)

DONE Join lines into paragraph

  • State "DONE" from "TODO" [2024-07-19 Fri 16:03]
  • State "DONE" from "TODO" [2023-07-06 Thu 12:31]
  (defun my/fill-or-unfill-paragraph (&optional unfill region)
    "Fill paragraph (or REGION). With the prefix argument UNFILL, fill it instead."
    (interactive (progn
                   (barf-if-buffer-read-only)
                   (list (if current-prefix-arg 'fill) t)))
    (let ((fill-column (if unfill fill-column (point-max))))
      (fill-paragraph nil region)))

  (bind-key "M-q" 'my/fill-or-unfill-paragraph)
  (defun my/fill-or-unfill-all-paragraphs (&optional unfill)
  "Fill or unfill all paragraphs in the current buffer.
With the prefix argument UNFILL, fill them instead."
  (interactive (list (if current-prefix-arg 'fill)))
  (let ((fill-column (if unfill fill-column (point-max))))
    (save-excursion
      (goto-char (point-min))
      (while (not (eobp))
        (fill-paragraph nil t)
        (forward-paragraph)))))

(bind-key "M-Q" 'my/fill-or-unfill-all-paragraphs)
  (remove-hook 'text-mode-hook #'turn-on-auto-fill)
  (add-hook 'text-mode-hook 'turn-on-visual-line-mode)

TODO Expand some words with auto-correct

(setq save-abbrevs 'silently)
(setq-default abbrev-mode t)

TODO ediff

  (setq ediff-window-setup-function 'ediff-setup-windows-plain)
  (setq ediff-split-window-function 'split-window-horizontally)

TODO tramp

  (setq tramp-default-method "ssh"
        tramp-backup-directory-alist backup-directory-alist
        tramp-ssh-controlmaster-options "ssh")

TODO Clean up space

  • State "DONE" from "TODO" [2023-07-06 Thu 12:32]
  (bind-key "M-SPC" 'cycle-spacing)

TODO Transform <a href> links into org links

  • State "DONE" from [2023-08-19 Sat 19:41]
  (defun my/transform-html-links-to-org ()
  "Transform all HTML <a> links in the current buffer into 'org-mode' links."
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "<a href=\"\\(.*?\\)\">\\(.*?\\)</a>" nil t)
    (replace-match (org-make-link-string (match-string 1) (match-string 2)))))

TODO Count words per minute

  • State "DONE" from "TODO" [2023-07-06 Thu 12:32]
  (require 'org-clock)
  (defun my/org-entry-wpm ()
    (interactive)
    (save-restriction
      (save-excursion
        (org-narrow-to-subtree)
        (goto-char (point-min))
        (let* ((words (count-words-region (point-min) (point-max)))
               (minutes (org-clock-sum-current-item))
               (wpm (/ words minutes)))
          (message "WPM: %d (words: %d, minutes: %d)" wpm words minutes)
          (kill-new (number-to-string wpm))
          )
        )
      )
    )

TODO Enable dict mode

  (setq dictionary-server "automatic")

TODO Pick out passive voice and weasel words

  • State "DONE" from "NEXT" [2023-08-09 Wed 13:52]
  (use-package writegood-mode
    :diminish writegood-mode
    :config
    (progn (add-hook 'text-mode-hook 'writegood-mode))
    )

TODO Org-babel docker

  • State "DONE" from "TODO" [2024-07-09 Tue 16:58]
  (use-package ob-docker-build
    :straight (ob-docker-build :type git :host github :repo "ifitzpat/ob-docker-build")
    :defer t
    :config
    (add-to-list 'org-babel-load-languages '(docker-build . t))
    (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)
  )

TODO [1/6] Spelling and syntax

DONE Spell checking
  • State "DONE" from "TODO" [2024-07-19 Fri 16:51]
  • State "DONE" from "TODO" [2023-07-05 Wed 16:09]

This requires installation of hunspell

  sudo apt install hunspell
  (use-package flyspell
    :config (setq ispell-program-name "hunspell"
                  ispell-default-dictionary "en_US"
                  )
    :diminish (flyspell-mode . "φ")
    :hook (text-mode . flyspell-mode)
    :bind (
           ("M-<f7>" . flyspell-buffer)
           ("<f7>" . flyspell-word)
           ("C-;" . flyspell-auto-correct-previous-word)
           )
    )
TODO Flyspell correct
  • State "DONE" from [2024-07-02 Tue 13:13]
  (use-package flyspell-correct
    :after flyspell
    :bind (:map flyspell-mode-map ("C-;" . flyspell-correct-wrapper))
  )
TODO Flycheck
  • State "DONE" from "TODO" [2024-07-19 Fri 14:30]
  • State "DONE" from "TODO" [2024-07-02 Tue 13:13]

Needs external checkers installed

  (use-package flycheck
    :init (global-flycheck-mode)
    :diminish (flycheck-mode . "")
    :config
    (add-hook 'after-init-hook #'global-flycheck-mode)
    (setq flycheck-emacs-lisp-load-path 'inherit)
    (setq flycheck-emacs-lisp-load-path (concat user-emacs-directory "straight/build"))
    )
TODO Flycheck bash
  sudo apt install devscripts
  (use-package flycheck-checkbashisms
    :config
    (flycheck-checkbashisms-setup)
  )
TODO Yaml
  • State "DONE" from "TODO" [2024-06-27 Thu 13:03]
  (use-package yaml-mode
    :config
    (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))
    (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode))
    )
TODO Docker
  • State "DONE" from "TODO" [2024-06-27 Thu 13:03]
  (use-package docker-compose-mode)

DONE Read ebooks

  • State "DONE" from "NEXT" [2023-08-09 Wed 13:27]
  (use-package calibredb
:defer t
    :config
      (setq calibredb-format-all-the-icons t)
      (setq calibredb-format-icons-in-terminal t)
      )
  ;; Forcefully reset the variable after loading calibredb
  (defvar calibredb-root-dir (concat (getenv "HOME") "/library/books"))
  (defvar calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir))
  					; (defvar calibredb-library-alist (concat (getenv "HOME") "/library/books"))
  ;;  (defvar calibredb-search-page-max-rows 1000)
  (defvar calibredb-id-width 6)
  (defvar calibredb-title-width 100)
  (defvar calibredb-format-width 0)
  (defvar calibredb-date-width 0)
  (defvar calibredb-author-width 20)
  (defvar calibredb-comment-width 0)
  (defvar calibredb-tag-width 0)

Some keybindings

  (defvar calibredb-show-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map "?" #'calibredb-entry-dispatch)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "q" #'calibredb-entry-quit)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "\M-/" #'calibredb-rga)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-show-mode'.")
  (defvar calibredb-search-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map [mouse-3] #'calibredb-search-mouse)
      (define-key map (kbd "<RET>") #'calibredb-find-file)
      (define-key map "?" #'calibredb-dispatch)
      (define-key map "a" #'calibredb-add)
      (define-key map "A" #'calibredb-add-dir)
      (define-key map "c" #'calibredb-clone)
      (define-key map "d" #'calibredb-remove)
      (define-key map "D" #'calibredb-remove-marked-items)
      (define-key map "j" #'calibredb-next-entry)
      (define-key map "k" #'calibredb-previous-entry)
      (define-key map "l" #'calibredb-virtual-library-list)
      (define-key map "L" #'calibredb-library-list)
      (define-key map "n" #'calibredb-virtual-library-next)
      (define-key map "N" #'calibredb-library-next)
      (define-key map "p" #'calibredb-virtual-library-previous)
      (define-key map "P" #'calibredb-library-previous)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "S" #'calibredb-switch-library)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "v" #'calibredb-view)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "b" #'calibredb-catalog-bib-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "r" #'calibredb-search-refresh-and-clear-filter)
      (define-key map "R" #'calibredb-search-clear-filter)
      (define-key map "q" #'calibredb-search-quit)
      (define-key map "m" #'calibredb-mark-and-forward)
      (define-key map "f" #'calibredb-toggle-favorite-at-point)
      (define-key map "x" #'calibredb-toggle-archive-at-point)
      (define-key map "h" #'calibredb-toggle-highlight-at-point)
      (define-key map "u" #'calibredb-unmark-and-forward)
      (define-key map "i" #'calibredb-edit-annotation)
      (define-key map (kbd "<DEL>") #'calibredb-unmark-and-backward)
      (define-key map (kbd "<backtab>") #'calibredb-toggle-view)
      (define-key map (kbd "TAB") #'calibredb-toggle-view-at-point)
      (define-key map "\M-n" #'calibredb-show-next-entry)
      (define-key map "\M-p" #'calibredb-show-previous-entry)
      (define-key map "/" #'calibredb-search-live-filter)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-search-mode'.")

DONE Annotate PDFs and EPUBs

  • State "DONE" from "TODO" [2024-07-19 Fri 14:35]
  • State "DONE" from "NEXT" [2023-08-09 Wed 15:06]
  (use-package org-noter)
  (defvar org-noter-notes-search-path (list (concat org-directory "/library/books")))
  (defvar org-noter-default-notes-file-names '("books.org"))

DONE Link PDFs

  • State "DONE" from "TODO" [2024-07-19 Fri 16:54]
  • State "DONE" from "NEXT" [2023-08-12 Sat 14:05]
  (use-package org-noter-pdftools
    :after org-noter
    :config
    ;; Add a function to ensure precise note is inserted
    (defun org-noter-pdftools-insert-precise-note (&optional toggle-no-questions)
      (interactive "P")
      (org-noter--with-valid-session
       (let ((org-noter-insert-note-no-questions (if toggle-no-questions
  						   (not org-noter-insert-note-no-questions)
                                                   org-noter-insert-note-no-questions))
             (org-pdftools-use-isearch-link t)
             (org-pdftools-use-freepointer-annot t))
         (org-noter-insert-note (org-noter--get-precise-info)))))

    ;; fix https://github.com/weirdNox/org-noter/pull/93/commits/f8349ae7575e599f375de1be6be2d0d5de4e6cbf
    (defun org-noter-set-start-location (&optional arg)
      "When opening a session with this document, go to the current location.
  With a prefix ARG, remove start location."
      (interactive "P")
      (org-noter--with-valid-session
       (let ((inhibit-read-only t)
             (ast (org-noter--parse-root))
             (location (org-noter--doc-approx-location (when (called-interactively-p 'any) 'interactive))))
         (with-current-buffer (org-noter--session-notes-buffer session)
           (org-with-wide-buffer
            (goto-char (org-element-property :begin ast))
            (if arg
                (org-entry-delete nil org-noter-property-note-location)
              (org-entry-put nil org-noter-property-note-location
                             (org-noter--pretty-print-location location))))))))
    (with-eval-after-load 'pdf-annot
      (add-hook 'pdf-annot-activate-handler-functions #'org-noter-pdftools-jump-to-note)
      )
    )

DONE View EPUBs   books

  • State "DONE" from "TODO" [2024-07-19 Fri 14:37]
  • State "DONE" from "NEXT" [2023-08-09 Wed 13:19]
  (use-package nov
    :config
    (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
    )

TODO Zotero

  (use-package helm-bibtex)
  (defvar bibtex-completion-bibliography '("~/bibliography/zotero.bib"))

DONE [2/2] Security

  • State "DONE" from "TODO" [2024-07-16 Tue 21:49]
  • State "DONE" from "TODO" [2024-07-09 Tue 14:16]

DONE Password-store

  • State "DONE" from "TODO" [2024-07-16 Tue 21:49]
  • State "DONE" from "TODO" [2023-07-06 Thu 11:44]
  (use-package password-store)

DONE Auth source

  • State "DONE" from "TODO" [2023-07-06 Thu 11:45]
  (use-package auth-source
    :config (auth-source-pass-enable)
    )

TODO [2/3] AI

DONE Ellama

  • State "DONE" from "TODO" [2024-07-19 Fri 17:27]
  • State "DONE" from "TODO" [2024-06-27 Thu 07:40]
  ;; YOU DON'T NEED NONE OF THIS CODE FOR SIMPLE INSTALL
  ;; IT IS AN EXAMPLE OF CUSTOMIZATION.
  (use-package ellama
    :init
    (require 'llm-openai)
    ;; setup key bindings
    (setq ellama-keymap-prefix "C-c e")
    )
  (setopt ellama-providers
  	'(
  	  ;; Ollama Provider (added here with a name)
  	  ("ollama" . (make-llm-ollama
                               ;; Consider a dedicated embedding model if gemma isn't ideal for it.
                               :chat-model "gemma3:latest"
                               :embedding-model "gemma3:latest" ; Or e.g., "nomic-embed-text"
                               :default-chat-non-standard-params '(("num_ctx" . 8192))))

  	  ("openai" . (make-llm-openai
                         :key (auth-source-pass-get "api-key" "www/openai.com/amr@gharbeia.net")
                         :chat-model "gpt-4o"
  		       :embedding-model "text-embedding-3-large"))

  	  ("google" . (make-llm-google
  		       :key (auth-source-pass-get "gemini-api-key" "www/google.com/amr.gharbeia")
                         :chat-model "latest" ; Use "latest" or specific version
                         :embedding-model "text-embedding-004")) ; Or gecko, but 004 is newer

            ("groq" . (make-llm-openai-compatible
                       :url "https://api.groq.com/openai/v1"
                       :key (auth-source-pass-get "api-key" "www/console.groq.com/groq@amr.gharbeia.net")
                       ;; Check Groq console for available models, these might change
                       :chat-model "llama3-70b-8192" ; Example, verify on Groq
                       :embedding-model "llama3-70b-8192")) ; Groq might not offer dedicated embedding models via this API
            ))

  ;; --- Set Active Providers ---
  ;; Choose your default provider from the list above by its name
  (setopt ellama-provider "ollama") ; Or "ollama", "openai", "groq"

  ;; You can specify different providers for different tasks if needed
  (setopt ellama-translation-provider "ollama")
  (setopt ellama-naming-provider "ollama")
  (setopt ellama-naming-scheme 'ellama-generate-name-by-llm)

  ;; --- Ensure auth-source is configured ---
  ;; (require 'auth-source)
  ;; (setq auth-sources '("~/.authinfo.gpg" "~/.authinfo" "~/.netrc"))
  ;; Make sure your API keys are correctly stored in one of these files.
  ;; Example .authinfo.gpg entry for OpenAI:
  ;; machine www/openai.com/amr@gharbeia.net login amr@gharbeia.net password YOUR_OPENAI_API_KEY
  ;; Example .authinfo.gpg entry for Google Gemini:
  ;; machine www/google.com/amr.gharbeia login amr.gharbeia password YOUR_GEMINI_API_KEY
  ;; Example .authinfo.gpg entry for Groq:
  ;; machine www/console.groq.com/groq@amr.gharbeia.net login groq@amr.gharbeia.net password YOUR_GROQ_API_KEY

  (setq llm-debug t)
  (use-package ellama
  :ensure t
  :bind ("C-c e" . ellama)
  ;; send last message in chat buffer with C-c C-c
  :hook (org-ctrl-c-ctrl-c-final . ellama-chat-send-last-message)
  :init (setopt ellama-auto-scroll t)
  :config
  ;; show ellama context in header line in all buffers
  (ellama-context-header-line-global-mode +1)
  ;; show ellama session id in header line in all buffers
  (ellama-session-header-line-global-mode +1))

CNCL GPTel

  • State "CNCL" from "DONE" [2024-04-01 Mon 15:32]
    Moved to Ellama
  • State "DONE" from "TODO" [2024-02-28 Wed 16:49]
  (use-package gptel)
  (setq gptel-api-key (auth-source-pass-get "api-key" "www/console.groq.com/groq@amr.gharbeia.net"))
  (gptel-make-openai "Groq"               ;Any name you want
  :host "api.groq.com"
  :endpoint "/openai/v1/chat/completions"
  :stream t
  :key (auth-source-pass-get "api-key" "www/console.groq.com/groq@amr.gharbeia.net")                   ;can be a function that returns the key
  :models '(llama-3.1-70b-versatile
            llama-3.1-8b-instant
            llama3-70b-8192
            llama3-8b-8192
            mixtral-8x7b-32768
            gemma-7b-it))

TODO Elisa

  (use-package elisa
    :init
    (setopt elisa-limit 5)
    (require 'llm-ollama)
    (setopt elisa-embeddings-provider (make-llm-ollama :embedding-model "nomic-embed-text"))
    (setopt elisa-chat-provider (make-llm-ollama
                                 :chat-model "sskostyaev/openchat:8k-rag"
                                 :embedding-model "nomic-embed-text"))
    )

DONE Accounting

  • State "DONE" from "TODO" [2024-07-19 Fri 15:07]
  • State "DONE" from "TODO" [2024-06-27 Thu 07:43]
  (use-package beancount
    :straight (beancount :type git :host github :repo "beancount/beancount-mode")
    :config
    (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
    (add-hook 'beancount-mode-hook #'outline-minor-mode)
    (define-key beancount-mode-map (kbd "C-c C-n") #'outline-next-visible-heading)
    (define-key beancount-mode-map (kbd "C-c C-p") #'outline-previous-visible-heading)
    (add-hook 'beancount-mode-hook #'flymake-bean-check-enable)
    )

On package.el, it is a manual install so far

  (make-directory (expand-file-name "manual-packages/" user-emacs-directory) t)
  (make-directory (expand-file-name "beancount/" (concat user-emacs-directory "manual-packages")) t)
  (add-to-list 'load-path "~/.emacs.d/manual-packages/beancount-mode")
  (require 'beancount)
  (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
  (add-hook 'beancount-mode-hook #'outline-minor-mode)
  (define-key beancount-mode-map (kbd "C-c C-n") #'outline-next-visible-heading)
  (define-key beancount-mode-map (kbd "C-c C-p") #'outline-previous-visible-heading)
  (add-hook 'beancount-mode-hook #'flymake-bean-check-enable)
  cd ~/.emacs.d/manual-packages/
  git clone https://github.com/beancount/beancount-mode/

DONE Browser

  • State "DONE" from "TODO" [2024-07-19 Fri 17:28]
  • State "DONE" from "TODO" [2023-07-07 Fri 15:29]
  (use-package eww
    :bind* (("M-m g x" . eww)
            ("M-m g :" . eww-browse-with-external-browser)
            ("M-m g #" . eww-list-histories)
            ("M-m g {" . eww-back-url)
            ("M-m g }" . eww-forward-url))
    :config
    (progn
      (add-hook 'eww-mode-hook 'visual-line-mode)
      )
    )

DONE Manage Docker in Emacs

  • State "DONE" from "TODO" [2024-07-19 Fri 17:29]
  (use-package docker
    :bind ("C-c d" . docker)
    )

DONE Periodic table of the elements   chemistry

  • State "DONE" from "TODO" [2024-07-19 Fri 17:29]
  • State "DONE" from "TODO" [2023-08-21 Mon 13:27]
  (use-package chemtable)

DONE End matter

  (provide 'config)
  ;;; config.el ends here
  (provide 'custom)
  ;;; custom.el ends here