From f66338dcf2be1f2b322ef0cd2fba0ce130255655 Mon Sep 17 00:00:00 2001 From: Jakub Date: Wed, 9 Jul 2025 16:51:22 +0800 Subject: [PATCH] Initial commit --- README.org | 1 + logos.el | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 README.org create mode 100644 logos.el diff --git a/README.org b/README.org new file mode 100644 index 0000000..1bebc06 --- /dev/null +++ b/README.org @@ -0,0 +1 @@ +An =org-mode= configuration to address my own perceived points of friction. More info in this blog post. diff --git a/logos.el b/logos.el new file mode 100644 index 0000000..a0afab8 --- /dev/null +++ b/logos.el @@ -0,0 +1,499 @@ +;;; logos.el --- A workflow for org-mode -*- lexical-binding: t -*- + +;; Author: Jakub +;; Maintainer: Jakub +;; Version: 1.0 +;; Package-Requires: (straight) +;; Homepage: homepage +;; Keywords: org + + +;; This file is not part of GNU Emacs + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + + +;;; Commentary: + +;; + +;;; Code: + +(require 'transient) +(require 'subr-x) + +(defun org-mode-init () + "This is called through `org-mode-hook', so that I don't need to `add-hook' 20 million times." + ;; I want org to display pictures inline. + (org-display-inline-images) + ;; I don't want monospace text by default. + (variable-pitch-mode) + ) + +(add-hook 'org-mode-hook #'org-mode-init) + +;; Change a bunch of default org-mode variables. +(setq org-log-done 'time ;; I want to keep track of the time tasks are completed. + org-return-follows-link t ;; I want to be able to follow links easily. + org-hide-emphasis-markers t ;; This is a big one: hide markup syntax like ** and //. + org-pretty-entities t ;; These two variables together render latex entities as UTF8, which means that Greek letters, subscript, and superscript are all rendered correctly. + org-pretty-entities-include-sub-superscripts t + org-enforce-todo-dependencies t ;; Prevent changing a parent to DONE if the children aren't + org-enforce-todo-checkbox-dependencies t + org-agenda-skip-scheduled-if-done t ;; Don't show done stuff in the agenda view + org-agenda-skip-deadline-if-done t + org-agenda-skip-timestamp-if-done t + + org-todo-keywords '((sequence "TODO" "WIP" "DELEGATED" "WAITING" "|" "CANCELLED" "DONE")) ;; Some extra keywords in addition to TODO and DONE + org-refile-targets '((org-agenda-files :maxlevel . 2)) ;; Allow any agenda files to be refile targets.z + + org-agenda-files (apply 'append (mapcar + (lambda (dir) + (directory-files-recursively dir org-agenda-file-regexp)) + '("~/.org/tasks"))) ;; Agenda files location - gathered recursively + org-cite-global-bibliography '("~/.org/global.bib") ;; Global bibliography location + + org-startup-indented t ;; Start indented - this fixed org-indent mode + + org-attach-id-dir "~/.org/lore/assets" + ) + +;; Org modern and other interface enhancements +(use-package org-modern + :hook ((org-mode . org-modern-mode) + (org-agenda-finalize . org-modern-agenda))) + +(use-package org-modern-indent + :straight (org-modern-indent :type git :host github :repo "jdtsmith/org-modern-indent") + :hook ((org-mode . org-modern-indent-mode))) + +(use-package wc-mode + :hook ((org-mode . wc-mode)) + :config (setq wc-modeline-format "WC[%tw]")) + +;; Sentence and word navigation and marking +(setq sentence-end-double-space nil) ;; Otherwise, M-e is broken in normal writing. + +;;; Generally useful things +;; Mark word and sentence +(defun mark-whole-word () + "Mark the whole word underneath the cursor." + (interactive) + (forward-word) + (set-mark-command nil) + (backward-word)) + +(defun mark-sentence () + "Mark the entire sentence underneath the cursor." + (interactive) + (forward-sentence) + (set-mark-command nil) + (backward-sentence)) + +(define-key org-mode-map (kbd "C-@") #'mark-whole-word) +(define-key org-mode-map (kbd "M-@") #'mark-sentence) + +(transient-define-prefix mark-menu-transient () + "Transient menu for marking units of text." + ["Mark" ("w" "Word" mark-whole-word) + ("s" "Sentence" mark-sentence) + ("p" "Paragraph" mark-paragraph) + ("b" "Buffer" mark-whole-buffer)]) + +(define-key org-mode-map (kbd "C-c o SPC") #'mark-menu-transient) + +;; Section navigation and reordering +(defun move-sentence-right (&optional arg) + "Move the whole sentence to the right of the next sentence, `ARG' times." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (forward-sentence) + (backward-sentence) + (left-char) ;; Capture previous space + (kill-sentence) + (forward-sentence) + (yank) + (backward-sentence) + (setq arg (1- arg)))) + +(defun move-sentence-left (&optional arg) + "Move the whole sentence to the left of the previous sentence, `ARG' times." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (forward-sentence) + (backward-sentence) + (left-char) ;; Capture previous space + (kill-sentence) + (backward-sentence) + (left-char) ;; Capture previous space + (yank) + (backward-sentence) + (setq arg (1- arg)))) + +(defun move-paragraph-up (&optional arg) + "Move the whole paragraph above the previous paragraph, `ARG' times." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (forward-paragraph) + (backward-paragraph) + (kill-paragraph nil) + (backward-paragraph) + (yank) + (backward-paragraph) + (setq arg (1- arg)))) + +(defun move-paragraph-down (&optional arg) + "Move the whole paragraph above the previous paragraph, `ARG' times." + (interactive "^p") + (or arg (setq arg 1)) + (while (> arg 0) + (forward-paragraph) + (backward-paragraph) + (kill-paragraph nil) + (forward-paragraph) + (yank) + (backward-paragraph) + (setq arg (1- arg)))) + +(define-key org-mode-map (kbd "C-M-") #'move-sentence-left) +(define-key org-mode-map (kbd "C-M-") #'move-sentence-right) +(define-key org-mode-map (kbd "C-M-") #'move-paragraph-up) +(define-key org-mode-map (kbd "C-M-") #'move-paragraph-down) + +(transient-define-prefix reorder-transient () + "Transient menu for text re-ordering commands." + ["Move sentence..." ("l" "Left" move-sentence-left) + ("r" "Right" move-sentence-right)] + ["Move paragraph..." ("u" "Up" move-paragraph-up) + ("d" "Down" move-paragraph-down)]) + +(define-key org-mode-map (kbd "C-c o r") #'reorder-transient) + +;; Focused rewriting +(defun break-out-sentence () + "Breaks a sentence out into it's own buffer for editing." + (interactive) + (backward-sentence) + (kill-sentence) + (switch-to-buffer-other-window "*break-out*") + (erase-buffer) + (yank)) + +(defun break-out-choose-sentence () + "Chooses a sentence from the break-out buffer." + (interactive) + (backward-sentence) + (kill-sentence) + (other-window -1) + (kill-buffer "*break-out*") + (yank)) + +(defun break-out-dwim () + "Either break-out or choose sentency depending on buffer name." + (interactive) + (if (equal (buffer-name) "*break-out*") + (break-out-choose-sentence) + (break-out-sentence))) + +(define-key org-mode-map (kbd "C-c o b") #'break-out-dwim) + +;; Powerthesaurus +(use-package powerthesaurus + :bind ("C-x t" . powerthesaurus-transient)) + +;; Harper +(when (and (not (equal system-type 'windows-nt)) (locate-file "harper-ls" exec-path)) + (with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs + '(org-mode . ("harper-ls" "--stdio")))) + + (setq-default eglot-workspace-configuration + '(:harper-ls (:dialect "Australian"))) + + (add-hook 'org-mode-hook 'eglot-ensure)) + +;; Company +(defun org-mode-company-hook () + "Set company backends for `org-mode' usage." + (setq-local company-backends + '((company-capf company-dabbrev company-files company-ispell)))) +(add-hook 'org-mode-hook #'org-mode-company-hook) + +;;; Exobrain +;;; Task management +;; Maintenance and project file creation shortcuts +(defun new-maintenance-file (maintenance-area) + "Create a new maintenance file for a given `MAINTENANCE-AREA'." + (interactive "sMaintenance area is: ") + (find-file (concat "~/.org/tasks/maintenance/" maintenance-area ".org")) + (insert "\n\n* Calendar\n\n* TO-DO\n\n* Routines\n\n* Archive\n") + (goto-char (point-min))) + +(defun new-project-file (project-name acceptance-criteria) + "Create a new project file for a given `PROJECT-NAME', with some `ACCEPTANCE-CRITERIA'." + (find-file (concat "~/.org/tasks/projects/" project-name ".org")) + (insert (concat "\n\n* Acceptance Criteria\n\n" acceptance-criteria "\n\n* Ideas\n\n* TODO Work\n\n** Calendar\n\n** TO-DO\n\n** Routines\n\n** Archive\n")) + (goto-char (point-min))) + +(defun new-generic-project (project-name acceptance-criteria) + "Interactive wrapper for `new-project-file', for generic projects." + (interactive "sProject name is: \nsAcceptance criteria is: ") + (new-project-file project-name acceptance-criteria)) + +(defun new-career-project (project-name acceptance-criteria) + "Interactive wrapper for `new-project-file', for career projects." + (interactive "sProject name is: \nsAcceptance criteria is: ") + (new-project-file (concat "career/" project-name) acceptance-criteria)) + +;; Captures +(defun select-task-area (location headline) + "Prompt for a file in ~/.org/tasks/maintenance/ to insert the capture." + (let* ((files (directory-files (concat "~/.org/tasks/" location) t "\\.org$")) + (choices (mapcar + (lambda (x) (string-remove-suffix ".org" x)) + (mapcar #'file-name-nondirectory files))) + (selected (completing-read "Choose a file: " choices nil t))) + (set-buffer (org-capture-target-buffer (concat "~/.org/tasks/" location selected ".org"))) + (org-capture-put-target-region-and-position) + (widen) + (goto-char (point-min)) + (setq headline (org-capture-expand-headline headline)) + (re-search-forward (format org-complex-heading-regexp-format + (regexp-quote headline)) + nil t) + (forward-line 0))) + +(defun select-maintenance-area (headline) + (select-task-area "maintenance/" headline)) + +(defun select-project (headline) + (select-task-area "projects/" headline)) + +(defun select-career-project (headline) + (select-task-area "projects/career/" headline)) + +(setq org-capture-templates + '(("i" "Idea") + ("ii" "Generic" entry + (file "~/.org/inbox.org") + "* %^{TITLE} :idea: \n:Created: %T\n%?" + :empty-lines 1) + ("ip" "Project" entry + (function (lambda () (select-project "Ideas"))) + "* %^{TITLE} \n:Created: %T\n%?" + :empty-lines 1) + ("ic" "Career" entry + (function (lambda () (select-career-project "Ideas"))) + "* %^{TITLE} \n:Created: %T\n%?" + :empty-lines 1) + + ("t" "Task") + ("tt" "Generic") + ("ttt" "Raw" entry + (file "~/.org/inbox.org") + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :task:\n:Created: %T\n%?" + :empty-lines 1) + ("ttm" "Maintenance" entry + (function (lambda () (select-maintenance-area "TO-DO"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n:Created: %T\n%?" + :empty-lines 1) + ("ttp" "Project" entry + (function (lambda () (select-project "TO-DO"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n:Created: %T\n%?" + :empty-lines 1) + ("ttc" "Career" entry + (function (lambda () (select-career-project "TO-DO"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE}\n:Created: %T\n%?" + :empty-lines 1) + ("ts" "Scheduled") + ("tss" "Raw" entry + (file "~/.org/inbox.org") + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} :scheduled:\n:SCHEDULED: %^T\n%?" + :empty-lines 1 + :time-prompt t) + ("tsm" "Maintenance" entry + (function (lambda () (select-maintenance-area "Calendar"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?" + :empty-lines 1 + :time-prompt t) + ("tsp" "Project" entry + (function (lambda () (select-project "Calendar"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?" + :empty-lines 1 + :time-prompt t) + ("tsc" "Career" entry + (function (lambda () (select-career-project "Calendar"))) + "* TODO [%^{priority|#A|#B|#C|#D}] %^{TITLE} \n:SCHEDULED: %^T\n%?" + :empty-lines 1 + :time-prompt t) + + ("n" "Note") + ("nn" "Raw" entry + (file "~/.org/inbox.org") + "* %^{TITLE} :note: \n:Created: %T\n%?" + :empty-lines 1) + ("nm" "Meeting") + ("nmm" "Raw" entry + (file "~/.org/inbox.org") + "* %^{TITLE} :meeting: \n:Created: %T\n%?" + :clock-in t + :clock-resume t) + ("nmc" "Career" entry + (function (lambda () (select-career-project "Calendar"))) + "* %^{TITLE} \n:Created: %T\n%?" + :empty-lines 1 + :clock-in t + :clock-resume t))) + +(defun open-inbox () + "Opens `~/.org/inbox.org'" + (interactive) + (find-file "~/.org/inbox.org")) + +(defun open-maintenance () + "Opens `~/.org/tasks/maintenance/'" + (interactive) + (find-file "~/.org/tasks/maintenance/")) + +(defun open-projects () + "Opens `~/.org/tasks/projects/'" + (interactive) + (find-file "~/.org/tasks/projects/")) + +(defun open-career () + "Opens `~/.org/tasks/projects/career/'" + (interactive) + (find-file "~/.org/tasks/projects/career/")) + +(transient-define-prefix task-management-menu () + "Transient menu for task management shortcuts." + ["Go To" ("i" "Inbox" open-inbox) + ("m" "Maintenance Directory" open-maintenance) + ("p" "Projects Directory" open-projects) + ("c" "Career Directory" open-career)] + ["Create New" ("M" "Maintenance File" new-maintenance-file) + ("P" "Project" new-generic-project) + ("C" "Career Project" new-career-project)]) + +(define-key global-map (kbd "C-c o t") #'task-management-menu) +(define-key global-map (kbd "C-c a") #'org-agenda) +(define-key global-map (kbd "C-c c") #'org-capture) + + +;; Org roam +(use-package org-roam + :straight nil + :ensure t + :init + (setq org-roam-v2-ack t) + :custom + (org-roam-directory (file-truename "~/.org/lore/")) + (org-dailies-directory (file-truename "~/.org/lore/refined/journal/")) + (org-roam-file-exclude-regexp "\\.git/.*\\|logseq/.*$") + (org-roam-completion-everywhere) + (org-roam-capture-templates + '(("c" "Raw" plain + "%?" + :target (file+head "raw/${slug}.org" "#+title: ${title}\n") + :unnarrowed t) + ("r" "Refined") + ("rm" "Mini-essay" plain + "%?" + :target (file+head "refined/wiki/${slug}.org" "#+title: ${title}\n#+author: Jakub Nowak") + :unnarrowed t))) + (org-roam-dailies-capture-templates + '(("d" "default" entry + "* %?" + :target (file+head "%<%Y-%m-%d>.org" ;; format matches Logseq + "#+title: %<%Y-%m-%d>\n")))) + :bind (("C-c n l" . org-roam-buffer-toggle) + ("C-c n f" . org-roam-node-find) + ("C-c n g" . org-roam-graph) + ("C-c n i" . org-roam-node-insert) + ("C-c n c" . org-roam-capture) + :map org-roam-dailies-map + ("Y" . org-roam-dailies-capture-yesterday) + ("T" . org-roam-dailies-capture-tomorrow)) + :bind-keymap ("C-c n d" . org-roam-dailies-map) + :config + (require 'org-roam-dailies) ;; Ensure the keymap is available + (org-roam-db-autosync-mode)) + +(defun org-roam-create-node-from-headline () + "Create an Org-roam note from the current headline and jump to it. + + Normally, insert the headline’s title using the ’#title:’ file-level property + and delete the Org-mode headline. However, if the current headline has a + Org-mode properties drawer already, keep the headline and don’t insert + ‘#+title:'. Org-roam can extract the title from both kinds of notes, but using + ‘#+title:’ is a bit cleaner for a short note, which Org-roam encourages." + (interactive) + (let ((title (nth 4 (org-heading-components))) + (has-properties (org-get-property-block))) + (org-cut-subtree) + (org-roam-find-file title nil nil 'no-confirm) + (org-paste-subtree) + (unless has-properties + (kill-line) + (while (outline-next-heading) + (org-promote))) + (goto-char (point-min)) + (when has-properties + (kill-line) + (kill-line)))) + +(transient-define-prefix org-roam-menu () + "Transient menu for task management shortcuts." + ["Node" ("c" "Capture" org-roam-node-find) + ("i" "Insert" org-roam-node-insert) + ("r" "Refile" org-roam-create-node-from-headline) + ("v" "Visit" org-roam-node-visit) ] + ["Dailies" ("t" "Today" org-roam-dailies-capture-today) + ("y" "Yesterday" org-roam-dailies-capture-yesterday) + ("n" "Tomorrow" org-roam-dailies-capture-tomorrow)] + ["Logseq" ("R" "Fix links" logseq-org-roam)]) + +(define-key global-map (kbd "C-c o n") #'org-roam-menu) + +;; Bibliography stuff +(setq bibtex-dialect 'biblatex) ;; Use biblatex instead of bibtex. + +(use-package biblio) + +(defun bibtex-online-entry () + (interactive) + (bibtex-entry "Online")) + +(defun bibtex-misc-entry () + (interactive) + (bibtex-entry "Misc")) + +(defun bibtex-software-entry () + (interactive) + (bibtex-entry "Software")) + +(transient-define-prefix bibtex-transient-menu () + "Transient menu for task management shortcuts." + ["Biblio" ("d" "doi.org" doi-insert-bibtex) + ("x" "arXiv" arxiv-lookup)] + ["Templates" ("o" "Online Resource" bibtex-online-entry) + ("m" "Misc" bibtex-misc-entry) + ("s" "Software" bibtex-software-entry)]) + +(define-key bibtex-mode-map (kbd "C-c r") #'bibtex-transient-menu) + +;;; logos.el ends here