diff --git a/emacs/.emacs.d/init.el b/emacs/.emacs.d/init.el index 4086221..198ea19 100644 --- a/emacs/.emacs.d/init.el +++ b/emacs/.emacs.d/init.el @@ -5,8 +5,13 @@ ;; right Org version, then tangle ~/.emacs.d/init.org to ;; ~/.emacs.d/jdormit-init.el -(when (not (file-exists-p - (expand-file-name "~/.emacs.d/jdormit-init.el"))) +(defvar init-org-file (expand-file-name "~/.emacs.d/init.org")) +(defvar config-base-file (expand-file-name "~/.emacs.d/config/base.el")) + +(when (not (file-exists-p "~/.emacs.d/config")) + (make-directory "~/.emacs.d/config")) + +(when (not (file-exists-p config-base-file)) (message "Bootstrapping init file...") (defvar bootstrapping-init t) (defvar bootstrap-version) @@ -25,8 +30,6 @@ (straight-use-package 'org-plus-contrib) (require 'org) - (org-babel-tangle-file - (expand-file-name "~/.emacs.d/init.org") - (expand-file-name "~/.emacs.d/jdormit-init.el"))) + (org-babel-tangle-file init-org-file)) -(load-file (expand-file-name "~/.emacs.d/jdormit-init.el")) +(load-file config-base-file) diff --git a/emacs/.emacs.d/init.org b/emacs/.emacs.d/init.org index 62cd896..1d7fc79 100755 --- a/emacs/.emacs.d/init.org +++ b/emacs/.emacs.d/init.org @@ -1,6 +1,6 @@ -*- eval: (add-hook 'after-save-hook 'org-babel-tangle 0 t) -*- #+PROPERTY: header-args :results silent -#+PROPERTY: header-args:emacs-lisp :lexical t :tangle ~/.emacs.d/jdormit-init.el +#+PROPERTY: header-args:emacs-lisp :lexical t :tangle ~/.emacs.d/config/base.el This is a literate init file holding my Emacs configuration. It is initially loaded by a [[file:init.el][bootstrap file]] that lives at ~/.emacs.d/init.el. @@ -239,7 +239,9 @@ Adds Evil commands to comment out lines of code: #+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: +I put lots of stuff in Syncthing, but the actual folder location +differs on my different computers. This function resolves to the +Syncthing directory: #+BEGIN_SRC emacs-lisp (defcustom syncthing-path (expand-file-name "~/Sync") "The absolute path to the Syncthing directory" @@ -249,978 +251,6 @@ I put lots of stuff in Dropbox, but the actual folder location differs on my dif (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 - (use-package evil-mc - :defer 0 - :config - (global-evil-mc-mode) - (general-def '(normal visual) - "gs" evil-mc-cursors-map - "M-n" #'evil-mc-make-and-goto-next-cursor - "M-p" #'evil-mc-make-and-goto-prev-cursor - "C-n" #'evil-mc-make-and-goto-next-match - "C-t" #'evil-mc-skip-and-goto-next-match - "C-p" #'evil-mc-make-and-goto-prev-match)) -#+END_SRC - -#+BEGIN_SRC emacs-lisp - (use-package evil-multiedit - :defer 0 - :config - (evil-multiedit-default-keybinds)) -#+END_SRC - -* Transient -A framework for creating Magit-style popups: -#+BEGIN_SRC emacs-lisp - (use-package transient - :commands (define-transient-command)) -#+END_SRC - -* Magit -Magit is objectively the best Git interface. -#+BEGIN_SRC emacs-lisp - (use-package magit - :commands (magit-status - magit-blame - magit-find-file - magit-name-local-branch)) -#+END_SRC - -#+BEGIN_SRC emacs-lisp - (jdormit/define-prefix "g" "git") - (leader-def-key "gs" #'magit-status) - (leader-def-key "gg" #'magit-file-dispatch) - (leader-def-key "gf" #'magit-find-file) -#+END_SRC - -Use ido-mode for completion within Magit: -#+BEGIN_SRC emacs-lisp -;; (setq magit-completing-read-function 'magit-ido-completing-read) -#+END_SRC - -** Forge -[[https://github.com/magit/forge][Forge]] is an extension for Magit that lets it interact with code forges (e.g. GitHub). -#+BEGIN_SRC emacs-lisp - (use-package forge - :after (magit) - :config - (add-to-list 'forge-alist '("git.jeremydormitzer.com" - "git.jeremydormitzer.com/api/v1" - "git.jeremydormitzer.com" - forge-gitea-repository)) - (with-eval-after-load 'evil-magit - (general-def '(normal motion) magit-mode-map - "yu" #'forge-copy-url-at-point-as-kill)) - :general - ((normal motion visual) forge-topic-list-mode-map - "y" #'forge-copy-url-at-point-as-kill - "q" #'quit-window) - :custom - (forge-owned-accounts '((jdormit . (remote-name "jdormit"))))) -#+END_SRC - -** evil-magit -Evil keybindings for magit! -#+BEGIN_SRC emacs-lisp - (use-package evil-magit - :after (evil magit) - :config - (with-eval-after-load 'magit - (require 'evil-magit)) - :general - ('normal magit-mode-map "SPC" leader-map)) -#+END_SRC - -** Transient -#+BEGIN_SRC emacs-lisp - (setq transient-default-level 7) -#+END_SRC - -* git-link -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) - (let ((git-link-open-in-browser nil)) - (call-interactively 'git-link))) - (leader-def-key "gl" #'git-link) - (leader-def-key "gy" #'git-link-copy) - :custom - (git-link-open-in-browser t) - (git-link-remote-alist '(("git.sr.ht" git-link-sourcehut) - ("github" git-link-github) - ("bitbucket" git-link-bitbucket) - ("gitorious" git-link-gitorious) - ("gitlab" git-link-gitlab) - ("visualstudio\\|azure" git-link-azure) - ("git.jeremydormitzer.com" git-link-bitbucket)))) -#+END_SRC - -* with-editor -A utility from the author of Magit to run shell commands using the current Emacs instance as $EDITOR. - -#+BEGIN_SRC emacs-lisp - (shell-command-with-editor-mode) - (add-hook 'shell-mode-hook #'with-editor-export-editor) - (add-hook 'term-exec-hook #'with-editor-export-editor) - (add-hook 'eshell-mode-hook #'with-editor-export-editor) -#+END_SRC - -* Password Store -Interfacing with Pass, the "standard Unix password manager". This should also be loaded before `exec-path-from-shell`. -#+BEGIN_SRC emacs-lisp - (defun password-store-synchronize () - (interactive) - (with-editor-async-shell-command "pass git pull && pass git push")) - - (use-package password-store - :defer t - :if (executable-find "pass") - :config - (setq password-store-password-length 20) - :init - (leader-def-key "P" 'password-store-copy) - (epa-file-enable)) - - (use-package pass - :if (executable-find "pass") - :commands pass - :general - ('(normal motion visual) pass-mode-map "S" #'password-store-synchronize)) - - (leader-def-key "ap" #'pass) - - (setq auth-sources '("~/.authinfo" password-store)) -#+END_SRC - * Emacs Lisp ** Packages Some helpful ELisp packages: @@ -1283,7 +313,8 @@ Some helpful ELisp packages: (use-package f :defer t :init - (add-hook 'emacs-startup-hook (lambda () (require 'f)))) + (add-hook 'emacs-startup-hook (lambda () (require 'f))) + (autoload 'f-join "f")) (use-package request :commands (request request-deferred)) #+END_SRC @@ -1598,6 +629,976 @@ Whenever the buffer changes, look up the major-mode to see if there is any code '((undo discard-info))) #+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 + org-agenda + org-capture) + :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 + :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 + :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 + :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 + :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) + :custom + (org-roam-directory (org-directory)) + :init + (defvar org-roam-map (make-sparse-keymap)) + (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") + (with-eval-after-load 'org + (org-roam-mode)) + :config + (add-hook 'org-roam-backlinks-mode-hook #'olivetti-mode) + :general + (leader-map "fo" #'org-roam-find-file + "of" #'org-roam-find-file + "on" org-roam-map) + (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 + :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)) + (jdormit/define-prefix "oj" "org-journal") + (leader-def-key "ojn" #'org-journal-new-entry) + (leader-def-key "ojt" #'org-journal-today) + :config + (add-to-list 'org-capture-templates + '("j" "Journal entry" entry (function org-journal-capture-func) + "* %(format-time-string org-journal-time-format)\n%?")) + :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 + (use-package evil-mc + :defer 0 + :config + (global-evil-mc-mode) + (general-def '(normal visual) + "gs" evil-mc-cursors-map + "M-n" #'evil-mc-make-and-goto-next-cursor + "M-p" #'evil-mc-make-and-goto-prev-cursor + "C-n" #'evil-mc-make-and-goto-next-match + "C-t" #'evil-mc-skip-and-goto-next-match + "C-p" #'evil-mc-make-and-goto-prev-match)) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + (use-package evil-multiedit + :defer 0 + :config + (evil-multiedit-default-keybinds)) +#+END_SRC + +* Transient +A framework for creating Magit-style popups: +#+BEGIN_SRC emacs-lisp + (use-package transient + :commands (define-transient-command)) +#+END_SRC + +* Magit +Magit is objectively the best Git interface. +#+BEGIN_SRC emacs-lisp + (use-package magit + :commands (magit-status + magit-blame + magit-find-file + magit-name-local-branch)) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + (jdormit/define-prefix "g" "git") + (leader-def-key "gs" #'magit-status) + (leader-def-key "gg" #'magit-file-dispatch) + (leader-def-key "gf" #'magit-find-file) +#+END_SRC + +Use ido-mode for completion within Magit: +#+BEGIN_SRC emacs-lisp +;; (setq magit-completing-read-function 'magit-ido-completing-read) +#+END_SRC + +** Forge +[[https://github.com/magit/forge][Forge]] is an extension for Magit that lets it interact with code forges (e.g. GitHub). +#+BEGIN_SRC emacs-lisp + (use-package forge + :after (magit) + :config + (add-to-list 'forge-alist '("git.jeremydormitzer.com" + "git.jeremydormitzer.com/api/v1" + "git.jeremydormitzer.com" + forge-gitea-repository)) + (with-eval-after-load 'evil-magit + (general-def '(normal motion) magit-mode-map + "yu" #'forge-copy-url-at-point-as-kill)) + :general + ((normal motion visual) forge-topic-list-mode-map + "y" #'forge-copy-url-at-point-as-kill + "q" #'quit-window) + :custom + (forge-owned-accounts '((jdormit . (remote-name "jdormit"))))) +#+END_SRC + +** evil-magit +Evil keybindings for magit! +#+BEGIN_SRC emacs-lisp + (use-package evil-magit + :after (evil magit) + :config + (with-eval-after-load 'magit + (require 'evil-magit)) + :general + ('normal magit-mode-map "SPC" leader-map)) +#+END_SRC + +** Transient +#+BEGIN_SRC emacs-lisp + (setq transient-default-level 7) +#+END_SRC + +* git-link +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) + (let ((git-link-open-in-browser nil)) + (call-interactively 'git-link))) + (leader-def-key "gl" #'git-link) + (leader-def-key "gy" #'git-link-copy) + :custom + (git-link-open-in-browser t) + (git-link-remote-alist '(("git.sr.ht" git-link-sourcehut) + ("github" git-link-github) + ("bitbucket" git-link-bitbucket) + ("gitorious" git-link-gitorious) + ("gitlab" git-link-gitlab) + ("visualstudio\\|azure" git-link-azure) + ("git.jeremydormitzer.com" git-link-bitbucket)))) +#+END_SRC + +* with-editor +A utility from the author of Magit to run shell commands using the current Emacs instance as $EDITOR. + +#+BEGIN_SRC emacs-lisp + (shell-command-with-editor-mode) + (add-hook 'shell-mode-hook #'with-editor-export-editor) + (add-hook 'term-exec-hook #'with-editor-export-editor) + (add-hook 'eshell-mode-hook #'with-editor-export-editor) +#+END_SRC + +* Password Store +Interfacing with Pass, the "standard Unix password manager". This should also be loaded before `exec-path-from-shell`. +#+BEGIN_SRC emacs-lisp + (defun password-store-synchronize () + (interactive) + (with-editor-async-shell-command "pass git pull && pass git push")) + + (use-package password-store + :defer t + :if (executable-find "pass") + :config + (setq password-store-password-length 20) + :init + (leader-def-key "P" 'password-store-copy) + (epa-file-enable)) + + (use-package pass + :if (executable-find "pass") + :commands pass + :general + ('(normal motion visual) pass-mode-map "S" #'password-store-synchronize)) + + (leader-def-key "ap" #'pass) + + (setq auth-sources '("~/.authinfo" password-store)) +#+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 @@ -2030,7 +2031,7 @@ leaves other lines alone: #+BEGIN_SRC emacs-lisp (use-package ws-butler :straight (ws-butler :host github :repo "lewang/ws-butler") - :hook ((prod-mode . ws-butler-mode))) + :hook ((prog-mode . ws-butler-mode))) #+END_SRC * Whitespace Visualation @@ -2133,16 +2134,17 @@ Smartparens enables structured editing of s-expressions and other pairs: :hook ((prog-mode . smartparens-strict-mode) (eshell-mode . smartparens-strict-mode) (geiser-repl-mode . smartparens-strict-mode) - (inferior-python-mode . smartparens-strict-mode)) + (inferior-python-mode . smartparens-strict-mode) + (after-init . smartparens-global-mode)) :init (defhydra hydra-smartparens (:hint nil) " - Moving^^^^ Slurp & Barf^^ Wrapping^^ Sexp juggling^^^^ Destructive - ------------------------------------------------------------------------------------------------------------------------ - [_a_] beginning [_n_] down [_h_] bw slurp [_R_] rewrap [_S_] split [_t_] transpose [_c_] change inner [_w_] copy - [_e_] end [_N_] bw down [_H_] bw barf [_u_] unwrap [_s_] splice [_A_] absorb [_C_] change outer - [_f_] forward [_p_] up [_l_] slurp [_U_] bw unwrap [_r_] raise [_E_] emit [_k_] kill [_g_] quit - [_b_] backward [_P_] bw up [_L_] barf [_(__{__[_] wrap (){}[] [_j_] join [_o_] convolute [_K_] bw kill [_q_] quit" + Moving^^^^ Slurp & Barf^^ Wrapping^^ Sexp juggling^^^^ Destructive + ------------------------------------------------------------------------------------------------------------------------ + [_a_] beginning [_n_] down [_h_] bw slurp [_R_] rewrap [_S_] split [_t_] transpose [_c_] change inner [_w_] copy + [_e_] end [_N_] bw down [_H_] bw barf [_u_] unwrap [_s_] splice [_A_] absorb [_C_] change outer + [_f_] forward [_p_] up [_l_] slurp [_U_] bw unwrap [_r_] raise [_E_] emit [_k_] kill [_g_] quit + [_b_] backward [_P_] bw up [_L_] barf [_(__{__[_] wrap (){}[] [_j_] join [_o_] convolute [_K_] bw kill [_q_] quit" ;; Moving ("a" sp-beginning-of-sexp) ("e" sp-end-of-sexp) @@ -2195,6 +2197,9 @@ Smartparens enables structured editing of s-expressions and other pairs: (defun sp-wrap-single-quotes (&optional arg) (interactive "P") (sp-wrap-with-pair "\'")) + (setq sp-ignore-modes-list + (delete 'minibuffer-inactive-mode sp-ignore-modes-list)) + (sp-local-pair 'minibuffer-inactive-mode "\'" nil :actions nil) :general (prog-mode-map "C-c p" 'hydra-smartparens/body) (normal prog-mode-map "g p" 'hydra-smartparens/body) @@ -2204,17 +2209,17 @@ Smartparens enables structured editing of s-expressions and other pairs: (normal "g\"" 'sp-wrap-double-quotes) (normal "g\'" 'sp-wrap-single-quotes)) - (use-package evil-smartparens - :after (evil smartparens) - :hook ((smartparens-enabled . evil-smartparens-mode))) + (use-package evil-smartparens + :after (evil smartparens) + :hook ((smartparens-enabled . evil-smartparens-mode))) - (jdormit/define-prefix "l" "lisp") - (jdormit/define-prefix "lw" "wrap") - (leader-def-key "lwr" 'sp-wrap-round) - (leader-def-key "lws" 'sp-wrap-square) - (leader-def-key "lwc" 'sp-wrap-curly) - (leader-def-key "ls" 'sp-forward-slurp-sexp) - (leader-def-key "lb" 'sp-forward-barf-sexp) + (jdormit/define-prefix "l" "lisp") + (jdormit/define-prefix "lw" "wrap") + (leader-def-key "lwr" 'sp-wrap-round) + (leader-def-key "lws" 'sp-wrap-square) + (leader-def-key "lwc" 'sp-wrap-curly) + (leader-def-key "ls" 'sp-forward-slurp-sexp) + (leader-def-key "lb" 'sp-forward-barf-sexp) #+END_SRC Enable ES6 arrow functions in web-mode ("borrowed" from [[https://github.com/Fuco1/smartparens/issues/823#issuecomment-403019519][this GitHub comment]]): @@ -2222,17 +2227,17 @@ Enable ES6 arrow functions in web-mode ("borrowed" from [[https://github.com/Fuc (with-eval-after-load 'smartparens (defun sp-after-equals-p (_id action _context) (when (memq action '(insert navigate)) - (sp--looking-back-p "=>" 2))) + (sp--looking-back-p "=>" 2))) - (defun sp-after-equals-skip (ms mb _me) + (defun sp-after-equals-skip-p (ms mb _me) (when (eq ms ">") - (save-excursion - (goto-char mb) - (sp--looking-back-p "=" 1)))) + (save-excursion + (goto-char mb) + (sp--looking-back-p "=" 1)))) (sp-local-pair '(web-mode) "<" nil - :unless '(:add sp-after-equals-p) - :skip-match 'sp-after-equals-skip-p)) + :unless '(:add sp-after-equals-p) + :skip-match 'sp-after-equals-skip-p)) #+END_SRC Parinfer infers parens from indentation and vice-versa. Currently @@ -2411,29 +2416,29 @@ UI-related keybindings: ;; Custom buffer groups (defun centaur-tabs-projectile-buffer-groups () - "Return the list of group names BUFFER belongs to." - (if centaur-tabs-projectile-buffer-group-calc - (symbol-value 'centaur-tabs-projectile-buffer-group-calc) - (set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc) + "Return the list of group names BUFFER belongs to." + (if centaur-tabs-projectile-buffer-group-calc + (symbol-value 'centaur-tabs-projectile-buffer-group-calc) + (set (make-local-variable 'centaur-tabs-projectile-buffer-group-calc) - (cond - ((or (get-buffer-process (current-buffer)) - (memq major-mode '(comint-mode compilation-mode))) '("Term")) - ((string-equal "*" (substring (buffer-name) 0 1)) '("Misc")) - ((condition-case _err - (projectile-project-root) - (error nil)) (list (projectile-project-name))) - ((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode - c++-mode javascript-mode js-mode - js2-mode makefile-mode - lua-mode vala-mode)) '("Coding")) - ((memq major-mode '(nxhtml-mode html-mode - mhtml-mode css-mode)) '("HTML")) - ((memq major-mode '(org-journal-mode)) '("Journal")) - ((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org")) - ((memq major-mode '(dired-mode)) '("Dir")) - (t '("Other")))) - (symbol-value 'centaur-tabs-projectile-buffer-group-calc))) + (cond + ((or (get-buffer-process (current-buffer)) + (memq major-mode '(comint-mode compilation-mode))) '("Term")) + ((string-equal "*" (substring (buffer-name) 0 1)) '("Misc")) + ((condition-case _err + (projectile-project-root) + (error nil)) (list (projectile-project-name))) + ((memq major-mode '(emacs-lisp-mode python-mode emacs-lisp-mode c-mode + c++-mode javascript-mode js-mode + js2-mode makefile-mode + lua-mode vala-mode)) '("Coding")) + ((memq major-mode '(nxhtml-mode html-mode + mhtml-mode css-mode)) '("HTML")) + ((memq major-mode '(org-journal-mode)) '("Journal")) + ((memq major-mode '(org-mode calendar-mode diary-mode)) '("Org")) + ((memq major-mode '(dired-mode)) '("Dir")) + (t '("Other")))) + (symbol-value 'centaur-tabs-projectile-buffer-group-calc))) ;; Don't show tabs for certain types of buffers (advice-add 'centaur-tabs-hide-tab :around @@ -2468,13 +2473,15 @@ UI-related keybindings: ;; Use Org-mode titles for tab names when possible (advice-add 'centaur-tabs-buffer-tab-label :around (lambda (oldfn tab &rest args) - (if-let ((title (or (car - (org-roam-db--get-titles - (buffer-file-name (car tab)))) - (org-get-title - (with-current-buffer (car tab) - (buffer-substring (point-min) - (min (point-max) 200))))))) + (if-let ((title (or (and + (fboundp 'org-roam-db--get-titles) + (car + (org-roam-db--get-titles + (buffer-file-name (car tab))))) + (org-get-title + (with-current-buffer (car tab) + (buffer-substring (point-min) + (min (point-max) 200))))))) (if (> centaur-tabs-label-fixed-length 0) (centaur-tabs-truncate-string centaur-tabs-label-fixed-length (format " %s" title)) @@ -3230,7 +3237,8 @@ Start with clojure-mode: (:+ 3) (match 1) (for-all 2) - (checking 2))) + (checking 2) + (let-flow 1))) #+END_SRC Add flycheck support: @@ -3247,16 +3255,17 @@ Sprinkle in some CIDER: :commands (cider-mode cider-jack-in cider-jack-in-clojurescript) :config (setq cider-known-endpoints - '(("local" "localhost" "4005")) - cider-prompt-for-symbol nil) + '(("local" "localhost" "4005")) + cider-prompt-for-symbol nil) (general-def cider-mode-map "C-c t" cider-test-commands-map) (add-hook 'cider-repl-mode-hook 'smartparens-strict-mode) :hook ((clojure-mode . cider-mode) - (clojurescript-mode . cider-mode) - (clojurec-mode . cider-mode)) + (clojurescript-mode . cider-mode) + (clojurec-mode . cider-mode)) :general (cider-stacktrace-mode-map "SPC" leader-map) - ('normal cider-mode-map "M-." #'cider-find-var)) + ('normal cider-mode-map "M-." #'cider-find-var) + ('normal cider-repl-mode-map "C-c C-l" #'cider-repl-clear-buffer)) (defun jdormit/cider-setup () (local-set-key (kbd "C-c M-b") 'cider-debug-defun-at-point)) @@ -4513,7 +4522,9 @@ Elfeed is a feed reader for Emacs. ;; The Economist - This Week ("https://www.kill-the-newsletter.com/feeds/x3cwlql9hhc5pn39ci31.xml" news) ;; The Economist - Today - ("https://www.kill-the-newsletter.com/feeds/rl6dr7kiotn6kn2ry6aa.xml" news)) + ("https://www.kill-the-newsletter.com/feeds/rl6dr7kiotn6kn2ry6aa.xml" news) + ;; AllSides Weekly Newsletter + ("https://kill-the-newsletter.com/feeds/31flkb42eg3v31yu7ecm.xml" news)) shr-use-colors nil)) #+END_SRC @@ -5603,7 +5614,7 @@ Some functions to make my day job easier. :cwd "~/lola/secrets" :truncate-output t :stop-signal 'int - :init-async (python-service-setup "secrets" + :init-async (python-service-setup "~/.pyenv/versions/secrets" "~/lola/secrets/.env")) (prodigy-define-service @@ -6692,6 +6703,5 @@ 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))) + :commands (typo-mode typo-global-mode)) #+END_SRC