From 2fa2da4328ae7a4a50851a0746355cffadf08c30 Mon Sep 17 00:00:00 2001 From: Emmet Date: Wed, 10 May 2023 21:10:52 -0500 Subject: [PATCH] Trying nix-doom-emacs --- user/app/doom-emacs/config.el | 965 ++++++++++ user/app/doom-emacs/doom.org | 1563 +++++++++++++++++ user/app/doom-emacs/init.el | 175 ++ user/app/doom-emacs/packages.el | 21 + .../copy-link-or-file-to-clipboard.sh | 10 + .../scripts/ox-odp/ox-odp-xml-parse.py | 157 ++ user/app/doom-emacs/scripts/ox-odp/ox-odp.sh | 47 + user/home.nix | 18 +- 8 files changed, 2943 insertions(+), 13 deletions(-) create mode 100644 user/app/doom-emacs/config.el create mode 100644 user/app/doom-emacs/doom.org create mode 100644 user/app/doom-emacs/init.el create mode 100644 user/app/doom-emacs/packages.el create mode 100755 user/app/doom-emacs/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh create mode 100755 user/app/doom-emacs/scripts/ox-odp/ox-odp-xml-parse.py create mode 100755 user/app/doom-emacs/scripts/ox-odp/ox-odp.sh diff --git a/user/app/doom-emacs/config.el b/user/app/doom-emacs/config.el new file mode 100644 index 0000000..a3dac0a --- /dev/null +++ b/user/app/doom-emacs/config.el @@ -0,0 +1,965 @@ +;;; $DOOMDIR/config.el -*- lexical-binding: t; -*- + +;;;------ User configuration ------;;; + +;; My default user identity as my yt alias +(setq user-full-name "librephoenix") + +;; I prefer visual lines +(setq display-line-numbers-type 'visual + line-move-visual t) +(use-package-hook! evil + :pre-init + (setq evil-respect-visual-line-mode t) ;; sane j and k behavior + t) + +;; I also like evil mode visual movement +(map! :map evil-normal-state-map + :desc "Move to next visual line" + "j" 'evil-next-visual-line + :desc "Move to previous visual line" + "k" 'evil-previous-visual-line) + +;; Theme and font +(setq doom-theme 'doom-old-hope) +(setq doom-font (font-spec :family "Inconsolata" :size 20)) + +;; Transparent background +(set-frame-parameter (selected-frame) 'alpha '(90 . 90)) +(add-to-list 'default-frame-alist '(alpha . (90 . 90))) + +;; Icons in completion buffers +(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup) +(all-the-icons-completion-mode) + +;; This makes non-main buffers dimmer, so you can focus on main buffers +(solaire-global-mode +1) + +;; Grammar tasing should be voluntary +(setq writegood-mode nil) + +;; Beacon shows where the cursor is, even when fast scrolling +(setq beacon-mode t) + +;; Quicker window management keybindings +(bind-key* "C-j" #'evil-window-down) +(bind-key* "C-k" #'evil-window-up) +(bind-key* "C-h" #'evil-window-left) +(bind-key* "C-l" #'evil-window-right) +(bind-key* "C-q" #'evil-window-delete) + +;; Mouse buffer management +(bind-key* "" #'previous-buffer) +(bind-key* "" #'next-buffer) + +;; Disables custom.el +(setq custom-file null-device) + +;; Fancy splash image +(setq fancy-splash-image "~/.doom.d/arch.png") + +(setq +doom-dashboard-menu-sections +'(("Open org roam overview" :icon + (all-the-icons-octicon "globe" :face 'doom-dashboard-menu-title) + :face + (:inherit + (doom-dashboard-menu-title bold)) + :action org-roam-default-overview) + ("Roam to another db" :icon + (all-the-icons-fileicon "org" :face 'doom-dashboard-menu-title) + :action org-roam-switch-db) + ("Open agenda" :icon + (all-the-icons-octicon "calendar" :face 'doom-dashboard-menu-title) + :when + (fboundp 'org-agenda) + :action org-agenda-list + :key "SPC o A a") + ("Open private configuration" :icon + (all-the-icons-octicon "tools" :face 'doom-dashboard-menu-title) + :when + (file-directory-p doom-user-dir) + :action doom/open-private-config) + ("Open documentation" :icon + (all-the-icons-octicon "book" :face 'doom-dashboard-menu-title) + :action doom/help) + ("Quit emacs" :icon + (all-the-icons-faicon "level-down" :face 'doom-dashboard-menu-title) + :action save-buffers-kill-terminal) + ) +) + +;; Requires for faster loading +(require 'org-agenda) +(require 'dired) + +;; Garbage collection to speed things up +(add-hook 'after-init-hook + #'(lambda () + (setq gc-cons-threshold (* 100 1000 1000)))) +(add-hook 'focus-out-hook 'garbage-collect) +(run-with-idle-timer 5 t 'garbage-collect) + +;; Enable autorevert globally so that buffers update when files change on disk. +;; Very useful when used with file syncing (i.e. syncthing) +(setq global-auto-revert-mode nil) +(setq auto-revert-use-notify t) + +;;;------ Registers ------;;; + +(map! :leader + :desc "Jump to register" + "r" 'jump-to-register) + +(set-register ?f '(file . "/home/librephoenix/Org/Family.s/Notes/hledger.org")) +(set-register ?r '(file . "/home/librephoenix/README.org")) +(set-register ?d '(file . "/home/librephoenix/.doom.d/doom.org")) +(set-register ?h '(file . "/home/librephoenix")) +(set-register ?x '(file . "/home/librephoenix/.xmonad/xmonad.org")) +(set-register ?s '(file . "/home/librephoenix/.install/install.org")) + +;;;------ Org mode configuration ------;;; + +;; Set default org directory +(setq org-directory "~/.Org") + +(remove-hook 'after-save-hook #'+literate|recompile-maybe) +(set-company-backend! 'org-mode nil) + +;; Automatically show images but manually control their size +(setq org-startup-with-inline-images t + org-image-actual-width nil) + +;; Top-level headings should be bigger! +(custom-set-faces! + '(org-level-1 :inherit outline-1 :height 1.3) + '(org-level-2 :inherit outline-2 :height 1.25) + '(org-level-3 :inherit outline-3 :height 1.2) + '(org-level-4 :inherit outline-4 :height 1.1) + '(org-level-5 :inherit outline-5 :height 1.1) + '(org-level-6 :inherit outline-6 :height 1.05) + '(org-level-7 :inherit outline-7 :height 1.05) + ) + +;(custom-set-faces! +; '(org-link :foreground nil)) + +;; Pretty org bullets +;;(use-package org-bullets +;; :ensure t +;; :init +;; (add-hook 'org-mode-hook (lambda () +;; (org-bullets-mode 1)))) + +(with-eval-after-load 'org (global-org-modern-mode)) + +;; Add frame borders and window dividers +(modify-all-frames-parameters + '((right-divider-width . 10) + (internal-border-width . 10))) +(dolist (face '(window-divider + window-divider-first-pixel + window-divider-last-pixel)) + (face-spec-reset-face face) + (set-face-foreground face (face-attribute 'default :background))) +(set-face-background 'fringe (face-attribute 'default :background)) + +(setq + ;; Edit settings + org-auto-align-tags nil + org-tags-column 0 + org-catch-invisible-edits 'show-and-error + org-special-ctrl-a/e t + org-insert-heading-respect-content t + + ;; Org styling, hide markup etc. + org-hide-emphasis-markers t + org-pretty-entities t + org-ellipsis "…") + +(setq-default line-spacing 0.1) + +; Automatic table of contents is nice +(if (require 'toc-org nil t) + (progn + (add-hook 'org-mode-hook 'toc-org-mode) + (add-hook 'markdown-mode-hook 'toc-org-mode)) + (warn "toc-org not found")) + +;;---- this block from http://fgiasson.com/blog/index.php/2016/06/21/optimal-emacs-settings-for-org-mode-for-literate-programming/ ----;; +;; Tangle Org files when we save them +(defun tangle-on-save-org-mode-file() + (when (string= (message "%s" major-mode) "org-mode") + (org-babel-tangle))) + +(add-hook 'after-save-hook 'tangle-on-save-org-mode-file) +;; ---- end block ---- ;; + +;; Better org table editing +(setq-default evil-insert-state-exit-hook '(org-update-parent-todo-statistics + t)) +(setq org-table-automatic-realign nil) + +;; Better for org source blocks +(setq electric-indent-mode nil) +(setq org-src-window-setup 'current-window) +(delete + '("^\\*Org Src" + (+popup-buffer) + (actions) + (side . bottom) + (size . 0.42) + (window-width . 40) + (window-height . 0.42) + (slot) + (vslot) + (window-parameters + (ttl) + (quit) + (select . t) + (modeline . t) + (autosave . t) + (transient . t) + (no-other-window . t))) + display-buffer-alist) + +(require 'org-download) + +;; Drag-and-drop to `dired` +(add-hook 'dired-mode-hook 'org-download-enable) + +(setq org-download-screenshot-method "flameshot gui -p %s") +(after! org-download + (setq org-download-method 'directory)) + +(after! org + (setq-default org-download-image-dir "img/" + org-download-heading-lvl nil)) + +(defun my-org-screenshot () + "Take a screenshot into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq filename + (concat + (make-temp-name + (concat (buffer-file-name) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) ".png")) + (shell-command (concat "emacs-wayshot " filename)) + (insert (concat "[[" filename "]]")) + (org-display-inline-images)) + +(defun my-org-paste() + "Take an image from the clipboard into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq filename + (concat + (make-temp-name + (concat (file-name-directory (buffer-file-name)) + "img/" + (file-name-nondirectory (buffer-file-name)) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) ".png")) + (shell-command (concat "wl-paste > " filename)) + (insert (concat "[[" filename "]]")) + (org-display-inline-images)) + +(defun my-org-new-file-from-template() + "Copy a template from ~/Templates into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq template-file (completing-read "Template file:" (directory-files "~/Templates"))) + (setq filename + (concat + (make-temp-name + (concat (file-name-directory (buffer-file-name)) + "files/" + (file-name-nondirectory (buffer-file-name)) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) (file-name-extension template-file t))) + (copy-file (concat "/home/librephoenix/Templates/" template-file) filename) + (setq prettyname (read-from-minibuffer "Pretty name:")) + (insert (concat "[[./files/" (file-name-nondirectory filename) "][" prettyname "]]")) + (org-display-inline-images)) + +(when (require 'openwith nil 'noerror) + (setq openwith-associations + (list + (list (openwith-make-extension-regexp + '("mpg" "mpeg" "mp3" "mp4" + "avi" "wmv" "wav" "mov" "flv" + "ogm" "ogg" "mkv")) + "mpv" + '(file)) + (list (openwith-make-extension-regexp + '("doc" "xls" "ppt" "odt" "ods" "odg" "odp")) + "libreoffice" + '(file)) + '("\\.lyx" "lyx" (file)) + '("\\.chm" "kchmviewer" (file)) + (list (openwith-make-extension-regexp + '("pdf" "ps" "ps.gz" "dvi")) + "atril" + '(file)) + (list (openwith-make-extension-regexp + '("kdenlive")) + "kdenlive" + '(file)) + (list (openwith-make-extension-regexp + '("kra")) + "krita" + '(file)) + (list (openwith-make-extension-regexp + '("blend" "blend1")) + "blender" + '(file)) + (list (openwith-make-extension-regexp + '("helio")) + "helio" + '(file)) + (list (openwith-make-extension-regexp + '("svg")) + "inkscape" + '(file)) + (list (openwith-make-extension-regexp + '("flp")) + "~/.local/bin/flstudio" + '(file)) + )) + (openwith-mode 1)) + +(add-to-list 'display-buffer-alist '("^*Async Shell Command*" . (display-buffer-no-window))) + +(map! :leader + :desc "Insert a screenshot" +;; "i s" 'my-org-screenshot) + "i s" 'org-download-screenshot) + +(defun org-download-clipboard-basename () + (interactive) + (setq org-download-path-last-dir org-download-image-dir) + (setq org-download-image-dir (completing-read "directory: " (-filter #'f-directory-p (directory-files-recursively "." "" t)) nil t)) + (org-download-clipboard (completing-read "basename: " '() nil nil)) + (setq org-download-image-dir org-download-path-last-dir) +) + +(map! :leader + :desc "Insert image from clipboard" + "i p" 'org-download-clipboard + "i P" 'org-download-clipboard-basename) + +(map! :leader + :desc "Create a new file from a template and insert a link at point" + "i t" 'my-org-new-file-from-template) + +(defun org-copy-link-to-clipboard-at-point () + "Copy current link at point into clipboard (useful for images and links)" + (interactive) + (if (eq major-mode #'org-mode) + (link-hint-copy-link-at-point) + ) + (if (eq major-mode #'ranger-mode) + (ranger-copy-absolute-file-paths) + ) + (if (eq major-mode #'image-mode) + (image-mode-copy-file-name-as-kill) + ) + (shell-command (concat "~/.doom.d/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh " (gui-get-selection 'CLIPBOARD)) nil nil) +) + +(map! :leader + :desc "Copy link/file at point into system clipbord (C-g to escape if copying a file)" + "y y" 'org-copy-link-to-clipboard-at-point) + +;; Online images inside of org mode is pretty cool +;; This snippit is from Tobias on Stack Exchange +;; https://emacs.stackexchange.com/questions/42281/org-mode-is-it-possible-to-display-online-images +(require 'org-yt) + +(defun org-image-link (protocol link _description) + "Interpret LINK as base64-encoded image data." + (cl-assert (string-match "\\`img" protocol) nil + "Expected protocol type starting with img") + (let ((buf (url-retrieve-synchronously (concat (substring protocol 3) ":" link)))) + (cl-assert buf nil + "Download of image \"%s\" failed." link) + (with-current-buffer buf + (goto-char (point-min)) + (re-search-forward "\r?\n\r?\n") + (buffer-substring-no-properties (point) (point-max))))) + +(org-link-set-parameters + "imghttp" + :image-data-fun #'org-image-link) + +(org-link-set-parameters + "imghttps" + :image-data-fun #'org-image-link) + +;; Mermaid diagrams +(setq ob-mermaid-cli-path "/usr/bin/mmdc") + +;; Print org mode +(defun org-simple-print-buffer () + "Open an htmlized form of current buffer and open in a web browser to print" + (interactive) + (htmlize-buffer) + (browse-url-of-buffer (concat (buffer-name) ".html")) + (sleep-for 1) + (kill-buffer (concat (buffer-name) ".html"))) + +;; Doesn't work yet, bc htmlize-region takes arguments BEG and END +;(defun org-simple-print-region() +; "Open an htmlized form of current region and open in a web browser to print" +; (interactive) +; (htmlize-region ) +; (browse-url-of-buffer (concat (buffer-name) ".html")) +; (sleep-for 1) +; (kill-buffer (concat (buffer-name) ".html"))) + +(map! :leader + :prefix ("P" . "Print") + :desc "Simple print buffer in web browser" + "p" 'org-simple-print-buffer) + +(map! :leader + :prefix ("P" . "Print") + :desc "Simple print buffer in web browser" + "b" 'org-simple-print-buffer) + +;(map! :leader +; :prefix ("P" . "Print") +; :desc "Simple print region in web browser" +; "r" 'org-simple-print-region) + +;; Custom function to convert org mode to ODP presentation +;; Depends on bash, libreoffice, and pandoc +(defun my-ox-odp () + "Convert an org mode file to an ODP presentation." + (interactive) + (setq file-name (buffer-file-name)) + (setq output-pptx-file-name (replace-regexp-in-string "\.org" "\.pptx" (buffer-file-name))) + (setq output-odp-file-name (replace-regexp-in-string "\.org" "\.odp" (buffer-file-name))) + (setq odp-style-file-name (completing-read "Choose style: " + '("/home/librephoenix/.doom.d/scripts/ox-odp/styles/water.odp" + "/home/librephoenix/.doom.d/scripts/ox-odp/styles/dark.odp" + ) nil t)) + (shell-command (concat "~/.doom.d/scripts/ox-odp/ox-odp.sh \"" (buffer-file-name) "\" \"" odp-style-file-name "\" > /dev/null")) + ) + +(map! :leader + :desc "Convert org document to odp presentation" + "e p" 'my-ox-odp) + +;;;------ Org roam configuration ------;;; + +(require 'org-roam) +(require 'org-roam-dailies) + +(setq org-roam-directory "~/Org/Personal/Notes" + org-roam-db-location "~/Org/Personal/Notes/org-roam.db") + +(setq org-roam-node-display-template + "${title:65}📝${tags:*}") + +(org-roam-db-autosync-mode) + +(setq full-org-roam-db-list nil) + +(setq full-org-roam-db-list (directory-files "~/Org" t "\\.[p,s]$")) +(dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list + (append (directory-files item t "\\.[p,s]$") full-org-roam-db-list))) + +(setq org-roam-db-choice "Default") +(setq full-org-roam-db-list-pretty (list "Default")) +(dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list-pretty + (append (list + (replace-regexp-in-string "\\/home\\/librephoenix\\/Org\\/" "" item)) full-org-roam-db-list-pretty))) + +(defun org-roam-open-dashboard () + "Open ${org-roam-directory}/dashboard.org (I use this naming convention to create dashboards for each of my org roam maps)" + (interactive) + (if (file-exists-p (concat org-roam-directory "/dashboard.org")) + (org-open-file (concat org-roam-directory "/dashboard.org")) + (dired org-roam-directory)) +) + +(defun org-roam-switch-db (&optional arg silent) + "Switch to a different org-roam database, arg" + (interactive) + (when (not arg) + (setq full-org-roam-db-list nil) + + (setq full-org-roam-db-list (directory-files "~/Org" t "\\.[p,s]$")) + (dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list + (append (directory-files item t "\\.[p,s]$") full-org-roam-db-list))) + + (setq full-org-roam-db-list-pretty (list "Default")) + (dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list-pretty + (append (list + (replace-regexp-in-string "\\/home\\/librephoenix\\/Org\\/" "" item)) full-org-roam-db-list-pretty))) + + (setq org-roam-db-choice (completing-read "Select org roam database: " + full-org-roam-db-list-pretty nil t))) + (when arg + (setq org-roam-db-choice arg)) + + (if (string= org-roam-db-choice "Default") + (setq org-roam-directory (file-truename "~/Org/Personal/Notes") + org-roam-db-location (file-truename "~/Org/Personal/Notes/org-roam.db") + org-directory (file-truename"~/Org/Personal/Notes")) + (setq org-roam-directory (file-truename (concat "~/Org/" org-roam-db-choice "/Notes")) + org-roam-db-location (file-truename (concat "~/Org/" org-roam-db-choice "/Notes/org-roam.db")) + org-directory (file-truename (concat "~/Org/" org-roam-db-choice "/Notes")))) + (when (not silent) + (org-roam-open-dashboard)) + + (org-roam-db-sync) + + (message (concat "Switched to " org-roam-db-choice " org-roam database!"))) + +(defun org-roam-default-overview () + (interactive) + (org-roam-switch-db "Default")) + +(defun org-roam-switch-db-id-open (arg ID &optional switchpersist) + "Switch to another org-roam db and visit file with id arg" + "If switchpersist is non-nil, stay in the new org-roam db after visiting file" + (interactive) + (setq prev-org-roam-db-choice org-roam-db-choice) + (org-roam-switch-db arg 1) + (org-roam-id-open ID) + (when (not switchpersist) + (org-roam-switch-db prev-org-roam-db-choice 1))) + +;;;------ Org-roam-agenda configuration ------;;; +(defun text-in-buffer-p (TEXT) +(save-excursion (goto-char (point-min)) (search-forward TEXT nil t))) + +(defun apply-old-todos-tag-maybe (&optional FILE) + (interactive) + (if (stringp FILE) + (setq the-daily-node-filename FILE) + (setq the-daily-node-filename buffer-file-name)) + (if (org-roam-dailies--daily-note-p the-daily-node-filename) + (if (<= (nth 2 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 2 org-agenda-current-date)) + (if (<= (nth 1 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 1 org-agenda-current-date)) + (if (<= (nth 0 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 0 org-agenda-current-date)) + (funcall (lambda () + (with-current-buffer (get-file-buffer the-daily-node-filename) (org-roam-tag-add '("old-todos"))) + (with-current-buffer (get-file-buffer the-daily-node-filename) (org-roam-tag-remove '("todos"))) + ) + ) + ) + ) + ) + ) +) + +(defun apply-old-todos-tag-maybe-and-save (FILE) + (interactive) + (find-file-noselect FILE) + (apply-old-todos-tag-maybe FILE) + (with-current-buffer (get-file-buffer the-daily-node-filename) (save-buffer)) + (with-current-buffer (get-file-buffer the-daily-node-filename) (kill-buffer)) +) + +; This has a bug where it won't sync a new agenda file +; if I'm editing an org roam node file while set to another +; org roam db +(defun add-todos-tag-on-save-org-mode-file() + (interactive) + (when (string= (message "%s" major-mode) "org-mode") + (if (org-roam-node-p (org-roam-node-at-point)) + (funcall (lambda() + (if (or (text-in-buffer-p "SCHEDULED: <") (text-in-buffer-p "DEADLINE: <")) + (org-roam-tag-add '("todos")) + (org-roam-tag-remove '("todos")) + ) + (apply-old-todos-tag-maybe) + ) + ) + ) + ) +) + +(add-hook 'before-save-hook 'add-todos-tag-on-save-org-mode-file) + +(defun org-roam-filter-by-tag (tag-name) + (lambda (node) + (member tag-name (org-roam-node-tags node)))) + +(defun org-roam-list-notes-by-tag (tag-name) + (mapcar #'org-roam-node-file + (seq-filter + (org-roam-filter-by-tag tag-name) + (org-roam-node-list)))) + +(defun org-roam-dailies-apply-old-todos-tags-to-all () +; (dolist (daily-node org-roam-dailies-files) +; (apply-old-todos-tag-maybe-and-save daily-node) +; ) + (setq num 0) + (while (< num (list-length (org-roam-list-notes-by-tag "todos"))) + (apply-old-todos-tag-maybe-and-save (nth num (org-roam-list-notes-by-tag "todos"))) + (setq num (1+ num)) + ) +) + +(defun org-roam-append-notes-to-agenda (tag-name db) + (org-roam-switch-db db t) +; (org-roam-dailies-apply-old-todos-tags-to-all) + (setq org-agenda-files (append org-agenda-files (org-roam-list-notes-by-tag "todos"))) +) + +(defun org-roam-refresh-agenda-list () + (interactive) + (setq prev-org-roam-db-choice org-roam-db-choice) + (setq org-agenda-files '()) + (dolist (DB full-org-roam-db-list-pretty) + (org-roam-append-notes-to-agenda "todos" DB) + ) + (org-roam-switch-db prev-org-roam-db-choice 1) +) + +;; Build agenda for first time during this session +(org-roam-refresh-agenda-list) + +(map! :leader + :prefix ("N" . "org-roam notes") + + :desc "Capture new roam node" + "c" 'org-roam-capture + + :desc "Insert roam node link at point" + "i" 'org-roam-node-insert + + :desc "Find roam node" + "." 'org-roam-node-find + + :desc "Switch org-roam database" + "s" 'org-roam-switch-db + + :desc "Update current org-roam database" + "u" 'org-roam-db-sync + + :desc "Re-zoom on current node in org-roam-ui" + "z" 'org-roam-ui-node-zoom + + :desc "Visualize org-roam database with org-roam-ui" + "O" 'org-roam-default-overview + + :desc "Visualize org-roam database with org-roam-ui" + "o" 'org-roam-open-dashboard) + +(after! org-roam + (setq org-roam-capture-templates + '(("d" "default" plain "%?" :target + (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n") + :unnarrowed t)))) + +(defun org-roam-olivetti-mode () + (interactive) + (if (org-roam-file-p) + (olivetti-mode)) + (if (org-roam-file-p) + (doom-disable-line-numbers-h))) + +(add-hook 'org-mode-hook 'org-roam-olivetti-mode) + +(use-package org-roam-dblocks + :hook (org-mode . org-roam-dblocks-autoupdate-mode)) + +(setq org-id-extra-files 'org-agenda-text-search-extra-files) + +;(add-to-list 'display-buffer-alist '("^\\ORUI" display-buffer-in-side-window +; '(side . right) +; (window-width . 50) +;)) +;(add-to-list 'display-buffer-alist '("^\\localhost:35901" display-buffer-in-side-window +; '(side . right) +; (window-width . 50) +;)) + +(defun open-org-roam-ui () + (interactive) + (+evil/window-vsplit-and-follow) + (org-roam-ui-open) + (evil-window-left)) + +(defun kill-org-roam-ui () + (interactive) + (delete-window (get-buffer-window "ORUI" t)) + (kill-buffer "ORUI") + (kill-buffer "*httpd*") +) + +(map! :leader + :prefix ("N" . "org-roam notes") + :desc "Visualize org-roam database with org-roam-ui" + "v" 'open-org-roam-ui) + +(map! :leader + :prefix ("N" . "org-roam notes") + :desc "Kill all org roam ui buffers" + "V" 'kill-org-roam-ui) + +;;;------ Org agenda configuration ------;;; + +;; Set span for agenda +(setq org-agenda-span 1 + org-agenda-start-day "+0d") + +;; Ricing org agenda +(setq org-agenda-current-time-string "") +(setq org-agenda-time-grid '((daily) () "" "")) + +(setq org-agenda-prefix-format '( +(agenda . " %?-2i %t ") + (todo . " %i %-12:c") + (tags . " %i %-12:c") + (search . " %i %-12:c"))) + +(setq org-agenda-hide-tags-regexp ".*") + +(setq org-agenda-category-icon-alist + `(("Teaching" ,(list (all-the-icons-faicon "graduation-cap" :height 0.8)) nil nil :ascent center) + ("Family" ,(list (all-the-icons-faicon "home" :v-adjust 0.005)) nil nil :ascent center) + ("Producer" ,(list (all-the-icons-faicon "youtube-play" :height 0.9)) nil nil :ascent center) + ("Bard" ,(list (all-the-icons-faicon "music" :height 0.9)) nil nil :ascent center) + ("Story" ,(list (all-the-icons-faicon "book" :height 0.9)) nil nil :ascent center) + ("Author" ,(list (all-the-icons-faicon "pencil" :height 0.9)) nil nil :ascent center) + ("Gamedev" ,(list (all-the-icons-faicon "gamepad" :height 0.9)) nil nil :ascent center) + ("Tech" ,(list (all-the-icons-faicon "laptop" :height 0.9)) nil nil :ascent center) +)) + +;; Function to be run when org-agenda is opened +(defun org-agenda-open-hook () + "Hook to be run when org-agenda is opened" + (olivetti-mode)) + +;; Adds hook to org agenda mode, making follow mode active in org agenda +(add-hook 'org-agenda-mode-hook 'org-agenda-open-hook) + +;; Function to list all my available org agenda files and switch to them +(defun list-and-switch-to-agenda-file () + "Lists all available agenda files and switches to desired one" + (interactive) + (setq full-agenda-file-list nil) + (setq choice (completing-read "Select agenda file:" org-agenda-files nil t)) + (find-file choice)) + +(map! :leader + :desc "Switch to specific org agenda file" + "o a s" 'list-and-switch-to-agenda-file) + +(map! :leader + :desc "Open org calendar" + "o c" #'cfw:open-org-calendar) + +(require 'org-super-agenda) + +(setq org-super-agenda-groups + '(;; Each group has an implicit boolean OR operator between its selectors. + (:name "Home Tech" + :and(:file-path "librephoenix/Agenda" :not (:tag "event")) + :order 3) + + (:name "Family" + :and(:file-path "Family" :not (:tag "event")) + :order 3) + + (:name "Teaching Prep" + :and(:file-path "Teaching.p" :tag "planning" :not (:tag "grading") :not (:tag "event")) + :order 3) + + (:name "Teaching Secretarial" + :and(:file-path "Teaching.p" :tag "secretarial" :not (:tag "grading") :not (:tag "event")) + :order 3) + + (:name "Teaching Grading" + :and(:file-path "Teaching.p" :tag "grading" :not (:tag "planning") :not (:tag "event")) + :order 3) + + (:name "School Side Projects" + :and(:file-path "Teaching.p" :tag "tech" :not (:tag "planning") :not (:tag "event")) + :order 3) + + (:name "Gamedev Current Projects" + :and (:file-path "Gamedev" :todo "STRT") + :order 5) + + (:name "Youtube" + :tag "youtube" + :order 6) + + (:name "Learning" + :tag "learning" + :order 7) + + (:name "Today" ; Optionally specify section name + :time-grid t + :date today + :scheduled today + :order 1) +)) + +(org-super-agenda-mode t) + +(map! :desc "Next line" + :map org-super-agenda-header-map + "j" 'org-agenda-next-line) + +(map! :desc "Next line" + :map org-super-agenda-header-map + "k" 'org-agenda-previous-line) + +;;;------ magit configuration ------;;; + +;; Need the following two blocks to make magit work with git bare repos +(defun ~/magit-process-environment (env) + "Add GIT_DIR and GIT_WORK_TREE to ENV when in a special directory. +https://github.com/magit/magit/issues/460 (@cpitclaudel)." + (let ((default (file-name-as-directory (expand-file-name default-directory))) + (home (expand-file-name "~/"))) + (when (string= default home) + (let ((gitdir (expand-file-name "~/.dotfiles.git/"))) + (push (format "GIT_WORK_TREE=%s" home) env) + (push (format "GIT_DIR=%s" gitdir) env)))) + env) + +(advice-add 'magit-process-environment + :filter-return #'~/magit-process-environment) + +;;;------ dired configuration ------;;; + +(add-hook 'dired-mode-hook 'all-the-icons-dired-mode) + +(map! :desc "Increase font size" + "C-=" 'text-scale-increase + + :desc "Decrease font size" + "C--" 'text-scale-decrease) + +;;;------ ranger configuration ------;;; + +(map! :map ranger-mode-map + :desc "Mark current file" + "m" 'ranger-mark + + :desc "Toggle mark on current file" + "x" 'ranger-toggle-mark + + :desc "Open ranger" + "o d" 'ranger) + +;;;-- hledger-mode configuration ;;;-- + +;;; Basic configuration +(require 'hledger-mode) + +;; To open files with .journal extension in hledger-mode +(add-to-list 'auto-mode-alist '("\\.journal\\'" . hledger-mode)) + +;; The default journal location is too opinionated. +(setq hledger-jfile "/home/librephoenix/Org/Family.s/Notes/hledger.journal") + +;;; Auto-completion for account names +;; For company-mode users: +(add-to-list 'company-backends 'hledger-company) + +(evil-define-key* 'normal hledger-view-mode-map "q" 'kill-current-buffer) +(evil-define-key* 'normal hledger-view-mode-map "[" 'hledger-prev-report) +(evil-define-key* 'normal hledger-view-mode-map "]" 'hledger-next-report) + +(map! :leader + :prefix ("l" . "hledger") + :desc "Exec hledger command" + "c" 'hledger-run-command + + :desc "Generate hledger balancesheet" + "b" 'hledger-balancesheet* + + :desc "Exec hledger command" + "d" 'hledger-daily-report*) + +(map! :localleader + :map hledger-mode-map + + :desc "Reschedule transaction at point" + "d s" 'hledger-reschedule + + :desc "Edit amount at point" + "t a" 'hledger-edit-amount) + +;;;-- tab-bar-mode configuration ;;;-- + +;; Kbd tab navigation +(map! + :map evil-normal-state-map + "H" #'tab-bar-switch-to-prev-tab + "L" #'tab-bar-switch-to-next-tab + "C-" #'tab-bar-switch-to-prev-tab + "C-" #'tab-bar-switch-to-next-tab) + +(evil-global-set-key 'normal (kbd "C-w") 'tab-bar-close-tab) +(evil-global-set-key 'normal (kbd "C-t") 'tab-bar-new-tab) + +(setq tab-bar-new-tab-choice "*doom*") + +(tab-bar-mode t) + +(require 'focus) + +(map! :leader + :prefix ("F" . "Focus mode") + :desc "Toggle focus mode" + "t" 'focus-mode + + :desc "Pin focused section" + "p" 'focus-pin + + :desc "Unpin focused section" + "u" 'focus-unpin) + +(add-to-list 'focus-mode-to-thing '(org-mode . org-element)) +(add-to-list 'focus-mode-to-thing '(python-mode . paragraph)) +(add-to-list 'focus-mode-to-thing '(lisp-mode . paragraph)) + +;(add-hook 'org-mode-hook #'focus-mode) + +;;;-- Load emacs application framework;;;-- +(use-package! eaf + :load-path "~/.local/bin/emacs-application-framework/" + :init + :custom + (eaf-browser-continue-where-left-off t) + (eaf-browser-enable-adblocker t) + (browse-url-browser-function 'eaf-open-browser) ;; Make EAF Browser my default browser + :config + (defalias 'browse-web #'eaf-open-browser) + + (require 'eaf-pdf-viewer) + (require 'eaf-browser) + + (require 'eaf-evil) + (define-key key-translation-map (kbd "SPC") + (lambda (prompt) + (if (derived-mode-p 'eaf-mode) + (pcase eaf--buffer-app-name + ("browser" (if (string= (eaf-call-sync "eval_function" eaf--buffer-id "is_focus") "True") + (kbd "SPC") + (kbd eaf-evil-leader-key))) + ("pdf-viewer" (kbd eaf-evil-leader-key)) + ("image-viewer" (kbd eaf-evil-leader-key)) + (_ (kbd "SPC"))) + (kbd "SPC"))))) + +(map! :leader + :desc "Open web browser" + "o w" #'eaf-open-browser-with-history) diff --git a/user/app/doom-emacs/doom.org b/user/app/doom-emacs/doom.org new file mode 100644 index 0000000..0c08c9e --- /dev/null +++ b/user/app/doom-emacs/doom.org @@ -0,0 +1,1563 @@ +#+TITLE: Doom Emacs Literate Config +#+AUTHOR: librephoenix + +* Table of Contents :TOC:QUOTE: +#+BEGIN_QUOTE +- [[#what-is-doom-emacs][What is Doom Emacs?]] +- [[#configuration-for-doom-emacs][Configuration for Doom Emacs]] +- [[#my-configel][My config.el]] + - [[#preamble--user-configuration][Preamble + User Configuration]] + - [[#registers][Registers]] + - [[#org-mode-configuration][Org Mode Configuration]] + - [[#org-roam-configuration][Org Roam Configuration]] + - [[#org-agenda-configuration][Org Agenda Configuration]] + - [[#magit-configuration][Magit Configuration]] + - [[#dired-configuration][Dired Configuration]] + - [[#ranger-configuration][Ranger Configuration]] + - [[#hledger-mode-configuration][hledger-mode Configuration]] + - [[#tab-bar-configuration][Tab Bar Configuration]] + - [[#focus-mode-configuration][Focus Mode Configuration]] + - [[#eaf][EAF]] +- [[#my-initel][My init.el]] +- [[#my-packagesel][My packages.el]] +#+END_QUOTE + +* What is Doom Emacs? +[[https://github.com/doomemacs/doomemacs][Doom Emacs]] is a distribution of the [[https://www.gnu.org/software/emacs/][Emacs Text Editor]] designed for [[https://www.vim.org/][Vim]] users. I like to use Emacs due to its extensibility and extra features it is capable of (besides text editing). Some of these extra features include: +- [[https://orgmode.org/][Org Mode]] (Hierarchical text-based document format) +- [[https://www.orgroam.com/][Org Roam]] (A second brain / personal wiki) +- [[https://orgmode.org/][Org Agenda]] (Calendar and todo list) +- [[https://magit.vc/][magit]] (Git Client) + +I have found Emacs to be incredibly efficient, and transferring my workflow to fit inside of Emacs has allowed me to get much more work done. + +* Configuration for Doom Emacs +Doom Emacs is configured via 3 main files, written in Elisp, a dialect of the Lisp programming langauge designed for Emacs. These 3 main files are: +- [[./config.el][config.el]] - Stores your main configuration and allows to set user variables. +- [[./init.el][init.el]] - Allows quick downloads of groups of Emacs packages. These groups of Emacs packages are curated by the Doom Emacs developers. +- [[./packages.el][packages.el]] - Allows you to download additional packages from Melpa (Emacs package manager). + +By storing your configuration in these 3 files, it allows for quick reproducible builds of Doom Emacs. + +You can also load separate files inside of [[./config.el][config.el]] via the =load!= function, like so: + +#+BEGIN_SRC emacs-lisp +(load! "~/.doom.d/private.el") +#+END_SRC + +I use this functionality to load my private config file with non-public information. + +* My [[./config.el][config.el]] +** Preamble + User Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;; $DOOMDIR/config.el -*- lexical-binding: t; -*- + +;;;------ User configuration ------;;; + +;; My default user identity as my yt alias +(setq user-full-name "librephoenix") + +;; I prefer visual lines +(setq display-line-numbers-type 'visual + line-move-visual t) +(use-package-hook! evil + :pre-init + (setq evil-respect-visual-line-mode t) ;; sane j and k behavior + t) + +;; I also like evil mode visual movement +(map! :map evil-normal-state-map + :desc "Move to next visual line" + "j" 'evil-next-visual-line + :desc "Move to previous visual line" + "k" 'evil-previous-visual-line) + +;; Theme and font +(setq doom-theme 'doom-old-hope) +(setq doom-font (font-spec :family "Inconsolata" :size 20)) + +;; Transparent background +(set-frame-parameter (selected-frame) 'alpha '(90 . 90)) +(add-to-list 'default-frame-alist '(alpha . (90 . 90))) + +;; Icons in completion buffers +(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup) +(all-the-icons-completion-mode) + +;; This makes non-main buffers dimmer, so you can focus on main buffers +(solaire-global-mode +1) + +;; Grammar tasing should be voluntary +(setq writegood-mode nil) + +;; Beacon shows where the cursor is, even when fast scrolling +(setq beacon-mode t) + +;; Quicker window management keybindings +(bind-key* "C-j" #'evil-window-down) +(bind-key* "C-k" #'evil-window-up) +(bind-key* "C-h" #'evil-window-left) +(bind-key* "C-l" #'evil-window-right) +(bind-key* "C-q" #'evil-window-delete) + +;; Mouse buffer management +(bind-key* "" #'previous-buffer) +(bind-key* "" #'next-buffer) + +;; Disables custom.el +(setq custom-file null-device) + +;; Fancy splash image +(setq fancy-splash-image "~/.doom.d/arch.png") + +(setq +doom-dashboard-menu-sections +'(("Open org roam overview" :icon + (all-the-icons-octicon "globe" :face 'doom-dashboard-menu-title) + :face + (:inherit + (doom-dashboard-menu-title bold)) + :action org-roam-default-overview) + ("Roam to another db" :icon + (all-the-icons-fileicon "org" :face 'doom-dashboard-menu-title) + :action org-roam-switch-db) + ("Open agenda" :icon + (all-the-icons-octicon "calendar" :face 'doom-dashboard-menu-title) + :when + (fboundp 'org-agenda) + :action org-agenda-list + :key "SPC o A a") + ("Open private configuration" :icon + (all-the-icons-octicon "tools" :face 'doom-dashboard-menu-title) + :when + (file-directory-p doom-user-dir) + :action doom/open-private-config) + ("Open documentation" :icon + (all-the-icons-octicon "book" :face 'doom-dashboard-menu-title) + :action doom/help) + ("Quit emacs" :icon + (all-the-icons-faicon "level-down" :face 'doom-dashboard-menu-title) + :action save-buffers-kill-terminal) + ) +) + +;; Requires for faster loading +(require 'org-agenda) +(require 'dired) + +;; Garbage collection to speed things up +(add-hook 'after-init-hook + #'(lambda () + (setq gc-cons-threshold (* 100 1000 1000)))) +(add-hook 'focus-out-hook 'garbage-collect) +(run-with-idle-timer 5 t 'garbage-collect) + +;; Enable autorevert globally so that buffers update when files change on disk. +;; Very useful when used with file syncing (i.e. syncthing) +(setq global-auto-revert-mode nil) +(setq auto-revert-use-notify t) + +#+END_SRC +** Registers +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ Registers ------;;; + +(map! :leader + :desc "Jump to register" + "r" 'jump-to-register) + +(set-register ?f '(file . "/home/librephoenix/Org/Family.s/Notes/hledger.org")) +(set-register ?r '(file . "/home/librephoenix/README.org")) +(set-register ?d '(file . "/home/librephoenix/.doom.d/doom.org")) +(set-register ?h '(file . "/home/librephoenix")) +(set-register ?x '(file . "/home/librephoenix/.xmonad/xmonad.org")) +(set-register ?s '(file . "/home/librephoenix/.install/install.org")) + +#+END_SRC +** Org Mode Configuration +*** Standard Org Mode Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ Org mode configuration ------;;; + +;; Set default org directory +(setq org-directory "~/.Org") + +(remove-hook 'after-save-hook #'+literate|recompile-maybe) +(set-company-backend! 'org-mode nil) + +;; Automatically show images but manually control their size +(setq org-startup-with-inline-images t + org-image-actual-width nil) + +;; Top-level headings should be bigger! +(custom-set-faces! + '(org-level-1 :inherit outline-1 :height 1.3) + '(org-level-2 :inherit outline-2 :height 1.25) + '(org-level-3 :inherit outline-3 :height 1.2) + '(org-level-4 :inherit outline-4 :height 1.1) + '(org-level-5 :inherit outline-5 :height 1.1) + '(org-level-6 :inherit outline-6 :height 1.05) + '(org-level-7 :inherit outline-7 :height 1.05) + ) + +;(custom-set-faces! +; '(org-link :foreground nil)) + +;; Pretty org bullets +;;(use-package org-bullets +;; :ensure t +;; :init +;; (add-hook 'org-mode-hook (lambda () +;; (org-bullets-mode 1)))) + +(with-eval-after-load 'org (global-org-modern-mode)) + +;; Add frame borders and window dividers +(modify-all-frames-parameters + '((right-divider-width . 10) + (internal-border-width . 10))) +(dolist (face '(window-divider + window-divider-first-pixel + window-divider-last-pixel)) + (face-spec-reset-face face) + (set-face-foreground face (face-attribute 'default :background))) +(set-face-background 'fringe (face-attribute 'default :background)) + +(setq + ;; Edit settings + org-auto-align-tags nil + org-tags-column 0 + org-catch-invisible-edits 'show-and-error + org-special-ctrl-a/e t + org-insert-heading-respect-content t + + ;; Org styling, hide markup etc. + org-hide-emphasis-markers t + org-pretty-entities t + org-ellipsis "…") + +(setq-default line-spacing 0.1) + +; Automatic table of contents is nice +(if (require 'toc-org nil t) + (progn + (add-hook 'org-mode-hook 'toc-org-mode) + (add-hook 'markdown-mode-hook 'toc-org-mode)) + (warn "toc-org not found")) + +;;---- this block from http://fgiasson.com/blog/index.php/2016/06/21/optimal-emacs-settings-for-org-mode-for-literate-programming/ ----;; +;; Tangle Org files when we save them +(defun tangle-on-save-org-mode-file() + (when (string= (message "%s" major-mode) "org-mode") + (org-babel-tangle))) + +(add-hook 'after-save-hook 'tangle-on-save-org-mode-file) +;; ---- end block ---- ;; + +;; Better org table editing +(setq-default evil-insert-state-exit-hook '(org-update-parent-todo-statistics + t)) +(setq org-table-automatic-realign nil) + +;; Better for org source blocks +(setq electric-indent-mode nil) +(setq org-src-window-setup 'current-window) +(delete + '("^\\*Org Src" + (+popup-buffer) + (actions) + (side . bottom) + (size . 0.42) + (window-width . 40) + (window-height . 0.42) + (slot) + (vslot) + (window-parameters + (ttl) + (quit) + (select . t) + (modeline . t) + (autosave . t) + (transient . t) + (no-other-window . t))) + display-buffer-alist) + +#+END_SRC +*** Org Download, Image Capture, and Opening Files in External Programs +#+BEGIN_SRC emacs-lisp :tangle config.el +(require 'org-download) + +;; Drag-and-drop to `dired` +(add-hook 'dired-mode-hook 'org-download-enable) + +(setq org-download-screenshot-method "flameshot gui -p %s") +(after! org-download + (setq org-download-method 'directory)) + +(after! org + (setq-default org-download-image-dir "img/" + org-download-heading-lvl nil)) + +(defun my-org-screenshot () + "Take a screenshot into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq filename + (concat + (make-temp-name + (concat (buffer-file-name) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) ".png")) + (shell-command (concat "emacs-wayshot " filename)) + (insert (concat "[[" filename "]]")) + (org-display-inline-images)) + +(defun my-org-paste() + "Take an image from the clipboard into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq filename + (concat + (make-temp-name + (concat (file-name-directory (buffer-file-name)) + "img/" + (file-name-nondirectory (buffer-file-name)) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) ".png")) + (shell-command (concat "wl-paste > " filename)) + (insert (concat "[[" filename "]]")) + (org-display-inline-images)) + +(defun my-org-new-file-from-template() + "Copy a template from ~/Templates into a time stamped unique-named file in the +same directory as the org-buffer and insert a link to this file." + (interactive) + (setq template-file (completing-read "Template file:" (directory-files "~/Templates"))) + (setq filename + (concat + (make-temp-name + (concat (file-name-directory (buffer-file-name)) + "files/" + (file-name-nondirectory (buffer-file-name)) + "_" + (format-time-string "%Y%m%d_%H%M%S_")) ) (file-name-extension template-file t))) + (copy-file (concat "/home/librephoenix/Templates/" template-file) filename) + (setq prettyname (read-from-minibuffer "Pretty name:")) + (insert (concat "[[./files/" (file-name-nondirectory filename) "][" prettyname "]]")) + (org-display-inline-images)) + +(when (require 'openwith nil 'noerror) + (setq openwith-associations + (list + (list (openwith-make-extension-regexp + '("mpg" "mpeg" "mp3" "mp4" + "avi" "wmv" "wav" "mov" "flv" + "ogm" "ogg" "mkv")) + "mpv" + '(file)) + (list (openwith-make-extension-regexp + '("doc" "xls" "ppt" "odt" "ods" "odg" "odp")) + "libreoffice" + '(file)) + '("\\.lyx" "lyx" (file)) + '("\\.chm" "kchmviewer" (file)) + (list (openwith-make-extension-regexp + '("pdf" "ps" "ps.gz" "dvi")) + "atril" + '(file)) + (list (openwith-make-extension-regexp + '("kdenlive")) + "kdenlive" + '(file)) + (list (openwith-make-extension-regexp + '("kra")) + "krita" + '(file)) + (list (openwith-make-extension-regexp + '("blend" "blend1")) + "blender" + '(file)) + (list (openwith-make-extension-regexp + '("helio")) + "helio" + '(file)) + (list (openwith-make-extension-regexp + '("svg")) + "inkscape" + '(file)) + (list (openwith-make-extension-regexp + '("flp")) + "~/.local/bin/flstudio" + '(file)) + )) + (openwith-mode 1)) + +(add-to-list 'display-buffer-alist '("^*Async Shell Command*" . (display-buffer-no-window))) + +(map! :leader + :desc "Insert a screenshot" +;; "i s" 'my-org-screenshot) + "i s" 'org-download-screenshot) + +(defun org-download-clipboard-basename () + (interactive) + (setq org-download-path-last-dir org-download-image-dir) + (setq org-download-image-dir (completing-read "directory: " (-filter #'f-directory-p (directory-files-recursively "." "" t)) nil t)) + (org-download-clipboard (completing-read "basename: " '() nil nil)) + (setq org-download-image-dir org-download-path-last-dir) +) + +(map! :leader + :desc "Insert image from clipboard" + "i p" 'org-download-clipboard + "i P" 'org-download-clipboard-basename) + +(map! :leader + :desc "Create a new file from a template and insert a link at point" + "i t" 'my-org-new-file-from-template) + +#+END_SRC +*** Copy Links/Files into Clipboard +#+BEGIN_SRC emacs-lisp :tangle config.el +(defun org-copy-link-to-clipboard-at-point () + "Copy current link at point into clipboard (useful for images and links)" + (interactive) + (if (eq major-mode #'org-mode) + (link-hint-copy-link-at-point) + ) + (if (eq major-mode #'ranger-mode) + (ranger-copy-absolute-file-paths) + ) + (if (eq major-mode #'image-mode) + (image-mode-copy-file-name-as-kill) + ) + (shell-command (concat "~/.doom.d/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh " (gui-get-selection 'CLIPBOARD)) nil nil) +) + +(map! :leader + :desc "Copy link/file at point into system clipbord (C-g to escape if copying a file)" + "y y" 'org-copy-link-to-clipboard-at-point) + +#+END_SRC +**** Copy Link/File to Clipboard Helper Script +Shamelessly stolen from [[https://unix.stackexchange.com/questions/30093/copy-image-from-command-line-to-clipboard][here]] and modified for my use. +#+BEGIN_SRC shell :tangle ./scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh :tangle-mode (identity #o755) +#!/bin/sh +#command -v xclip >/dev/null 2>&1 || { echo "Need command xclip. Aborting." >&2; exit 1; } +if [[ -f "$1" ]]; then + TYPE=$(file -b --mime-type "$1") + xclip -selection clipboard -t "$TYPE" -i "$1" +else + echo $1 | xclip -selection clipboard -t text/plain &> /dev/null + exit +fi +exit +#+END_SRC +*** Org Online Images +#+BEGIN_SRC emacs-lisp :tangle config.el +;; Online images inside of org mode is pretty cool +;; This snippit is from Tobias on Stack Exchange +;; https://emacs.stackexchange.com/questions/42281/org-mode-is-it-possible-to-display-online-images +(require 'org-yt) + +(defun org-image-link (protocol link _description) + "Interpret LINK as base64-encoded image data." + (cl-assert (string-match "\\`img" protocol) nil + "Expected protocol type starting with img") + (let ((buf (url-retrieve-synchronously (concat (substring protocol 3) ":" link)))) + (cl-assert buf nil + "Download of image \"%s\" failed." link) + (with-current-buffer buf + (goto-char (point-min)) + (re-search-forward "\r?\n\r?\n") + (buffer-substring-no-properties (point) (point-max))))) + +(org-link-set-parameters + "imghttp" + :image-data-fun #'org-image-link) + +(org-link-set-parameters + "imghttps" + :image-data-fun #'org-image-link) +#+END_SRC +*** Org Mermaid Diagrams +#+BEGIN_SRC emacs-lisp :tangle config.el +;; Mermaid diagrams +(setq ob-mermaid-cli-path "/usr/bin/mmdc") +#+END_SRC +*** Org Simple Printing +#+BEGIN_SRC emacs-lisp :tangle config.el +;; Print org mode +(defun org-simple-print-buffer () + "Open an htmlized form of current buffer and open in a web browser to print" + (interactive) + (htmlize-buffer) + (browse-url-of-buffer (concat (buffer-name) ".html")) + (sleep-for 1) + (kill-buffer (concat (buffer-name) ".html"))) + +;; Doesn't work yet, bc htmlize-region takes arguments BEG and END +;(defun org-simple-print-region() +; "Open an htmlized form of current region and open in a web browser to print" +; (interactive) +; (htmlize-region ) +; (browse-url-of-buffer (concat (buffer-name) ".html")) +; (sleep-for 1) +; (kill-buffer (concat (buffer-name) ".html"))) + +(map! :leader + :prefix ("P" . "Print") + :desc "Simple print buffer in web browser" + "p" 'org-simple-print-buffer) + +(map! :leader + :prefix ("P" . "Print") + :desc "Simple print buffer in web browser" + "b" 'org-simple-print-buffer) + +;(map! :leader +; :prefix ("P" . "Print") +; :desc "Simple print region in web browser" +; "r" 'org-simple-print-region) + +#+END_SRC +*** Org -> ODP Presentation Export +#+BEGIN_SRC emacs-lisp :tangle config.el +;; Custom function to convert org mode to ODP presentation +;; Depends on bash, libreoffice, and pandoc +(defun my-ox-odp () + "Convert an org mode file to an ODP presentation." + (interactive) + (setq file-name (buffer-file-name)) + (setq output-pptx-file-name (replace-regexp-in-string "\.org" "\.pptx" (buffer-file-name))) + (setq output-odp-file-name (replace-regexp-in-string "\.org" "\.odp" (buffer-file-name))) + (setq odp-style-file-name (completing-read "Choose style: " + '("/home/librephoenix/.doom.d/scripts/ox-odp/styles/water.odp" + "/home/librephoenix/.doom.d/scripts/ox-odp/styles/dark.odp" + ) nil t)) + (shell-command (concat "~/.doom.d/scripts/ox-odp/ox-odp.sh \"" (buffer-file-name) "\" \"" odp-style-file-name "\" > /dev/null")) + ) + +(map! :leader + :desc "Convert org document to odp presentation" + "e p" 'my-ox-odp) + +#+END_SRC +**** Org -> ODP Helper Scripts +These are helper scripts for =my-ox-odp= (defined above), which converts an org document to an ODP presentation. This fundamentally functions by running a shell script ([[./scripts/ox-odp/ox-odp.sh][ox-odp.sh]]), which also calls a Python script ([[./scripts/ox-odp/ox-odp-xml-parse.py][ox-odp-xml-parse.py]]). +***** [[./scripts/ox-odp/ox-odp.sh][ox-odp.sh]] +#+BEGIN_SRC shell :tangle ./scripts/ox-odp/ox-odp.sh :tangle-mode (identity #o755) +#!/bin/sh + +filename=$1 +echo $filename +stylefile=$2 +echo $stylefile + +filenamebase=$(basename "$filename") +filenameext="${filenamebase##*.}" +echo $filenameext + +if [ $filenameext = "org" ]; then + stylefilebase=$(basename "$stylefile") + stylefileext="${stylefilebase##*.}" + + if [ $stylefileext = "odp" ]; then + output="${filename//\.org/\.pptx}" + finaloutput="${filename//\.org/\.odp}" + pandoc "$filename" -o "$output" + soffice --convert-to odp "$output" + unzip "$finaloutput" content.xml + unzip "$stylefile" styles.xml + + sed 's~~~g' content.xml + sed 's~~~g' content.xml + + python3 ~/.doom.d/scripts/ox-odp/ox-odp-xml-parse.py + + zip -d $finaloutput styles.xml + zip -m $finaloutput styles.xml + + zip -d $finaloutput content.xml + zip -m $finaloutput content.xml + + rm $output + + exit + + else + echo "Style file is not an odp file." + fi +else + echo "Base file is not an org file." + exit +fi + +exit + +#+END_SRC +***** [[./scripts/ox-odp/ox-odp-xml-parse.py][ox-odp-xml-parse.py]] +This script parses through the raw LibreOffice XML to fix some common formatting errors with the standard Org to pptx to odp conversion strategy. +#+BEGIN_SRC python :tangle ./scripts/ox-odp/ox-odp-xml-parse.py :tangle-mode (identity #o755) +#!/usr/bin/env python3 + +import xml.etree.ElementTree as ET +import copy + +# Read content.xml into parser +mytree = ET.parse('./content.xml') +myroot = mytree.getroot() + +# Read styles.xml into parser +styletree = ET.parse('./styles.xml') +styleroot = styletree.getroot() + +# Remove direct-formatting from text:style-name attributes in text:p elements +counter = 0 +for text in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}p'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in text.keys(): + stylename = text.attrib['{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name'] + if stylename[0] == "P": + counter += 1 + text.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name') +print('Deleted '+str(counter)+' text:style-name attributes in text:p elements.') + +# Remove direct-formatting from text:style-name attributes in text:span elements +counter = 0 +for span in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}span'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in span.keys(): + span.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name') +print('Deleted '+str(counter)+' text:style-name attributes in text:span elements.') + +# Remove direct-formatting from draw:text-style-name attributes in draw:frame elements +counter = 0 +for drawing in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}frame'): + if '{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name' in drawing.keys(): + stylename = drawing.attrib['{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name'] + if stylename[0] == "P": + counter += 1 + drawing.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name') +print('Deleted '+str(counter)+' draw:text-style-name attributes in text:p elements.') + +# Redefine default styles (style:style elements) and purge unnecessary ones +counter = 0 +kounter = 0 +for style in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style'): + if '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name' in style.keys(): + stylename = style.attrib['{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name'] + if stylename == "pr1": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-title') + elif stylename == "pr2": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-subtitle') + elif stylename == "pr3": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-notes') + elif stylename == "pr4": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-outline1') +print('Redefined '+str(counter)+' style:parent-style-name attributes in style:style elements.') +print('Deleted '+str(kounter)+' style:style elements.') + +# Search for automatic-styles element +i = 0 +col1 = 0 +while (i < len(myroot)): + print(myroot[i].tag) + if myroot[i].tag=="{urn:oasis:names:tc:opendocument:xmlns:office:1.0}automatic-styles": + col1 = i + i += 1 + +# Remove unnecessary style:style and test:list-style elements underneath automatic-styles +i = 0 +while (i < len(myroot[col1])): + if (myroot[col1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style"): + if ("{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name" in myroot[col1][i].keys()): + if myroot[col1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"] in ["pr5","pr6","pr7","pr8","pr9"]: + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + elif myroot[col1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"][0] == "P": + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + if (myroot[col1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list-style"): + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + i += 1 + +#i = 0 +#while (i < len(myroot[col1])): +# print(myroot[col1][i].attrib) +# i += 1 + +# Find ML1 in styles.xml and copy it into L1 in content.xml +# Search for automatic-styles element +i = 0 +stylecol1 = 0 +while (i < len(styleroot)): + print(styleroot[i].tag) + if styleroot[i].tag=="{urn:oasis:names:tc:opendocument:xmlns:office:1.0}automatic-styles": + stylecol1 = i + i += 1 + +# Remove unnecessary style:style and test:list-style elements underneath automatic-styles +i = 0 +while (i < len(styleroot[stylecol1])): + if (styleroot[stylecol1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list-style"): + if (styleroot[stylecol1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"] == "ML1"): + liststyle_copy = copy.deepcopy(styleroot[stylecol1][i]) + myroot[col1].append(liststyle_copy) + myroot[col1][-1].attrib['{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name'] = "L1" + i += 1 + +# Update presentation:style-name attribute of all draw:frame elements +counter = 0 +for frame in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}frame'): + if '{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}class' in frame.keys(): + classname = frame.attrib['{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}class'] + if classname == "title": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr1') + elif classname == "subtitle": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr2') + elif classname == "notes": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr3') + elif classname == "outline": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr4') +print("Updated "+str(counter)+" draw:frame elements") + +# Update draw:master-page-name attributes in all draw:page elements +# Also delete all presentation:presentation-page-layout attributes +counter = 0 +for page in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}page'): + if '{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}master-page-name' in page.keys(): + page.set('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}master-page-name','DefaultTheme') + counter += 1 + if '{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}presentation-page-layout' in page.keys(): + page.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}presentation-page-layout') + +print("Updated "+str(counter)+" draw:page elements") + +# Update all text:list elements to have text:style-name = L1 +counter = 0 +for page in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in page.keys(): + page.set('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name','L1') + counter += 1 + +print("Updated "+str(counter)+" text:list elements") + +#mytree.canonicalize(out='content.xml') +mytree.write('content.xml') +styletree.write('styles.xml') + +#+END_SRC +** Org Roam Configuration +*** Standard Org Roam Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ Org roam configuration ------;;; + +(require 'org-roam) +(require 'org-roam-dailies) + +(setq org-roam-directory "~/Org/Personal/Notes" + org-roam-db-location "~/Org/Personal/Notes/org-roam.db") + +(setq org-roam-node-display-template + "${title:65}📝${tags:*}") + +(org-roam-db-autosync-mode) + +#+END_SRC +*** Multi Org Roam Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +(setq full-org-roam-db-list nil) + +(setq full-org-roam-db-list (directory-files "~/Org" t "\\.[p,s]$")) +(dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list + (append (directory-files item t "\\.[p,s]$") full-org-roam-db-list))) + +(setq org-roam-db-choice "Default") +(setq full-org-roam-db-list-pretty (list "Default")) +(dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list-pretty + (append (list + (replace-regexp-in-string "\\/home\\/librephoenix\\/Org\\/" "" item)) full-org-roam-db-list-pretty))) + +(defun org-roam-open-dashboard () + "Open ${org-roam-directory}/dashboard.org (I use this naming convention to create dashboards for each of my org roam maps)" + (interactive) + (if (file-exists-p (concat org-roam-directory "/dashboard.org")) + (org-open-file (concat org-roam-directory "/dashboard.org")) + (dired org-roam-directory)) +) + +(defun org-roam-switch-db (&optional arg silent) + "Switch to a different org-roam database, arg" + (interactive) + (when (not arg) + (setq full-org-roam-db-list nil) + + (setq full-org-roam-db-list (directory-files "~/Org" t "\\.[p,s]$")) + (dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list + (append (directory-files item t "\\.[p,s]$") full-org-roam-db-list))) + + (setq full-org-roam-db-list-pretty (list "Default")) + (dolist (item full-org-roam-db-list) + (setq full-org-roam-db-list-pretty + (append (list + (replace-regexp-in-string "\\/home\\/librephoenix\\/Org\\/" "" item)) full-org-roam-db-list-pretty))) + + (setq org-roam-db-choice (completing-read "Select org roam database: " + full-org-roam-db-list-pretty nil t))) + (when arg + (setq org-roam-db-choice arg)) + + (if (string= org-roam-db-choice "Default") + (setq org-roam-directory (file-truename "~/Org/Personal/Notes") + org-roam-db-location (file-truename "~/Org/Personal/Notes/org-roam.db") + org-directory (file-truename"~/Org/Personal/Notes")) + (setq org-roam-directory (file-truename (concat "~/Org/" org-roam-db-choice "/Notes")) + org-roam-db-location (file-truename (concat "~/Org/" org-roam-db-choice "/Notes/org-roam.db")) + org-directory (file-truename (concat "~/Org/" org-roam-db-choice "/Notes")))) + (when (not silent) + (org-roam-open-dashboard)) + + (org-roam-db-sync) + + (message (concat "Switched to " org-roam-db-choice " org-roam database!"))) + +(defun org-roam-default-overview () + (interactive) + (org-roam-switch-db "Default")) + +(defun org-roam-switch-db-id-open (arg ID &optional switchpersist) + "Switch to another org-roam db and visit file with id arg" + "If switchpersist is non-nil, stay in the new org-roam db after visiting file" + (interactive) + (setq prev-org-roam-db-choice org-roam-db-choice) + (org-roam-switch-db arg 1) + (org-roam-id-open ID) + (when (not switchpersist) + (org-roam-switch-db prev-org-roam-db-choice 1))) + +#+END_SRC +*** Org Roam "todos" Tagging for Org Agenda +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ Org-roam-agenda configuration ------;;; +(defun text-in-buffer-p (TEXT) +(save-excursion (goto-char (point-min)) (search-forward TEXT nil t))) + +(defun apply-old-todos-tag-maybe (&optional FILE) + (interactive) + (if (stringp FILE) + (setq the-daily-node-filename FILE) + (setq the-daily-node-filename buffer-file-name)) + (if (org-roam-dailies--daily-note-p the-daily-node-filename) + (if (<= (nth 2 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 2 org-agenda-current-date)) + (if (<= (nth 1 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 1 org-agenda-current-date)) + (if (<= (nth 0 (org-roam-dailies-calendar--file-to-date the-daily-node-filename)) (nth 0 org-agenda-current-date)) + (funcall (lambda () + (with-current-buffer (get-file-buffer the-daily-node-filename) (org-roam-tag-add '("old-todos"))) + (with-current-buffer (get-file-buffer the-daily-node-filename) (org-roam-tag-remove '("todos"))) + ) + ) + ) + ) + ) + ) +) + +(defun apply-old-todos-tag-maybe-and-save (FILE) + (interactive) + (find-file-noselect FILE) + (apply-old-todos-tag-maybe FILE) + (with-current-buffer (get-file-buffer the-daily-node-filename) (save-buffer)) + (with-current-buffer (get-file-buffer the-daily-node-filename) (kill-buffer)) +) + +; This has a bug where it won't sync a new agenda file +; if I'm editing an org roam node file while set to another +; org roam db +(defun add-todos-tag-on-save-org-mode-file() + (interactive) + (when (string= (message "%s" major-mode) "org-mode") + (if (org-roam-node-p (org-roam-node-at-point)) + (funcall (lambda() + (if (or (text-in-buffer-p "SCHEDULED: <") (text-in-buffer-p "DEADLINE: <")) + (org-roam-tag-add '("todos")) + (org-roam-tag-remove '("todos")) + ) + (apply-old-todos-tag-maybe) + ) + ) + ) + ) +) + +(add-hook 'before-save-hook 'add-todos-tag-on-save-org-mode-file) + +#+END_SRC +*** Setup Org Agenda from Org Roam +#+BEGIN_SRC emacs-lisp :tangle config.el +(defun org-roam-filter-by-tag (tag-name) + (lambda (node) + (member tag-name (org-roam-node-tags node)))) + +(defun org-roam-list-notes-by-tag (tag-name) + (mapcar #'org-roam-node-file + (seq-filter + (org-roam-filter-by-tag tag-name) + (org-roam-node-list)))) + +(defun org-roam-dailies-apply-old-todos-tags-to-all () +; (dolist (daily-node org-roam-dailies-files) +; (apply-old-todos-tag-maybe-and-save daily-node) +; ) + (setq num 0) + (while (< num (list-length (org-roam-list-notes-by-tag "todos"))) + (apply-old-todos-tag-maybe-and-save (nth num (org-roam-list-notes-by-tag "todos"))) + (setq num (1+ num)) + ) +) + +(defun org-roam-append-notes-to-agenda (tag-name db) + (org-roam-switch-db db t) +; (org-roam-dailies-apply-old-todos-tags-to-all) + (setq org-agenda-files (append org-agenda-files (org-roam-list-notes-by-tag "todos"))) +) + +(defun org-roam-refresh-agenda-list () + (interactive) + (setq prev-org-roam-db-choice org-roam-db-choice) + (setq org-agenda-files '()) + (dolist (DB full-org-roam-db-list-pretty) + (org-roam-append-notes-to-agenda "todos" DB) + ) + (org-roam-switch-db prev-org-roam-db-choice 1) +) + +;; Build agenda for first time during this session +(org-roam-refresh-agenda-list) + +#+END_SRC +*** Org Roam Keybindings +#+BEGIN_SRC emacs-lisp :tangle config.el +(map! :leader + :prefix ("N" . "org-roam notes") + + :desc "Capture new roam node" + "c" 'org-roam-capture + + :desc "Insert roam node link at point" + "i" 'org-roam-node-insert + + :desc "Find roam node" + "." 'org-roam-node-find + + :desc "Switch org-roam database" + "s" 'org-roam-switch-db + + :desc "Update current org-roam database" + "u" 'org-roam-db-sync + + :desc "Re-zoom on current node in org-roam-ui" + "z" 'org-roam-ui-node-zoom + + :desc "Visualize org-roam database with org-roam-ui" + "O" 'org-roam-default-overview + + :desc "Visualize org-roam database with org-roam-ui" + "o" 'org-roam-open-dashboard) + +#+END_SRC +*** Org Roam Capture Templates +#+BEGIN_SRC emacs-lisp :tangle config.el +(after! org-roam + (setq org-roam-capture-templates + '(("d" "default" plain "%?" :target + (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n") + :unnarrowed t)))) + +#+END_SRC +*** Org Roam Olivetti Mode +#+BEGIN_SRC emacs-lisp :tangle config.el +(defun org-roam-olivetti-mode () + (interactive) + (if (org-roam-file-p) + (olivetti-mode)) + (if (org-roam-file-p) + (doom-disable-line-numbers-h))) + +(add-hook 'org-mode-hook 'org-roam-olivetti-mode) + +#+END_SRC +*** Org Roam Dynamic Blocks +#+BEGIN_SRC emacs-lisp :tangle config.el +(use-package org-roam-dblocks + :hook (org-mode . org-roam-dblocks-autoupdate-mode)) + +#+END_SRC +*** Org Roam Export Setup +#+BEGIN_SRC emacs-lisp :tangle config.el +(setq org-id-extra-files 'org-agenda-text-search-extra-files) + +#+END_SRC +*** TODO Org Roam UI Setup +I want this to be able to automatically open ORUI in EAF Browser in a split to the right. This kinda works now? +#+BEGIN_SRC emacs-lisp :tangle config.el +;(add-to-list 'display-buffer-alist '("^\\ORUI" display-buffer-in-side-window +; '(side . right) +; (window-width . 50) +;)) +;(add-to-list 'display-buffer-alist '("^\\localhost:35901" display-buffer-in-side-window +; '(side . right) +; (window-width . 50) +;)) + +(defun open-org-roam-ui () + (interactive) + (+evil/window-vsplit-and-follow) + (org-roam-ui-open) + (evil-window-left)) + +(defun kill-org-roam-ui () + (interactive) + (delete-window (get-buffer-window "ORUI" t)) + (kill-buffer "ORUI") + (kill-buffer "*httpd*") +) + +(map! :leader + :prefix ("N" . "org-roam notes") + :desc "Visualize org-roam database with org-roam-ui" + "v" 'open-org-roam-ui) + +(map! :leader + :prefix ("N" . "org-roam notes") + :desc "Kill all org roam ui buffers" + "V" 'kill-org-roam-ui) + +#+END_SRC +** Org Agenda Configuration +*** Standard Org Agenda Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ Org agenda configuration ------;;; + +;; Set span for agenda +(setq org-agenda-span 1 + org-agenda-start-day "+0d") + +;; Ricing org agenda +(setq org-agenda-current-time-string "") +(setq org-agenda-time-grid '((daily) () "" "")) + +(setq org-agenda-prefix-format '( +(agenda . " %?-2i %t ") + (todo . " %i %-12:c") + (tags . " %i %-12:c") + (search . " %i %-12:c"))) + +(setq org-agenda-hide-tags-regexp ".*") + +(setq org-agenda-category-icon-alist + `(("Teaching" ,(list (all-the-icons-faicon "graduation-cap" :height 0.8)) nil nil :ascent center) + ("Family" ,(list (all-the-icons-faicon "home" :v-adjust 0.005)) nil nil :ascent center) + ("Producer" ,(list (all-the-icons-faicon "youtube-play" :height 0.9)) nil nil :ascent center) + ("Bard" ,(list (all-the-icons-faicon "music" :height 0.9)) nil nil :ascent center) + ("Story" ,(list (all-the-icons-faicon "book" :height 0.9)) nil nil :ascent center) + ("Author" ,(list (all-the-icons-faicon "pencil" :height 0.9)) nil nil :ascent center) + ("Gamedev" ,(list (all-the-icons-faicon "gamepad" :height 0.9)) nil nil :ascent center) + ("Tech" ,(list (all-the-icons-faicon "laptop" :height 0.9)) nil nil :ascent center) +)) + +;; Function to be run when org-agenda is opened +(defun org-agenda-open-hook () + "Hook to be run when org-agenda is opened" + (olivetti-mode)) + +;; Adds hook to org agenda mode, making follow mode active in org agenda +(add-hook 'org-agenda-mode-hook 'org-agenda-open-hook) + +#+END_SRC +*** Org Agenda Convenience Functions +#+BEGIN_SRC emacs-lisp :tangle config.el +;; Function to list all my available org agenda files and switch to them +(defun list-and-switch-to-agenda-file () + "Lists all available agenda files and switches to desired one" + (interactive) + (setq full-agenda-file-list nil) + (setq choice (completing-read "Select agenda file:" org-agenda-files nil t)) + (find-file choice)) + +(map! :leader + :desc "Switch to specific org agenda file" + "o a s" 'list-and-switch-to-agenda-file) + +(map! :leader + :desc "Open org calendar" + "o c" #'cfw:open-org-calendar) + +#+END_SRC +*** Org Super Agenda Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +(require 'org-super-agenda) + +(setq org-super-agenda-groups + '(;; Each group has an implicit boolean OR operator between its selectors. + (:name "Home Tech" + :and(:file-path "librephoenix/Agenda" :not (:tag "event")) + :order 3) + + (:name "Family" + :and(:file-path "Family" :not (:tag "event")) + :order 3) + + (:name "Teaching Prep" + :and(:file-path "Teaching.p" :tag "planning" :not (:tag "grading") :not (:tag "event")) + :order 3) + + (:name "Teaching Secretarial" + :and(:file-path "Teaching.p" :tag "secretarial" :not (:tag "grading") :not (:tag "event")) + :order 3) + + (:name "Teaching Grading" + :and(:file-path "Teaching.p" :tag "grading" :not (:tag "planning") :not (:tag "event")) + :order 3) + + (:name "School Side Projects" + :and(:file-path "Teaching.p" :tag "tech" :not (:tag "planning") :not (:tag "event")) + :order 3) + + (:name "Gamedev Current Projects" + :and (:file-path "Gamedev" :todo "STRT") + :order 5) + + (:name "Youtube" + :tag "youtube" + :order 6) + + (:name "Learning" + :tag "learning" + :order 7) + + (:name "Today" ; Optionally specify section name + :time-grid t + :date today + :scheduled today + :order 1) +)) + +(org-super-agenda-mode t) + +(map! :desc "Next line" + :map org-super-agenda-header-map + "j" 'org-agenda-next-line) + +(map! :desc "Next line" + :map org-super-agenda-header-map + "k" 'org-agenda-previous-line) + +#+END_SRC +** Magit Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ magit configuration ------;;; + +;; Need the following two blocks to make magit work with git bare repos +(defun ~/magit-process-environment (env) + "Add GIT_DIR and GIT_WORK_TREE to ENV when in a special directory. +https://github.com/magit/magit/issues/460 (@cpitclaudel)." + (let ((default (file-name-as-directory (expand-file-name default-directory))) + (home (expand-file-name "~/"))) + (when (string= default home) + (let ((gitdir (expand-file-name "~/.dotfiles.git/"))) + (push (format "GIT_WORK_TREE=%s" home) env) + (push (format "GIT_DIR=%s" gitdir) env)))) + env) + +(advice-add 'magit-process-environment + :filter-return #'~/magit-process-environment) + +#+END_SRC +** Dired Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ dired configuration ------;;; + +(add-hook 'dired-mode-hook 'all-the-icons-dired-mode) + +(map! :desc "Increase font size" + "C-=" 'text-scale-increase + + :desc "Decrease font size" + "C--" 'text-scale-decrease) + +#+END_SRC +** Ranger Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;------ ranger configuration ------;;; + +(map! :map ranger-mode-map + :desc "Mark current file" + "m" 'ranger-mark + + :desc "Toggle mark on current file" + "x" 'ranger-toggle-mark + + :desc "Open ranger" + "o d" 'ranger) + +#+END_SRC +** hledger-mode Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;-- hledger-mode configuration ;;;-- + +;;; Basic configuration +(require 'hledger-mode) + +;; To open files with .journal extension in hledger-mode +(add-to-list 'auto-mode-alist '("\\.journal\\'" . hledger-mode)) + +;; The default journal location is too opinionated. +(setq hledger-jfile "/home/librephoenix/Org/Family.s/Notes/hledger.journal") + +;;; Auto-completion for account names +;; For company-mode users: +(add-to-list 'company-backends 'hledger-company) + +(evil-define-key* 'normal hledger-view-mode-map "q" 'kill-current-buffer) +(evil-define-key* 'normal hledger-view-mode-map "[" 'hledger-prev-report) +(evil-define-key* 'normal hledger-view-mode-map "]" 'hledger-next-report) + +(map! :leader + :prefix ("l" . "hledger") + :desc "Exec hledger command" + "c" 'hledger-run-command + + :desc "Generate hledger balancesheet" + "b" 'hledger-balancesheet* + + :desc "Exec hledger command" + "d" 'hledger-daily-report*) + +(map! :localleader + :map hledger-mode-map + + :desc "Reschedule transaction at point" + "d s" 'hledger-reschedule + + :desc "Edit amount at point" + "t a" 'hledger-edit-amount) + +#+END_SRC +** Tab Bar Configuration +I don't have this active right now since I'm exploring tab-bar mode instead! +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;-- tab-bar-mode configuration ;;;-- + +;; Kbd tab navigation +(map! + :map evil-normal-state-map + "H" #'tab-bar-switch-to-prev-tab + "L" #'tab-bar-switch-to-next-tab + "C-" #'tab-bar-switch-to-prev-tab + "C-" #'tab-bar-switch-to-next-tab) + +(evil-global-set-key 'normal (kbd "C-w") 'tab-bar-close-tab) +(evil-global-set-key 'normal (kbd "C-t") 'tab-bar-new-tab) + +(setq tab-bar-new-tab-choice "*doom*") + +(tab-bar-mode t) + +#+END_SRC +** Focus Mode Configuration +#+BEGIN_SRC emacs-lisp :tangle config.el +(require 'focus) + +(map! :leader + :prefix ("F" . "Focus mode") + :desc "Toggle focus mode" + "t" 'focus-mode + + :desc "Pin focused section" + "p" 'focus-pin + + :desc "Unpin focused section" + "u" 'focus-unpin) + +(add-to-list 'focus-mode-to-thing '(org-mode . org-element)) +(add-to-list 'focus-mode-to-thing '(python-mode . paragraph)) +(add-to-list 'focus-mode-to-thing '(lisp-mode . paragraph)) + +;(add-hook 'org-mode-hook #'focus-mode) + +#+END_SRC +** EAF +#+BEGIN_SRC emacs-lisp :tangle config.el +;;;-- Load emacs application framework;;;-- +(use-package! eaf + :load-path "~/.local/bin/emacs-application-framework/" + :init + :custom + (eaf-browser-continue-where-left-off t) + (eaf-browser-enable-adblocker t) + (browse-url-browser-function 'eaf-open-browser) ;; Make EAF Browser my default browser + :config + (defalias 'browse-web #'eaf-open-browser) + + (require 'eaf-pdf-viewer) + (require 'eaf-browser) + + (require 'eaf-evil) + (define-key key-translation-map (kbd "SPC") + (lambda (prompt) + (if (derived-mode-p 'eaf-mode) + (pcase eaf--buffer-app-name + ("browser" (if (string= (eaf-call-sync "eval_function" eaf--buffer-id "is_focus") "True") + (kbd "SPC") + (kbd eaf-evil-leader-key))) + ("pdf-viewer" (kbd eaf-evil-leader-key)) + ("image-viewer" (kbd eaf-evil-leader-key)) + (_ (kbd "SPC"))) + (kbd "SPC"))))) + +(map! :leader + :desc "Open web browser" + "o w" #'eaf-open-browser-with-history) + +#+END_SRC +* My [[./init.el][init.el]] +This section is the [[./init.el][init.el]] section, which controls which Doom modules are loaded. + +=SPC h d h= (vim) or =C-h d h= (non-vim) can be used to access Doom's documentation (including a "Module Index"). + +=K= (vim) or =C-c c k= (non-vim) can be used to view a module's documentation (this can help you discover module flags as well). + +=gd= (vim) or =C-c c d= (non-vim) will let you browse a module's directory (source code). + +#+BEGIN_SRC emacs-lisp :tangle init.el +(doom! :input + ;;chinese + ;;japanese + ;;layout ; auie,ctsrnm is the superior home row + + :completion + company ; the ultimate code completion backend + ;;helm ; the *other* search engine for love and life + ;;ido ; the other *other* search engine... + ;;ivy ; a search engine for love and life + vertico ; the search engine of the future + + :ui + ;;deft ; notational velocity for Emacs + doom ; what makes DOOM look the way it does + doom-dashboard ; a nifty splash screen for Emacs + doom-quit ; DOOM quit-message prompts when you quit Emacs + (emoji +unicode) ; 🙂 + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + ;;hydra + ;;indent-guides ; highlighted indent columns + ;;ligatures ; ligatures and symbols to make your code pretty again + ;;minimap ; show a map of the code on the side + modeline ; snazzy, Atom-inspired modeline, plus API + nav-flash ; blink cursor line after big motions + neotree ; a project drawer, like NERDTree for vim + ophints ; highlight the region an operation acts on + (popup +defaults) ; tame sudden yet inevitable temporary windows + ;;tabs ; a tab bar for Emacs + treemacs ; a project drawer, like neotree but cooler + unicode ; extended unicode support for various languages + vc-gutter ; vcs diff in the fringe + vi-tilde-fringe ; fringe tildes to mark beyond EOB + window-select ; visually switch windows + workspaces ; tab emulation, persistence & separate workspaces + ;;zen ; distraction-free coding or writing + + :editor + (evil +everywhere); come to the dark side, we have cookies + file-templates ; auto-snippets for empty files + fold ; (nigh) universal code folding + (format +onsave) ; automated prettiness + ;;god ; run Emacs commands without modifier keys + ;;lispy ; vim for lisp, for people who don't like vim + ;;multiple-cursors ; editing in many places at once + ;;objed ; text object editing for the innocent + ;;parinfer ; turn lisp into python, sort of + ;;rotate-text ; cycle region at point between text candidates + snippets ; my elves. They type so I don't have to + word-wrap ; soft wrapping with language-aware indent + + :emacs + (dired +ranger) ; making dired pretty [functional] + electric ; smarter, keyword-based electric-indent + ibuffer ; interactive buffer management + undo ; persistent, smarter undo for your inevitable mistakes + vc ; version-control and Emacs, sitting in a tree + + :term + eshell ; the elisp shell that works everywhere + ;;shell ; simple shell REPL for Emacs + ;;term ; basic terminal emulator for Emacs + vterm ; the best terminal emulation in Emacs + + :checkers + syntax ; tasing you for every semicolon you forget + (spell +flyspell) ; tasing you for misspelling mispelling + ;;grammar ; tasing grammar mistake every you make + + :tools + ;;ansible + ;;biblio ; Writes a PhD for you (citation needed) + ;;debugger ; FIXME stepping through code, to help you add bugs + ;;direnv + ;;docker + ;;editorconfig ; let someone else argue about tabs vs spaces + ;;ein ; tame Jupyter notebooks with emacs + (eval +overlay) ; run code, run (also, repls) + ;;gist ; interacting with github gists + lookup ; navigate your code and its documentation + lsp ; M-x vscode + magit ; a git porcelain for Emacs + ;;make ; run make tasks from Emacs + ;;pass ; password manager for nerds + ;;pdf ; pdf enhancements + ;;prodigy ; FIXME managing external services & code builders + rgb ; creating color strings + ;;taskrunner ; taskrunner for all your projects + ;;terraform ; infrastructure as code + ;;tmux ; an API for interacting with tmux + ;;upload ; map local to remote projects via ssh/ftp + + :os + ;;(:if IS-MAC macos) ; improve compatibility with macOS + tty ; improve the terminal Emacs experience + + :lang + ;;agda ; types of types of types of types... + ;;beancount ; mind the GAAP + cc ; C > C++ == 1 + ;;clojure ; java with a lisp + common-lisp ; if you've seen one lisp, you've seen them all + ;;coq ; proofs-as-programs + ;;crystal ; ruby at the speed of c + ;;csharp ; unity, .NET, and mono shenanigans + data ; config/data formats + ;;(dart +flutter) ; paint ui and not much else + ;;dhall + ;;elixir ; erlang done right + ;;elm ; care for a cup of TEA? + emacs-lisp ; drown in parentheses + ;;erlang ; an elegant language for a more civilized age + ;;ess ; emacs speaks statistics + ;;factor + ;;faust ; dsp, but you get to keep your soul + ;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER) + ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 + gdscript ; the language you waited for + ;;(go +lsp) ; the hipster dialect + (haskell +lsp) ; a language that's lazier than I am + ;;hy ; readability of scheme w/ speed of python + ;;idris ; a language you can depend on + json ; At least it ain't XML + ;;(java +meghanada) ; the poster child for carpal tunnel syndrome + ;;javascript ; all(hope(abandon(ye(who(enter(here)))))) + ;;julia ; a better, faster MATLAB + ;;kotlin ; a better, slicker Java(Script) + latex ; writing papers in Emacs has never been so fun + ;;lean ; for folks with too much to prove + ;;ledger ; be audit you can be + lua ; one-based indices? one-based indices + markdown ; writing docs for people to ignore + ;;nim ; python + lisp at the speed of c + ;;nix ; I hereby declare "nix geht mehr!" + ;;ocaml ; an objective camel + (org +roam2) ; organize your plain life in plain text + ;;php ; perl's insecure younger brother + ;;plantuml ; diagrams for confusing people more + ;;purescript ; javascript, but functional + python ; beautiful is better than ugly + ;;qt ; the 'cutest' gui framework ever + ;;racket ; a DSL for DSLs + ;;raku ; the artist formerly known as perl6 + ;;rest ; Emacs as a REST client + ;;rst ; ReST in peace + ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + ;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + ;;scala ; java, but good + ;;(scheme +guile) ; a fully conniving family of lisps + sh ; she sells {ba,z,fi}sh shells on the C xor + ;;sml + ;;solidity ; do you need a blockchain? No. + ;;swift ; who asked for emoji variables? + ;;terra ; Earth and Moon in alignment for performance. + web ; the tubes + yaml ; JSON, but readable + ;;zig ; C, but simpler + + :email + ;;(mu4e +org) + ;;notmuch + ;;(wanderlust +gmail) + + :app + calendar + ;;emms + ;;everywhere ; *leave* Emacs!? You must be joking + ;;irc ; how neckbeards socialize + ;;(rss +org) ; emacs as an RSS reader + ;;twitter ; twitter client https://twitter.com/vnought + + :config + ;;literate + (default +bindings +smartparens)) + +#+END_SRC + +* My [[./packages.el][packages.el]] +The [[./packages.el][packages.el]] file allows extra packages to be configured outside of the typical Doom modules from [[./init.el][init.el]]. + +Packages are declared via =(package! some-package)= where =some-package= is from MELPA, ELPA, or emacsmirror. + +There are other ways to install packages outside of Emacs package archives, including directly from git. Installing a package directly from git requires a =:recipe=. Here is [[https://github.com/raxod502/straight.el#the-recipe-format][a full documentation of the recipe format]]. + +Doom's built-in packages can also be modified here: +- =(package! builtin-package :disable t)= to disable +- =(package! builtin-package-2 :recipe (:repo "myfork/package"))= to override the recipe + - Side-note: the full recipe for built-in packages does not need specification, as the override will inherit the unspecified properties directly from Doom + +Any git package can be configured for a particular commit or branch: +- =(package! builtin-package :recipe (:branch "develop")= for a particular branch +- =(package! builtin-package :pin "1a2b3c4d5e")= for a particular commit +- =(unpin! pinned-package another-pinned-package)= to get bleeding edge instead of Doom's stability + +#+BEGIN_SRC emacs-lisp :tangle packages.el +;;(package! org-bullets) +(package! org-modern) +(package! org-super-agenda) +(package! emacsql :pin "c1a4407") +(package! org-roam-ui) +(package! org-roam-nursery :recipe (:repo "https://github.com/chrisbarrett/nursery")) +(package! lister) +(package! org-download) +(package! org-yt) +(package! toc-org) +(package! all-the-icons-dired) +(package! all-the-icons-completion) +(package! ox-reveal) +(package! hledger-mode) +(package! rainbow-mode) +(package! crdt) +(package! ess) +(package! openwith) +(package! ob-mermaid) +(package! focus) +(package! olivetti) +#+END_SRC diff --git a/user/app/doom-emacs/init.el b/user/app/doom-emacs/init.el new file mode 100644 index 0000000..d9356c9 --- /dev/null +++ b/user/app/doom-emacs/init.el @@ -0,0 +1,175 @@ +(doom! :input + ;;chinese + ;;japanese + ;;layout ; auie,ctsrnm is the superior home row + + :completion + company ; the ultimate code completion backend + ;;helm ; the *other* search engine for love and life + ;;ido ; the other *other* search engine... + ;;ivy ; a search engine for love and life + vertico ; the search engine of the future + + :ui + ;;deft ; notational velocity for Emacs + doom ; what makes DOOM look the way it does + doom-dashboard ; a nifty splash screen for Emacs + doom-quit ; DOOM quit-message prompts when you quit Emacs + (emoji +unicode) ; 🙂 + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + ;;hydra + ;;indent-guides ; highlighted indent columns + ;;ligatures ; ligatures and symbols to make your code pretty again + ;;minimap ; show a map of the code on the side + modeline ; snazzy, Atom-inspired modeline, plus API + nav-flash ; blink cursor line after big motions + neotree ; a project drawer, like NERDTree for vim + ophints ; highlight the region an operation acts on + (popup +defaults) ; tame sudden yet inevitable temporary windows + ;;tabs ; a tab bar for Emacs + treemacs ; a project drawer, like neotree but cooler + unicode ; extended unicode support for various languages + vc-gutter ; vcs diff in the fringe + vi-tilde-fringe ; fringe tildes to mark beyond EOB + window-select ; visually switch windows + workspaces ; tab emulation, persistence & separate workspaces + ;;zen ; distraction-free coding or writing + + :editor + (evil +everywhere); come to the dark side, we have cookies + file-templates ; auto-snippets for empty files + fold ; (nigh) universal code folding + (format +onsave) ; automated prettiness + ;;god ; run Emacs commands without modifier keys + ;;lispy ; vim for lisp, for people who don't like vim + ;;multiple-cursors ; editing in many places at once + ;;objed ; text object editing for the innocent + ;;parinfer ; turn lisp into python, sort of + ;;rotate-text ; cycle region at point between text candidates + snippets ; my elves. They type so I don't have to + word-wrap ; soft wrapping with language-aware indent + + :emacs + (dired +ranger) ; making dired pretty [functional] + electric ; smarter, keyword-based electric-indent + ibuffer ; interactive buffer management + undo ; persistent, smarter undo for your inevitable mistakes + vc ; version-control and Emacs, sitting in a tree + + :term + eshell ; the elisp shell that works everywhere + ;;shell ; simple shell REPL for Emacs + ;;term ; basic terminal emulator for Emacs + vterm ; the best terminal emulation in Emacs + + :checkers + syntax ; tasing you for every semicolon you forget + (spell +flyspell) ; tasing you for misspelling mispelling + ;;grammar ; tasing grammar mistake every you make + + :tools + ;;ansible + ;;biblio ; Writes a PhD for you (citation needed) + ;;debugger ; FIXME stepping through code, to help you add bugs + ;;direnv + ;;docker + ;;editorconfig ; let someone else argue about tabs vs spaces + ;;ein ; tame Jupyter notebooks with emacs + (eval +overlay) ; run code, run (also, repls) + ;;gist ; interacting with github gists + lookup ; navigate your code and its documentation + lsp ; M-x vscode + magit ; a git porcelain for Emacs + ;;make ; run make tasks from Emacs + ;;pass ; password manager for nerds + ;;pdf ; pdf enhancements + ;;prodigy ; FIXME managing external services & code builders + rgb ; creating color strings + ;;taskrunner ; taskrunner for all your projects + ;;terraform ; infrastructure as code + ;;tmux ; an API for interacting with tmux + ;;upload ; map local to remote projects via ssh/ftp + + :os + ;;(:if IS-MAC macos) ; improve compatibility with macOS + tty ; improve the terminal Emacs experience + + :lang + ;;agda ; types of types of types of types... + ;;beancount ; mind the GAAP + cc ; C > C++ == 1 + ;;clojure ; java with a lisp + common-lisp ; if you've seen one lisp, you've seen them all + ;;coq ; proofs-as-programs + ;;crystal ; ruby at the speed of c + ;;csharp ; unity, .NET, and mono shenanigans + data ; config/data formats + ;;(dart +flutter) ; paint ui and not much else + ;;dhall + ;;elixir ; erlang done right + ;;elm ; care for a cup of TEA? + emacs-lisp ; drown in parentheses + ;;erlang ; an elegant language for a more civilized age + ;;ess ; emacs speaks statistics + ;;factor + ;;faust ; dsp, but you get to keep your soul + ;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER) + ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 + gdscript ; the language you waited for + ;;(go +lsp) ; the hipster dialect + (haskell +lsp) ; a language that's lazier than I am + ;;hy ; readability of scheme w/ speed of python + ;;idris ; a language you can depend on + json ; At least it ain't XML + ;;(java +meghanada) ; the poster child for carpal tunnel syndrome + ;;javascript ; all(hope(abandon(ye(who(enter(here)))))) + ;;julia ; a better, faster MATLAB + ;;kotlin ; a better, slicker Java(Script) + latex ; writing papers in Emacs has never been so fun + ;;lean ; for folks with too much to prove + ;;ledger ; be audit you can be + lua ; one-based indices? one-based indices + markdown ; writing docs for people to ignore + ;;nim ; python + lisp at the speed of c + ;;nix ; I hereby declare "nix geht mehr!" + ;;ocaml ; an objective camel + (org +roam2) ; organize your plain life in plain text + ;;php ; perl's insecure younger brother + ;;plantuml ; diagrams for confusing people more + ;;purescript ; javascript, but functional + python ; beautiful is better than ugly + ;;qt ; the 'cutest' gui framework ever + ;;racket ; a DSL for DSLs + ;;raku ; the artist formerly known as perl6 + ;;rest ; Emacs as a REST client + ;;rst ; ReST in peace + ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + ;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + ;;scala ; java, but good + ;;(scheme +guile) ; a fully conniving family of lisps + sh ; she sells {ba,z,fi}sh shells on the C xor + ;;sml + ;;solidity ; do you need a blockchain? No. + ;;swift ; who asked for emoji variables? + ;;terra ; Earth and Moon in alignment for performance. + web ; the tubes + yaml ; JSON, but readable + ;;zig ; C, but simpler + + :email + ;;(mu4e +org) + ;;notmuch + ;;(wanderlust +gmail) + + :app + calendar + ;;emms + ;;everywhere ; *leave* Emacs!? You must be joking + ;;irc ; how neckbeards socialize + ;;(rss +org) ; emacs as an RSS reader + ;;twitter ; twitter client https://twitter.com/vnought + + :config + ;;literate + (default +bindings +smartparens)) diff --git a/user/app/doom-emacs/packages.el b/user/app/doom-emacs/packages.el new file mode 100644 index 0000000..2172fec --- /dev/null +++ b/user/app/doom-emacs/packages.el @@ -0,0 +1,21 @@ +;;(package! org-bullets) +(package! org-modern) +(package! org-super-agenda) +(package! emacsql :pin "c1a4407") +(package! org-roam-ui) +(package! org-roam-nursery :recipe (:repo "https://github.com/chrisbarrett/nursery")) +(package! lister) +(package! org-download) +(package! org-yt) +(package! toc-org) +(package! all-the-icons-dired) +(package! all-the-icons-completion) +(package! ox-reveal) +(package! hledger-mode) +(package! rainbow-mode) +(package! crdt) +(package! ess) +(package! openwith) +(package! ob-mermaid) +(package! focus) +(package! olivetti) diff --git a/user/app/doom-emacs/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh b/user/app/doom-emacs/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh new file mode 100755 index 0000000..85b3f06 --- /dev/null +++ b/user/app/doom-emacs/scripts/copy-link-or-file/copy-link-or-file-to-clipboard.sh @@ -0,0 +1,10 @@ +#!/bin/sh +#command -v xclip >/dev/null 2>&1 || { echo "Need command xclip. Aborting." >&2; exit 1; } +if [[ -f "$1" ]]; then + TYPE=$(file -b --mime-type "$1") + xclip -selection clipboard -t "$TYPE" -i "$1" +else + echo $1 | xclip -selection clipboard -t text/plain &> /dev/null + exit +fi +exit diff --git a/user/app/doom-emacs/scripts/ox-odp/ox-odp-xml-parse.py b/user/app/doom-emacs/scripts/ox-odp/ox-odp-xml-parse.py new file mode 100755 index 0000000..e4f34e0 --- /dev/null +++ b/user/app/doom-emacs/scripts/ox-odp/ox-odp-xml-parse.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 + +import xml.etree.ElementTree as ET +import copy + +# Read content.xml into parser +mytree = ET.parse('./content.xml') +myroot = mytree.getroot() + +# Read styles.xml into parser +styletree = ET.parse('./styles.xml') +styleroot = styletree.getroot() + +# Remove direct-formatting from text:style-name attributes in text:p elements +counter = 0 +for text in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}p'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in text.keys(): + stylename = text.attrib['{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name'] + if stylename[0] == "P": + counter += 1 + text.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name') +print('Deleted '+str(counter)+' text:style-name attributes in text:p elements.') + +# Remove direct-formatting from text:style-name attributes in text:span elements +counter = 0 +for span in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}span'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in span.keys(): + span.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name') +print('Deleted '+str(counter)+' text:style-name attributes in text:span elements.') + +# Remove direct-formatting from draw:text-style-name attributes in draw:frame elements +counter = 0 +for drawing in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}frame'): + if '{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name' in drawing.keys(): + stylename = drawing.attrib['{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name'] + if stylename[0] == "P": + counter += 1 + drawing.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}text-style-name') +print('Deleted '+str(counter)+' draw:text-style-name attributes in text:p elements.') + +# Redefine default styles (style:style elements) and purge unnecessary ones +counter = 0 +kounter = 0 +for style in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style'): + if '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name' in style.keys(): + stylename = style.attrib['{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name'] + if stylename == "pr1": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-title') + elif stylename == "pr2": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-subtitle') + elif stylename == "pr3": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-notes') + elif stylename == "pr4": + counter += 1 + style.set('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}parent-style-name','DefaultTheme-outline1') +print('Redefined '+str(counter)+' style:parent-style-name attributes in style:style elements.') +print('Deleted '+str(kounter)+' style:style elements.') + +# Search for automatic-styles element +i = 0 +col1 = 0 +while (i < len(myroot)): + print(myroot[i].tag) + if myroot[i].tag=="{urn:oasis:names:tc:opendocument:xmlns:office:1.0}automatic-styles": + col1 = i + i += 1 + +# Remove unnecessary style:style and test:list-style elements underneath automatic-styles +i = 0 +while (i < len(myroot[col1])): + if (myroot[col1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style"): + if ("{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name" in myroot[col1][i].keys()): + if myroot[col1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"] in ["pr5","pr6","pr7","pr8","pr9"]: + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + elif myroot[col1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"][0] == "P": + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + if (myroot[col1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list-style"): + print("Removing "+myroot[col1][i].tag) + myroot[col1].remove(myroot[col1][i]) + i -= 1 + i += 1 + +#i = 0 +#while (i < len(myroot[col1])): +# print(myroot[col1][i].attrib) +# i += 1 + +# Find ML1 in styles.xml and copy it into L1 in content.xml +# Search for automatic-styles element +i = 0 +stylecol1 = 0 +while (i < len(styleroot)): + print(styleroot[i].tag) + if styleroot[i].tag=="{urn:oasis:names:tc:opendocument:xmlns:office:1.0}automatic-styles": + stylecol1 = i + i += 1 + +# Remove unnecessary style:style and test:list-style elements underneath automatic-styles +i = 0 +while (i < len(styleroot[stylecol1])): + if (styleroot[stylecol1][i].tag == "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list-style"): + if (styleroot[stylecol1][i].attrib["{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name"] == "ML1"): + liststyle_copy = copy.deepcopy(styleroot[stylecol1][i]) + myroot[col1].append(liststyle_copy) + myroot[col1][-1].attrib['{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name'] = "L1" + i += 1 + +# Update presentation:style-name attribute of all draw:frame elements +counter = 0 +for frame in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}frame'): + if '{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}class' in frame.keys(): + classname = frame.attrib['{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}class'] + if classname == "title": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr1') + elif classname == "subtitle": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr2') + elif classname == "notes": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr3') + elif classname == "outline": + counter += 1 + frame.set('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}style-name','pr4') +print("Updated "+str(counter)+" draw:frame elements") + +# Update draw:master-page-name attributes in all draw:page elements +# Also delete all presentation:presentation-page-layout attributes +counter = 0 +for page in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}page'): + if '{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}master-page-name' in page.keys(): + page.set('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}master-page-name','DefaultTheme') + counter += 1 + if '{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}presentation-page-layout' in page.keys(): + page.attrib.pop('{urn:oasis:names:tc:opendocument:xmlns:presentation:1.0}presentation-page-layout') + +print("Updated "+str(counter)+" draw:page elements") + +# Update all text:list elements to have text:style-name = L1 +counter = 0 +for page in myroot.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}list'): + if '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name' in page.keys(): + page.set('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name','L1') + counter += 1 + +print("Updated "+str(counter)+" text:list elements") + +#mytree.canonicalize(out='content.xml') +mytree.write('content.xml') +styletree.write('styles.xml') diff --git a/user/app/doom-emacs/scripts/ox-odp/ox-odp.sh b/user/app/doom-emacs/scripts/ox-odp/ox-odp.sh new file mode 100755 index 0000000..47dc7ce --- /dev/null +++ b/user/app/doom-emacs/scripts/ox-odp/ox-odp.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +filename=$1 +echo $filename +stylefile=$2 +echo $stylefile + +filenamebase=$(basename "$filename") +filenameext="${filenamebase##*.}" +echo $filenameext + +if [ $filenameext = "org" ]; then + stylefilebase=$(basename "$stylefile") + stylefileext="${stylefilebase##*.}" + + if [ $stylefileext = "odp" ]; then + output="${filename//\.org/\.pptx}" + finaloutput="${filename//\.org/\.odp}" + pandoc "$filename" -o "$output" + soffice --convert-to odp "$output" + unzip "$finaloutput" content.xml + unzip "$stylefile" styles.xml + + sed 's~~~g' content.xml + sed 's~~~g' content.xml + + python3 ~/.doom.d/scripts/ox-odp/ox-odp-xml-parse.py + + zip -d $finaloutput styles.xml + zip -m $finaloutput styles.xml + + zip -d $finaloutput content.xml + zip -m $finaloutput content.xml + + rm $output + + exit + + else + echo "Style file is not an odp file." + fi +else + echo "Base file is not an org file." + exit +fi + +exit diff --git a/user/home.nix b/user/home.nix index 5c94a1a..eca56af 100644 --- a/user/home.nix +++ b/user/home.nix @@ -97,19 +97,6 @@ in lxappearance (pkgs.writeScriptBin "phoenix" myPhoenixScript) - # Doom emacs - emacs - binutils - (ripgrep.override {withPCRE2 = true;}) - gnutls - fd - imagemagick - zstd - nodePackages.javascript-typescript-langserver - sqlite - editorconfig-core-c - emacs-all-the-icons-fonts - # Office libreoffice-qt mate.atril @@ -358,6 +345,11 @@ in shellAliases = myAliases; }; + programs.doom-emacs = { + enable = true; + doomPrivateDir = ./app/doom-emacs; + }; + nixpkgs.overlays = [ (self: super: {