Skip to content

Latest commit

 

History

History
3183 lines (2874 loc) · 115 KB

config.org

File metadata and controls

3183 lines (2874 loc) · 115 KB
;; -*- lexical-binding:t -*-

Setup

In order to setup the system, this needs to be run

<<setup-system>>

Files

Here are the files of my emacs config:

Emacs packages

<<emacs-packages>>
(package! consult-recoll :pin "ba68d052d9479aeaa5dda15a57a2c070df7d9bca")
(package! org-roam-ui)
(package! lexic
  :recipe (:local-repo "~/Programs/lexic/"))
(package! mailscripts)
(package! piem :recipe (:local-repo "~/Programs/piem/" :nonrecursive t))
(package! color-identifiers-mode :pin "75a837548b58d0ade286f32559e3d49bae844d6d")

;; (package! auto-activating-snippets :recipe
;;   (:host github :repo "ymarco/auto-activating-snippets"
;;    :files (:defaults "*.el")))
;; (package! latex-auto-activating-snippets
;;   :recipe (:host github :repo"ymarco/auto-activating-snippets"
;;            :files (:defaults "*.el")))
(package! el-secretario
  :recipe (:repo "https://git.sr.ht/~zetagon/el-secretario"
           :branch "develop"
           :host nil
           :files ("*.el")))
(package! ox-hugo)
(package! org-cite-csl-activate :recipe (:host github :repo "andras-simonyi/org-cite-csl-activate"))
(package! engrave-faces)
(package! vulpea)
(package! elfeed-tube)
(package! mpv)

Config

