diff --git a/modules/lang/org/+attach.el b/modules/lang/org/+attach.el new file mode 100644 index 000000000..d085bbbd4 --- /dev/null +++ b/modules/lang/org/+attach.el @@ -0,0 +1,44 @@ +;;; emacs/org/+attach.el --- -*- no-byte-compile: t; -*- + +;; FIXME +;; Initializes my own org-mode attachment system. I didn't like Org's native +;; one. Mine stores attachments in a global org .attach directory. It also +;; implements drag-and-drop file support and attachment icons. It also treats +;; images specially. +;; +;; To clean up unreferenced attachments, call `doom/org-cleanup-attachments' +(add-hook '+org-init-hook '+org|init-attach t) + +(defun +org|init-attach () + (setq org-attach-directory +org-attachment-dir) + + ;; Don't track attachments in recentf or projectile + (push (format "/%s.+$" (regexp-quote +org-attachment-dir)) recentf-exclude) + (push ".attach" projectile-globally-ignored-file-suffixes) + + ;; FIXME Use all-the-icons + ;; (doom-fix-unicode '("FontAwesome" 13) ? ? ? ? ? ? ? ?) + ;; Drag-and-drop support + (require 'org-download) + (setq-default org-download-image-dir +org-attachment-dir + org-download-heading-lvl nil + org-download-timestamp "_%Y%m%d_%H%M%S") + + (setq org-download-screenshot-method + (cond (IS-MAC "screencapture -i %s") + (IS-LINUX "maim --opengl -s %s"))) + + ;; Write download paths relative to current file + (advice-add 'org-download--dir-2 :override 'ignore) + (defun +org*download-fullname (path) + (file-relative-name path (f-dirname (buffer-file-name)))) + (advice-add 'org-download--fullname :filter-return '+org*download-fullname) + + ;; Add another drag-and-drop handler that will handle anything but image files + (setq dnd-protocol-alist `(("^\\(https?\\|ftp\\|file\\|nfs\\):\\(//\\)?" . doom/org-download-dnd) + ,@dnd-protocol-alist)) + + ;; keybinds + ;; (@map :leader :n "oa" (@find-file-in +org-attachment-dir)) + ) + diff --git a/modules/lang/org/+capture.el b/modules/lang/org/+capture.el new file mode 100644 index 000000000..b168498ec --- /dev/null +++ b/modules/lang/org/+capture.el @@ -0,0 +1,54 @@ +;;; emacs/org/+capture.el --- -*- no-byte-compile: t; -*- + +;; Sets up a sane `org-capture' workflow, wherein the org-capture buffer is +;; opened in a popup frame, and can be invoked from outside Emacs as well. +;; +;; See `+org/capture' + +(add-hook '+org-init-hook '+org|init-capture t) + +(defun +org|init-capture () + "Set up a sane `org-capture' workflow." + (setq org-default-notes-file +org-quicknote-dir) + + (require 'org-capture) + (require 'org-protocol) + (@set :popup "*Org Select*" :size 0.4) + + (defadvice org-capture (after make-full-window-frame activate) + "If org-capture creates a new frame, this initializes it properly, by +deleting other windows and blanking out the mode-line." + (when (equal "org-capture" (frame-parameter nil 'name)) + (setq mode-line-format nil) + (delete-other-windows))) + + (defadvice org-capture-finalize (after delete-capture-frame activate) + "Closes the frame once org-capture is done." + (when (equal "org-capture" (frame-parameter nil 'name)) + (delete-frame))) + + (setq org-capture-templates + '(;; TODO: New Task (todo) + ;; TODO: New vocabulary word + + ("c" "Changelog" entry + (file+headline (expand-file-name "CHANGELOG.org" (doom/project-root)) "Unreleased") + "* %?") + + ;; ("p" "Project Notes" entry + ;; (file+headline org-default-notes-file "Inbox") + ;; "* %u %?\n%i" :prepend t) + + ;; ("m" "Major-mode Notes" entry + ;; (file+headline org-default-notes-file "Inbox") + ;; "* %u %?\n%i" :prepend t) + + ("n" "Notes" entry + (file+headline org-default-notes-file "Inbox") + "* %u %?\n%i" :prepend t) + + ;; ("v" "Vocab" entry + ;; (file+headline (concat org-directory "topics/vocab.org") "Unsorted") + ;; "** %i%?\n") + ))) + diff --git a/modules/lang/org/+export.el b/modules/lang/org/+export.el new file mode 100644 index 000000000..58f6fe21e --- /dev/null +++ b/modules/lang/org/+export.el @@ -0,0 +1,28 @@ +;;; emacs/org/+export.el --- -*- no-byte-compile: t; -*- + +;; My own, centralized exporting system as well. + +(add-hook '+org-init-hook '+org|init-export t) + +(defun +org|init-export () + (setq org-export-directory (expand-file-name ".export" +org-dir) + org-export-backends '(ascii html latex md) + org-export-with-toc t + org-export-with-author t) + + ;; Export to a central directory (why isn't this easier?) + (unless (file-directory-p org-export-directory) + (make-directory org-export-directory t)) + (defun +org*export-output-file-name (args) + (unless (nth 2 args) + (setq args (append args (list org-export-directory)))) + args) + (advice-add 'org-export-output-file-name :filter-args '+org*export-output-file-name) + + ;; (require 'ox-pandoc) + ;; (setq org-pandoc-options '((standalone . t) (mathjax . t) (parse-raw . t))) + + ;; keybinds + ;; (@map :leader :n "oe" (@find-file-in org-export-directory)) + ) + diff --git a/modules/lang/org/autoload/attach.el b/modules/lang/org/autoload/attach.el new file mode 100644 index 000000000..3268739a1 --- /dev/null +++ b/modules/lang/org/autoload/attach.el @@ -0,0 +1,52 @@ +;;; emacs/org/autoload/attach.el + +(defun doom--org-attach-icon (path) + (char-to-string (pcase (downcase (f-ext path)) + ("jpg" ?) ("jpeg" ?) ("png" ?) ("gif" ?) + ("pdf" ?) + ("ppt" ?) ("pptx" ?) + ("xls" ?) ("xlsx" ?) + ("doc" ?) ("docx" ?) + ("ogg" ?) ("mp3" ?) ("wav" ?) + ("mp4" ?) ("mov" ?) ("avi" ?) + ("zip" ?) ("gz" ?) ("tar" ?) ("7z" ?) ("rar" ?) + (_ ?)))) + +;;;###autoload +(defun +org-cleanup-attachments () + ;; "Deletes any attachments that are no longer present in the org-mode buffer." + (let* ((attachments-local (+org-attachments)) + (attachments (f-entries org-attach-directory)) + (to-delete (-difference attachments-local attachments))) + ;; TODO + to-delete)) + +(defun +org-attachments () + (unless (eq major-mode 'org-mode) + (user-error "Not an org buffer")) + (org-save-outline-visibility nil + (let ((attachments '()) + element + file) + (when (and (f-dir? org-attach-directory) + (> (length (f-glob (concat (f-slash org-attach-directory) "*"))) 0)) + (save-excursion + (goto-char (point-min)) + (while (progn (org-next-link) (not org-link-search-failed)) + (setq element (org-element-lineage (org-element-context) '(link) t)) + (when element + (setq file (expand-file-name (org-element-property :path element))) + (when (and (string= (org-element-property :type element) "file") + (string= (concat (f-base (f-dirname file)) "/") org-attach-directory) + (file-exists-p file)) + (push file attachments)))))) + (-distinct attachments)))) + +;;;###autoload +(defun +org-download-dnd (uri action) + (if (eq major-mode 'org-mode) + (doom:org-attach uri) ;; FIXME + (let ((dnd-protocol-alist + (rassq-delete-all '+org-download-dnd (copy-alist dnd-protocol-alist)))) + (dnd-handle-one-url nil action uri)))) + diff --git a/modules/lang/org/autoload/capture.el b/modules/lang/org/autoload/capture.el new file mode 100644 index 000000000..b2c17f36e --- /dev/null +++ b/modules/lang/org/autoload/capture.el @@ -0,0 +1,18 @@ +;;; emacs/org/autoload/capture.el + +;;;###autoload +(defun +org/capture (&optional template string) + "Run `org-capture' in a new, disposable popup frame." + (interactive) + (let ((org-capture-entry (org-capture-select-template template))) + (cond ((equal org-capture-entry "C") + (find-file (expand-file-name "module-org-notes.el" doom-modules-dir)) + (re-search-forward "^\\s-+(setq org-capture-templates" (point-max) t) + (recenter)) + ((not (equal org-capture-entry "q")) + (let ((frame (make-frame '((name . "org-capture") (height . 15) (width . 80))))) + (with-selected-frame frame + (if string + (org-capture-string string) + (org-capture)))))))) + diff --git a/modules/lang/org/autoload/evil.el b/modules/lang/org/autoload/evil.el new file mode 100644 index 000000000..bb679b27d --- /dev/null +++ b/modules/lang/org/autoload/evil.el @@ -0,0 +1,42 @@ +;;; emacs/org/autoload/evil.el + +;;;###autoload (autoload '+org:capture "emacs/org/autoload/evil" nil t) +(evil-define-operator +org:capture (&optional beg end bang) + "Send a selection to `doom/org-capture'." + :move-point nil :type inclusive + (interactive "") + (doom/org-capture + (if (and (evil-visual-state-p) beg end) + (buffer-substring beg end) + ""))) + +;;;###autoload (autoload '+org:attach "emacs/org/autoload/evil" nil t) +(evil-define-command +org:attach (&optional uri) + (interactive "") + (unless (eq major-mode 'org-mode) + (user-error "Not in an org-mode buffer")) + (if uri + (let* ((rel-path (org-download--fullname uri)) + (new-path (f-expand rel-path)) + (image-p (image-type-from-file-name uri))) + (cond ((string-match-p (concat "^" (regexp-opt '("http" "https" "nfs" "ftp" "file")) ":/") uri) + (url-copy-file uri new-path)) + (t (copy-file uri new-path))) + (unless new-path + (user-error "No file was provided")) + (if (evil-visual-state-p) + (org-insert-link nil (format "./%s" rel-path) + (concat (buffer-substring-no-properties (region-beginning) (region-end)) + " " (doom--org-attach-icon rel-path))) + + (insert (if image-p + (format "[[./%s]] " rel-path) + (format "%s [[./%s][%s]] " + (doom--org-attach-icon rel-path) + rel-path (f-filename rel-path))))) + (when (string-match-p (regexp-opt '("jpg" "jpeg" "gif" "png")) (f-ext rel-path)) + (org-redisplay-inline-images))) + (let ((default-directory ".attach/")) + (if (file-exists-p default-directory) + (call-interactively 'find-file) + (user-error "No attachments"))))) diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el new file mode 100644 index 000000000..8f300e189 --- /dev/null +++ b/modules/lang/org/autoload/org.el @@ -0,0 +1,268 @@ +;;; emacs/org/autoload/org.el + +;;;###autoload +(defun +org/indent () + "Indent the current item (header or item). Otherwise, forward to +`self-insert-command'." + (interactive) + (cond ((org-at-item-p) + (org-indent-item-tree)) + ((org-at-heading-p) + (ignore-errors (org-demote))) + (t (call-interactively 'self-insert-command)))) + +;;;###autoload +(defun +org/indent-or-next-field () + "Depending on the context either indent the current item or go the next table field." + (interactive) + (call-interactively (if (org-at-table-p) 'org-table-next-field '+org/indent))) + +;;;###autoload +(defun +org/dedent () + "Dedent the current item (header or item). Otherwise, forward to +`self-insert-command'." + (interactive) + (cond ((org-at-item-p) + (let ((struct (if (org-region-active-p) + (save-excursion (goto-char (region-beginning)) + (org-list-struct)) + (org-list-struct)))) + (org-list-indent-item-generic -1 nil struct))) + ((org-at-heading-p) + (ignore-errors (org-promote))) + (t (call-interactively 'self-insert-command)))) + +;;;###autoload +(defun +org/dedent-or-prev-field () + "Depending on the context either dedent the current item or go the previous +table field." + (interactive) + (call-interactively (if (org-at-table-p) 'org-table-previous-field '+org/dedent))) + +;;;###autoload +(defun +org/insert-item (direction) + "Inserts a new heading, table cell or item, depending on the context. +DIRECTION can be 'above or 'below. + +I use this instead of `org-insert-item' or `org-insert-heading' which are too +opinionated and perform this simple task incorrectly (e.g. whitespace in the +wrong places)." + (interactive) + (let* ((context (org-element-lineage + (org-element-context) + '(table table-row headline inlinetask item plain-list) + t)) + (type (org-element-type context))) + (cond ((eq type 'item) + (let ((marker (org-element-property :bullet context))) + (cl-case direction + ('below + (goto-char (line-end-position)) + (insert (concat "\n" marker))) + ('above + (goto-char (line-beginning-position)) + (insert marker) + (save-excursion (insert "\n"))))) + (when (org-element-property :checkbox context) + (insert "[ ] "))) + ((memq type '(table table-row)) + (cl-case direction + ('below (org-table-insert-row t)) + ('above (+org/table-prepend-row-or-shift-up)))) + (t + (let ((level (save-excursion + (org-back-to-heading) + (org-element-property + :level (org-element-lineage (org-element-context) + '(headline) t))))) + (cl-case direction + ('below + (let ((at-eol (= (point) (1- (line-end-position))))) + (goto-char (line-end-position)) + (org-end-of-subtree) + (insert (concat "\n" + (when (= level 1) + (if at-eol + (ignore (cl-incf level)) + "\n")) + (make-string level ?*) + " ")))) + ('above + (org-back-to-heading) + (org-insert-heading) + (when (= level 1) + (save-excursion (evil-open-above 1)) + (save-excursion (insert "\n"))))) + (when (org-element-property :todo-type context) + (org-todo 'todo))))) + (evil-append-line 1))) + +;;;###autoload +(defun +org/toggle-checkbox () + (interactive) + (let ((context (org-element-lineage (org-element-context) '(item) t))) + (when context + (org-end-of-line) + (org-beginning-of-line) + (if (org-element-property :checkbox context) + (when (search-backward-regexp "\\[[ +-]\\]" (line-beginning-position) t) + (delete-char 4)) + (insert "[ ] "))))) + +;;;###autoload +(defun +org/toggle-fold () + "Toggle the local fold at the point (as opposed to cycling through all levels +with `org-cycle')." + (interactive) + (cond ((org-at-heading-p) + (outline-toggle-children)) + ((org-at-item-p) + (org-cycle)))) + +;;;###autoload +(defun +org/dwim-at-point () + "Do-what-I-mean at point. This includes following timestamp links, aligning +tables, toggling checkboxes/todos, executing babel blocks, previewing latex +fragments, opening links, or refreshing images." + (interactive) + (let* ((scroll-pt (window-start)) + (context (org-element-context)) + (type (org-element-type context)) + (value (org-element-property :value context))) + (cond + ((memq type '(planning timestamp)) + (org-follow-timestamp-link)) + + ((memq type '(table table-row)) + (if (org-element-property :tblfm (org-element-property :parent context)) + (org-table-recalculate t) + (org-table-align))) + + ((and (memq type '(item)) + (org-element-property :checkbox context)) + (org-toggle-checkbox)) + + ((and (memq type '(headline)) + (org-element-property :todo-type context)) + (org-todo + (if (eq (org-element-property :todo-type context) 'done) 'todo 'done))) + + ((and (memq type '(headline)) + (string= "ARCHIVE" (car-safe (org-get-tags)))) + (org-force-cycle-archived)) + + ((memq type '(headline)) + (org-remove-latex-fragment-image-overlays) + (org-preview-latex-fragment '(4))) + + ((memq type '(babel-call)) + (org-babel-lob-execute-maybe)) + + ((memq type '(src-block inline-src-block)) + (org-babel-execute-src-block)) + + ((memq type '(latex-fragment latex-environment)) + (org-preview-latex-fragment)) + + ((memq type '(link)) + (let ((path (org-element-property :path (org-element-lineage (org-element-context) '(link) t)))) + (if (and path (image-type-from-file-name path)) + (+org/refresh-inline-images) + (org-open-at-point)))) + + (t (+org/refresh-inline-images))) + (set-window-start nil scroll-pt))) + +;;;###autoload +(defun +org/refresh-inline-images () + "Refresh image previews in the current heading/tree." + (interactive) + (if (> (length org-inline-image-overlays) 0) + (org-remove-inline-images) + (org-display-inline-images + t t + (if (org-before-first-heading-p) + (line-beginning-position) + (save-excursion (org-back-to-heading) (point))) + (if (org-before-first-heading-p) + (line-end-position) + (save-excursion (org-end-of-subtree) (point)))))) + +;;;###autoload +(defun +org-surround (delim) + "Surround the cursor (or selected region) with DELIM." + (if (region-active-p) + (save-excursion + (goto-char (region-beginning)) + (insert delim) + (goto-char (region-end)) + (insert delim)) + (insert delim) + (save-excursion (insert delim)))) + + +;; +;; tables +;; + +;;;###autoload +(defun +org/table-next-row () + (interactive) + (if (org-at-table-p) (org-table-next-row) (org-down-element))) + +;;;###autoload +(defun +org/table-previous-row () + "Go to the previous row (same column) in the current table. Before doing so, +re-align the table if necessary. (Necessary because org-mode has a +`org-table-next-row', but not `org-table-previous-row')" + (interactive) + (if (org-at-table-p) + (progn + (org-table-maybe-eval-formula) + (org-table-maybe-recalculate-line) + (if (and org-table-automatic-realign + org-table-may-need-update) + (org-table-align)) + (let ((col (org-table-current-column))) + (beginning-of-line 0) + (when (or (not (org-at-table-p)) (org-at-table-hline-p)) + (beginning-of-line)) + (org-table-goto-column col) + (skip-chars-backward "^|\n\r") + (when (org-looking-at-p " ") (forward-char)))) + (org-up-element))) + +;;;###autoload +(defun +org/table-next-field () + (interactive) + (if (org-at-table-p) (org-table-next-field) (org-end-of-line))) + +;;;###autoload +(defun +org/table-previous-field () + (interactive) + (if (org-at-table-p) (org-table-previous-field) (org-beginning-of-line))) + +;;;###autoload +(defun +org/table-append-field-or-shift-right () + (interactive) + (org-shiftmetaright) + (when (org-at-table-p) (org-metaright))) + +;;;###autoload +(defun +org/table-prepend-field-or-shift-left () + (interactive) + (if (org-at-table-p) (org-shiftmetaright) (org-shiftmetaleft))) + +;;;###autoload +(defun +org/table-append-row-or-shift-down () + (interactive) + (org-shiftmetadown) + (when (org-at-table-p) (org-metadown))) + +;;;###autoload +(defun +org/table-prepend-row-or-shift-up () + (interactive) + (if (org-at-table-p) + (org-shiftmetadown) + (org-shiftmetaup))) + diff --git a/modules/lang/org/autoload/util.el b/modules/lang/org/autoload/util.el new file mode 100644 index 000000000..3743976ea --- /dev/null +++ b/modules/lang/org/autoload/util.el @@ -0,0 +1,10 @@ +;;; emacs/org/autoload/util.el + +;;;###autoload +(defun +org-get-property (name &optional file) + "Get a propery from an org file." + (save-excursion + (goto-char 1) + (re-search-forward (format "^#\\+%s:[ \t]*\\([^\n]+\\)" (upcase name)) nil t) + (buffer-substring-no-properties (match-beginning 1) (match-end 1)))) + diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el new file mode 100644 index 000000000..4acbe41b5 --- /dev/null +++ b/modules/lang/org/config.el @@ -0,0 +1,386 @@ +;;; emacs/org/config.el --- -*- no-byte-compile: t; -*- + +;; A few things you can expect +;; + `org-capture' in a popup frame (can be invoked from outside emacs too) +;; + A simpler attachment system (with auto-deleting support) and +;; drag-and-drop for images and documents into org files +;; + Exported files are put in a centralized location (see +;; `org-export-directory') +;; + TODO Custom links for class notes +;; + TODO An org-mode based CRM (including invoicing and pdf exporting) (see custom-crm) +;; + TODO A tag-based file browser reminiscient of Evernote and Quiver (there's neotree too!) + +(defvar +org-init-hook nil + "TODO") + +(add-hook 'org-load-hook '+org|init) +(add-hook 'org-mode-hook '+org|hook) + +;; Custom variables +(defvar +org-dir "~/work/org/" + "The directory where org files are kept.") + +(defvaralias 'org-directory '+org-dir) + +(defvar +org-notes-dir (concat +org-dir "notes") + "The directory where the notes are kept") + +(defvar +org-quicknote-dir (concat +org-dir "inbox") + "The directory to store quick notes produced by `doom:org-capture' (individual org files)") + +(defvar +org-attachment-dir ".attach/" + "Where to store attachments (relative to current org file).") + +;; (defvar-local +org-attachments-list '() +;; "A list of attachments for the current buffer. This is so my custom attachment +;; system can keep track of each buffer's attachments.") + +(@load +attach) +(@load +capture) +(@load +export) + + +;; +;; Config +;; + +(defun +org|hook () + "Run everytime `org-mode' is enabled." + (evil-org-mode +1) + (visual-line-mode +1) + (setq line-spacing 1) + + ;; If saveplace places the point in a folded position, unfold it on load + (when (outline-invisible-p) + (ignore-errors + (save-excursion + (outline-previous-visible-heading 1) + (org-show-subtree)))) + + (defun +org|update () + "Update counts on headlines (\"cookies\")." + (when (file-exists-p buffer-file-name) + (org-update-statistics-cookies t))) + + (add-hook 'before-save-hook '+org|update nil t) + (add-hook 'evil-insert-state-exit-hook '+org|update nil t)) + + +(defun +org|init () + "Initializes org core." + (define-minor-mode evil-org-mode + "Evil-mode bindings for org-mode." + :init-value nil + :lighter " !" + :keymap (make-sparse-keymap) + :group 'evil-org) + + (@set :popup + '(" *Agenda Commands*" :size 30 :noselect t) + '(" *Org todo*" :size 5 :noselect t) + '("*Calendar*" :size 0.4 :noselect t) + '("*Org Links*" :size 5 :noselect t) + '("^\\*Org Agenda.+" :size 0.4 :regexp t) + '("^\\*Org Src .+\\*$" :size 0.4 :regexp t) + '("^\\*Org-Babel.*\\*$" :size 0.4 :regexp t)) + + (setq-default + org-export-coding-system 'utf-8 + + ;; Appearance + outline-blank-line t + org-indent-mode-turns-on-hiding-stars t + org-adapt-indentation nil + org-blank-before-new-entry '((heading . nil) (plain-list-item . auto)) + org-cycle-separator-lines 1 + org-cycle-include-plain-lists t + org-ellipsis 'doom-folded-face + org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") + ("sharp" "\\sharp" nil "" "" "266F" "♯")) + org-fontify-done-headline t + org-fontify-quote-and-verse-blocks t + org-fontify-whole-heading-line t + org-footnote-auto-label 'plain + org-hide-emphasis-markers t + org-hide-leading-stars t + org-image-actual-width nil + org-indent-indentation-per-level 2 + org-pretty-entities t + org-pretty-entities-include-sub-superscripts t + org-startup-folded t + org-startup-indented t + org-startup-with-inline-images nil + org-tags-column 0 + org-use-sub-superscripts '{} + + ;; Behavior + org-catch-invisible-edits 'show + org-checkbox-hierarchical-statistics nil + org-completion-use-ido nil ; Use ivy/counsel for refiling + org-confirm-elisp-link-function nil + org-default-priority ?C + org-hidden-keywords '(title) + org-hierarchical-todo-statistics t + org-log-done t + org-loop-over-headlines-in-active-region t + org-outline-path-complete-in-steps nil + org-refile-use-outline-path t + org-special-ctrl-a/e t + + ;; Sorting/refiling + org-archive-location (concat +org-dir "/archived/%s::") + org-refile-targets '((nil . (:maxlevel . 2))) ; display full path in refile completion + + ;; Agenda + org-agenda-restore-windows-after-quit nil + org-agenda-skip-unavailable-files nil + org-agenda-dim-blocked-tasks nil + org-agenda-window-setup 'other-frame ; to get org-agenda to behave with shackle... + org-agenda-inhibit-startup t + org-agenda-files (directory-files +org-dir t "\\.org$" t) + org-todo-keywords '((sequence "[ ](t)" "[-](p)" "[?](m)" "|" "[X](d)") + (sequence "TODO(T)" "|" "DONE(D)") + (sequence "IDEA(i)" "NEXT(n)" "ACTIVE(a)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)")) + + + ;; Babel + org-confirm-babel-evaluate nil ; you don't need my permission + org-src-fontify-natively t ; make code pretty + org-src-preserve-indentation t + org-src-tab-acts-natively t + org-src-window-setup 'current-window + org-edit-src-content-indentation 0 + + ;; Latex + org-format-latex-options + (plist-put org-format-latex-options :scale 1.5) + org-format-latex-options + (plist-put org-format-latex-options + :background (face-attribute (or (cadr (assq 'default face-remapping-alist)) + 'default) + :background nil t)) + + org-highlight-latex-and-related '(latex) + org-latex-create-formula-image-program 'dvipng + org-latex-image-default-width ".9\\linewidth" + org-latex-preview-ltxpng-directory (concat doom-cache-dir "/ltxpng/") + org-latex-remove-logfiles nil + org-startup-with-latex-preview nil + ;; org-latex-packages-alist + ;; '(("" "gauss" t) + ;; ("" "physics" t) TODO Install this) + ) + + (org-babel-do-load-languages + 'org-babel-load-languages + '((python . t) (ruby . t) (sh . t) (js . t) (css . t) + (plantuml . t) (emacs-lisp . t) (matlab . t) + (latex . t) (calc . t) (lisp . t) (lilypond . t) + ;; (go . t) + ;; (http . t) + ;; (rust . t) + )) + + (let ((ext-regexp (regexp-opt '("GIF" "JPG" "JPEG" "SVG" "TIF" "TIFF" "BMP" "XPM" + "gif" "jpg" "jpeg" "svg" "tif" "tiff" "bmp" "xpm")))) + (setq iimage-mode-image-regex-alist + `((,(concat "\\(`?file://\\|\\[\\[\\|<\\|`\\)?\\([-+./_0-9a-zA-Z]+\\." + ext-regexp "\\)\\(\\]\\]\\|>\\|'\\)?") . 2) + (,(concat "<\\(http://.+\\." ext-regexp "\\)>") . 1)))) + + ;; Fontify checkboxes and dividers + (defface org-list-bullet + '((t (:inherit font-lock-keyword-face))) + "Face for list bullets") + + (font-lock-add-keywords + 'org-mode '(("^ *\\([-+]\\|[0-9]+[).]\\) " + (1 'org-list-bullet)) + ("^ *\\(-----+\\)$" + (1 'org-meta-line)))) + + ;; Enable gpg support + (require 'epa-file) + (epa-file-enable) + (require 'org-crypt) + (org-crypt-use-before-save-magic) + (setq org-tags-exclude-from-inheritance '("crypt") + org-crypt-key user-mail-address + epa-file-encrypt-to user-mail-address) + + ;; smartparens config + (sp-with-modes '(org-mode) + (sp-local-pair "\\[" "\\]" :post-handlers '(("| " "SPC"))) + (sp-local-pair "\\(" "\\)" :post-handlers '(("| " "SPC"))) + (sp-local-pair "$$" "$$" :post-handlers '((:add " | ")) :unless '(sp-point-at-bol-p)) + (sp-local-pair "{" nil)) + + ;; bullets + (@def-package org-bullets :commands org-bullets-mode) + + ;; Keybinds + (@map (:map org-mode-map + "RET" nil + "C-j" nil + "C-k" nil + :i [remap doom/inflate-space-maybe] 'org-self-insert-command + :i "RET" 'org-return-indent) + + (:map evil-org-mode-map + :n "RET" '+org/dwim-at-point + ;; + :ni "A-l" 'org-metaright + :ni "A-h" 'org-metaleft + :ni "A-k" 'org-metaup + :ni "A-j" 'org-metadown + :ni "A-L" 'org-shiftmetaright + :ni "A-H" 'org-shiftmetaleft + :ni "A-K" 'org-shiftmetaup + :ni "A-J" 'org-shiftmetadown + ;; Expand tables (or shiftmeta move) + :ni "C-S-l" '+org/table-append-field-or-shift-right + :ni "C-S-h" '+org/table-prepend-field-or-shift-left + :ni "C-S-k" '+org/table-prepend-row-or-shift-up + :ni "C-S-j" '+org/table-append-row-or-shift-down + ;; Navigate table cells + :i "C-L" '+org/table-next-field + :i "C-H" '+org/table-previous-field + :i "C-K" '+org/table-previous-row + :i "C-J" '+org/table-next-row + + :i "C-e" 'org-end-of-line + :i "C-a" 'org-beginning-of-line + + :i "" '+org/indent-or-next-field + :i [S-iso-lefttab] '+org/dedent-or-prev-field ; for GNU Emacs + :i [(shift tab)] '+org/dedent-or-prev-field + :i [backtab] '+org/dedent-or-prev-field + + :n "" '+org/toggle-fold + + :nv "j" 'evil-next-visual-line + :nv "k" 'evil-previous-visual-line + :v "" '+snippets/expand-on-region + + :i "M-a" (@λ (evil-visual-state) (org-mark-element)) + :n "M-a" 'org-mark-element + :v "M-a" 'mark-whole-buffer + + :ni "" (@λ (doom/org-insert-item 'below)) + :ni "" (@λ (doom/org-insert-item 'above)) + + ;; Formatting shortcuts + :i "M-b" (@λ (doom/org-surround "*")) ; bold + :i "M-u" (@λ (doom/org-surround "_")) ; underline + :i "M-i" (@λ (doom/org-surround "/")) ; italics + :i "M-`" (@λ (doom/org-surround "+")) ; strikethrough + + :v "M-b" "S*" + :v "M-u" "S_" + :v "M-i" "S/" + :v "M-`" "S+" + + (:localleader + :n "RET" 'org-archive-subtree + :n "SPC" 'doom/org-toggle-checkbox + :n "/" 'org-sparse-tree + :n "=" 'org-align-all-tags + :n "?" 'org-tags-view + :n "D" 'org-deadline + :nv "L" 'org-store-link + :n "R" (@λ (org-metaleft) (org-archive-to-archive-sibling)) ; archive to parent sibling + :n "T" 'org-todo + :n "a" 'org-agenda + :n "d" 'org-time-stamp + :n "e" 'org-edit-special + :n "i" 'doom/org-toggle-inline-images-at-point + :n "l" 'org-insert-link + :n "n" (@λ (if (buffer-narrowed-p) (widen) (org-narrow-to-subtree))) + :n "r" 'org-refile + :n "s" 'org-schedule + :n "t" (@λ (org-todo (if (org-entry-is-todo-p) 'none 'todo))) + :v "t" (@λ (evil-ex-normal evil-visual-beginning evil-visual-end "\\t")) + :n "v" 'variable-pitch-mode + ;; :n "w" 'writing-mode + :n "x" 'doom/org-remove-link) + + ;; TODO Improve folding bindings + :n "za" 'doom/org-toggle-fold + :n "zA" 'org-shifttab + :n "zc" 'outline-hide-subtree + :n "zC" (@λ (outline-hide-sublevels 1)) + :n "zd" (lambda (&optional arg) (interactive "p") (outline-hide-sublevels (or arg 3))) + :n "zm" (@λ (outline-hide-sublevels 1)) + :n "zo" 'outline-show-subtree + :n "zO" 'outline-show-all + :n "zr" 'outline-show-all + + :m "]]" (@λ (call-interactively 'org-forward-heading-same-level) (org-beginning-of-line)) + :m "[[" (@λ (call-interactively 'org-backward-heading-same-level) (org-beginning-of-line)) + :m "]l" 'org-next-link + :m "[l" 'org-previous-link + + :m "gh" 'outline-up-heading + :m "gj" 'org-forward-heading-same-level + :m "gk" 'org-backward-heading-same-level + :m "gl" (@λ (call-interactively 'outline-next-visible-heading) (show-children)) + + :n "go" 'org-open-at-point + :n "gO" (@λ (let ((org-link-frame-setup (append '((file . find-file-other-window)) org-link-frame-setup)) + (org-file-apps '(("\\.org$" . emacs) + (t . "open \"%s\"")))) + (call-interactively 'org-open-at-point))) + + :n "gQ" 'org-fill-paragraph + :m "$" 'org-end-of-line + :m "^" 'org-beginning-of-line + :n "<" 'org-metaleft + :n ">" 'org-metaright + :v "<" (@λ (org-metaleft) (evil-visual-restore)) + :v ">" (@λ (org-metaright) (evil-visual-restore)) + :n "-" 'org-cycle-list-bullet + :m "" 'org-cycle) + + (:map org-src-mode-map + :n "" (@λ (message "Exited") (org-edit-src-exit))) + + (:after org-agenda + (:map org-agenda-mode-map + :e "" 'org-agenda-Quit + :e "m" 'org-agenda-month-view + :e "C-j" 'org-agenda-next-item + :e "C-k" 'org-agenda-previous-item + :e "C-n" 'org-agenda-next-item + :e "C-p" 'org-agenda-previous-item))) + + ;; Initialize everything else + (run-hooks '+org-init-hook) + (+org|hacks)) + + +(defun +org|hacks () + "Getting org to behave." + ;; Don't open separate windows + (push '(file . find-file) org-link-frame-setup) + + ;; Let OS decide what to do with files when opened + (setq org-file-apps + `(("\\.org$" . emacs) + (t . ,(cond (IS-MAC "open -R \"%s\"") + (IS-LINUX "xdg-open \"%s\""))))) + + ;; Don't clobber recentf with agenda files + (defun +org-is-agenda-file (filename) + (find (file-truename filename) org-agenda-files :key 'file-truename + :test 'equal)) + (add-to-list 'recentf-exclude '+org-is-agenda-file) + + ;; Remove highlights on ESC + (defun +org*remove-occur-highlights (&rest args) + (when (eq major-mode 'org-mode) + (org-remove-occur-highlights))) + (advice-add 'evil-force-normal-state :before '+org*remove-occur-highlights) + + ;; Don't reset org-hide! + (advice-add 'org-find-invisible-foreground :override 'ignore)) + diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el new file mode 100644 index 000000000..0b0f31d1d --- /dev/null +++ b/modules/lang/org/packages.el @@ -0,0 +1,10 @@ +;; -*- no-byte-compile: t; -*- +;;; emacs/org/packages.el + +(@package org-plus-contrib) +(@package org-download) +;; (@package ox-pandox) +;; (@package org-bullets) +;; (@package ob-go :recipe (:fetcher github :repo "pope/ob-go")) +;; (@package ob-http) +