;; -*- lexical-binding:t -*-
In order to setup the system, this needs to be run
<<setup-system>>
Here are the files of my emacs config:
<<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)
(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")
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)
(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"))
(setq doom-localleader-key ",")
(map!
;; I need these maps to be able to use jk in the agenda buffer properly
<<emacs-keybinds>>
)
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))))
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)
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")
(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)
;; 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)))
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))
(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)))
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))
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))
(after! ivy
(setq ivy-read-action-function #'ivy-hydra-read-action)
(setq +ivy-buffer-preview t))
(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))))))
(map! :map isearch-mode-map "M-j" #'avy-isearch)
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))
(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-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))
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")))
(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)))))
(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")))
(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))))
(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))))
(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)
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))))
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)))))))
(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))
(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)))))))
(use-package! ox-hugo
:after ox
:config
(setq org-hugo-base-dir "~/Documents/blog/")
(setq org-hugo-paired-shortcodes "%alert %notetocssless %spoiler %verse"))
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))))))))
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)))
(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")))
(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))
(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)))
I apparently need to set this in my bashrc
export BIBINPUTS="~/Documents/notes/Zotero_articles.bib"
(package! org-roam-bibtex)
(use-package! org-roam-bibtex
:config
(org-roam-bibtex-mode))
(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")))
(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)))
(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)))
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@)")))
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
)
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))))
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")))
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")))
(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)
(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")))
(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))))
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)))))
(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)))
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))))
(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)))
(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"))
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
(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)) " ")))
(setq +latex-viewers '(okular))
(setq TeX-master nil)
A man is not dead while his name is still spoken.
(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)
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)))
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))
(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/")))
(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)))
I’m trying out recoll for my searching my notes.
(map! :leader
"s n" (λ! (consult-recoll--open (consult-recoll--search "mime:text/x-orgmode "))))
(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)))))
(after! lsp-ui
(setq lsp-ui-doc-position 'top)
(setq lsp-ui-doc-show-with-cursor t))
(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)))
(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}
]}
].
(after! flycheck
(flycheck-package-setup))
(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))
))))))))
(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"))
(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"))))
(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
(after! elfeed-org
(setq rmh-elfeed-org-files '("~/org/orgzly/feeds.org")))
(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)))
(load! "./org-indexcards.el")
(map! :leader "w w" #'org-indexcards-bsp-split)
(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))))))
(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)
(after! vterm
(setq vterm-shell "/usr/bin/fish"))
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)))))
(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))
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")
Here’s some code for when I need to read a book by some deadline.
Use it like this:
Ideal pages per day | 16 |
Ideal progress | 416 pages |
Actual progress | 140 pages |
Pace compared to ideal | -276 pages |
Pages left | 818 |
Days left | 31 |
Pages per day | 26 |
Current page | 140 |
Next page | 166 |
#+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))
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)))
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.
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
typ för att navigera och byta.
det ska vara exakt samma interface för båda då kan jag lätt skriva anteckningar för det och länka mot org-roam