(defun my/exwm ()
  (interactive)
  (require 'exwm)
  (require 'exwm-config)
  (exwm-config-example))

This makes sure that I eww, man-pages and info manuals are linkable in org.

(add-to-list 'org-modules 'ol-eww)
(add-to-list 'org-modules 'ol-info)
(use-package ol-man
  :after (org))
<<test>>


(after! notmuch
  ;;This notmuch config is made for the doom module
  ;;
  ;;I need this line to be able to link to notmuch mails
  (require 'ol-notmuch)
  <<emacs-notmuch>>
  )
(use-package! color-identifiers-mode
  :config
  (global-color-identifiers-mode))
(use-package! org-ql
  :defer t)
(use-package! org-super-agenda
  :config (org-super-agenda-mode))
(after! org
  <<emacs-org-mode>>
  )
(after! org-roam
  <<emacs-org-roam>>
  )
(after! org-noter
  <<emacs-org-noter>>
  )

;; (use-package! auto-activating-snippets
;;   :hook (LaTeX-mode . auto-activating-snippets-mode)
;;   :config (require 'latex-auto-activating-snippets))

;; (use-package! latex-auto-activating-snippets)


(setq doom-theme 'doom-flatwhite)
<<emacs-misc>>

I like the swedish postfix input method. It allows me to use the vim friendly us layout while still being able to input Swedish. åäö seems to be easier to type too.

(setq default-input-method "swedish-postfix")

Doom modules

Put the doom CLI:s in my path.

export PATH="$PATH:$HOME/.emacs.d/bin"

Every time I change this file I have to invoke doom sync.

There is an issue with the doom CLI, where it ignores my noweb stuff. The solution so far is to apply this patch.

- "--eval" (format "(org-babel-tangle-file %S %S)"
- org dest)))
+ "--eval" (format "(org-babel-tangle-file %S)"
+ org)))
(setq fancy-splash-image "/home/leo/.doom.d/meditate.png")
(doom! :input
       ;;chinese
       ;;japanese

       :completion
       ;; (company)           ; the ultimate code completion backend
       ;; helm              ; the *other* search engine for love and life
       ;;ido               ; the other *other* search engine...
       ;; ivy               ; a search engine for love and life
       vertico       ; the search engine of the future

       :ui
       emoji
       ;;deft              ; notational velocity for Emacs
       doom              ; what makes DOOM look the way it does
       doom-dashboard    ; a nifty splash screen for Emacs
       doom-quit         ; DOOM quit-message prompts when you quit Emacs
       ;;fill-column       ; a `fill-column' indicator
       hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
       hydra
       indent-guides     ; highlighted indent columns
       modeline          ; snazzy, Atom-inspired modeline, plus API
       ;;nav-flash         ; blink cursor line after big motions
       ;;neotree           ; a project drawer, like NERDTree for vim
       ophints           ; highlight the region an operation acts on
       (popup +defaults)   ; tame sudden yet inevitable temporary windows
       ;; (ligatures +extra)       ; ligatures or substitute text with pretty symbols
       ;;tabs              ; an tab bar for Emacs
       ;;treemacs          ; a project drawer, like neotree but cooler
       ;;unicode           ; extended unicode support for various languages
       vc-gutter         ; vcs diff in the fringe
       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
       ;;window-select     ; visually switch windows
       workspaces        ; tab emulation, persistence & separate workspaces
       zen               ; distraction-free coding or writing

       :editor
       (evil +everywhere); come to the dark side, we have cookies
       file-templates    ; auto-snippets for empty files
       fold              ; (nigh) universal code folding
       ;;(format +onsave)  ; automated prettiness
       ;;god               ; run Emacs commands without modifier keys
       lispy             ; vim for lisp, for people who don't like vim
       ;;multiple-cursors  ; editing in many places at once
       ;;objed             ; text object editing for the innocent
       ;;parinfer          ; turn lisp into python, sort of
       ;;rotate-text       ; cycle region at point between text candidates
       snippets          ; my elves. They type so I don't have to
       word-wrap         ; soft wrapping with language-aware indent

       :emacs
       dired             ; making dired pretty [functional]
       electric          ; smarter, keyword-based electric-indent
       ;;ibuffer         ; interactive buffer management
       undo              ; persistent, smarter undo for your inevitable mistakes
       vc                ; version-control and Emacs, sitting in a tree

       :term
       eshell            ; the elisp shell that works everywhere
       ;;shell             ; simple shell REPL for Emacs
       term              ; basic terminal emulator for Emacs
       ;; vterm             ; the best terminal emulation in Emacs

       :checkers
       syntax              ; tasing you for every semicolon you forget
       ( spell +aspell +flyspell)             ; tasing you for misspelling mispelling
       ;;grammar           ; tasing grammar mistake every you make

       :tools
       biblio
       ;;ansible
       ;;debugger          ; FIXME stepping through code, to help you add bugs
       ;;direnv
       ;;docker
       ;;editorconfig      ; let someone else argue about tabs vs spaces
       ;;ein               ; tame Jupyter notebooks with emacs
       (eval +overlay)     ; run code, run (also, repls)
       ;;gist              ; interacting with github gists
       (lookup +dictionary +offline +docsets)              ; navigate your code and its documentation
       ;; (lsp +eglot)
       ;;macos             ; MacOS-specific commands
       (magit +forge)             ; a git porcelain for Emacs
       ;;make              ; run make tasks from Emacs
       ;; pass              ; password manager for nerds
       ;; pdf               ; pdf enhancements
       ;;prodigy           ; FIXME managing external services & code builders
       ;;rgb               ; creating color strings
       ;;terraform         ; infrastructure as code
       tmux              ; an API for interacting with tmux
       ;;upload            ; map local to remote projects via ssh/ftp

       :lang
       ;;agda              ; types of types of types of types...
       ;; (cc +lsp)                ; C/C++/Obj-C madness
       ;; clojure           ; java with a lisp
       ;;common-lisp       ; if you've seen one lisp, you've seen them all
       ;;coq               ; proofs-as-programs
       ;;crystal           ; ruby at the speed of c
       ;;csharp            ; unity, .NET, and mono shenanigans
       ;;data              ; config/data formats
       ;;(dart +flutter)   ; paint ui and not much else
       ;;elixir            ; erlang done right
       ;;elm               ; care for a cup of TEA?
       emacs-lisp        ; drown in parentheses
       ;; erlang            ; an elegant language for a more civilized age
       ;;ess               ; emacs speaks statistics
       ;;faust             ; dsp, but you get to keep your soul
       ;;fsharp           ; ML stands for Microsoft's Language
       ;;fstar             ; (dependent) types and (monadic) effects and Z3
       ;;(go +lsp)         ; the hipster dialect
       (haskell)  ; a language that's lazier than I am
       ;;hy                ; readability of scheme w/ speed of python
       ;;idris             ;
       ;;json              ; At least it ain't XML
       ;;(java +meghanada) ; the poster child for carpal tunnel syndrome
       ;;javascript        ; all(hope(abandon(ye(who(enter(here))))))
       ;;julia             ; a better, faster MATLAB
       ;;kotlin            ; a better, slicker Java(Script)
       (latex +latexmk +fold +cdlatex)             ; writing papers in Emacs has never been so fun
       lean
       ;;factor
       ;;ledger            ; an accounting system in Emacs
       ;;lua               ; one-based indices? one-based indices
       markdown          ; writing docs for people to ignore
       ;;nim               ; python + lisp at the speed of c
       ;; nix               ; I hereby declare "nix geht mehr!"
       ;;ocaml             ; an objective camel
       (org +roam2 +pomodoro +journal noter +gnuplot)               ; organize your plain life in plain text
       ;;perl              ; write code no one else can comprehend
       ;;php               ; perl's insecure younger brother
       ;; plantuml          ; diagrams for confusing people more
       ;;purescript        ; javascript, but functional
       ;; python            ; beautiful is better than ugly
       ;;qt                ; the 'cutest' gui framework ever
       ;;racket            ; a DSL for DSLs
       ;;rest              ; Emacs as a REST client
       ;;rst               ; ReST in peace
       ;;(ruby +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
       ;;rust              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
       ;;scala             ; java, but good
       ;; scheme            ; a fully conniving family of lisps
       sh                ; she sells {ba,z,fi}sh shells on the C xor
       ;;sml
       ;;solidity          ; do you need a blockchain? No.
       ;;swift             ; who asked for emoji variables?
       ;;terra             ; Earth and Moon in alignment for performance.
       ;;web               ; the tubes
       ;; yaml              ; JSON, but readable

       :email
       (:if (string= "sakura"
                     (system-name))
        mu4e)
       notmuch
       ;;(wanderlust +gmail)

       :app
       ;;calendar
       ;; irc               ; how neckbeards socialize
       (rss +org)        ; emacs as an RSS reader
       ;;twitter           ; twitter client https://twitter.com/vnought

       :config
       ;; literate
       (default +bindings +smartparens))
(setq evil-want-abbrev-expand-on-insert-exit nil)

Random settings

Make deleting files safer
(setq delete-by-moving-to-trash t)

I want to get into the habit of using two spaces for sentences.

(setq sentence-end-double-space t)
(setq doom-font (font-spec :family "Source Code Pro" :size 14))

I have disabled using jk as escape because it messes with swedish-postfix and lispy.

(setq evil-escape-key-sequence nil)

I like being able to view documentation inside Emacs.

(setq +lookup-open-url-fn #'eww)
(set-docsets! 'python-mode "Python 3")

The flatwhite and modus-operandi themes have a face that makes comments hard to read. This makes it much easier to read while making it distinct from normal text.

(custom-theme-set-faces! 'doom-flatwhite
  '(font-lock-comment-face  :background "#fcf2bf")
  '(hi-yellow :background "#d9c6c3"))

(custom-theme-set-faces! 'modus-operandi
  '(font-lock-comment-face  :background "#fcf2bf")
  '(hi-yellow :background "#d9c6c3"))
(custom-theme-set-faces! 'doom-flatwhite
  '(font-lock-type-face  :background "#f9e0c7"))

Keybindings

I want to use “,” as my localleader key
(setq doom-localleader-key ",")
(map!
;; I need these maps to be able to use jk in the agenda buffer properly
<<emacs-keybinds>>
 )

Actual keybindings

Outline-mode works in Woman buffers but I need some keybindings:

(:map Man-mode-map
:ivmn "C-j" #'outline-next-visible-heading
:ivmn "C-k" #'outline-previous-visible-heading
:ivmn "<backtab>" #'outline-hide-sublevels
:ivmn "<tab>" #'outline-toggle-children)

I want to use Dd as avy delete line.

:n "D" nil
(:prefix "D"
:n "d" #'avy-kill-ring-save-whole-line
:n "r" #'avy-kill-ring-save-region)

I want to recompile more often than changing compilation command

(:leader
 "c c" #'recompile
 "c C" #'+default/compile)

I have mapped tab to both tab and super, so it becomes clunky to use for keybindings.

(:leader
 "\\n" #'+workspace/new
 "\\." #'+workspace/switch-to
 "\\r" #'+workspace/rename)

Expand region seems very nice so I will try to bind it to visual mode v

:v "v" #'er/expand-region

C-x C-s is hard to reach so I will rebind it to an easier binding.

:i "C-s C-s" #'company-yasnippet

I want some alt tab functionality

:n "C-M-i" #'+workspace/other

I want to move my right hand less so I put this to reduce movement

(map!
 :i "M-l" "("
 :i "M-;" "-"
 :i "M-'" "="
 :i "M-h" #'delete-backward-char)
(:leader
 "i i" #'iedit-mode)
(:map dired-mode-map
       :n "C-<return>" (λ! (consult-file-externally (dired-get-file-for-visit)))
       :n "C-RET" (λ! (consult-file-externally (dired-get-file-for-visit))))

org-pile

This is too small for a package, so I’ll put it here in my config instead.

Essentially what it does is that it creates a scratchboard of sorts, where you can pile all the stuff you’re working on. Say you’re working on a patch and so you need to keep track of the email you’re working on. Then you need to keep track of which code files are relevant. And you have some notes lying around.

Just add all of them to the pile, which is just a temporary org file and open them with org-pile-link-hint-open-link.

(defvar org-pile-file "/tmp/pile.org")
(defvar org-pile-buffer "*org-pile*")

(defun org-pile-add ()
  (interactive)
  (let ((org-capture-templates `(("x" "pile" entry
                                  (file ,org-pile-file)
                                  "
* %a
" :immediate-finish t)))
        (org-id-link-to-org-use-id 'create-if-interactive))
    (org-capture nil "x")
    (with-current-buffer (org-pile--get-buffer)
      (save-buffer))))

(defun org-pile-show ()
  (interactive)
  (pop-to-buffer (org-pile--get-buffer)))

(defun org-pile-link-hint-open-link ()
  "Open the pile and open one link."
  (interactive)
  (org-pile-show)
  (call-interactively #'link-hint-open-link))

(defun org-pile--get-buffer ()
  (or (get-buffer org-pile-buffer)
      (with-current-buffer (find-file-noselect org-pile-file)
        (rename-buffer org-pile-buffer)
        (current-buffer))))

Here are some Doom specific stuff

(set-popup-rule! org-pile-buffer :side 'left :width 0.2)

(map! :leader
      "o p"  #'org-pile-show
      "p a" #'org-pile-add
      "p l" #'org-pile-link-hint-open-link)

Email

Host address

I’m not sure why I need to set this, but otherwise it uses my host machine for some things when sending email.

(setq mail-host-address "relevant-information.com")

Notmuch Emacs

Doom does some honestly stupid shit with notmuch windows. This should undo that:
(set-popup-rule! "^\\*notmuch-hello" :ignore)
(set-popup-rule! "^\\*subject:" :ignore t)

I don’t want to accidentally mark an email as read. This ensures that I have to explicitly remove the unread tag.

(setq notmuch-show-mark-read-tags nil)

These are my saved searches. Any mail that is not deleted and unread is in a inbox.

(setq notmuch-saved-searches
      '((:name "inbox" :query "tag:unread AND NOT tag:deleted NOT tag:gmail/Inbox" :key "i")
        (:name "unread" :query "tag:unread" :key "u")
        (:name "flagged" :query "tag:flagged" :key "f")
        (:name "sent" :query "tag:sent" :key "t")
        (:name "drafts" :query "tag:draft" :key "d")
        (:name "all mail" :query "*" :key "a")))

When I have read a mail there are three cases:

  • I have not actually read the mail, in which case I leave it be
  • I have read the mail and done what I can do now, in which case I remove the unread tag
  • I have read the mail and but there actions left to perform, in which case I capture it to my gtd system, and remove the unread tag
(map!
 :n "<f1>" #'=notmuch
 :after notmuch
 (:map notmuch-show-mode-map
   :n "D" #'evil-collection-notmuch-show-toggle-delete

   :n "d" (λ! (notmuch-show-tag-all '("-unread"))
              (notmuch-show-next-thread-show)))
 (:map notmuch-tree-mode-map
  :n "d" (λ! (notmuch-tree-tag-thread '("-unread")))))

I want to run the following command when syncing:

TODO There is an issue where the notmuch command and the afew commands won’t run.

(setq +notmuch-sync-command "mbsync -a ; notmuch new; afew --tag --new")
(setq +notmuch-mail-folder "~/.mail")
(setq +notmuch-sync-backend 'mbsync)
(setq sendmail-program "/usr/bin/msmtp")

The fcc header controls wherer sent in which folders sent mails go.

(setq notmuch-fcc-dirs "relevant-info/Sent +sent")

I want to see more of cited lines and set it to a better colour (grey is hard to read).

(after! (:and ui notmuch)
  (setq notmuch-wash-citation-lines-prefix 10)
  (custom-theme-set-faces 'doom-one
                          '(notmuch-wash-cited-text
                            ((t (:background "#434a59" :foreground "#8fb3f7"))))
                          '(notmuch-message-summary-face
                            ((t (:foreground "#50b1c9"))))))

I don’t want to have auto-fill-mode when I write email. It is just confusing for the receivers most of the time. I also want flyspell-mode to be on.

(add-hook! 'notmuch-message-mode
  (auto-fill-mode -1)
  (flyspell-mode)
  (ispell-change-dictionary "swedish"))

This ensures that wordwrapping doesn’t cause indentation when I write emails.

(add-to-list '+word-wrap-text-modes 'notmuch-message-mode)

Mu4e

;; Each path is relative to the path of the maildir you passed to mu
(set-email-account! "relevant-info"
  '((mu4e-sent-folder       . "/relevant-info/Sent")
    (mu4e-drafts-folder     . "/relevant-info/Drafts")
    (mu4e-trash-folder      . "/relevant-info/Trash")
    (mu4e-refile-folder     . "/relevant-info/Archive")
    (smtpmail-smtp-user     . "[email protected]")
    (user-mail-address     . "[email protected]")
    (mu4e-compose-signature . "---\Leo Okawa Ericson"))
  t)
;; I don't like threading by default. It makes it hard to work with longer email threads and pollutes my unread messages view
(map!
 :leader
 :prefix-map ("o" . "open")
 "m" (lambda ()
       (interactive)
       (setq mu4e-headers-show-threads t)
       (setq mu4e-view-auto-mark-as-read nil)
       (setq mu4e-headers-sort-direction 'ascending)
       (setq mu4e-headers-include-related t)
       (=mu4e)))

Sending

I have to set mail-specify-envelope-from to t for msmtp to use the address that I specify in the mail. I am not sure which of these variables are correct, but they seem to be doing the same thing.
(setq mail-envelope-from 'header)
(setq mail-specify-envelope-from t)
(setq message-sendmail-envelope-from 'header)
(after! mu4e
  (setq sendmail-program (executable-find "msmtp")
        send-mail-function #'smtpmail-send-it
        message-sendmail-f-is-evil t
        message-sendmail-extra-arguments '("--read-envelope-from")
        message-send-mail-function #'message-send-mail-with-sendmail))

Completion

Default

(map!
 :i "C-SPC" #'completion-at-point
 :i "M-/" #'hippie-expand)
(setq hippie-expand-try-functions-list
      '(try-expand-all-abbrevs
        try-expand-dabbrev-visible
        try-expand-dabbrev
        try-expand-whole-kill
        try-expand-dabbrev-all-buffers
        try-expand-line
        try-complete-lisp-symbol-partially
        try-complete-lisp-symbol))
; I don't think expanding on line is useful in text mode buffers
(add-hook 'text-mode-hook (lambda () (remove #'try-expand-line hippie-expand-try-functions-list)))

Double keys

The idea is based on based on: https://kisaragi-hiu.com/blog/2021-06-02-insert-key-double-key. This macro is a generalization of the command found in the link. It allows creation of double-key commands from any commands. Code modified is licensed under MIT. Copyright 2021 Kisaragi Hiu

(defmacro my/define-double-key (name command)
  "Define a new command NAME, that calls COMMAND if NAME was called twice with the same key."

  `(defun ,name ()
     (interactive)
     (cond
      ((and (eq last-command (quote ,name))
            (eq (char-before) last-command-event))
       (delete-char -1)
       (call-interactively ,command))
      (t (insert (string last-command-event))))))

I have one double-key for org-roam links, and one for elisp functions and variables respectively.

(defun my/insert-function ()
  (interactive)
  (insert  (symbol-name (helpful--read-symbol
                         "Callable: "
                         nil
                         #'fboundp))))

(defun my/insert-variable ()
  (interactive)
  (insert  (symbol-name (helpful--read-symbol
                 "Callable: "
                 nil
                 #'helpful--variable-p))))

(my/define-double-key my/org-roam-node-insert #'org-roam-node-insert)
(my/define-double-key my/insert-diary-tag-double-key #'my/insert-diary-tag)
(my/define-double-key my/citar-insert-citation (cmd!! #'citar-insert-citation '(4)))
(my/define-double-key my/insert-function/double-key #'my/insert-function)
(my/define-double-key my/insert-variable/double-key #'my/insert-variable)



(map! (:map org-mode-map
       :i "{" #'my/citar-insert-citation
       :i "#" #'my/insert-diary-tag-double-key
       :i "[" #'my/org-roam-node-insert)
      (:map emacs-lisp-mode-map
       :i "." #'my/insert-variable/double-key
       :i "/" #'my/insert-function/double-key))

Company

This is the way I want to interact with company

(map! :map company-active-map
      "C-SPC" #'company-other-backend
      [return] nil
      "TAB" nil
      [tab] nil
      "C-j" #'company-select-next
      "C-k" #'company-select-previous
      [backtab] nil
      [return] #'company-complete-selection)
(setq company-idle-delay nil)
(set-company-backend! 'prog-mode '(company-capf))

Ivy

(after! ivy
  (setq ivy-read-action-function #'ivy-hydra-read-action)
  (setq +ivy-buffer-preview t))

Embark

Imenu

(after! embark
  (embark-define-keymap embark-imenu-map
                        "TODO")
  (add-to-list 'embark-keymap-alist '(imenu . embark-imenu-map))

  (map! (:leader
         "a" #'embark-act)
        (:map minibuffer-local-map
         "C-o" (λ! (let ((embark-quit-after-action nil))
                     (embark-act))))
        (:map embark-imenu-map
         ;; store org links from an imenu target
         "l" (lambda (&rest _)
               (interactive)
               (save-excursion
                 (consult-imenu--select (consult-imenu--all-items (consult-imenu--project-buffers)))
                 (call-interactively #'org-store-link))))))

Avy

This lets me switch from an isearch to an avy selection. Avy will use the isearch search term as candidates.
(map! :map isearch-mode-map "M-j" #'avy-isearch)

Avy Actions

Blogpost that inspired this part https://karthinks.com/software/avy-can-do-anything/. My novel contribution is just binding the evil delete and copy operators so that I can copy and delete evil text objects/motions on avy targets.

(after! avy
  (setf (alist-get ?w avy-dispatch-alist)
        #'my/avy-evil-delete)
  (setf (alist-get ?y avy-dispatch-alist)
        #'my/avy-evil-copy)
  (setf (alist-get ?c avy-dispatch-alist)
        #'my/avy-evil-change)
  (setf (alist-get ?a avy-dispatch-alist)
        #'my/avy-embark-act))

Avy-fied functions

(defun my/avy-embark-act (pt)
  (unwind-protect
      (save-excursion
        (goto-char pt)
        (embark-act))
    (select-window
     (cdr
      (ring-ref avy-ring 0))))
  t)

(defvar my/evil-extract-count-keys nil)

(defadvice! my/evil-extract-count (orig-fn keys)
  "When an evil operation is called from avy `evil-extract-count'
will get the last key given to avy as input. In order to behave
normally we wrap the function so that we can give the input
manually using a let-binding."
  :around #'evil-extract-count
  (funcall orig-fn
           (or my/evil-extract-count-keys keys)))

(defun my/avy-evil-delete (pt)
  (interactive)
  (save-excursion
    (goto-char pt)
    (let ((my/evil-extract-count-keys "d"))
      (call-interactively #'evil-delete)))
  (select-window
   (cdr
    (ring-ref avy-ring 0)))
  t)

(defun my/avy-evil-copy (pt)
  (interactive)
  (save-excursion
    (goto-char pt)
    (let ((my/evil-extract-count-keys "y"))
      (call-interactively #'evil-yank)))
  (select-window
   (cdr
    (ring-ref avy-ring 0)))
  t)

(defvar my/avy-evil-change-marker nil
  "The place where the user called ivy from.")

(defun my/avy-evil-change (pt)
  (interactive)
  (setq my/avy-evil-change-marker (point-marker))
  (goto-char pt)
  (add-hook 'evil-insert-state-exit-hook #'my/avy-evil-change-h)
  (call-interactively #'evil-change)
  t)

(defun my/avy-evil-change-h ()
  (remove-hook 'evil-insert-state-exit-hook #'my/avy-evil-change-h)
  (select-window
   (cdr
    (ring-ref avy-ring 0)))
  (goto-char (marker-position my/avy-evil-change-marker)))

Org-mode

org-checklist has a functionality for resetting checklists when a todo is done. Is done by setting :RESET_CHECK_BOXES: t

(require 'org-checklist)

I don’t want logs to clutter my documents

(setq org-log-into-drawer t)

I want to use unique ID:s for linking to org notes. The default heading scheme is error prone, especially when editing headlines.

(require 'org-id)
(setq org-id-link-to-org-use-id 'create-if-interactive)

very lucky

(after! (org company)
  (set-company-backend! 'org-mode
    'my/company-org
    'company-capf
    'company-ispell
    'company-dabbrev
    'company-yasnippet))
(setq org-agenda-clockreport-parameter-plist
      '(:link t :maxlevel 5))

Sometimes I want to export to markdown on save, for example when I’m writing a readme for a project on sourcehut.

(add-to-list 'safe-local-variable-values
             '(after-save-hook . org-md-export-to-markdown))

Completion for noweb-ref

This is a sorta working company backend that can complete noweb-ref style header arguments in src blocks. It completes all links enclosed with << >>

(defun my/company-org (command &optional arg &rest _)
  "Complete :noweb-ref links in org-mode src blocks"
  (interactive (list 'interactive))
  (cl-case command
    (interactive (company-begin-backend #'my/company-org))
    (prefix
     (let ((on-begin-src)
           (on-colon))
       (save-excursion
         (goto-char (line-beginning-position))
         (setq on-begin-src (search-forward "#+BEGIN_SRC" (line-end-position) t)))
       (when on-begin-src
         (save-excursion
           (search-backward " " nil t)
           (forward-char)
           (setq on-colon
                 (string= ":"
                          (buffer-substring-no-properties
                           (point)
                           (+ 1 (point))))))
         (when on-colon
           (thing-at-point 'symbol 'no-properties)))))
    (candidates
     (my/company-org--candidates arg))
    (post-completion
     (insert
      (ivy-read ":noweb "
                (-map
                 (lambda (x) (concat " " (car (cdr  x))))
                 (s-match-strings-all "<<\\(.+?\\)>>"
                                      (buffer-substring-no-properties
                                       (point-min)
                                       (point-max)))))))))

(defun my/company-org--candidates (arg)
  (-filter (lambda (x) (s-prefix? arg x)) '("noweb-ref")))

Writing

This setting makes just the headings tagged with :ignore: ignored when exporting(the contents are kept.) On the other hand, all headlines tagged with \:noexport: with subtrees are ignored when exporting.
(require 'ox-extra)
(ox-extras-activate '(ignore-headlines))

Enable “type writer mode” (the cursor is always at the center of the screen) in writeroom-mode.

(defun my/recenter ()
  (interactive)
  (recenter 8))
(add-hook! 'writeroom-mode-hook
  (if writeroom-mode
      (add-hook 'post-command-hook #'my/recenter nil t)
    (remove-hook 'post-command-hook #'my/recenter t)))

Sometimes I want to use the koma-script document classes. For that I need this:

(after! ox-latex
  (add-to-list 'org-latex-classes
               '("koma-article" "\\documentclass{scrartcl}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))

Org-mode uses it’s own system for invoking latex and bibtex the correct amount of times, but I find latexmk to be easier, especially when used with biblatex and biber.

(setq org-latex-pdf-process '("latexmk -shell-escape -bibtex -pdf %f"))

Some default classes I use:

(add-to-list 'org-latex-packages-alist '("" "microtype"))

The engrave-faces package allows the org-export to use an emacs theme for syntax highlighting of source blocks.

(setq org-latex-src-block-backend 'engraved)
;; Modus Operandi seems to be a good theme for syntax highlighting in pdf
;; documents
(setq org-latex-engraved-theme 'modus-operandi)
(after! ox-html (require 'engrave-faces))

Sometimes I want to have a specific buffer open all the time, and open other files in other windows (e.g. when clicking on links).

(defun my/toggle-window-dedication ()
  "Toggles window dedication in the selected window."
  (interactive)
  (set-window-dedicated-p (selected-window)
     (not (window-dedicated-p (selected-window)))))

Refiling

(defun my/org-refile-to-monthly-review ()
  (interactive)
  (my/org-refile-to-query '(parent (string-equal (org-id-get) "b5fd67ea-2459-472f-836e-deb113602913"))))
(defun my/org-parse-headline ()
  "Parse headline at point and put in some more relevant information"
  (--> (org-element-headline-parser (line-end-position))
       (nth 1 it)
       (plist-put it :entry-text
                  (concat
                   (buffer-file-name)
                   ":"
                   (number-to-string (line-number-at-pos))
                   ":"
                   (buffer-substring (line-beginning-position)
                                     (line-end-position))))
       (plist-put it :file-name (buffer-file-name))
       (plist-put it :id (org-id-get-create))
       (plist-put it :buffer (current-buffer))))

(defun my/org-refile-to-query (query &optional files map-fun)
  "Refile to a target specified by QUERY.
Prompt user to choose between the results. If MAP-FUN is non-nil,
use it to transform the list returned by `org-ql-select' before prompting the user.
"
  (if-let ((entry-list (--> query
                            (org-ql-select (or files (org-agenda-files)) it
                              :action #'my/org-parse-headline)
                            (if map-fun
                                (funcall map-fun it)
                              it))))

      (-as->
       (--> (completing-read "Refile target" (mapcar (lambda (x) (plist-get x :entry-text)) entry-list))
            (-filter (lambda (x) (string-equal (plist-get x :entry-text) it)) entry-list)
            car)
       that
       (org-refile nil nil (list
                            (plist-get that :raw-value)
                            (plist-get that :file-name)
                            nil (plist-get that :begin))))
    (error "No matching targets to refile to")))

(defun my/org-capture-to-query (query template &optional files map-fun &rest capture-plist)
  "Capture to a org-ql query.
Capture to one of the results of QUERY. Prompt the user with
`completing-read' if there are multiple results.

TEMPLATE is a `org-capture-templates' template (often a string).

If FILES is provided, search among FILES, otherwise use agenda files.

If MAP-FUN is non-nil, use it to transform the list returned by
`org-ql-select' before prompting the user.

Example: (my/org-capture-to-query '(tags \"foo\" \"* %?\" nil #'car )
This will capture to the first entry that has the tag \"foo\".
"
  (if-let ((entry-list (--> query
                            (org-ql-select (or files (org-agenda-files)) it
                              :action #'my/org-parse-headline)
                            (if map-fun
                                (funcall map-fun it)
                              it))))

      (-as->
       (if (cdr entry-list)
           (-->
            entry-list
            (completing-read "Refile target" (mapcar (lambda (x) (plist-get x :entry-text)) it))
            (-filter (lambda (x) (string-equal (plist-get x :entry-text) it)) entry-list)
            (car it))
         (car entry-list))
       that
       (let ((org-capture-templates `(("x" "auto-generated" entry
                                       (id ,(plist-get that :id))
                                       ;; (file "/tmp/test.org")
                                       ,template
                                       ,@capture-plist))))
         (org-capture nil "x")))

    (error "No matching targets to capture to")))

Org-Roam

(setq org-roam-capture-templates
      '(("d" "default" plain "%?"
         :if-new (file+head "%<%Y%m%d%H%M%S>--${slug}.org"
         "#+title: ${title}\n#+Created: %t\n#+Time-stamp: <>\n\n\n* TODO add to outline")
         :unnarrowed t
         :immediate-finish t)
        ("t" "text" plain "%?"
         :if-new
         (file+head "texter/${slug}.org"
                    "#+title: ${title}\n#+Created: %t\n#+hugo_publishdate: %t\n#+hugo_draft: true\n\n @@hugo:{{< rawhtml >}}@@\n#+include: \"~/Documents/blog/themes/paper/layouts/partials/webring.html\" export html\n@@hugo:{{</ rawhtml >}}@@")
         :unnarrowed t
         :immediate-finish t)
        ("b" "blog" plain "%?"
         :if-new
         (file+head "weblog/%<%Y-%m-%d>-${slug}.org"
                    "#+title: ${title}\n#+Created: %t\n#+hugo_publishdate: %t\n\n@@hugo:{{< rawhtml >}}@@\n#+include: \"~/Documents/blog/themes/paper/layouts/partials/webring.html\" export html\n@@hugo:{{</ rawhtml >}}@@ ")
         :unnarrowed t
         :immediate-finish t)))
(setq org-roam-db-location "~/.emacs.d/.local/org-roam/org-roam.db")
(setq org-roam-directory "~/Documents/notes/")

I want to have a timestamp that updates when I last edited a file. time-stamp will write a time-stamp between if it finds a string: “Time-stamp: <>” in the first 8 lines of the buffer.

(require 'time-stamp)
(add-hook 'write-file-functions 'time-stamp)
(setq time-stamp-format "%:y-%02m-%02d %02H:%02M:%02S")

I don’t want Scrapbook or the journal to be in the graph as i creates artificial connections.

(setq org-roam-graph-exclude-matcher '("Scrapbook.org" "journal/"))
(setq org-roam-file-exclude-regexp "\\.stversions/*" )
;;(add-to-list 'company-box-backends-colors '(company-org-roam . (:all "light slate blue" :selected (:background "light slate blue" :foreground "black"))))

I don’t want Doom to open the roam buffer automatically

(setq +org-roam-open-buffer-on-find-file nil)

I want to review the notes I write regularly. This function brings up all the notes I created during the same month last year. I have a todo item that el-secretario will bring up for me once a month here.

(defun my/review-files-from-last-year ()
  (interactive)
  (el-secretario-start-session
   (el-secretario-files-make-source
    (directory-files "~/Documents/notes/" t     (ts-format "%Y%m" (ts-dec 'year 1 (ts-now)))))))

(defun my/review-files ()
  (interactive)
  (el-secretario-start-session
   (el-secretario-files-make-source
    (directory-files "~/Documents/notes/" t
                     (ts-format "%Y%m" (ts-dec 'month
                                               (read-number "How many months back? ")
                                               (ts-now)))))))
(defun my/take-needles (ns lst)
  (let ((rest lst)
        ret)
    (dolist (n ns)
      (setq rest (seq-drop rest n))
      (push (car rest) ret))
    (reverse ret)))

(defun my/review-sorted (n)
  (interactive "p")
  (let ((nodes (mapcar #'cdr
                       (my/take-needles '(0 1 1 1 2 2 3 3 5 10 10 10 50 100 300 600 1000)
                                        (seq-drop (org-roam-node-read--completions
                                                   (my/org-roam-filter-away-tag "index")
                                                   #'org-roam-node-read-sort-by-file-mtime)
                                                  (- n 1))))))
   (el-secretario-start-session
     (el-secretario-org-roam-make-source nodes))))

Helper functions

(defun my/org-roam-filter-away-tag (tag-name)
    (lambda (node)
      (not (member tag-name (org-roam-node-tags node)))))

(defun my/org-roam-filter-for-tag (tag-name)
    (lambda (node)
        (member tag-name (org-roam-node-tags node))))

Dailes

(setq! org-roam-dailies-capture-templates
       '(("d" "default" entry "* %?" :target
          (file+head "%<%Y-%m-%d>.org"
                     "#+title: %<%Y-%m-%d>
#+begin_src sh :results output raw
python3 ~/Documents/anki-rss/anki_rss.py -n | sed 's/\\\\//g'
#+end_src

* TODO Look at the day's agenda
 [[elisp:(my/today)]]
* TODO look at previous day's todos
* TODO index previous day's notes
 [[elisp:(org-roam-dailies-goto-previous-note 3)]]


* TODO Go through calendar of tomorrow and add tasks as appropriate
* Previous notes
 [[elisp:(progn (my/review-sorted 3) nil)]]"))))

I’ll also try doing a daily each day on start up

(add-to-list 'doom-init-ui-hook 'org-roam-dailies-goto-today t)
(require 'midnight)
(remove-hook 'midnight-hook 'clean-buffer-list)
(add-hook 'midnight-hook #'org-roam-dailies-goto-today)

I maintain an index over my dailies (and other things). It is something I want to access often and quickly, so I bind it to the Fn key on my Thinkpad.

(defun my/toggle-index ()
  (interactive)
  (if-let ((win (get-buffer-window (find-file-noselect "~/Documents/notes/20201104205912.org"))))
      (delete-window win)
    (select-window
     (display-buffer-in-side-window (find-file-noselect "~/Documents/notes/20201104205912.org") '((side . left))))))


(map! :n "<WakeUp>" #'my/toggle-index)

diary tags

I have a system for tags in my diary. These I find useful for fragmentary thoughts that span multiple days. Often I think about similar topics a lot, and indexing them is annoying and ineffective.

They get recognized by embark like this:

(defun my/diary-tag ()
    "Target a link at point of the form wikipedia:Page_Name."
    (save-excursion
      (let* ((start (progn (skip-chars-backward "[:alnum:]_\\-:#/") (point)))
             (end (progn (skip-chars-forward "[:alnum:]_\\-:#/") (point)))
             (str (buffer-substring-no-properties start end)))
        (save-match-data
          (when (string-match "\\(#[^ ]+\\)" str)
            `(symbol
              ,(match-string 1 str)
              ,start . ,end))))))
(after! embark
 (add-to-list 'embark-target-finders 'my/diary-tag))

The tags can be inserted with this command

(defun my/insert-diary-tag ()
  (interactive)
  (let* ((tags (with-temp-buffer
                 (insert-file-contents "~/Documents/notes/daily/tags")
                 (split-string (buffer-string) "\n" t)))
         (selected-tag (completing-read "tag: " tags)))
    (unless (member selected-tag tags)
      (with-temp-buffer
        (insert-file-contents "~/Documents/notes/daily/tags")
        (goto-char (point-max))
        (insert selected-tag "\n")
        (write-file "~/Documents/notes/daily/tags")))
    (insert "#" selected-tag)))

And then you can search for them with the built in grep interface. Use next-error and previous error to cycle through the results.

(defun my/grep (query)
  (interactive "sQuery: ")
  (let ((file-name (file-name-nondirectory (buffer-file-name)))
        (line-number (line-number-at-pos)))
    (grep-compute-defaults)
    (grep (format "grep --color=auto --after-context=2 -nH --null -e '%s' *.org" query))

    ;; Go to the location of the word at point so that the search results don't
    ;; start at the beginning or the end
    (with-current-buffer grep-last-buffer
      (sleep-for 1)
      (search-forward (format "%s\0%s"
                              file-name
                              line-number)
                      nil
                      t)
      (compile-goto-error))))

(map! :map embark-general-map
      "C G" #'my/grep)

I also made a new link type to easily link to tags from outside the dailies directory

(org-link-set-parameters "diary-tag"
                         :follow (lambda (tag _)
                                   (grep-find
                                    (format "find daily/ -type f -exec grep -A 2 --color=auto -nH --null -e \\#%s \{\} +"
                                            tag))))

Anki

The following command sends the region to Anki. There I will ruminate on the notes, and when I’m done I can suspend the card. Using anki-rss, the suspended cards are harvested to be processed into daily notes again.

(defun my/send-region-to-anki ()
  (interactive)
  (let* ((json (json-encode `((action . "addNote")
                              (version . 6)
                              (params
                               (note
                                (deckName . "Feeds")
                                (modelName . "Feed entry")
                                (fields
                                 (Title . ,(concat
                                            "Note created from org-mode on "
                                            (format-time-string
                                             "%Y-%m-%d-%H:%M:%S"
                                             (current-time))))
                                 (notes . ,(let* ((s (buffer-substring-no-properties
                                                      (region-beginning)
                                                      (region-end)))
                                                  (linked-s (if-let ((file (buffer-file-name)))
                                                                (concat s
                                                                        (format "\n\n[[file:%s]]" file))
                                                              s)))
                                             (if (equal major-mode 'org-mode)
                                                 (org-export-string-as linked-s
                                                                       'html
                                                                       t)
                                               linked-s)))
                                 (id . ,(format-time-string
                                         "%Y-%m-%d-%H:%M:%S"
                                         (current-time))))
                                (options
                                 (allowDuplicate . :json-false)
                                 (duplicateScope . "deck")
                                 (duplicateScopeOptions
                                  (deckName . "Default")
                                  (checkChildren . :json-false)
                                  (checkAllModels . :json-false))))))))
         (response (request-response-data
                    (request "http://127.0.0.1:8765"
                      :parser #'json-read
                      :sync t
                      :data json))))
    (pcase response
      (`((result . ,result)
         (error . ,response-error))
       (if response-error
           (message "error: %s" response-error)

         (kill-region (region-beginning)
                      (region-end))
         (insert (format "
#+begin_src sh :results org
anki-rss --extract-specific-note %s
#+end_src
"
                         result)))))))

org-roam-ui

(use-package! org-roam-ui
    :after org-roam ;; or :after org
;;         normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;;         a hookable mode anymore, you're advised to pick something yourself
;;         if you don't care about startup time, use
;;  :hook (after-init . org-roam-ui-mode)
    :config
    (setq org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start t))

Dired Pile

(defvar-local my/dired-pile nil)
(defun my/dired-pile-add ()
  "TODO"
  (interactive)
  (let* ((node (org-roam-node-read))
         (file (org-roam-node-file node)))
    (if (derived-mode-p 'dired-mode)
        (push node my/dired-pile)
      (dired org-roam-directory)
      (setq my/dired-pile (list node)))
    (setq dired-directory
          (cons org-roam-directory
                (mapcar (-compose #'dired-make-relative #'org-roam-node-file)
                        my/dired-pile)))
    ;; We don't want to sort here, so that dired-directory matches with
    ;; my/dired-pile
    (setq dired-sort-inhibit t)
    (revert-buffer)
    (save-excursion
      (goto-char (point-min))
      (delete-all-overlays)
      (dolist (node my/dired-pile)
        (forward-line 1)
        (goto-char (line-end-position))
        (let* ((ov (make-overlay (point) (1- (point))))
               (text (concat " -- " (org-roam-node-title node))))
          (overlay-put ov 'after-string
                       (propertize text 'face font-lock-comment-face)))))))

Blog

(use-package! ox-hugo
  :after ox
  :config
  (setq org-hugo-base-dir "~/Documents/blog/")
  (setq org-hugo-paired-shortcodes "%alert %notetocssless %spoiler %verse"))

exporting

I want to keep the source files of what I write for my blog in the version control of the blog. In order to do that, I simply copy over the org-mode file from my notes to the blog repository. This is fine for me because I call a command to export the file anyways.

(defun my/export-blog-post ()
  (interactive)
  (when (string-prefix-p "/home/leo/Documents/notes/" default-directory)
    (copy-file (buffer-file-name)
               (concat "~/Documents/blog/org-content/"
                       (string-remove-prefix "/home/leo/Documents/notes/" default-directory))
               t)
    (org-hugo-export-to-md)

    (let ((default-directory "/home/leo/Documents/blog/org-content/"))
      (magit-stage-file (list
                         (concat
                          "/home/leo/Documents/blog/org-content/"
                          (string-remove-prefix "/home/leo/Documents/notes/" (buffer-file-name))))))))

Footnotes

Use tufte-style sidenotes according to this website: https://www.gwern.net/Sidenotes. I have the proper styling on my blog.

We can’t output markdown inside sidenotes, since hugo won’t convert it to html. Instead we export to html directly via a derived backend and org-export-data-with-backend.

(defun my/id (&rest _f))
(defun org-export-hugo-releinfo-sidenote-reference (footnote-reference _contents info)
  "Transcode a FOOTNOTE-REFERENCE element from Org to HTML.
CONTENTS is nil.  INFO is a plist holding contextual information."
  (let ((id (org-export-get-footnote-number footnote-reference info)))
    (format "<label for=\"%s\" class=\"margin-toggle sidenote-number\"></label><span class=\"sidenote-surround-text\">(sidenote: </span><input type=\"checkbox\" id=\"%s\" class=\"margin-toggle\"/><span class=\"sidenote\">%s </span><span class=\"sidenote-surround-text\">)</span> "
            id id
            (concat
             (org-trim (org-export-data-with-backend
                        (org-export-get-footnote-definition footnote-reference info)
                        'my-nested-hugo
                        info))
             (org-blackfriday-footnote-reference footnote-reference _contents info)))))

(org-export-define-derived-backend 'hugo-releinfo 'hugo
  :translate-alist '((footnote-reference . org-export-hugo-releinfo-sidenote-reference)
                     (src-block . org-html-src-block)))
(setq org-hugo-backend 'hugo-releinfo)

I don’t want nested sidenotes to perpetually be pushed to the right. Therefore I define a new derived backend that’s only used when transcoding the contents of the footnote. This new backend has some extra css rules.

(defun org-export-hugo-releinfo-nested-footnote-reference (footnote-reference _contents info)
  "Transcode a FOOTNOTE-REFERENCE element from Org to HTML.
CONTENTS is nil.  INFO is a plist holding contextual information."
  (let ((id (org-export-get-footnote-number footnote-reference info)))
    (format "<label for=\"%s\" class=\"margin-toggle sidenote-number\"> </label><input type=\"checkbox\" id=\"%s\" class=\"margin-toggle\"/> <span class=\"sidenote\" style=\"margin-right: 50%%;margin-top: 2.3rem\"> %s </span>  "
            id id
            (org-trim (org-export-data (org-export-get-footnote-definition footnote-reference info) info)))))

(org-export-define-derived-backend 'my-nested-hugo 'hugo-releinfo
  :translate-alist '((footnote-reference . org-export-hugo-releinfo-nested-footnote-reference)))

Link-blog

(setq org-publish-project-alist
      '(("weblog"
         :author ""
         :email ""
         :base-directory "~/Documents/notes/weblog/"
         :recursive t
         :base-extension "org"
         :publishing-function ((lambda (plist filename pub-dir)
                                  (org-publish-org-to 'hugo-releinfo filename ".md" plist pub-dir)))
         :publishing-directory"~/Documents/blog/content/weblog")))

Journaling

To integrate journaling with Org-Roam I set the directory to the same as roam.
(after! org-journal
  (setq org-journal-dir "~/Documents/notes/journal")
  (setq org-journal-file-type 'weekly)
;; Don't use stupid american date format
  (calendar-set-date-style 'iso))

Org-noter

(map!
 :map org-noter-doc-mode-map
 :n "I" #'org-noter-insert-note-toggle-no-questions
 :n "i" #'org-noter-insert-note)

I have all my documents I want to annotate at the same place, named after the bibtex key so it’s easy to fetch the correct document.

(defun my/read-citar-file-at-point ()
  (let* ((refs (org-roam-node-refs  (org-roam-node-from-id (org-roam-id-at-point))))
         (files (citar-get-files refs)))
    (completing-read "Attached file: "
                     (sort
                      (apply #'append (mapcar (lambda (x) (gethash x files))
                                              refs))
                      #'string<))))

(defun my/noter-set-notes-property ()
  "Set NOTER_DOCUMENT property to the correct pdf."
  (interactive)
  (org-entry-put (point) "NOTER_DOCUMENT"
                 (my/read-citar-file-at-point)))

Bibliography managment

I apparently need to set this in my bashrc

export BIBINPUTS="~/Documents/notes/Zotero_articles.bib"

Org-roam bibtex

This packages allows org-ref notes to be org-roam files
(package! org-roam-bibtex)
(use-package! org-roam-bibtex
  :config
  (org-roam-bibtex-mode))

Org-ref

(package! org-ref)
(use-package! org-ref
  :after (org)
  :init
  (setq org-ref-completion-library 'org-ref-ivy-cite)
  :config
  (setq org-ref-notes-directory "~/Documents/notes"
        org-ref-default-bibliography '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")
        org-ref-pdf-directory "~/Documents/notes/pdfs/"
        org-ref-default-ref-type "cref"
        org-ref-default-citation-link "autocite"
        org-latex-prefer-user-labels t
        org-footnote-auto-label 'confirm
        org-ref-ref-types '("cref" "Cref" "ref" "eqref" "pageref" "nameref" "autoref")))

citar

(after! org
  (setq org-cite-global-bibliography  '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")))
(after! citar
  (setq citar-bibliography '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")
        bibtex-completion-bibliography '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")
        org-cite-global-bibliography '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")
        reftex-default-bibliography '("~/Documents/notes/Zotero_articles.bib" "~/Documents/notes/mika-leo-notes/references.bib")
        citar-library-paths '("~/Documents/notes/pdfs")
        citar-file-extensions '("pdf" "txt")
        citar-notes-paths '("~/Documents/notes/")
        org-cite-follow-processor 'citar
        org-cite-activate-processor 'citar
        citar-at-point-function 'embark-act
        citar-open-note-function 'orb-citar-edit-note)
  ;; I want to open pdfs externally
  (add-to-list 'citar-file-open-functions '("pdf" . citar-file-open-external))
  (when (featurep! :completion ivy)
    (setq org-ref-cite-onclick-function (lambda (x)  (org-ref-cite-hydra/body)))
    (setq ivy-bibtex-default-action
          #'ivy-bibtex-insert-citation)))

(use-package! citar-org-roam
  :after citar org-roam
  :no-require
  :config (citar-org-roam-mode))

(after! citar-org-roam
  (defun my/citar-open-roam ()
    (interactive)
    (citar-open (org-roam-node-refs (org-roam-node-at-point)))))

(map!
 :leader
 ;; Open a file that is linked to the selected entry, e.g. links or files.
 :n "nB" #'citar-open-roam
 :n "nb" #'citar-open)

If I put an url into the ROAM_REFS property any time I link to that url in org-roam I will get a cite backlink. Therefore I want to add an url to every reference note if that field exists in the bibtex entry. This advice shouls accomplish that.

(defadvice! my/citar-org-roam--create-capture-note-url-ref (citekey entry)
  "Also add the url as a ref if possible."
  :after #'citar-org-roam--create-capture-note
  (when-let ((url (alist-get "url" entry nil nil #'string-equal)))
    (org-roam-ref-add url)))

org-cite csl

(setq org-cite-csl-locales-dir "~/.doom.d/csl/"
      org-cite-csl-styles-dir  "~/.doom.d/csl/")
(after! oc
  (setq org-cite-activate-processor 'csl-activate))
(after! org-cite-csl-activate
  (setq org-cite-csl-activate-use-citar-cache t))
(add-hook 'org-mode-hook (lambda () (cursor-sensor-mode 1)))

Task management

Basic

I use Org-mode for managing my tasks, duh.

I use Org-QL Org Super Agenda for easier queries.

(package! org-ql)
(package! org-super-agenda)

Here are my agenda files:

(setq org-agenda-files'("~/org/orgzly/Todo.org"
                        "~/org/orgzly/PhoneInbox.org"
                        "~/org/orgzly/InboxComputer.org"
                        "~/org/orgzly/writing-inbox.org"
                        "~/org/orgzly/Projects.org"))

Here are my todo-keywords. NEXT is used in dailies to signify that it was postponed to the next day

(setq org-todo-keywords
      '((sequence "TODO(t)" "WAITING" "PROJ(p)" "|" "NEXT(n)" "DONE(d!)" "CANCELLED(c@)")))

Keymaps

I need these maps to be able to use jk in the agenda buffer properly

(:map org-super-agenda-header-map
 :map org-super-agenda-header-map
 "j" #'org-agenda-next-line
 "k" #'org-agenda-previous-line
 :map org-agenda-keymap
 :map org-agenda-mode-map
 "k" #'org-agenda-previous-line
 "j" #'org-agenda-next-line)
(:leader
:desc "Today" "ot" #'my/today
:desc "Create schedule for today" "ost" #'my/create-schedule-for-today
:desc "Create schedule for this week" "osw" #'my/create-schedule-for-week
:desc "Create schedule for this month" "osm" #'my/create-schedule-for-month
 )

Deciding what to do

my/today is the primary way I see what to do right now. It should only give items that are relevant to today.
(defun my/today ()
  (interactive)
  (setq org-agenda-view-columns-initially nil
        org-habit-scheduled-past-days 10000
        org-scheduled-past-days 2
        org-habit-preceding-days 0
        org-habit-following-days 0
        org-habit-following-days 3
        org-deadline-warning-days 14
        org-super-agenda-groups '((:name "Habits"
                                   :habit t
                                   :order 30)
                                  (:scheduled t
                                   :time-grid t
                                   :order 9000)
                                  (:name "Done Today"
                                   :order 100
                                   :and (:date today
                                         :todo "DONE"))
                                  (:name "Städa"
                                   :category "clean")
                                  (:name "Todos"
                                   :todo t))
        org-agenda-dim-blocked-tasks t
        org-agenda-start-day "0d"
        org-agenda-span 1)
  (let ((org-agenda-custom-commands
         '(("l" "Today"

            ((org-ql-block '(and (and (todo "TODO")
                                      (not (property "style" "habit"))
                                      (deadline :to 14 :from 0))
                                 (not (tags "noreview")))
             ((org-ql-block-header "Deadlines"))              )
             (tags-todo "onnotebook" ((org-agenda-overriding-header "Active tasks")))
             (agenda "" nil))))))
    (org-agenda nil "l")))
(defun my/deadlines ()
  (interactive)
  (org-ql-search org-agenda-files '(and (and (todo "TODO")
                           (not (property "style" "habit"))
                           (deadline :to 14 :from 0))
                                        (not (tags "noreview")))))

As an experiment, I’ll try bringing up the agenda when I’ve been idle. This should hopefully make me use it more. During the weekends I want to focus more on my org-roam notes, so then I will use el-secretario to bring up stuff to reflect back on.

(load! "~/Documents/el-secretario/el-secretario-org-roam.el")

(defun my/reflect-weekend ()
  (interactive)
  (el-secretario-start-session
   (list
    (el-secretario-org-roam-make-source
     ;; Pick a random node that is tagged with TODO
     ;; and review it first
     (list
      (org-roam-node-from-id
       (car (el-secretario--shuffle
             (mapcar #'car (org-roam-db-query [:select [node-id] :from tags
                                               :where (or (= tag "TODO")
                                                          (= tag "outline")
                                                          (= tag "question"))])))))))

    (el-secretario-files-make-source
     (el-secretario--shuffle

      (directory-files "~/Documents/notes/" t
                       (ts-format "%Y%m" (ts-dec 'month
                                                 (if (= 1 (cl-random 10))
                                                     2
                                                   1)
                                                 (ts-now)))))))))

(run-with-idle-timer 300 t (lambda ()
                             (unless (org-clocking-p)
                               ;; On weekends review notes from the past month,
                               (if (>
                                    ;; Make weeks start at mondays
                                    (ts-day-of-week-num
                                     (ts-adjust 'day -1 (ts-now)))
                                    5)
                                   (my/reflect-weekend)
                                 (my/today)))))

And this function is my way of deciding what to do when I have free time.

(defun my/anytime-todos ()
  (interactive)
  (org-ql-search (org-agenda-files)
    '(and (todo "TODO" "NEXT" "WAITING")
          (tags "anytime"))
    :super-groups
    '((:auto-category))))

View only items from parent in the agenda

(map! :map org-agenda-mode-map
      "<" #'my/org-agenda-set-restriction-lock-from-agenda
      ">" #'org-agenda-remove-restriction-lock)
(defun my/org-agenda-set-restriction-lock-from-agenda (arg)
  "Set the restriction lock to the parent of agenda item at point from within the agenda.
When called with a `\\[universal-argument]' prefix, restrict to
the file which contains the item.
Argument ARG is the prefix argument."
  (interactive "P")
  (unless  (derived-mode-p 'org-agenda-mode)
    (user-error "Not in an Org agenda buffer"))
  (let* ((marker (or (org-get-at-bol 'org-marker)
                     (org-agenda-error)))
         (buffer (marker-buffer marker))
         (pos (marker-position marker)))
    (with-current-buffer buffer
      (goto-char pos)
      (outline-up-heading 1)
      (org-agenda-set-restriction-lock arg))))

Planning

I want to be able to set efforts to tasks through the collumns view so I have to set this variable with some fixed preset.

(setq org-global-properties
      '(("Effort_ALL" .
         "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")))

El Secretario

Email

When I don’t want to read an email I want to capture it so that I can deal with it in my todo system instead of turning email into an inferior secondary todo system.

(add-to-list 'org-capture-templates
            '("m" "Mail" entry (file "~/org/orgzly/InboxComputer.org")
              "* TODO Deal with Email: %a "
              :immediate-finish t))

Then when I go through my email during daily review if I can’t complete the email there I capture the email and mark it as done.

(defvar my/secretary-notmuch-map
  (let ((km (make-sparse-keymap)))
    (define-key km
      "n" `("next" . ,(cmd!
                      (notmuch-show-archive-thread)
                      (org-capture nil "m")
                      (el-secretario-next-item))))
    (define-key km
      "d" `("Done" . ,(cmd!
                      (notmuch-show-archive-thread)
                      (el-secretario-next-item))))
    (define-key km
      "q" `("Quit" . el-secretario-end-sesion))
    (define-key km
      (kbd "SPC") '("Advance and Archive" . el-secretario-notmuch-advance-and-archive))
    (define-key km
      "c" `("Capture" . ,(cmd!
                         (org-capture nil "f"))))
    (define-key km
      "]" `("Next message" . notmuch-show-next-message))
    (define-key km
      "[" `("Next message" . notmuch-show-previous-message))
    km))

(defvar my/secretary-mu4e-map (make-sparse-keymap))
(map!
 :map my/secretary-mu4e-map
 :desc "next" "n" (cmd!
                   (org-capture nil "m")
                   (el-secretario-next-item))

 :desc "Quit" "q" #'el-secretario-end-sesion
 :desc "Mark message as done" "d" (cmd! (el-secretario-next-item))

 :desc "Capture" "c" (cmd! (org-capture nil "f")))

TODOs

I want to see the context of this todo if it is part of a project (i.e. there is a parent which is a todo).
(setq el-secretario-org-narrow-function #'el-secretario-org-narrow-to-highest-todo)
(defadvice! el-secretario-org-space--increment ()
  "Try an exponential increase in scheduling"
  (unless (and el-secretario-org-space-increment-percentage
             (<= el-secretario-org-space-increment-percentage (random 100)))
    (let ((cap
           (-some-> (org-entry-get (point)
                                   "EL-SECRETARIO-DELTA-CAP")
             string-to-number))
          (reset-cap
           (-some-> (org-entry-get (point)
                                   "EL-SECRETARIO-DELTA-RESET-CAP")
             string-to-number)))
      (--> (org-entry-get (point)
                          "EL-SECRETARIO-DELTA")
        (or it "1")
        (string-to-number it)
        (if (and cap (>= it cap))
            it
          (* 2 it))
        (if (and reset-cap (>= it reset-cap))
            2
          it)
        (number-to-string it)
        (org-set-property "EL-SECRETARIO-DELTA" it)))))
(defvar my/el-secretario-org-map (make-sparse-keymap))

(map! :map my/el-secretario-org-map
      ;; To activate a project is to put the tag :active: on the highest todo
      ;; parent. Because ~el-secretario-org-narrow-to-highest-todo~ narrows so that
      ;; the highest todo is the first in
      ;; the buffer after narrowing, the follwing accomplishes that.
      :desc "Activate task (and send to notebook)" "a"
      (cmd!

       (el-secretario-org-add-tag "onnotebook")
       (el-secretario-org-space--reset)
       (org-schedule nil "+7d")
       (el-secretario-next-item))

      :desc "next (and spaced rep)" "n" (cmd!
                                          (el-secretario-org-space-reschedule)
                                          (el-secretario-next-item) )
      :desc "previous" "p" #'el-secretario-previous-item
      :desc "next" "RET" #'el-secretario-next-item
      :desc "Refile" "r" (cmd!
                          (let ((org-reverse-note-order nil))
                            (org-refile)
                            (el-secretario-next-item)))
      :desc "Refile to top" "R" (cmd!
                                 (let ((org-reverse-note-order t))
                                   (org-refile)
                                   (el-secretario-next-item)))
      :desc "Tags" "t" #'org-set-tags-command
      :desc "TODO" "T" #'org-todo
      :desc "Link" "l" #'link-hint-open-link
      :desc "Schedule" "s" (cmd!
                             (call-interactively #'el-secretario-org-space-schedule-and-reset)
                             (el-secretario-next-item))
      :desc "Deadline" "d" #'org-deadline
      :desc "Delete visible" "D" (cmd! (delete-region (point-min) (point-max)))
      :desc "Quit" "q" #'el-secretario-end-sesion)
(map! :map el-secretario-org-map
           :desc "Link" "l" #'link-hint-open-link)

Backups

(defun my/usb-backup ()
  "Call the backupscript to backup to the usb-stick."
  (interactive)
  (save-some-buffers)
  (when (y-or-n-p "Have you plugged in the USB?")
    (shell-command "ruby ~/.doom.d/usb-backup.rb")))

Writing Inbox

I want a writing inbox like Andy https://notes.andymatuschak.org/z7yRMBXGc81KkUwLxefodzfnnfKXx63vXzP88?stackedNotes=z4AX7pHAu5uUfmrq4K4zig9x8jmmF62XgaMXm&stackedNotes=z5aJUJcSbxuQxzHr2YvaY4cX5TuvLQT7r27Dz
(defvar my/el-secretario-writing-map (make-sparse-keymap))
(map!
 :map my/el-secretario-writing-map
 :desc "next (and spaced rep)" "n" (cmd!
                                    (el-secretario-org-space-reschedule)
                                    (el-secretario-next-item))
 :desc  "Link" "l" #'link-hint-open-link
 :desc "Schedule" "s" (cmd!
                       (el-secretario-org-space-schedule-and-reset nil "-365d")
                       (el-secretario-next-item))
 :desc "Send to notebook" "a" (cmd!
                               (el-secretario-org-add-tag "onnotebook")
                               (el-secretario-org-space--reset)
                               (el-secretario--next-source))
 :desc  "Delete visible" "D" (cmd! (delete-region (point-min) (point-max)))
 :desc  "Quit" "q" (cmd! (el-secretario-end-sesion)))

(defvar my/el-secretario-writing-inbox-source (el-secretario-org-make-source
                                               '(todo)
                                               '("~/org/orgzly/writing-inbox.org")
                                               :keymap #'my/el-secretario-writing-map
                                               :shuffle-p t
                                               :tag-transitions '(("onnotebook" . ""))
                                               :compare-fun #'el-secretario-org-space-compare-le))
(defun my/writing-inbox ()
  (interactive)
  (el-secretario-start-session
   my/el-secretario-writing-inbox-source))
(defun anchor-act-writing-inbox ()
  "Prompt the user for action and perform it on the current context.

If no context is found, first prompt for a context with `completing-read`."
  (interactive)
  (anchor-act-on-org-query '(and (tags "onnotebook")
                                            (todo))))

Sync phone inbox

I use Syncthing to sync my org files, which means that it’s pretty messy to deal with my phone inbox because there would always be conflicts. This should fix this by copying all the new unprocessed parts of the inbox file to a copy. Then all processing happens in the copy so that no modifications happen on the file that the phone touches.

(defvar my/inbox-max-point nil)
(defun my/sync-inbox ()
  (interactive)
  ;; Load cache
  (setq my/inbox-max-point
        (ignore-errors
          (with-temp-buffer
            (insert-file-contents  "~/.emacs.d/.local/cache/my-inbox-max-pos")
            ;; this will blow up if the contents of the file aren't
            ;; lisp data structures
            (read (buffer-string)))))

  (with-current-buffer (find-file-noselect "~/org/orgzly/Inbox.org")
    (let ((new-inbox (buffer-substring (or my/inbox-max-point (point-min)) (point-max))))
      (with-current-buffer (find-file-noselect "~/org/orgzly/PhoneInbox.org")
        (goto-char (point-max))
        (insert new-inbox)))
    (setq my/inbox-max-point  (point-max))

    ;; Save cache
    (with-temp-file "~/.emacs.d/.local/cache/my-inbox-max-pos"
      (insert (prin1-to-string my/inbox-max-point)))))

El Secretario

(defun my/dailyreview-secretary ()
  (list
   (el-secretario-function-source :func
                                  (lambda () (org-ql-search (org-agenda-files) '(and (ltags "onnotebook")
                                                                                     (todo))
                                               :title
                                               (propertize "Mark tasks that are done in the notebook as done here too"
                                                           'face '(:height 150
                                                                   :weight bold)))))

   (el-secretario-function-source :func
                                  (lambda ()
                                    (el-secretario-message-display-message-prompt "Go through todo journal!\nMove tasks marked with △ to org-mode")
                                    (el-secretario-activate-keymap)))
   (if (string= (system-name)
                "sakura")

       (progn
         (el-secretario-mu4e-make-source
          "flag:unread"
          nil
          (lambda ()
            (setq mu4e-headers-include-related nil)
            (setq mu4e-headers-show-threads nil)
            (setq mu4e-view-auto-mark-as-read t)
            (setq mu4e-headers-sort-direction 'ascending))))
     (el-secretario-notmuch-make-source   "to:[email protected] AND tag:unread AND NOT tag:deleted NOT tag:gmail/Inbox" #'my/secretary-notmuch-map)
     (el-secretario-notmuch-make-source   "tag:lists AND tag:unread AND NOT tag:deleted NOT tag:gmail/Inbox" #'my/secretary-notmuch-map)
     (el-secretario-notmuch-make-source   "tag:unread AND NOT tag:deleted NOT tag:gmail/Inbox" #'my/secretary-notmuch-map))

   (when (string= (system-name)
                  "hako")
     (el-secretario-elfeed-make-source "@2-month-ago +unread"))
   (el-secretario-org-make-source nil '("~/org/orgzly/InboxComputer.org"
                                        "~/org/orgzly/PhoneInbox.org"))
   (el-secretario-org-make-source '(and (todo "WAITING")
                                        (not (scheduled :from 2)))
                                  '("~/org/orgzly/Todo.org"
                                    "~/org/orgzly/Projects.org")
                                  :keymap #'my/el-secretario-org-map)
   (el-secretario-org-make-source '(and (and (todo "TODO")
                                             (not (org-entry-blocked-p))
                                             (not (property "style" "habit"))
                                             (not (scheduled :from 2)))
                                        (not (tags "noreview")))
                                  '("~/org/orgzly/Todo.org"
                                    "~/org/orgzly/Projects.org")
                                  :tag-transitions '(("onnotebook" . ""))
                                  :keymap 'my/el-secretario-org-map)
   my/el-secretario-writing-inbox-source
   (el-secretario-function-source :func #'my/empty-remarkable-outbox)
   (el-secretario-function-source :func (lambda ()
                                          (shell-command "~/.doom.d/dropbox-backup.sh"))) ))
(defun my/el-secretario-daily-review ()
  (interactive)
  (my/sync-inbox)
  (when (string= (system-name)
                 "hako")
    (elfeed)
    (elfeed-update))
  (when (string= (system-name)
                "sakura")
    (require 'mu4e)
    (mu4e-update-mail-and-index t))
  (el-secretario-start-session (my/dailyreview-secretary)))

Fun tasks

I want to do a lot of things because I think they are fun. I’d like to keep a queue of such things so that I can easily choose a fun thing to do. It is very similar to Writing Inbox so I can reuse the hydra from there.

(defun my/choose-fun-thing ()
  (interactive)
  (el-secretario-start-session (list
                                (el-secretario-org-make-source
                                 '(and (todo)
                                       (tags-inherited "fun"))
                                 '("~/org/orgzly/Todo.org"
                                   "~/org/orgzly/Projects.org")
                                 :keymap #'my/el-secretario-writing-map                                 :shuffle-p t
                                 :ids '("dc0de31b-24a5-49f9-add3-7af77ca19206" ;; Writing inbox's id
                                        )
                                 :compare-fun #'el-secretario-org-space-compare-le))))

Capturing

(setq org-capture-templates
        '(("x" "Scrapbook with clipboard" entry (file "~/Documents/notes/commonplace-book.org")
           "* %?
%x
")
          ("c" "Link inbox with clipboard" entry (file "~/org/orgzly/link-inbox.org")
           "* %?
%x
")

          ("w" "Weekly Summary" entry (file+datetree "~/org/reviews.org")
           "* Weekly summary :weekly:
\** What went well :good:
\** What can be improved :improve:
\** Summary
The Last column is the % of 40hours
#+BEGIN: clocktable :scope (\"~/org/orgzly/Projects.org\") :maxlevel 2 :block thisweek :stepskip0 t :formula % :match \"school\"
#+TBLFM: $6=40:00;t::$7=$4;t:: $8=100 * $7/$6
#+END:
#+BEGIN: clocktable :scope (\"~/org/orgzly/Projects.org\" \"~/org/orgzly/Log.org\" \"~/org/orgzly/Todo.org\") :maxlevel 5 :block thisweek :stepskip0 t :fileskip0 t
#+END:
"
           :jump-to-captured t)
          ("t" "Todo" entry (file "~/org/orgzly/InboxComputer.org")
           "* TODO %? ")
          ("f" "Todo" entry (file "~/org/orgzly/InboxComputer.org")
           "* TODO %?\n %a "
           :created t)
          ("e" "Email" entry (file "~/org/orgzly/InboxComputer.org")
           "* WAITING for %a\n
%(el-secretario-notmuch-capture-get-thread-link) ")

          ("T" "clipboard" entry (file "~/org/orgzly/InboxComputer.org")
           "* %?
%T
%x")
          ("z" "Gather info for clocked task" item (clock)
           "%x")
          ("o" "Clock in a other task" entry (file+olp+datetree "~/org/orgzly/Log.org" "Log")
           "* %?"
           :clock-in t
           :clock-resume t)

          ("s" "Clock in subtask" entry (clock)
           "* %?"
           :clock-in t
           :clock-resume t)))

Clocking

(add-hook! '(org-clock-in-hook org-clock-out-hook) #'org-save-all-org-buffers)
(setq org-clock-out-remove-zero-time-clocks t)

The default volume is way too loud.

(after! org-pomodoro
  (setq org-pomodoro-finished-sound-args "--volume=30000")
  (setq org-pomodoro-short-break-sound-args "--volume=30000")
  (setq org-pomodoro-killed-sound-args "--volume=30000")
  (setq org-pomodoro-ticking-sound-args "--volume=30000")
  (setq org-pomodoro-start-sound-args "--volume=30000")
  (setq org-pomodoro-long-break-sound-args "--volume=30000")
  (setq org-pomodoro-overtime-sound-args "--volume=30000")
  (setq org-pomodoro-finished-hook (λ! (org-notify "Break time!")))
  (setq org-pomodoro-break-finished-hook (λ! (org-notify "Time to start again"))))
(after! org-pomodoro
  (setq org-pomodoro-clock-break nil)
  (setq org-pomodoro-keep-killed-pomodoro-time t))
(package! org-mru-clock)
(use-package! org-mru-clock
  :config
  (map!
   :leader
   "n O" #'org-mru-clock-in)
  (setq org-mru-clock-capture-if-no-match '((".*" . "o"))
        org-mru-clock-completing-read #'ivy-completing-read))
(setq org-clock-mode-line-total 'today)
(setq org-duration-format 'h:mm)
(map!
 :leader
 "nf" nil
 (:prefix ("nf" . "Quick Clock-in")
  "d" (cmd! (my/clock-in-specific "Dötid under skola" t)) :desc "Dötid under skola"
  "n" (cmd! (my/clock-in-specific "Nothing" t)) :desc  "Nothing"))

Kan vara bra att sätta upp kategorier

ALla tasks och projekt hamnar under kategorier

  • kategori
    • task
    • projekt
      • task

Detta är bra för att då kan jag ha capture templates för att klocka in en misc. event i en kategori, ex. ordf

Man kan klocka det som finns i arkiven också.

Time Tracking functions

Clock in quickly

(defun my/clock-in-specific (name &optional no-ping)
  (interactive)
  (with-current-buffer (find-file-noselect "/home/leo/org/orgzly/Log.org")
    (save-excursion
      (goto-char (point-min))
      (unless (search-forward (concat "** " name) nil t)
        (goto-char (point-max))
        (insert (concat "** " name)))
      (goto-char (line-end-position))
      (org-clock-in)
      (unless no-ping
        (kdeconnect-ping-msg
         (concat "Clocking in: "
                 (org-no-properties (org-get-heading)))))
      (save-buffer))))

(defun my/kdec-clock-out ()
  (interactive)
  (save-excursion
    (org-clock-goto)
    (kdeconnect-ping-msg
     (concat "Clocking out: "
             (org-no-properties (org-get-heading))))
    (org-clock-out)
    (buffer-save)))

;; From https://github.com/carldotac/kdeconnect.el
;;;###autoload
(defun kdeconnect-ping-msg (message)
  "Ping the active device with MESSAGE."
  (interactive "MEnter message: ")
  (shell-command
   (mapconcat 'identity
              (list "kdeconnect-cli" "-d"
                    (shell-quote-argument "5f9f969c142cd64b")
                    "--ping-msg" (shell-quote-argument message)) " ")))

Latex

(setq +latex-viewers '(okular))
(setq TeX-master nil)

Misc.

A man is not dead while his name is still spoken.

www.gnuterrypratchett.com/

(after! sendmail
  (setq mail-default-headers "X-Clacks-Overhead: GNU Terry Pratchett\n"))

When playing something with emms the modeline gets filled up with too much stuff that I can’t see if I have clocked in to a heading or not. I therefore have that info displayed in the frame title instead.

(setq frame-title-format '("%b - Doom Emacs - " mode-line-misc-info))
(after! dired-x
  (setq dired-omit-files (concat ".+\\.sdr\\|"
                                 dired-omit-files)))

(setq whitespace-style '(face space-before-tab indentation tabs tab-mark trailing lines-tail))
;; I use visual line mode to write in org-mode, so all paragraphs gets marked anywas
(setq whitespace-global-modes '(prog-mode))
(setq global-hl-line-modes (seq-filter (lambda (x) (not (eq x 'text-mode)))
                                       global-hl-line-modes))
(global-whitespace-mode)

I want to have some local variables and prefer to not have them ignored.

(setq-default enable-local-variables t)
(after! ispell
  (setq ispell-personal-dictionary nil))
(setq ispell-dictionary "english")

I want the prompt for when I unlock my gpg key

(after! epa (setq epa-pinentry-mode 'ask))

Pasting over text puts the deleted text in the kill ring by default. This is annoying since it makes it difficult to paste over multiple things with the same text.

(setq evil-kill-on-visual-paste t)

I want j and k and other evil movements to treat soft-wrapped lines like normal lines.

(setq evil-respect-visual-line-mode t)
(setq evil-kill-on-visual-paste nil)

This is needed for up and down to work as well.

(map!
 :m "<up>" #'evil-previous-visual-line
 :m "<down>" #'evil-next-visual-line)

I don’t like doom’s default action for what to do after switching projects. I often don’t know which file I want to open, and some projects I interact with differently. If I want to open my org-roam notes in a project I don’t want to use the normal find-file dialogue, I want to use org-roam’s. And for repos where it’s available, I like opening magit status the first thing I do.

(defun my/switch-project-f (dir)
  "Function to be used with `+workspaces-switch-project-function'."
  (cond
   ((equal dir "~/Documents/notes/")
    (org-roam-node-find))
   ((equal dir "~/Documents/notes/texter/")
    (org-roam-node-find nil "@texter "))
   ((magit-git-repo-p dir) (magit-status))
   (t (doom-project-find-file dir))))
(setq +workspaces-switch-project-function #'my/switch-project-f)
(push "~/.nix-profile/bin" tramp-remote-path)
(push "/etc/profiles/per-user/leo/bin" tramp-remote-path)
;; Let magit find the git executable by path
(setq magit-git-executable "git")

I don’t want the lines to be too long when writing using writeroom.

(setq writeroom-width 0.08)
(remove-hook 'org-mode-hook #'+literate-enable-recompile-h)

Popup rules

Put info manual on the side and src blocks on the other

(set-popup-rules!
  '(("^\\*info\\*" :slot 2 :vslot 2 :side left :width 85 :quit nil :height 0.50))
  '(("^\\*info\\*<2>" :slot 3 :vslot 2 :side left :width 85 :quit nil :height 0.50))
  '(("^\\*Org Src*" :slot 3 :side right :quit nil :width 0.5))
  '(("*eww*" :ignore t))
  '(("\\*notmuch-*" :ignore t)
    ("\\*subject*" :ignore t)))

Remarkable

Send files to a reMarkble tablet using curl (https://remarkablewiki.com/tech/webinterface#accessing_files_via_curl).

(defun my/send-to-remarkable-dwim (prompt)
  "Send files to remarkable.
If in a dired buffer, send the currently marked files.
If in a doc-view buffer, send the current buffer's file.

With a prefix argument, prompt for a new name to use for each file.
"
  (interactive "P")
  (let* ((file-names (pcase major-mode
                       ('dired-mode (dired-get-marked-files))
                       ('doc-view-mode (list (buffer-file-name)))))
         (name-file-name-alist (mapcar (lambda (f)
                                         `(,(if prompt
                                                (read-from-minibuffer "File name to use: "
                                                                      (file-name-base f))
                                                (file-name-base f)) . ,f))
                                       file-names)))
    (mapc (lambda (pair)
            (shell-command (concat "curl 'http://10.11.99.1/upload' -H 'Origin: http://10.11.99.1' -H 'Accept: */*' -H 'Referer: http://10.11.99.1/' -H 'Connection: keep-alive' -F \"file=@"
                                   (cdr pair)
                                   ";filename="
                                   (car pair)
                                   ";type=application/pdf\" ")))
          name-file-name-alist)))
(defun my/empty-remarkable-outbox ()
  (interactive)
  (hercules--hide)
  (dired "~/Downloads/remarkable-outbox/")
  (dired-mark-sexp '(string= (file-name-extension name) "pdf"))
  (when (y-or-n-p "Send marked files to remarkable?")
    (call-interactively #'my/send-to-remarkable-dwim)
    (when (y-or-n-p "Delete marked files?")
      (dired-do-delete)))
  (el-secretario-activate-keymap))

git

(defun my/sourcehut-magit-add-remote ()
  (interactive)
  (magit-remote-add (completing-read "remote: " (list "fork" "origin"))
                    (completing-read "url: " (list (concat "[email protected]:~zetagon/"
                                                           (file-name-nondirectory (directory-file-name (magit-toplevel)))))
                                     nil nil  "[email protected]:~zetagon/")))

git-branchless

(load! "~/Programs/git-branchless.el/git-branchless.el")
(set-popup-rule! git-branchless-smartlog-buffer :side 'left :select t :width 0.3)
(add-to-list 'evil-emacs-state-modes 'git-branchless-smartlog-mode)
(map! (:leader
       (:prefix "g"
                "l" #'git-branchless-smartlog
                "p" #'git-branchless-prev
                "n" #'git-branchless-next)))

recoll

I’m trying out recoll for my searching my notes.

(map! :leader
      "s n" (λ! (consult-recoll--open (consult-recoll--search "mime:text/x-orgmode "))))

Emms

(after! emms
  (setq emms-source-file-default-directory "~/Music/")
  (setq emms-player-mpv-parameters
        '("--quiet" "--really-quiet" "--no-video"
          "--no-audio-display" "--force-window=no" "--vo=null"))
  (setq emms-browser-covers #'emms-browser-cache-thumbnail-async)
  (add-to-list 'emms-browser--covers-filename '("cover.jpg"))
  ;; I don't think I will need any other player besides mpv
 (setq emms-player-list '(emms-player-mpv)))
(map!
 "<XF86Launch7>" #'=emms
 :map emms-playlist-mode-map
 :mn "b" #'emms-browser
 :map emms-browser-mode-map
 :mn "b" #'emms-playlist-mode-go)
(defun =emms ()
  "Activate (or switch to) `notmuch' in its workspace."
  (interactive)
  (condition-case-unless-debug e
      (progn
        (when (modulep! :ui workspaces)
          (+workspace-switch "*emms*" t))
        (if-let* ((win (cl-find-if (lambda (it) (string-match-p "^\\*emms" (buffer-name (window-buffer it))))
                                   (doom-visible-windows))))
            (select-window win)
          (emms-browser))
        (when (modulep! :ui workspaces)
          (+workspace/display)))
    ('error
     ;; (+notmuch/quit)
     (signal (car e) (cdr e)))))

LSP

(after! lsp-ui
  (setq lsp-ui-doc-position 'top)
  (setq lsp-ui-doc-show-with-cursor t))

Lispy

I’d rather want the result of evaluating expressions go directly to the repl buffer instead of being shown ephemerally in the modeline.
(after! racket-mode
  (setf (alist-get 'racket-mode lispy-eval-alist)
        '(le-racket (lambda (_)
                      (let ((bounds (lispy--bounds-dwim)))
                        (racket--send-region-to-repl (car bounds)
                                                     (cdr bounds))
                        "")))))
(after! lispyville
  (setq lispyville-key-theme
        '((operators normal)
          c-w
          (prettify insert)
          (atom-movement t)
          slurp/barf-lispy
          additional
          text-objects
          additional-motions
          additional-insert)))

Erlang

This function allows me to extract logs in the format I used in the cacofonix project
(defun my/extract-erlang-log (query)
  (interactive "sQuery pls: ")
  (save-excursion)
  (goto-char (point-min))
  (let* ((beg (point-min))
         (end)
         (continue t)
         pos)
    (while continue
      (setq pos (point))
      (setq continue (search-forward query nil t))
      (setq end (or (re-search-backward "\n\n" nil t) (- (line-beginning-position) 1)))
      (delete-region beg end)
      (setq beg (search-forward "\n\n" nil t))
      (message "Position: %d" pos))
    (delete-region pos (point-max))))

This is the log configuration for the above function

[{
  kernel, [{
   logger, [{
     handler, default, logger_std_h,
       #{
         level => debug,
         formatter => {logger_formatter,
                       #{template => [time, " ",  level,":", pid, "\n",file, ":", line, "\n", msg, "\n\n"],
                         single_line => false}}
        }
    }]},
    {logger_level, debug}
 ]}
].

Emacs-lisp

(after! flycheck
  (flycheck-package-setup))

Lean

(after! helm-lean
  (add-to-list 'helm-lean-definitions-actions
               '("Add to working lemmas" .
                 (lambda (c) (with-helm-current-buffer
                               (message (prin1-to-string c))
                               (let* ((buf (get-buffer-create "*helm-lean-working-lemmas*"))
                                      (win (get-buffer-window buf)))
                                 (with-current-buffer buf
                                   (goto-char (point-max))
                                   (insert  (plist-get c :text)
                                            " : "
                                            (plist-get c :type)
                                            "\n")
                                   (save-excursion
                                     (unless win
                                       (setq win (split-window (selected-window)
                                                               nil
                                                               'below))
                                       (switch-to-buffer buf)
                                       ;; (setq win (get-buffer-window (current-buffer)))
                                       )
                                     (fit-window-to-buffer win)
                                     ;; (select-window (other-window 1))
                                     ))))))))

Java

(when (and (featurep! :tools lsp)
           (featurep! :lang java))
  (add-hook! java-mode-hook #'lsp-lens-mode #'lsp-jt-lens-mode))
(after! java-mode
  (set-docsets! 'java-mode :add "Java"))

IRC

(after! circe
  (set-irc-server! "chat.freenode.net"
                   `(:tls t
                     :port 6697
                     :nick "Zetagon"
                     :sasl-username "Zetagon"
                     :sasl-password (lambda (&rest _) (+pass-get-secret "freenode/[email protected]"))
                     :channels ("#emacs-circe" "#emacs" "#haskell" "#org-mode" "#xmonad" "#fsf" "#libreplanet" "#gnu" "#cryptoparty-uppsala" "#wikimedia" "#wikibooks"))))

RSS

These are my feeds
(defvar my/feed-file "~/org/orgzly/feeds.org" )
(setq org-feed-alist `(("Org mode" "https://updates.orgmode.org/feed/bugs"
                       ,my/feed-file "Org Mode Bugs")
                       ("Lexi Lambda" "https://lexi-lambda.github.io/feeds/all.rss.xml"
                        ,my/feed-file"Lexi Lambda"
                        :template "\n* %h\n  %U\n  %description\n  %a\n")
                       ("Reasonably Polymorphic" "https://reasonablypolymorphic.com/feed.rss"
                        ,my/feed-file "Reasonably Polymorphic")
                       ("Eccentric J" "https://eccentric-j.com/feed.rss"
                        ,my/feed-file "idle-parens")))

(setq org-feed-default-template
      "\n* %h\n  %U\n  LINK: %a\n  %description\n")

This function will only work with the templates specified above.

(defun my/open-feed-in-eww ()
  "Open feed in eww.
If there is a line on the format LINK: %a, in the first two lines
of the heading use that link to open with eww. Otherwise open the entry in eww."
  (interactive)
  (save-excursion
    (save-restriction
      (outline-back-to-heading)
      (let ((has-link (save-excursion
                        (when (search-forward "LINK:"
                                              (save-excursion (next-line)
                                                              (next-line)
                                                              (line-end-position))
                                              't)
                          (forward-to-word 1)
                          (when-let ((link (org-element-link-parser)))
                            (plist-get (car (cdr link)) :raw-link))))))

        (if has-link
            (progn
              (eww-browse-url has-link))
          (org-narrow-to-subtree)
          (copy-region-as-kill (point-min) (point-max))))))
  (with-current-buffer (find-file-noselect "/tmp/feed.html")
    (delete-region (point-min) (point-max))
    (yank)
    (basic-save-buffer))
  (eww-open-file "/tmp/feed.html")
  (writeroom-mode))

I want to be able to use org links with eww

Elfeed

(after! elfeed-org
  (setq rmh-elfeed-org-files '("~/org/orgzly/feeds.org")))

Elfeed-tube

(use-package! elfeed-tube
  :after elfeed
  :demand t
  :config
  (setq elfeed-tube-auto-save-p nil)
  (setq elfeed-tube-auto-fetch-p t)
  (elfeed-tube-setup)

  :bind (:map elfeed-show-mode-map
         ("F" . elfeed-tube-fetch)
         ([remap save-buffer] . elfeed-tube-save)
         :map elfeed-search-mode-map
         ("F" . elfeed-tube-fetch)
         ([remap save-buffer] . elfeed-tube-save)))
(use-package! elfeed-tube-mpv
  :bind (:map elfeed-show-mode-map
              ("C-c C-f" . elfeed-tube-mpv-follow-mode)
              ("C-c C-w" . elfeed-tube-mpv-where)))

Org Indexcards

(load! "./org-indexcards.el")
(map! :leader "w w" #'org-indexcards-bsp-split)

Piem

(after! piem
  (setq piem-inboxes
        '(("el-secretario"
           :url "https://lists.sr.ht/~zetagon/el-secretario-devel"
           :address "~zetagon/[email protected]"
           :listid "~zetagon/el-secretario-devel.lists.sr.ht"
           :coderepo "~/Documents/el-secretario")
          ("piem"
           :url "https://inbox.kyleam.com/piem/"
           :address "[email protected]"
           :coderepo "~/Programs/piem/")))
  (setq piem-notmuch-extract-patch-executable "~/.emacs.d/.local/straight/repos/mailscripts/notmuch-extract-patch")
  (setq piem-am-ready-mbox-functions #'piem-notmuch-extract-patch-am-ready-mbox)
  (after! notmuch
    (piem-notmuch-mode)))

I want to bring up the messages that pertain to the project I am in if any.

(defun my/open-piem-repo-mailing-list ()
  "Open the mailing list associated with current project."
  (interactive)
  (require 'piem)
  (let ((repo-path (unwind-protect (project-root (project-current))
                     default-directory)))
    (when-let ((inboxes (seq-filter (lambda (x)
                                      (string= (plist-get (cdr x) :coderepo)
                                               repo-path))
                                    piem-inboxes)))
      (notmuch-search (or (plist-get (cdr (car inboxes))  :listid)
                          (plist-get (cdr (car inboxes))  :address))))))

anchor

(add-load-path! "~/Documents/anchor/")
(after! anchor
  (require 'piem)
  (setq anchor-contexts (setq piem-inboxes
                              '(("el-secretario"
                                 :url "https://lists.sr.ht/~zetagon/el-secretario-devel"
                                 :address "~zetagon/[email protected]"
                                 :listid "~zetagon/el-secretario-devel.lists.sr.ht"
                                 :projectile-project "el-secretario")
                                ("piem"
                                 :url "https://inbox.kyleam.com/piem/"
                                 :address "[email protected]"
                                 :projectile-project "piem")
                                ("anchor"))))
  (defun my/doom-anchor-open-project ()
    (interactive)
    (let ((projectile-switch-project-action
           (lambda ()
             (funcall +workspaces-switch-project-function default-directory))))
      (anchor-open-project)))
  (define-key anchor-map (kbd "p") #'my/doom-anchor-open-project))
(map! :leader
      "z" #'anchor-act)

Vterm

(after! vterm
  (setq vterm-shell "/usr/bin/fish"))

Message passing

You can call elisp functions from vterm: https://github.com/akermu/emacs-libvterm#message-passing fish-side configuration: Fish functions

(after! vterm
  (setf (alist-get "magit-status" vterm-eval-cmds nil nil #'equal)
        '((lambda (path)
            (magit-status path))))
  (setf (alist-get "dired" vterm-eval-cmds nil nil #'equal)
        '((lambda (dir)
            (dired dir)))))

Lexic

https://tecosaur.github.io/emacs-config/config.html#dictionary https://github.com/tecosaur/lexic
(use-package! lexic
  :commands lexic-search lexic-list-dictionary
  :config
  (map! :map lexic-mode-map
        :n "q" #'lexic-return-from-lexic
        :nv "RET" #'lexic-search-word-at-point
        :n "a" #'outline-show-all
        :n "h" (cmd! (outline-hide-sublevels 3))
        :n "o" #'lexic-toggle-entry
        :n "n" #'lexic-next-entry
        :n "N" (cmd! (lexic-next-entry t))
        :n "p" #'lexic-previous-entry
        :n "P" (cmd! (lexic-previous-entry t))
        :n "E" (cmd! (lexic-return-from-lexic) ; expand
                     (switch-to-buffer (lexic-get-buffer)))
        :n "M" (cmd! (lexic-return-from-lexic) ; minimise
                     (lexic-goto-lexic))
        :n "C-p" #'lexic-search-history-backwards
        :n "C-n" #'lexic-search-history-forwards
        :n "/" (cmd! (call-interactively #'lexic-search))))

(defadvice! +lookup/dictionary-definition-lexic (identifier &optional arg)
  "Look up the definition of the word at point (or selection) using `lexic-search'."
  :override #'+lookup/dictionary-definition
  (interactive
   (list (or (doom-thing-at-point-or-region 'word)
             (read-string "Look up in dictionary: "))
         current-prefix-arg))
  (lexic-search identifier nil nil t))

Curated Podcast feeds

I listen to a lot of podcasts, and sometimes some of those episodes are really good. Here is some code so that I can create an RSS feed of podcast episodes I like:

(defun my/add-podcast-to-curated-list ()
  (interactive)
  (let* ((filename "~/Documents/blog/curated-podcast-episodes.json")
         (curator-description (read-from-minibuffer "Add a description as the curator: "))
         (s (json-encode
             (list
              (cons 'title
                    (format "%s: %s"
                            (elfeed-feed-title (elfeed-entry-feed elfeed-show-entry))
                            (elfeed-entry-title elfeed-show-entry)))
              (cons 'description
                    (let ((description (elfeed-deref (elfeed-entry-content elfeed-show-entry)))
                          (feed-url (elfeed-feed-url (elfeed-entry-feed elfeed-show-entry))))
                      (concat
                       (format "<a href=\"%s\">Subscribe to this podcast</a><br><br>"
                               feed-url)
                       (and (not (string-empty-p curator-description))
                            (format "<h2>Curator's description</h2><br><p>%s</p>"
                                    curator-description))
                       (format "<h2>Original description</h2><br>%s"
                               description))))
              (cons 'link  (elfeed-entry-link elfeed-show-entry))
              (cons 'guid  (cdr (elfeed-entry-id elfeed-show-entry)))
              (cons 'enclosure  (or (car (elfeed-entry-enclosures elfeed-show-entry))
                                    (user-error "no enclosure!")))
              (cons 'date (let ((system-time-locale "en_US.UTF-8"))
                            (format-time-string "%a, %d %m %Y %H:%M %z")))))))
    (elfeed-show-tag 'curated)
    (with-temp-buffer
      (insert s "\n")
      (write-region (point-min) (point-max) filename t))
    (shell-command "python3 ~/.local/bin/create-curated-podcast-feed.py")))
#!/usr/bin/env python3
import json
import PyRSS2Gen as RSS2
import datetime
from urllib.parse import urljoin, urlparse


with open("/home/leo/Documents/blog/curated-podcast-episodes.json") as file:
        f = reversed(list(file))
items = []
for entry in f:
        feed = json.loads(entry)
        enclosure_link = feed["enclosure"][0]
        enclosure_link = urljoin(enclosure_link, urlparse(enclosure_link).path)  # 'http://example.com/'
        items.append(
                RSS2.RSSItem(
                        title = feed["title"],
                        link = feed["link"],
                        description = feed["description"],
                        enclosure = RSS2.Enclosure(enclosure_link,  feed["enclosure"][1],  feed["enclosure"][2]) ,
                        guid = RSS2.Guid("https://a-blog-with.relevant-information.com/" + feed["guid"], False),
                        pubDate = feed["date"]),
        )


rss = RSS2.RSS2(
        title = "Relevant Podcasts",
        link = "https://a-blog-with.relevant-information.com/",
        description = "A selection of podcast episodes I like",
        lastBuildDate = datetime.datetime.now(),
        items = items)

rss.write_xml(open("/home/leo/Documents/blog/static/curated-podcast-episodes.xml", "w"), "utf-8")

Keeping pace on books

Here’s some code for when I need to read a book by some deadline.

Use it like this:

Ideal pages per day16
Ideal progress416 pages
Actual progress140 pages
Pace compared to ideal-276 pages
Pages left818
Days left31
Pages per day26
Current page140
Next page166

#+end

(defun my/ts-duration-days (start end)
  (round (/ (ts-diff end start)
            (* 60 60 24))))

(defun my/calculate-pages-per-day (pages pages-done start deadline &optional days-per-week)
  (let* ((total-range (my/ts-duration-days start deadline))
         (percent-days-reading (/ (or days-per-week 7) 7.0))
         (ideal-pages-per-day (/ pages total-range))
         (days-passed (my/ts-duration-days start (ts-now)))
         (ideal-pages-done (* ideal-pages-per-day days-passed percent-days-reading))
         (current-pace (- pages-done ideal-pages-done))
         (pages-left (- pages pages-done))
         (days-left (my/ts-duration-days (ts-now) deadline))
         (pages-per-day (/ pages-left (* percent-days-reading
                                         days-left)))
         (pages-after-reading-today (round (+ pages-done pages-per-day))))
    (format "| Ideal pages per day | %s |
| Ideal progress | %s pages|
| Actual progress | %s pages |
| Pace compared to ideal | %s pages |
| Pages left | %s |
| Days left | %s  |
| Pages per day |  %s |
| Current page | %s |
| Next page | %s |"
            ideal-pages-per-day
            (round ideal-pages-done)
            (round pages-done)
            (round current-pace)
            pages-left
            days-left
            (round pages-per-day)
            pages-done
            pages-after-reading-today)))

(defun org-dblock-write:pages-per-day (params)
  (insert (my/calculate-pages-per-day (plist-get params :pages)
                                      (plist-get params :pages-done)
                                      (ts-parse (plist-get params :start))
                                      (ts-parse (plist-get params :deadline))
                                      (plist-get params :days-per-week)))
  (org-table-align))

Japanese Ebooks

I use Koreader for reading ebooks which has good dictionary support. Therefore I don’t need to have furigana on.

(defun my/remove-furigana ()
  "Remove all furigana in the current html buffer."
  (interactive)
  (goto-char 0)
  (while (re-search-forward "\\(<rp>.*?</rp>\\)\\|\\(<rt>.*?<rt>\\)"
                            nil t)
    (replace-match "" nil nil)))

Secret Service

Keepassxc has the option to always “Confirm when passwords are retrieved by clients” when using Secret Service. Emacs’ dbus client can’t handle that interface so I have to disable that feature.

There is apparently a newer secrets.el that isn’t in emacs27.2 that I got from https://github.com/hpfr/system/commit/26bd3fa602584bd4255750e218737dd07fb21cdd#diff-56e606900d3d58bb34c0fe9a7ab47b7b7694d23c43f687e8427b5cb3f83f23c7

(add-load-path! "backport")
(require 'secrets)
(setq auth-sources `(,(concat "secrets:" (system-name) "-keyring")
                     "secrets:Default Database"
                     ;; for keepassxc, this is whatever database is active (focused)
                     default))

When using Keepassxc as a secret service backend for storing Magit Forge access tokens, I have to store api.github.com in the host attribute, and my username in the user attribute of the Keepassxc entry that stores the token.

Further improvements

Multiple org-files for config

It’s possible to have multiple source files for org.

\#+BEGIN_SRC :noweb-ref test

\#+END_SRC

then in this file:

\#+include ./included.org

Lastly call org-org-export-to-org to get the final org document to tangle

göra en hydra för mina agendaviews

göra en hydra för när jag är i mina agendaviews

typ för att navigera och byta.

Fixa en hydra för att läsa mail och rss feeds

det ska vara exakt samma interface för båda

Fixa journal agenda

Fixa så att journal hamnar i agenda

Fixa så att todos i journal går över till nästa dag

Fixa en capture template för att lägga saker jag vill läsa i min journal

då kan jag lätt skriva anteckningar för det och länka mot org-roam

göra en arkiveringsfunktion som tar hänsyn till breadcrumbs