From cecf1f50bd471d43f36608db7240b59891596d5d Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Sat, 22 Aug 2020 23:33:53 -0400 Subject: [PATCH] Optimize Emacs startup time --- emacs/.emacs.d/init.org | 1822 +++++++++++++++++++-------------------- 1 file changed, 872 insertions(+), 950 deletions(-) diff --git a/emacs/.emacs.d/init.org b/emacs/.emacs.d/init.org index 5c7ffef..19b8e49 100755 --- a/emacs/.emacs.d/init.org +++ b/emacs/.emacs.d/init.org @@ -113,58 +113,6 @@ Load [[https://github.com/raxod502/straight.el][straight.el]] to manage package (add-hook 'after-init-hook 'benchmark-init/deactivate)) #+END_SRC -* Load org mode -Load org-mode early to [[https://github.com/raxod502/straight.el#the-wrong-version-of-my-package-was-loaded][avoid a version clash]]. -#+BEGIN_SRC emacs-lisp - (use-package org - :straight org-plus-contrib - :commands (org-element-map) - :mode (("\\.org\\'" . org-mode))) - - ;; Annoying that this is necessary... - (require 'org) - (require 'org-refile) - (require 'org-protocol) -#+END_SRC - -* Doom themes -#+BEGIN_SRC emacs-lisp - (use-package doom-themes) - - (use-package doom-modeline) - (doom-modeline-mode 1) -#+END_SRC - -* Ewal theme -#+BEGIN_SRC emacs-lisp - (use-package doom-ewal-themes - :straight (doom-ewal-themes - :host github - :repo "jdormit/doom-ewal-themes" - :files ("themes" :defaults))) -#+END_SRC - -* Customization File -I don't want anything to write to my init.el, so save customizations in a separate file: -#+BEGIN_SRC emacs-lisp - (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) - (load custom-file t) -#+END_SRC - -* Path -`exec-path-from-shell` uses Bash to set MANPATH, PATH, and exec-path from those defined in the user's shell config. This won't work on Windows. -#+BEGIN_SRC emacs-lisp - (use-package exec-path-from-shell - :if (memq window-system '(mac ns x)) - :config - (setq exec-path-from-shell-variables '("PATH" "MANPATH" "LEDGER_FILE" "LOLA_HOME" - "MODELS_HOME" "LOLA_TRAVEL_SERVICE_HOME" "WORKON_HOME" - "PIPENV_VERBOSITY" "PIPENV_DONT_LOAD_ENV" - "PIPENV_MAX_DEPTH" "PYENV_ROOT") - exec-path-from-shell-check-startup-files nil) - (exec-path-from-shell-initialize)) -#+END_SRC - * General Better keybinding. #+BEGIN_SRC emacs-lisp @@ -180,18 +128,6 @@ Better keybinding. replace))) #+END_SRC -* Autocompletion -There seems to be [[https://github.com/company-mode/company-mode/issues/68][some contention]] about whether autocomplete or company are better autocomplete packages. I'm going with company for now because the maintainer seems nicer... -#+BEGIN_SRC emacs-lisp - (use-package company - :config - (setq company-idle-delay 0.3) - (add-hook 'after-init-hook #'global-company-mode)) - - (general-def "C-M-i" #'company-complete) - (general-def "M-" #'company-complete) -#+END_SRC - * Which-key `which-key` makes keybindings discoverable. #+BEGIN_SRC emacs-lisp @@ -240,9 +176,9 @@ A collection of evil bindings for various modes #+BEGIN_SRC emacs-lisp (use-package evil-collection :after (evil) + :hook ((after-init . evil-collection-init)) :config - (setq evil-collection-company-use-tng nil) - (evil-collection-init)) + (setq evil-collection-company-use-tng nil)) #+END_SRC ** leader key @@ -299,6 +235,846 @@ Adds Evil commands to comment out lines of code: (general-def 'normal prodigy-view-mode-map "0" nil :keymaps 'override) (general-def 'normal messages-buffer-mode-map "SPC" leader-map :keymaps 'override) #+END_SRC +* Hydra +[[https://github.com/abo-abo/hydra][Hydra]]s are convenient keybinding menus. +#+BEGIN_SRC emacs-lisp + (use-package hydra + :defer t) +#+END_SRC + +* Syncthing +I put lots of stuff in Dropbox, but the actual folder location differs on my different computers. This function resolves to the Dropbox directory: +#+BEGIN_SRC emacs-lisp + (defcustom syncthing-path (expand-file-name "~/Sync") + "The absolute path to the Syncthing directory" + :type 'directory) + + (defun syncthing-directory (&optional path) + (f-join syncthing-path (s-chop-prefix (f-path-separator) (or path "")))) +#+END_SRC + +* Org Mode +Notes, agenda, calendar, blogging, journaling, etc. + +Loaded early to [[https://github.com/raxod502/straight.el#the-wrong-version-of-my-package-was-loaded][avoid a version clash]]. + +First, a function to get my notes directory: +#+BEGIN_SRC emacs-lisp + (defun org-directory (&optional path) + "Returns the directory for my org notes, appends PATH if given" + (f-join (syncthing-directory "org") + (s-chop-prefix (f-path-separator) (or path "")))) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + (use-package org + :straight org-plus-contrib + :commands (org-element-map) + :mode (("\\.org\\'" . org-mode)) + :init + (jdormit/define-prefix "o" "org") + (leader-def-key "oa" 'org-agenda) + (leader-def-key "oc" 'org-capture) + :config + (defun agenda-files (&optional file) + (let ((agenda-dir (org-directory))) + (if file + (concat (file-name-as-directory agenda-dir) file) + agenda-dir))) + (setq org-src-fontify-natively t + org-ellipsis " ▼" + org-directory (org-directory) + org-link-elisp-confirm-function 'y-or-n-p + org-startup-with-inline-images t + org-return-follows-link t + org-log-done 'time + org-file-apps '(("log" . emacs) + (auto-mode . emacs) + (directory . emacs) + ("\\.pdf\\'" . emacs) + ("\\.mm\\'" . default) + ("\\.x?html?\\'" . default)) + org-agenda-files (list (agenda-files)) + org-capture-templates `(("L" "Lola task" entry + (file+headline ,(agenda-files "todo.org") "Lola") + "* TODO %i%?") + ("p" "Personal task" entry + (file+headline ,(agenda-files "todo.org") "Personal") + "* TODO %i%?") + ("n" "Note" entry + (file ,(agenda-files "notes.org")) + "* %^{Description}\n%i%?") + ("l" "Log" entry + (file ,(agenda-files "log.org")) + "* %<%Y-%m-%d %H:%M:%S>%?")) + org-refile-use-outline-path 'file + org-refile-targets `((org-agenda-files :level . 0) + (,(agenda-files "notes.org") :level . 1) + (,(agenda-files "todo.org") :level . 1)) + org-todo-keywords '((sequence + "TODO(t)" + "IN PROGRESS(i)" + "BLOCKED(b)" + "|" + "DONE(d)" + "CANCELLED(c)")) + org-agenda-todo-ignore-scheduled 'future + org-agenda-tags-todo-honor-ignore-options t + org-agenda-span 'day + org-agenda-custom-commands + '(("L" "Lola" ((tags-todo "@lola"))) + ("t" "TODOs" + ((agenda) + (alltodo))))) + (defun setup-org-mode () + (require 'org-attach) + (org-display-inline-images nil t) + (org-redisplay-inline-images) + (auto-fill-mode)) + (add-hook 'org-mode-hook #'setup-org-mode) + :general + (normal org-mode-map + "T" #'org-insert-todo-heading + "K" #'org-move-subtree-up + "J" #'org-move-subtree-down + "" #'org-return + "TAB" #'org-cycle + "SPC" leader-map + "gn" #'org-next-link + "gp" #'org-previous-link) + (org-mode-map "C-c e" #'org-preview-latex-fragment) + ("C-c l" #'org-store-link)) +#+END_SRC + +** Evil-Org +Set up evil keybindings for Org mode: +#+BEGIN_SRC emacs-lisp + (use-package evil-org + :after (evil org) + :hook (org-mode . evil-org-mode) + :config + (add-hook 'evil-org-mode-hook + (lambda () + (evil-org-set-key-theme + '(textobjects + insert + navigation + additional + shift + todo)) + (general-def 'insert org-mode-map [backspace] 'org-delete-backward-char))) + (require 'evil-org-agenda) + (evil-org-agenda-set-keys) + (general-def 'motion org-agenda-mode-map "SPC" leader-map) + (general-def 'motion org-agenda-mode-map "gs" 'org-save-all-org-buffers) + (general-def '(normal motion) evil-org-mode-map + "C-S-j" nil + "C-S-k" nil + "C-S-h" nil + "C-S-l" nil)) +#+END_SRC + +** Org-mode hydra +A helpful agenda-mode hydra: +#+BEGIN_SRC emacs-lisp + (with-eval-after-load 'org-agenda + (defhydra hydra-org-agenda (:pre (setq which-key-inhibit t) + :post (setq which-key-inhibit nil) + :hint none) + " + Org agenda (_q_uit) + + ^Clock^ ^Visit entry^ ^Date^ ^Other^ + ^-----^---- ^-----------^------------ ^----^----------- ^-----^--------- + _ci_ in _SPC_ in other window _ds_ schedule _gr_ reload + _co_ out _TAB_ & go to location _dd_ set deadline _._ go to today + _cq_ cancel _RET_ & del other windows _dt_ timestamp _gd_ go to date + _cj_ jump _o_ link _+_ do later ^^ + ^^ ^^ _-_ do earlier ^^ + ^^ ^^ ^^ ^^ + ^View^ ^Filter^ ^Headline^ ^Toggle mode^ + ^----^-------- ^------^--------------- ^--------^------- ^-----------^---- + _vd_ day _ft_ by tag _ht_ set status _tf_ follow + _vw_ week _fr_ refine by tag _hk_ kill _tl_ log + _vt_ fortnight _fc_ by category _hr_ refile _ta_ archive trees + _vm_ month _fh_ by top headline _hA_ archive _tA_ archive files + _vy_ year _fx_ by regexp _h:_ set tags _tr_ clock report + _vn_ next span _fd_ delete all filters _hp_ set priority _td_ diaries + _vp_ prev span ^^ ^^ ^^ + _vr_ reset ^^ ^^ ^^ + ^^ ^^ ^^ ^^ + " + ;; Entry + ("hA" org-agenda-archive-default) + ("hk" org-agenda-kill) + ("hp" org-agenda-priority) + ("hr" org-agenda-refile) + ("h:" org-agenda-set-tags) + ("ht" org-agenda-todo) + ;; Visit entry + ("o" link-hint-open-link :exit t) + ("" org-agenda-goto :exit t) + ("TAB" org-agenda-goto :exit t) + ("SPC" org-agenda-show-and-scroll-up) + ("RET" org-agenda-switch-to :exit t) + ;; Date + ("dt" org-agenda-date-prompt) + ("dd" org-agenda-deadline) + ("+" org-agenda-do-date-later) + ("-" org-agenda-do-date-earlier) + ("ds" org-agenda-schedule) + ;; View + ("vd" org-agenda-day-view) + ("vw" org-agenda-week-view) + ("vt" org-agenda-fortnight-view) + ("vm" org-agenda-month-view) + ("vy" org-agenda-year-view) + ("vn" org-agenda-later) + ("vp" org-agenda-earlier) + ("vr" org-agenda-reset-view) + ;; Toggle mode + ("ta" org-agenda-archives-mode) + ("tA" (org-agenda-archives-mode 'files)) + ("tr" org-agenda-clockreport-mode) + ("tf" org-agenda-follow-mode) + ("tl" org-agenda-log-mode) + ("td" org-agenda-toggle-diary) + ;; Filter + ("fc" org-agenda-filter-by-category) + ("fx" org-agenda-filter-by-regexp) + ("ft" org-agenda-filter-by-tag) + ("fr" org-agenda-filter-by-tag-refine) + ("fh" org-agenda-filter-by-top-headline) + ("fd" org-agenda-filter-remove-all) + ;; Clock + ("cq" org-agenda-clock-cancel) + ("cj" org-agenda-clock-goto :exit t) + ("ci" org-agenda-clock-in :exit t) + ("co" org-agenda-clock-out) + ;; Other + ("q" nil :exit t) + ("gd" org-agenda-goto-date) + ("." org-agenda-goto-today) + ("gr" org-agenda-redo)) + (general-def '(normal visual motion insert emacs) org-agenda-mode-map "." 'hydra-org-agenda/body)) +#+END_SRC + +** Exporting +*** HTML +Export to HTML: +#+BEGIN_SRC emacs-lisp + (use-package htmlize + :commands htmlize-buffer) +#+END_SRC + +Don't put section numbers in front of headers: +#+BEGIN_SRC emacs-lisp + (setq org-export-with-section-numbers nil) +#+END_SRC + +Disable the preamble and postamble: +#+BEGIN_SRC emacs-lisp + (setq org-html-preamble nil + org-html-postamble nil) +#+END_SRC + +Redefine org-html-src-block to wrap code blocks in
 and language class for use by highlight.js:
+#+BEGIN_SRC emacs-lisp
+  (defun org-html-src-block (src-block _contents info)
+    "Transcode a SRC-BLOCK element from Org to HTML.
+  CONTENTS holds the contents of the item.  INFO is a plist holding
+  contextual information."
+    (if (org-export-read-attribute :attr_html src-block :textarea)
+	(org-html--textarea-block src-block)
+      (let* ((lang (org-element-property :language src-block))
+	     (code (org-html-format-code src-block info))
+	     (label (let ((lbl (and (org-element-property :name src-block)
+				    (org-export-get-reference src-block info))))
+		      (if lbl (format " id=\"%s\"" lbl) "")))
+	     (klipsify  (and  (plist-get info :html-klipsify-src)
+			      (member lang '("javascript" "js"
+					     "ruby" "scheme" "clojure" "php" "html")))))
+	(if (not lang) (format "
\n%s
" label code) + (format "
\n%s%s\n
" + ;; Build caption. + (let ((caption (org-export-get-caption src-block))) + (if (not caption) "" + (let ((listing-number + (format + "%s " + (format + (org-html--translate "Listing %d:" info) + (org-export-get-ordinal + src-block info nil #'org-html--has-caption-p))))) + (format "" + listing-number + (org-trim (org-export-data caption info)))))) + ;; Contents. + (if klipsify + (format "
%s
" + lang + label + (if (string= lang "html") + " data-editor-type=\"html\"" + "") + code) + (format "
%s
" + lang lang label code))))))) +#+END_SRC + +*** Github-flavored markdown +#+BEGIN_SRC emacs-lisp + (use-package ox-gfm + :after org + :hook (org-mode . (lambda () (require 'ox-gfm)))) +#+END_SRC + +*** Jira +#+BEGIN_SRC emacs-lisp + (use-package ox-jira + :after org + :straight (:host github :repo "stig/ox-jira.el") + :hook (org-mode . (lambda () (require 'ox-jira)))) +#+END_SRC + +** org-babel +Literate programming! +#+BEGIN_SRC emacs-lisp + (add-hook 'org-mode-hook + (lambda () + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (python . t) + (shell . t) + (clojure . t) + (lisp . t) + (scheme . t) + (java . t) + (js . t) + (dot . t) + (ditaa . t) + (ledger . t) + (sql . t) + (jq . t) + (restclient . t) + (plantuml . t))))) +#+END_SRC + +Get rid of the confirmation prompt: +#+BEGIN_SRC emacs-lisp + (setq org-confirm-babel-evaluate nil) +#+END_SRC + +Display inline images after executing a source block: +#+BEGIN_SRC emacs-lisp + (add-hook 'org-babel-after-execute-hook + (lambda () + (org-display-inline-images nil t) + (org-redisplay-inline-images))) +#+END_SRC + +Enable async source block execution. Note: this execute the contents of the source block in a separate Emacs process, so blocks that rely on anything defined in init.org or the current Emacs process won't work as expected. +#+BEGIN_SRC emacs-lisp + (use-package ob-async + :straight (ob-async :host github :repo "astahlman/ob-async") + :defer t + :hook (org-mode . (lambda () (require 'ob-async)))) +#+END_SRC + +Filter out the "u" from unicode results in org tabels returned from Python source blocks: +#+BEGIN_SRC emacs-lisp + (with-eval-after-load 'ob-python + (defun org-babel-python-table-or-string (results) + "Convert RESULTS into an appropriate elisp value. + If the results look like a list or tuple, then convert them into an + Emacs-lisp table, otherwise return the results as a string." + (let ((res (org-babel-script-escape results))) + (if (listp res) + (mapcar (lambda (el) + (cond + ((eq el 'None) org-babel-python-None-to) + ((listp el) (-filter (lambda (m) (not (eq m 'u))) el)) + (t el))) + res) + res)))) +#+END_SRC + +** Images +#+BEGIN_SRC emacs-lisp + (setq org-image-actual-width nil) +#+END_SRC + +** org-scratch +It's very useful to open a new org buffer for a quick org-babel exploration. +#+BEGIN_SRC emacs-lisp + (defun org-scratch (&optional make-new) + "Switches to an org-mode buffer not associated with any file. + If called with a prefix argument, always creates a new buffer; + otherwise, switches to the existing *org-scratch* buffer if it exists." + (interactive "P") + (let ((org-scratch-buffer-name + (generate-new-buffer-name + "*org-scratch*" + (unless make-new "*org-scratch*")))) + (switch-to-buffer org-scratch-buffer-name) + (org-mode))) + + (leader-def-key "os" #'org-scratch) +#+END_SRC + +** org-gcal +Integrate Google calendar with org-mode: +#+BEGIN_SRC emacs-lisp + (defun get-calendar-file (name) + (org-directory name)) + + (defun org-gcal-sync-advice (oldfn &rest args) + (deferred:nextc (apply oldfn args) + (lambda (_) + (dolist (entry org-gcal-fetch-file-alist) + (let ((file (cdr entry))) + (with-current-buffer (find-file-noselect file) + (save-buffer))))))) + + (use-package org-gcal + :commands (org-gcal-sync + org-gcal-fetch + org-gcal-post-at-point + org-gcal-delete-at-point + org-gcal-request-token) + :config + (advice-add 'org-gcal-sync :around #'org-gcal-sync-advice) + (setq org-gcal-client-id (password-store-get "lola-org-gcal-client-id") + org-gcal-client-secret (password-store-get "lola-org-gcal-client-secret") + org-gcal-fetch-file-alist `(("jeremydormitzer@lola.com" . ,(get-calendar-file "lola-gcal.org")) + ("jeremy.dormitzer@gmail.com" . ,(get-calendar-file "personal-gcal.org")) + ("lut2o2moohg6qkdsto1qfq7th4@group.calendar.google.com" . ,(get-calendar-file "j-n-gcal.org"))) + org-gcal-notify-p nil)) + + (defun org-agenda-redo-and-fetch-gcal (&optional all) + (interactive "P") + (let ((cb (if all #'org-agenda-redo-all #'org-agenda-redo))) + (deferred:nextc (org-gcal-fetch) cb))) + + (with-eval-after-load 'org-agenda + (general-def '(normal motion) org-agenda-mode-map "gR" #'org-agenda-redo-and-fetch-gcal)) +#+END_SRC + +** Utilities +A function to get the TITLE property of the current org buffer: +#+BEGIN_SRC emacs-lisp + (defun org-get-title (&optional contents) + (let ((raw (or contents + (buffer-substring (point-min) (point-max))))) + (with-temp-buffer + (insert raw) + (car + (org-element-map + (org-element-parse-buffer) + 'keyword + (lambda (el) + (when (string-match-p "TITLE" + (org-element-property :key el)) + (org-element-property :value el)))))))) +#+END_SRC + +** org-present +An ultra-minimalist presentation mode for Org: +#+BEGIN_SRC emacs-lisp + (use-package org-present + :after org + :commands (org-present) + :config + (defun org-present-on () + (org-present-big) + (org-display-inline-images) + (org-present-hide-cursor) + (display-line-numbers-mode -1) + (org-present-read-only)) + (defun org-present-off () + (org-present-small) + (org-present-show-cursor) + (unless org-startup-with-inline-images + (org-remove-inline-images)) + (display-line-numbers-mode) + (org-present-read-write)) + (add-hook 'org-present-mode-hook #'org-present-on) + (add-hook 'org-present-mode-quit-hook #'org-present-off) + ;; Redefine org-present to call org-present-narrow after running hooks + (defun org-present () + "init." + (interactive) + (setq org-present-mode t) + (org-present-add-overlays) + (run-hooks 'org-present-mode-hook) + (org-present-narrow) + (org-present-run-after-navigate-functions)) + :init + (general-def '(normal visual motion emacs) org-present-mode-keymap + "" #'org-present-prev + "" #'org-present-next + "C-k" #'org-present-prev + "C-j" #'org-present-next + "q" #'org-present-quit)) +#+END_SRC + +** org-cliplink +Intelligently inserts an org-mode link from the clipboard. +#+BEGIN_SRC emacs-lisp + (use-package org-cliplink + :after org + :commands (org-cliplink + org-cliplink-clipboard-content) + :general + (org-mode-map "C-c C-L" #'org-cliplink)) +#+END_SRC + +** org-board +[[https://github.com/scallywag/org-board][Org-board]] is a bookmarking/archiving system built on Org mode: +#+BEGIN_SRC emacs-lisp + (use-package org-board + :after org + :config + + ;; Org-capture setup + + (defvar org-capture-bookmark-last-url nil) + (defvar org-capture-bookmark-last-title nil) + + (defun org-capture-bookmark-get-url () + (let* ((clip (org-cliplink-clipboard-content)) + (parsed (url-generic-parse-url clip))) + (if (url-type parsed) + clip + (read-string "Bookmark URL: ")))) + + (defun org-capture-bookmark-get-title (url) + (or (org-cliplink-retrieve-title-synchronously url) + (read-string "Bookmark title: "))) + + (defun bookmark-file (title) + (org-directory (format "%s.org" (org-roam--get-new-id title)))) + + (defun org-capture-bookmark-file () + (let* ((url (org-capture-bookmark-get-url)) + (title (org-capture-bookmark-get-title url))) + (setq org-capture-bookmark-last-url url) + (setq org-capture-bookmark-last-title title) + (bookmark-file title))) + + (defun org-capture-bookmark-link () + (format "[[%s][%s]]" + org-capture-bookmark-last-url + org-capture-bookmark-last-title)) + + (defun org-capture-bookmark-title () + org-capture-bookmark-last-title) + + (defun save-bookmark (url) + (save-excursion + (goto-char (point-min)) + (when (search-forward "* Bookmark" nil t) + (org-board-new url) + (wallabag-add-entry url + (cl-function + (lambda (&key data &allow-other-keys) + (let ((entry-url (format "%s/view/%s" + wallabag-base-url + (alist-get 'id data)))) + (message "Added bookmark to Wallabag: %s" entry-url)))))))) + + (defun org-capture-bookmark-after-finalize () + "Runs `org-board-new' on the captured entry. + Also saves to Wallabag." + (let ((success (not org-note-abort)) + (key (plist-get org-capture-plist :key)) + (desc (plist-get org-capture-plist :description))) + (when (and success + (equal key "b") + (equal desc "Bookmark") + org-capture-bookmark-last-url) + (save-bookmark org-capture-bookmark-last-url) + (setq org-capture-bookmark-last-url nil) + (setq org-capture-bookmark-last-title nil)))) + + (add-hook 'org-capture-prepare-finalize-hook + #'org-capture-bookmark-after-finalize) + + (add-to-list 'org-capture-templates + '("b" "Bookmark" plain + (file org-capture-bookmark-file) + "#+TITLE: %(org-capture-bookmark-title)\n\n- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n- source :: %(org-capture-bookmark-link)\n%?\n* Bookmark")) + + ;; Org-protocol setup + (defun make-org-protocol-bookmark (url title) + (with-temp-buffer + (let ((filename (bookmark-file title))) + (save-excursion + (insert (concat (format "#+TITLE: %s\n\n" title) + "- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n" + (format "- source :: [[%s][%s]]\n\n" url title) + "* Bookmark")) + (write-file filename) + (save-bookmark url) + (save-buffer))))) + + (defun bookmark-via-org-protocol (url) + (org-cliplink-retrieve-title (url-unhex-string url) #'make-org-protocol-bookmark)) + + (with-eval-after-load 'org-protocol + (add-to-list 'org-protocol-protocol-alist + '("Bookmark" + :protocol "bookmark" + :function bookmark-via-org-protocol + :kill-client t))) + + (add-to-list 'org-board-wget-switches "--recursive") + (add-to-list 'org-board-wget-switches "--level=1") + (add-to-list 'org-board-wget-switches "--span-hosts") + ;; Use w3m instead of eww to open org-board archived links + (advice-add 'org-board-open-with :around + (lambda (oldfn filename-string arg &rest args) + (cond + ((not (file-exists-p filename-string)) 1) + ((and filename-string + (or (and arg (eq org-board-default-browser 'system)) + (and (not arg) (eq org-board-default-browser 'eww)))) + (let ((filename (concat "file://" + (s-chop-prefix "file://" + filename-string)))) + (w3m filename t) + 0)) + (:else (apply oldfn filename-string arg args))))) + :general + (org-mode-map "C-c b" org-board-keymap)) +#+END_SRC + +** org-rifle +Quickly find stuff in Org files: +#+BEGIN_SRC emacs-lisp + (use-package helm-org-rifle + :after org + :commands (helm-org-rifle + helm-org-rifle-agenda-files + helm-org-rifle-current-buffer + helm-org-rifle-directories + helm-org-rifle-files + helm-org-rifle-org-directory + helm-org-rifle-occur + helm-org-rifle-occur-agenda-files + helm-org-rifle-occur-current-buffer + helm-org-rifle-occur-directories + helm-org-rifle-occur-files + helm-org-rifle-occur-org-directory) + :init + (defvar helm-org-rifle-commands-map (make-sparse-keymap)) + (general-def helm-org-rifle-commands-map + "r" #'helm-org-rifle + "a" #'helm-org-rifle-agenda-files + "b" #'helm-org-rifle-current-buffer + "d" #'helm-org-rifle-directories + "f" #'helm-org-rifle-files + "o" #'helm-org-rifle-org-directory + "R" #'helm-org-rifle-occur + "A" #'helm-org-rifle-occur-agenda-files + "B" #'helm-org-rifle-occur-current-buffer + "D" #'helm-org-rifle-occur-directories + "F" #'helm-org-rifle-occur-files + "O" #'helm-org-rifle-occur-org-directory) + (leader-def-key "or" helm-org-rifle-commands-map) + (jdormit/define-prefix "or" "org-rifle")) +#+END_SRC + +** Org Noter +[[https://github.com/weirdNox/org-noter][Org Noter]] lets me take org-mode notes on PDFs, epubs, and DocView files: +#+BEGIN_SRC emacs-lisp + (use-package org-noter + :after org + :commands org-noter + :general + (normal org-noter-doc-mode-map + "i" #'org-noter-insert-note + "q" #'org-noter-kill-session + "C-M-n" #'org-noter-sync-next-note + "C-M-p" #'org-noter-sync-prev-note + "M-." #'org-noter-sync-current-page-or-chapter + "M-i" #'org-noter-insert-precise-note + "M-n" #'org-noter-sync-next-page-or-chapter + "M-p" #'org-noter-sync-prev-page-or-chapter + "C-M-." #'org-noter-sync-current-note)) +#+END_SRC + +** Org Roam +[[https://org-roam.readthedocs.io/en/develop/][Org-roam]] is another backlink package for org-mode: +#+BEGIN_SRC emacs-lisp + (use-package org-roam + :after org + :straight (:host github :repo "jethrokuan/org-roam") + :commands + (org-roam + org-roam-today + org-roam-find-file + org-roam-insert + org-roam-show-graph + org-roam--get-new-id) + :hook + (org-mode . org-roam-mode) + :custom + (org-roam-directory (org-directory)) + :init + (leader-def-key "fo" #'org-roam-find-file) + (leader-def-key "of" #'org-roam-find-file) + (defvar org-roam-map (make-sparse-keymap)) + (leader-def-key "on" org-roam-map) + (jdormit/define-prefix "on" "org-roam") + (which-key-add-key-based-replacements "C-c n" "org-roam") + (which-key-add-major-mode-key-based-replacements + 'org-mode "gn" "org-roam") + :config + (add-hook 'org-roam-backlinks-mode-hook #'olivetti-mode) + :general + (org-roam-map "l" #'org-roam + "t" #'org-roam-today + "f" #'org-roam-find-file + "i" #'org-roam-insert + "g" #'org-roam-show-graph) + (normal org-mode-map + "gr" org-roam-map) + (normal org-roam-backlinks-mode-map + "" #'org-open-at-point + "q" #'quit-window) + ("C-c n" org-roam-map)) +#+END_SRC + +** org-journal +[[https://github.com/bastibe/org-journal][org-journal]] is a package that provides some convenience functions +around keeping a daily Org-mode journal. I also set it up so it plays +nice with Org-roam: +#+BEGIN_SRC emacs-lisp + (use-package org-journal + :after org + :commands (org-journal-today + org-journal-new-entry) + :init + (defun org-journal-file-header-func (time) + (format "#+TITLE: %s" + (format-time-string "%Y-%m-%d" time))) + (defun org-journal-today () + (interactive) + (org-journal-new-entry t)) + (defun org-journal-capture-func () + (org-journal-new-entry t) + (goto-char (point-min)) + ;; Account for the #+TITLE + (forward-line)) + (add-to-list 'org-capture-templates + '("j" "Journal entry" entry (function org-journal-capture-func) + "* %(format-time-string org-journal-time-format)\n%?")) + (jdormit/define-prefix "oj" "org-journal") + (leader-def-key "ojn" #'org-journal-new-entry) + (leader-def-key "ojt" #'org-journal-today) + :custom + (org-journal-file-type 'daily) + (org-journal-dir (org-directory)) + (org-journal-file-format "%Y-%m-%d.org") + (org-journal-file-header 'org-journal-file-header-func) + (org-journal-carryover-items "") + :general + (org-roam-map "t" 'org-journal-today)) +#+END_SRC + +** org-super-agenda +#+BEGIN_SRC emacs-lisp + (use-package org-super-agenda + :after org + :init + (setq org-super-agenda-groups + '((:name "In progress" + :todo "IN PROGRESS") + (:name "Lola" + :tag "@lola") + (:name "unifyDB" + :tag "@unifydb") + (:name "Personal" + :tag "@personal"))) + (org-super-agenda-mode) + :config + (setq org-super-agenda-header-map (make-sparse-keymap))) +#+END_SRC + +** Org-Jira +Jira in Emacs: +#+BEGIN_SRC emacs-lisp + (use-package org-jira + :after org + :init + (setq jiralib-url "https://lola.atlassian.net" + org-jira-working-dir (org-directory "jira") + org-jira-jira-status-to-org-keyword-alist '(("To Do" . "TODO") + ("Blocked" . "BLOCKED") + ("In Progress" . "IN PROGRESS") + ("Code Review" . "IN PROGRESS") + ("Awaiting Release" . "IN PROGRESS") + ("Done" . "DONE") + ("Won't Fix" . "CANCELLED"))) + (add-to-list 'org-agenda-files org-jira-working-dir) + (add-to-list 'org-super-agenda-groups + `(:name "Jira" :file-path ,org-jira-working-dir) + t)) +#+END_SRC + +* Doom themes +#+BEGIN_SRC emacs-lisp + (use-package doom-themes) + + (use-package doom-modeline) + (doom-modeline-mode 1) +#+END_SRC + +* Ewal theme +#+BEGIN_SRC emacs-lisp + (use-package doom-ewal-themes + :straight (doom-ewal-themes + :host github + :repo "jdormit/doom-ewal-themes" + :files ("themes" :defaults))) +#+END_SRC + +* Customization File +I don't want anything to write to my init.el, so save customizations in a separate file: +#+BEGIN_SRC emacs-lisp + (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) + (load custom-file t) +#+END_SRC + +* Path +`exec-path-from-shell` uses Bash to set MANPATH, PATH, and exec-path from those defined in the user's shell config. This won't work on Windows. +#+BEGIN_SRC emacs-lisp + (use-package exec-path-from-shell + :if (memq window-system '(mac ns x)) + :config + (setq exec-path-from-shell-variables '("PATH" "MANPATH" "LEDGER_FILE" "LOLA_HOME" + "MODELS_HOME" "LOLA_TRAVEL_SERVICE_HOME" "WORKON_HOME" + "PIPENV_VERBOSITY" "PIPENV_DONT_LOAD_ENV" + "PIPENV_MAX_DEPTH" "PYENV_ROOT") + exec-path-from-shell-check-startup-files nil) + (exec-path-from-shell-initialize)) +#+END_SRC + +* Autocompletion +There seems to be [[https://github.com/company-mode/company-mode/issues/68][some contention]] about whether autocomplete or company are better autocomplete packages. I'm going with company for now because the maintainer seems nicer... +#+BEGIN_SRC emacs-lisp + (use-package company + :commands (company-complete + company-mode) + :hook ((prog-mode . company-mode)) + :config + (setq company-idle-delay 0.3)) + + (general-def "C-M-i" #'company-complete) + (general-def "M-" #'company-complete) +#+END_SRC * Multiple cursors #+BEGIN_SRC emacs-lisp @@ -329,13 +1105,6 @@ A framework for creating Magit-style popups: :commands (define-transient-command)) #+END_SRC -* Hydra -[[https://github.com/abo-abo/hydra][Hydra]]s are convenient keybinding menus. -#+BEGIN_SRC emacs-lisp - (use-package hydra - :defer t) -#+END_SRC - * Magit Magit is objectively the best Git interface. #+BEGIN_SRC emacs-lisp @@ -400,6 +1169,7 @@ Evil keybindings for magit! Open files in Git forges (GitHub, GitLab, etc.): #+BEGIN_SRC emacs-lisp (use-package git-link + :commands (git-link) :init (defun git-link-copy () (interactive) @@ -832,22 +1602,6 @@ Whenever the buffer changes, look up the major-mode to see if there is any code '((undo discard-info))) #+END_SRC -* Syncthing -I put lots of stuff in Dropbox, but the actual folder location differs on my different computers. This function resolves to the Dropbox directory: -#+BEGIN_SRC emacs-lisp - (defcustom syncthing-path (expand-file-name "~/Sync") - "The absolute path to the Syncthing directory" - :type 'directory) - - (defun syncthing-directory (&optional path) - (f-join syncthing-path (s-chop-prefix (f-path-separator) (or path "")))) -#+END_SRC - -Load up libraries from Dropbox, if there are any: -#+BEGIN_SRC emacs-lisp - (add-to-list 'load-path (syncthing-directory "site-lisp")) -#+END_SRC - * Init File A function to reload my init file. It reloads the major mode after the init file is loaded to rebind keymappings. #+BEGIN_SRC emacs-lisp @@ -1511,532 +2265,6 @@ The JSON multitool. :commands (jq-mode jq-interactively)) #+END_SRC -* Org Mode -Notes, agenda, calendar, blogging, journaling, etc. - -A function to get my org directory: -#+BEGIN_SRC emacs-lisp - (defun org-directory (&optional path) - "Returns the directory for my org notes, appends PATH if given" - (f-join (syncthing-directory "org") (s-chop-prefix (f-path-separator) (or path "")))) -#+END_SRC - - -#+BEGIN_SRC emacs-lisp - (jdormit/define-prefix "o" "org") - (leader-def-key "oa" 'org-agenda) - (leader-def-key "oc" 'org-capture) - (setq org-src-fontify-natively t - org-ellipsis " ▼" - org-directory (org-directory) - org-link-elisp-confirm-function 'y-or-n-p - org-startup-with-inline-images t) - (add-hook 'org-mode-hook #'auto-fill-mode) -#+END_SRC - -Use RET to follow links: -#+BEGIN_SRC emacs-lisp - (setq org-return-follows-link t) -#+END_SRC - -Always show inline images: -#+BEGIN_SRC emacs-lisp - (add-hook 'org-mode-hook - (lambda () - (org-display-inline-images nil t) - (org-redisplay-inline-images))) -#+END_SRC - -Include a timestamp when TODO entries are closed: -#+BEGIN_SRC emacs-lisp - (setq org-log-done 'time) -#+END_SRC - -Tell Emacs how to open file links of various types: -#+BEGIN_SRC emacs-lisp - (with-eval-after-load 'org - (setq org-file-apps '(("log" . emacs) - (auto-mode . emacs) - (directory . emacs) - ("\\.pdf\\'" . emacs) - ("\\.mm\\'" . default) - ("\\.x?html?\\'" . default)))) -#+END_SRC - -A function to add ids to every heading in an Org file: -#+BEGIN_SRC emacs-lisp - (defun org-get-create-ids-all () - (interactive) - (save-excursion - (goto-char (point-max)) - (while (outline-previous-heading) - (org-id-get-create)))) -#+END_SRC - -** Hook -#+BEGIN_SRC emacs-lisp - (defun setup-org-mode () - (load-library "org-attach")) - - (add-hook 'org-mode-hook #'setup-org-mode) -#+END_SRC - - -** Agenda files -#+BEGIN_SRC emacs-lisp - (defun agenda-files (&optional file) - (let ((agenda-dir (org-directory))) - (if file - (concat (file-name-as-directory agenda-dir) file) - agenda-dir))) - - (setq org-agenda-files `(,(agenda-files))) -#+END_SRC - -** Capture templates -#+BEGIN_SRC emacs-lisp - (setq org-capture-templates - `(("L" "Lola task" entry - (file+headline ,(agenda-files "todo.org") "Lola") - "* TODO %i%?") - ("p" "Personal task" entry - (file+headline ,(agenda-files "todo.org") "Personal") - "* TODO %i%?") - ("n" "Note" entry - (file ,(agenda-files "notes.org")) - "* %^{Description}\n%i%?") - ("l" "Log" entry - (file ,(agenda-files "log.org")) - "* %<%Y-%m-%d %H:%M:%S>%?"))) -#+END_SRC - -** Refile targets -#+BEGIN_SRC emacs-lisp - (setq org-refile-use-outline-path 'file - org-refile-targets `((org-agenda-files :level . 0) - (,(agenda-files "notes.org") :level . 1) - (,(agenda-files "todo.org") :level . 1))) -#+END_SRC - -** Todo keywords -#+BEGIN_SRC emacs-lisp - (setq org-todo-keywords - '((sequence "TODO(t)" "IN PROGRESS(i)" "BLOCKED(b)" "|" "DONE(d)" "CANCELLED(c)"))) -#+END_SRC - -** Agenda views -#+BEGIN_SRC emacs-lisp - (setq org-agenda-todo-ignore-scheduled 'future - org-agenda-tags-todo-honor-ignore-options t - org-agenda-span 'day - org-agenda-custom-commands - '(("L" "Lola" ((tags-todo "@lola"))) - ("t" "TODOs" - ((agenda) - (alltodo))))) -#+END_SRC - -** Keybindings -#+BEGIN_SRC emacs-lisp - (general-def 'normal org-mode-map "T" #'org-insert-todo-heading) - (general-def 'normal org-mode-map "K" #'org-move-subtree-up) - (general-def 'normal org-mode-map "J" #'org-move-subtree-down) - (general-def 'normal org-mode-map "" #'org-return) - (general-def 'normal org-mode-map "TAB" #'org-cycle) - (general-def 'normal org-mode-map "SPC" leader-map) - (general-def '(normal motion visual) org-mode-map "gn" #'org-next-link) - (general-def '(normal motion visual) org-mode-map "gp" #'org-previous-link) - (general-def org-mode-map "C-c e" #'org-preview-latex-fragment) - (general-def "C-c l" #'org-store-link) -#+END_SRC - -Set up evil keybindings: -#+BEGIN_SRC emacs-lisp - (use-package evil-org - :after (evil org) - :hook (org-mode . evil-org-mode) - :config - (add-hook 'evil-org-mode-hook - (lambda () - (evil-org-set-key-theme - '(textobjects - insert - navigation - additional - shift - todo)) - (general-def 'insert org-mode-map [backspace] 'org-delete-backward-char))) - (require 'evil-org-agenda) - (evil-org-agenda-set-keys) - (general-def 'motion org-agenda-mode-map "SPC" leader-map) - (general-def 'motion org-agenda-mode-map "gs" 'org-save-all-org-buffers) - (general-def '(normal motion) evil-org-mode-map - "C-S-j" nil - "C-S-k" nil - "C-S-h" nil - "C-S-l" nil)) -#+END_SRC - -And a global keybinding to open an org file: -#+BEGIN_SRC emacs-lisp - (defun find-org-file (file) - (interactive - (list (completing-read - "Find org file: " - (directory-files (agenda-files) t)))) - (find-file file)) - - (leader-def-key "fo" #'find-org-file) -#+END_SRC - -Finally, add a helpful agenda-mode hydra: -#+BEGIN_SRC emacs-lisp - (with-eval-after-load 'org-agenda - (defhydra hydra-org-agenda (:pre (setq which-key-inhibit t) - :post (setq which-key-inhibit nil) - :hint none) - " - Org agenda (_q_uit) - - ^Clock^ ^Visit entry^ ^Date^ ^Other^ - ^-----^---- ^-----------^------------ ^----^----------- ^-----^--------- - _ci_ in _SPC_ in other window _ds_ schedule _gr_ reload - _co_ out _TAB_ & go to location _dd_ set deadline _._ go to today - _cq_ cancel _RET_ & del other windows _dt_ timestamp _gd_ go to date - _cj_ jump _o_ link _+_ do later ^^ - ^^ ^^ _-_ do earlier ^^ - ^^ ^^ ^^ ^^ - ^View^ ^Filter^ ^Headline^ ^Toggle mode^ - ^----^-------- ^------^--------------- ^--------^------- ^-----------^---- - _vd_ day _ft_ by tag _ht_ set status _tf_ follow - _vw_ week _fr_ refine by tag _hk_ kill _tl_ log - _vt_ fortnight _fc_ by category _hr_ refile _ta_ archive trees - _vm_ month _fh_ by top headline _hA_ archive _tA_ archive files - _vy_ year _fx_ by regexp _h:_ set tags _tr_ clock report - _vn_ next span _fd_ delete all filters _hp_ set priority _td_ diaries - _vp_ prev span ^^ ^^ ^^ - _vr_ reset ^^ ^^ ^^ - ^^ ^^ ^^ ^^ - " - ;; Entry - ("hA" org-agenda-archive-default) - ("hk" org-agenda-kill) - ("hp" org-agenda-priority) - ("hr" org-agenda-refile) - ("h:" org-agenda-set-tags) - ("ht" org-agenda-todo) - ;; Visit entry - ("o" link-hint-open-link :exit t) - ("" org-agenda-goto :exit t) - ("TAB" org-agenda-goto :exit t) - ("SPC" org-agenda-show-and-scroll-up) - ("RET" org-agenda-switch-to :exit t) - ;; Date - ("dt" org-agenda-date-prompt) - ("dd" org-agenda-deadline) - ("+" org-agenda-do-date-later) - ("-" org-agenda-do-date-earlier) - ("ds" org-agenda-schedule) - ;; View - ("vd" org-agenda-day-view) - ("vw" org-agenda-week-view) - ("vt" org-agenda-fortnight-view) - ("vm" org-agenda-month-view) - ("vy" org-agenda-year-view) - ("vn" org-agenda-later) - ("vp" org-agenda-earlier) - ("vr" org-agenda-reset-view) - ;; Toggle mode - ("ta" org-agenda-archives-mode) - ("tA" (org-agenda-archives-mode 'files)) - ("tr" org-agenda-clockreport-mode) - ("tf" org-agenda-follow-mode) - ("tl" org-agenda-log-mode) - ("td" org-agenda-toggle-diary) - ;; Filter - ("fc" org-agenda-filter-by-category) - ("fx" org-agenda-filter-by-regexp) - ("ft" org-agenda-filter-by-tag) - ("fr" org-agenda-filter-by-tag-refine) - ("fh" org-agenda-filter-by-top-headline) - ("fd" org-agenda-filter-remove-all) - ;; Clock - ("cq" org-agenda-clock-cancel) - ("cj" org-agenda-clock-goto :exit t) - ("ci" org-agenda-clock-in :exit t) - ("co" org-agenda-clock-out) - ;; Other - ("q" nil :exit t) - ("gd" org-agenda-goto-date) - ("." org-agenda-goto-today) - ("gr" org-agenda-redo)) - (general-def '(normal visual motion insert emacs) org-agenda-mode-map "." 'hydra-org-agenda/body)) -#+END_SRC - -** Exporting -*** HTML -Export to HTML: -#+BEGIN_SRC emacs-lisp - (use-package htmlize - :commands htmlize-buffer) -#+END_SRC - -Don't put section numbers in front of headers: -#+BEGIN_SRC emacs-lisp - (setq org-export-with-section-numbers nil) -#+END_SRC - -Disable the preamble and postamble: -#+BEGIN_SRC emacs-lisp - (setq org-html-preamble nil - org-html-postamble nil) -#+END_SRC - -Redefine org-html-src-block to wrap code blocks in
 and language class for use by highlight.js:
-#+BEGIN_SRC emacs-lisp
-  (defun org-html-src-block (src-block _contents info)
-    "Transcode a SRC-BLOCK element from Org to HTML.
-  CONTENTS holds the contents of the item.  INFO is a plist holding
-  contextual information."
-    (if (org-export-read-attribute :attr_html src-block :textarea)
-	(org-html--textarea-block src-block)
-      (let* ((lang (org-element-property :language src-block))
-	     (code (org-html-format-code src-block info))
-	     (label (let ((lbl (and (org-element-property :name src-block)
-				    (org-export-get-reference src-block info))))
-		      (if lbl (format " id=\"%s\"" lbl) "")))
-	     (klipsify  (and  (plist-get info :html-klipsify-src)
-			      (member lang '("javascript" "js"
-					     "ruby" "scheme" "clojure" "php" "html")))))
-	(if (not lang) (format "
\n%s
" label code) - (format "
\n%s%s\n
" - ;; Build caption. - (let ((caption (org-export-get-caption src-block))) - (if (not caption) "" - (let ((listing-number - (format - "%s " - (format - (org-html--translate "Listing %d:" info) - (org-export-get-ordinal - src-block info nil #'org-html--has-caption-p))))) - (format "" - listing-number - (org-trim (org-export-data caption info)))))) - ;; Contents. - (if klipsify - (format "
%s
" - lang - label - (if (string= lang "html") - " data-editor-type=\"html\"" - "") - code) - (format "
%s
" - lang lang label code))))))) -#+END_SRC - -*** Github-flavored markdown -#+BEGIN_SRC emacs-lisp - (use-package ox-gfm - :defer t - :init - (with-eval-after-load 'org - (require 'ox-gfm))) -#+END_SRC - -*** Jira -#+BEGIN_SRC emacs-lisp - (use-package ox-jira - :defer t - :straight (:host github :repo "stig/ox-jira.el") - :init - (with-eval-after-load 'org - (require 'ox-jira))) -#+END_SRC - -** org-babel -Literate programming! -#+BEGIN_SRC emacs-lisp - (add-hook 'org-mode-hook - (lambda () - (org-babel-do-load-languages - 'org-babel-load-languages - '((emacs-lisp . t) - (python . t) - (shell . t) - (clojure . t) - (lisp . t) - (scheme . t) - (java . t) - (js . t) - (dot . t) - (ditaa . t) - (ledger . t) - (sql . t) - (jq . t) - (restclient . t) - (plantuml . t))))) -#+END_SRC - -Get rid of the confirmation prompt: -#+BEGIN_SRC emacs-lisp - (setq org-confirm-babel-evaluate nil) -#+END_SRC - -Display inline images after executing a source block: -#+BEGIN_SRC emacs-lisp - (add-hook 'org-babel-after-execute-hook - (lambda () - (org-display-inline-images nil t) - (org-redisplay-inline-images))) -#+END_SRC - -Enable async source block execution. Note: this execute the contents of the source block in a separate Emacs process, so blocks that rely on anything defined in init.org or the current Emacs process won't work as expected. -#+BEGIN_SRC emacs-lisp - (use-package ob-async - :straight (ob-async :host github :repo "astahlman/ob-async") - :defer t - :hook (org-mode . (lambda () (require 'ob-async)))) -#+END_SRC - -Filter out the "u" from unicode results in org tabels returned from Python source blocks: -#+BEGIN_SRC emacs-lisp - (with-eval-after-load 'ob-python - (defun org-babel-python-table-or-string (results) - "Convert RESULTS into an appropriate elisp value. - If the results look like a list or tuple, then convert them into an - Emacs-lisp table, otherwise return the results as a string." - (let ((res (org-babel-script-escape results))) - (if (listp res) - (mapcar (lambda (el) - (cond - ((eq el 'None) org-babel-python-None-to) - ((listp el) (-filter (lambda (m) (not (eq m 'u))) el)) - (t el))) - res) - res)))) -#+END_SRC - -** Images -#+BEGIN_SRC emacs-lisp - (setq org-image-actual-width nil) -#+END_SRC - -** org-scratch -It's very useful to open a new org buffer for a quick org-babel exploration. -#+BEGIN_SRC emacs-lisp - (defun org-scratch (&optional make-new) - "Switches to an org-mode buffer not associated with any file. - If called with a prefix argument, always creates a new buffer; - otherwise, switches to the existing *org-scratch* buffer if it exists." - (interactive "P") - (let ((org-scratch-buffer-name - (generate-new-buffer-name - "*org-scratch*" - (unless make-new "*org-scratch*")))) - (switch-to-buffer org-scratch-buffer-name) - (org-mode))) - - (leader-def-key "os" #'org-scratch) -#+END_SRC - -** org-gcal -Integrate Google calendar with org-mode: -#+BEGIN_SRC emacs-lisp - (defun get-calendar-file (name) - (org-directory name)) - - (defun org-gcal-sync-advice (oldfn &rest args) - (deferred:nextc (apply oldfn args) - (lambda (_) - (dolist (entry org-gcal-fetch-file-alist) - (let ((file (cdr entry))) - (with-current-buffer (find-file-noselect file) - (save-buffer))))))) - - (use-package org-gcal - :hook ((emacs-startup . org-gcal-fetch)) - :commands (org-gcal-sync - org-gcal-fetch - org-gcal-post-at-point - org-gcal-delete-at-point - org-gcal-request-token) - :config - (advice-add 'org-gcal-sync :around #'org-gcal-sync-advice) - (setq org-gcal-client-id (password-store-get "lola-org-gcal-client-id") - org-gcal-client-secret (password-store-get "lola-org-gcal-client-secret") - org-gcal-fetch-file-alist `(("jeremydormitzer@lola.com" . ,(get-calendar-file "lola-gcal.org")) - ("jeremy.dormitzer@gmail.com" . ,(get-calendar-file "personal-gcal.org")) - ("lut2o2moohg6qkdsto1qfq7th4@group.calendar.google.com" . ,(get-calendar-file "j-n-gcal.org"))) - org-gcal-notify-p nil)) - - (defun org-agenda-redo-and-fetch-gcal (&optional all) - (interactive "P") - (let ((cb (if all #'org-agenda-redo-all #'org-agenda-redo))) - (deferred:nextc (org-gcal-fetch) cb))) - - (with-eval-after-load 'org-agenda - (general-def '(normal motion) org-agenda-mode-map "gR" #'org-agenda-redo-and-fetch-gcal)) -#+END_SRC - -** Utilities -A function to get the TITLE property of the current org buffer: -#+BEGIN_SRC emacs-lisp - (defun org-get-title (&optional contents) - (let ((raw (or contents - (buffer-substring (point-min) (point-max))))) - (with-temp-buffer - (insert raw) - (car - (org-element-map - (org-element-parse-buffer) - 'keyword - (lambda (el) - (when (string-match-p "TITLE" - (org-element-property :key el)) - (org-element-property :value el)))))))) -#+END_SRC - -* org-present -An ultra-minimalist presentation mode for Org: -#+BEGIN_SRC emacs-lisp - (use-package org-present - :commands (org-present) - :config - (defun org-present-on () - (org-present-big) - (org-display-inline-images) - (org-present-hide-cursor) - (display-line-numbers-mode -1) - (org-present-read-only)) - (defun org-present-off () - (org-present-small) - (org-present-show-cursor) - (unless org-startup-with-inline-images - (org-remove-inline-images)) - (display-line-numbers-mode) - (org-present-read-write)) - (add-hook 'org-present-mode-hook #'org-present-on) - (add-hook 'org-present-mode-quit-hook #'org-present-off) - ;; Redefine org-present to call org-present-narrow after running hooks - (defun org-present () - "init." - (interactive) - (setq org-present-mode t) - (org-present-add-overlays) - (run-hooks 'org-present-mode-hook) - (org-present-narrow) - (org-present-run-after-navigate-functions)) - :init - (general-def '(normal visual motion emacs) org-present-mode-keymap - "" #'org-present-prev - "" #'org-present-next - "C-k" #'org-present-prev - "C-j" #'org-present-next - "q" #'org-present-quit)) -#+END_SRC - * link-hint A very helpful package that provides jump-to-link functionality: #+BEGIN_SRC emacs-lisp @@ -2049,314 +2277,14 @@ A very helpful package that provides jump-to-link functionality: (leader-def-key "olc" #'link-hint-copy-link)) #+END_SRC -* org-cliplink -Intelligently inserts an org-mode link from the clipboard. -#+BEGIN_SRC emacs-lisp - (use-package org-cliplink - :commands (org-cliplink - org-cliplink-clipboard-content) - :general - (org-mode-map "C-c C-S-l" #'org-cliplink)) -#+END_SRC - -* org-board -[[https://github.com/scallywag/org-board][Org-board]] is a bookmarking/archiving system built on Org mode: -#+BEGIN_SRC emacs-lisp - (use-package org-board - :defer t - :after org - :init - - ;; Org-capture setup - - (defvar org-capture-bookmark-last-url nil) - (defvar org-capture-bookmark-last-title nil) - - (defun org-capture-bookmark-get-url () - (let* ((clip (org-cliplink-clipboard-content)) - (parsed (url-generic-parse-url clip))) - (if (url-type parsed) - clip - (read-string "Bookmark URL: ")))) - - (defun org-capture-bookmark-get-title (url) - (or (org-cliplink-retrieve-title-synchronously url) - (read-string "Bookmark title: "))) - - (defun bookmark-file (title) - (org-directory (format "%s.org" (org-roam--get-new-id title)))) - - (defun org-capture-bookmark-file () - (let* ((url (org-capture-bookmark-get-url)) - (title (org-capture-bookmark-get-title url))) - (setq org-capture-bookmark-last-url url) - (setq org-capture-bookmark-last-title title) - (bookmark-file title))) - - (defun org-capture-bookmark-link () - (format "[[%s][%s]]" - org-capture-bookmark-last-url - org-capture-bookmark-last-title)) - - (defun org-capture-bookmark-title () - org-capture-bookmark-last-title) - - (defun save-bookmark (url) - (save-excursion - (goto-char (point-min)) - (when (search-forward "* Bookmark" nil t) - (org-board-new url) - (wallabag-add-entry url - (cl-function - (lambda (&key data &allow-other-keys) - (let ((entry-url (format "%s/view/%s" - wallabag-base-url - (alist-get 'id data)))) - (message "Added bookmark to Wallabag: %s" entry-url)))))))) - - (defun org-capture-bookmark-after-finalize () - "Runs `org-board-new' on the captured entry. - Also saves to Wallabag." - (let ((success (not org-note-abort)) - (key (plist-get org-capture-plist :key)) - (desc (plist-get org-capture-plist :description))) - (when (and success - (equal key "b") - (equal desc "Bookmark") - org-capture-bookmark-last-url) - (save-bookmark org-capture-bookmark-last-url) - (setq org-capture-bookmark-last-url nil) - (setq org-capture-bookmark-last-title nil)))) - - (add-hook 'org-capture-prepare-finalize-hook - #'org-capture-bookmark-after-finalize) - - (add-to-list 'org-capture-templates - '("b" "Bookmark" plain - (file org-capture-bookmark-file) - "#+TITLE: %(org-capture-bookmark-title)\n\n- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n- source :: %(org-capture-bookmark-link)\n%?\n* Bookmark")) - - ;; Org-protocol setup - (defun make-org-protocol-bookmark (url title) - (with-temp-buffer - (let ((filename (bookmark-file title))) - (save-excursion - (insert (concat (format "#+TITLE: %s\n\n" title) - "- tags :: [[file:deft/bookmarks.org][Bookmarks]]\n" - (format "- source :: [[%s][%s]]\n\n" url title) - "* Bookmark")) - (write-file filename) - (save-bookmark url) - (save-buffer))))) - - (defun bookmark-via-org-protocol (url) - (org-cliplink-retrieve-title (url-unhex-string url) #'make-org-protocol-bookmark)) - - (add-to-list 'org-protocol-protocol-alist - '("Bookmark" - :protocol "bookmark" - :function bookmark-via-org-protocol - :kill-client t)) - - :config - (add-to-list 'org-board-wget-switches "--recursive") - (add-to-list 'org-board-wget-switches "--level=1") - (add-to-list 'org-board-wget-switches "--span-hosts") - ;; Use w3m instead of eww to open org-board archived links - (advice-add 'org-board-open-with :around - (lambda (oldfn filename-string arg &rest args) - (cond - ((not (file-exists-p filename-string)) 1) - ((and filename-string - (or (and arg (eq org-board-default-browser 'system)) - (and (not arg) (eq org-board-default-browser 'eww)))) - (let ((filename (concat "file://" - (s-chop-prefix "file://" - filename-string)))) - (w3m filename t) - 0)) - (:else (apply oldfn filename-string arg args))))) - :general - (org-mode-map "C-c b" org-board-keymap)) -#+END_SRC - -* org-rifle -Quickly find stuff in Org files: -#+BEGIN_SRC emacs-lisp - (use-package helm-org-rifle - :defer t - :init - (defvar helm-org-rifle-commands-map (make-sparse-keymap)) - (general-def helm-org-rifle-commands-map - "r" #'helm-org-rifle - "a" #'helm-org-rifle-agenda-files - "b" #'helm-org-rifle-current-buffer - "d" #'helm-org-rifle-directories - "f" #'helm-org-rifle-files - "o" #'helm-org-rifle-org-directory - "R" #'helm-org-rifle-occur - "A" #'helm-org-rifle-occur-agenda-files - "B" #'helm-org-rifle-occur-current-buffer - "D" #'helm-org-rifle-occur-directories - "F" #'helm-org-rifle-occur-files - "O" #'helm-org-rifle-occur-org-directory) - (leader-def-key "or" helm-org-rifle-commands-map) - (jdormit/define-prefix "or" "org-rifle")) -#+END_SRC - -* Org Noter -[[https://github.com/weirdNox/org-noter][Org Noter]] lets me take org-mode notes on PDFs, epubs, and DocView files: -#+BEGIN_SRC emacs-lisp - (use-package org-noter - :commands org-noter - :general - ((normal visual motion) org-noter-doc-mode-map "i" #'org-noter-insert-note) - ((normal visual motion) org-noter-doc-mode-map "q" #'org-noter-kill-session) - ((normal visual motion) org-noter-doc-mode-map "C-M-n" #'org-noter-sync-next-note) - ((normal visual motion) org-noter-doc-mode-map "C-M-p" #'org-noter-sync-prev-note) - ((normal visual motion) org-noter-doc-mode-map "M-." #'org-noter-sync-current-page-or-chapter) - ((normal visual motion) org-noter-doc-mode-map "M-i" #'org-noter-insert-precise-note) - ((normal visual motion) org-noter-doc-mode-map "M-n" #'org-noter-sync-next-page-or-chapter) - ((normal visual motion) org-noter-doc-mode-map "M-p" #'org-noter-sync-prev-page-or-chapter) - ((normal visual motion) org-noter-doc-mode-map "C-M-." #'org-noter-sync-current-note)) -#+END_SRC - -* Org Roam -[[https://org-roam.readthedocs.io/en/develop/][Org-roam]] is another backlink package for org-mode: -#+BEGIN_SRC emacs-lisp - (use-package org-roam - :after org - :straight (:host github :repo "jethrokuan/org-roam") - :commands - (org-roam - org-roam-today - org-roam-find-file - org-roam-insert - org-roam-show-graph - org-roam--get-new-id) - :hook - (after-init . org-roam-mode) - :custom - (org-roam-directory (org-directory)) - :init - (leader-def-key "fo" #'org-roam-find-file) - (leader-def-key "of" #'org-roam-find-file) - (defvar org-roam-map (make-sparse-keymap)) - (leader-def-key "on" org-roam-map) - (jdormit/define-prefix "on" "org-roam") - (which-key-add-key-based-replacements "C-c n" "org-roam") - (which-key-add-major-mode-key-based-replacements - 'org-mode "gn" "org-roam") - :config - (add-hook 'org-roam-backlinks-mode-hook #'olivetti-mode) - :general - (org-roam-map "l" #'org-roam) - (org-roam-map "t" #'org-roam-today) - (org-roam-map "f" #'org-roam-find-file) - (org-roam-map "i" #'org-roam-insert) - (org-roam-map "g" #'org-roam-show-graph) - ((normal motion visual) org-mode-map "gr" org-roam-map) - ((normal motion visual) org-roam-backlinks-mode-map "" #'org-open-at-point) - ((normal motion visual emacs) org-roam-backlinks-mode-map "q" #'quit-window) - ("C-c n" org-roam-map)) -#+END_SRC - -* org-journal -[[https://github.com/bastibe/org-journal][org-journal]] is a package that provides some convenience functions -around keeping a daily Org-mode journal. I also set it up so it plays -nice with Org-roam: -#+BEGIN_SRC emacs-lisp - (use-package org-journal - :defer t - :init - (defun org-journal-file-header-func (time) - (format "#+TITLE: %s" - (format-time-string "%Y-%m-%d" time))) - (defun org-journal-today () - (interactive) - (org-journal-new-entry t)) - (defun org-journal-capture-func () - (org-journal-new-entry t) - (goto-char (point-min)) - ;; Account for the #+TITLE - (forward-line)) - (add-to-list 'org-capture-templates - '("j" "Journal entry" entry (function org-journal-capture-func) - "* %(format-time-string org-journal-time-format)\n%?")) - (jdormit/define-prefix "oj" "org-journal") - (leader-def-key "ojn" #'org-journal-new-entry) - (leader-def-key "ojt" #'org-journal-today) - :custom - (org-journal-file-type 'daily) - (org-journal-dir (org-directory)) - (org-journal-file-format "%Y-%m-%d.org") - (org-journal-file-header 'org-journal-file-header-func) - (org-journal-carryover-items "") - :general - (org-roam-map "t" 'org-journal-today)) -#+END_SRC - -* org-super-agenda -#+BEGIN_SRC emacs-lisp - (use-package org-super-agenda - :init - (setq org-super-agenda-groups - '((:name "In progress" - :todo "IN PROGRESS") - (:name "Lola" - :tag "@lola") - (:name "unifyDB" - :tag "@unifydb") - (:name "Personal" - :tag "@personal"))) - (org-super-agenda-mode) - :config - (setq org-super-agenda-header-map (make-sparse-keymap))) -#+END_SRC - * Projectile #+BEGIN_SRC emacs-lisp (use-package projectile - :commands (projectile-find-file - projectile-grep - projectile-switch-project - projectile-project-root) - :init - (defhydra hydra-projectile (:color teal :hint nil) " - PROJECTILE: %(projectile-project-root) - - Find File Search/Tags Buffers Cache - ------------------------------------------------------------------------------------------ - _s-f_: file _a_: ag _i_: Ibuffer _c_: cache clear _ff_: file dwim - _g_: update gtags _b_: switch to buffer _x_: remove known project - _fd_: file curr dir _o_: multi-occur _s-k_: Kill all buffers _X_: - cleanup non-existing _r_: recent file ^^^^_z_: cache current _d_: - dir - - " - ("a" projectile-ag) - ("b" projectile-switch-to-buffer) - ("c" projectile-invalidate-cache) - ("d" projectile-find-dir) - ("s-f" projectile-find-file) - ("ff" projectile-find-file-dwim) - ("fd" projectile-find-file-in-directory) - ("g" ggtags-update-tags) - ("s-g" ggtags-update-tags) - ("i" projectile-ibuffer) - ("K" projectile-kill-buffers) - ("s-k" projectile-kill-buffers) - ("m" projectile-multi-occur) - ("o" projectile-multi-occur) - ("s-p" projectile-switch-project "switch project") - ("p" projectile-switch-project) - ("s" projectile-switch-project) - ("r" projectile-recentf) - ("x" projectile-remove-known-project) - ("X" projectile-cleanup-known-projects) - ("z" projectile-cache-current-file) - ("`" hydra-projectile-other-window/body "other window") - ("q" nil "cancel" :color blue)) + :commands (projectile-mode + projectile-find-file + projectile-grep + projectile-switch-project + projectile-project-root) :config (projectile-mode) (jdormit/define-prefix "p" "projectile") @@ -2365,7 +2293,7 @@ nice with Org-roam: (defmacro with-projectile-root (&rest body) `(with-temp-buffer (when (projectile-project-root) - (cd (projectile-project-root))) + (cd (projectile-project-root))) ,@body)) #+END_SRC @@ -4846,6 +4774,7 @@ Or Gnus can read RSS feeds directly: #+BEGIN_SRC emacs-lisp (use-package all-the-icons) (use-package all-the-icons-dired + :defer t :hook ((dired-mode . (lambda () (unless (eq major-mode 'dired-sidebar-mode) (all-the-icons-dired-mode)))))) @@ -4936,13 +4865,14 @@ In case I need an =emacsclient= for some reason. YASnippet is Yet Another Snippet template system. #+BEGIN_SRC emacs-lisp (use-package yasnippet + :hook ((text-mode . yas-minor-mode) + (prog-mode . yas-minor-mode)) :config (unless (file-exists-p (expand-file-name "~/.emacs.d/snippets")) (mkdir (expand-file-name "~/.emacs.d/snippets") t)) (setq yas-snippet-dirs `(,(syncthing-directory "yasnippet") - ,(expand-file-name "~/.emacs.d/snippets"))) - (yas-global-mode)) + ,(expand-file-name "~/.emacs.d/snippets")))) (use-package yasnippet-snippets :after (yasnippet) @@ -6097,18 +6027,19 @@ A fuzzy-finder for notes. :commands (deft) :init (setq deft-extensions '("org" "txt" "md" "markdown" "text") - deft-recursive t - deft-directory (org-directory)) + deft-recursive t + deft-directory (org-directory)) ;; Still lots of notes in the old Deft directory - (add-to-list 'org-agenda-files (org-directory "deft")) + (with-eval-after-load 'org + (add-to-list 'org-agenda-files (org-directory "deft"))) (leader-def-key "D" #'deft) (leader-def-key "od" #'deft) :config (setq deft-use-filter-string-for-filename t - deft-file-naming-rules '((noslash . "-") - (nospace . "-") - (case-fn . downcase)) - deft-auto-save-interval 0) + deft-file-naming-rules '((noslash . "-") + (nospace . "-") + (case-fn . downcase)) + deft-auto-save-interval 0) (add-to-list 'evil-emacs-state-modes 'deft-mode)) #+END_SRC @@ -6484,6 +6415,7 @@ to use instead: Like [[info:emacs#Debuggers][GUD]], but better! #+BEGIN_SRC emacs-lisp (use-package realgud + :commands (realgud:pdb) :init (defun projectile-pdb () (interactive) @@ -6717,26 +6649,6 @@ And a handy hydra for it: ("I" hydra-vuiet-info/body) ("q" nil)) #+END_SRC -* Jira -Jira in Emacs: -#+BEGIN_SRC emacs-lisp - (use-package org-jira - :init - (setq jiralib-url "https://lola.atlassian.net" - org-jira-working-dir (org-directory "jira") - org-jira-jira-status-to-org-keyword-alist '(("To Do" . "TODO") - ("Blocked" . "BLOCKED") - ("In Progress" . "IN PROGRESS") - ("Code Review" . "IN PROGRESS") - ("Awaiting Release" . "IN PROGRESS") - ("Done" . "DONE") - ("Won't Fix" . "CANCELLED"))) - (add-to-list 'org-agenda-files org-jira-working-dir) - (add-to-list 'org-super-agenda-groups - `(:name "Jira" :file-path ,org-jira-working-dir) - t)) -#+END_SRC - * Twitter Socially networking from Emacs: #+BEGIN_SRC emacs-lisp @@ -6763,6 +6675,7 @@ Socially networking from Emacs: #+BEGIN_SRC emacs-lisp (use-package structlog-mode :straight (structlog-mode :repo "https://git.jeremydormitzer.com/jdormit/structlog-el.git") + :commands (structlog) :config (setq structlog-db-username "jdormit" structlog-db-database "fluentd")) @@ -6771,9 +6684,18 @@ Socially networking from Emacs: * PlantUML #+BEGIN_SRC emacs-lisp (use-package plantuml-mode + :commands (plantuml-mode) :config (setq plantuml-default-exec-mode 'jar plantuml-jar-path "~/bin/plantuml.jar" org-plantuml-jar-path "~/bin/plantuml.jar")) #+END_SRC +* Typo mode +An incredibly useful mode for editing typographic text - automatically +makes quotes curly and dashes long, etc. +#+BEGIN_SRC emacs-lisp + (use-package typo + :commands (typo-mode typo-global-mode) + :hook ((text-mode . typo-mode))) +#+END_SRC