From 19acf44a2997f675fe38511a91d4b7264b527eb4 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Fri, 1 Jan 2021 10:57:27 -0600 Subject: Change org-return-dwim function --- config.org | 171 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 71 deletions(-) diff --git a/config.org b/config.org index b4a6d6f..0435dc9 100644 --- a/config.org +++ b/config.org @@ -1053,79 +1053,108 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m (define-key acdw/map (kbd "C-a") #'org-agenda) #+END_SRC -*** [[http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/][A better return in Org mode]] +*** [[https://github.com/alphapapa/unpackaged.el#org-return-dwim][Org Return: DWIM]] #+BEGIN_SRC emacs-lisp -(require 'org-inlinetask) - -(defun scimax/org-return (&optional ignore) - "Add new list item, heading or table row with RET. -A double return on an empty element deletes it. -Use a prefix arg to get regular RET." - (interactive "P") - (if ignore - (org-return) - (cond - - ((eq 'line-break (car (org-element-context))) - (org-return t)) - - ;; Open links like usual, unless point is at the end of a line. - ;; and if at beginning of line, just press enter. - ((or (and (eq 'link (car (org-element-context))) (not (eolp))) - (bolp)) - (org-return)) - - ;; It doesn't make sense to add headings in inline tasks. Thanks Anders - ;; Johansson! - ((org-inlinetask-in-task-p) - (org-return)) - - ;; checkboxes too - ((org-at-item-checkbox-p) - (org-insert-todo-heading nil)) - - ;; lists end with two blank lines, so we need to make sure we are also not - ;; at the beginning of a line to avoid a loop where a new entry gets - ;; created with only one blank line. - ((org-in-item-p) - (if (save-excursion (beginning-of-line) (org-element-property :contents-begin (org-element-context))) - (org-insert-heading) - (beginning-of-line) - (delete-region (line-beginning-position) (line-end-position)) - (org-return))) - - ;; org-heading - ((org-at-heading-p) - (if (not (string= "" (org-element-property :title (org-element-context)))) - (progn (org-end-of-meta-data) - (org-insert-heading-respect-content) - (outline-show-entry)) - (beginning-of-line) - (setf (buffer-substring - (line-beginning-position) (line-end-position)) ""))) - - ;; tables - ((org-at-table-p) - (if (-any? - (lambda (x) (not (string= "" x))) - (nth - (- (org-table-current-dline) 1) - (org-table-to-lisp))) - (org-return) - ;; empty row - (beginning-of-line) - (setf (buffer-substring - (line-beginning-position) (line-end-position)) "") - (org-return))) - - ;; fall-through case - (t - (org-return))))) - - -(define-key org-mode-map (kbd "RET") - 'scimax/org-return) + (defun unpackaged/org-element-descendant-of (type element) + "Return non-nil if ELEMENT is a descendant of TYPE. + TYPE should be an element type, like `item' or `paragraph'. + ELEMENT should be a list like that returned by `org-element-context'." + ;; MAYBE: Use `org-element-lineage'. + (when-let* ((parent (org-element-property :parent element))) + (or (eq type (car parent)) + (unpackaged/org-element-descendant-of type parent)))) + + ;;;###autoload + (defun unpackaged/org-return-dwim (&optional default) + "A helpful replacement for `org-return'. With prefix, call `org-return'. + + On headings, move point to position after entry content. In + lists, insert a new item or end the list, with checkbox if + appropriate. In tables, insert a new row or end the table." + ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ + (interactive "P") + (if default + (org-return) + (cond + ;; Act depending on context around point. + + ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be + ;; followed. + + ;; ((eq 'link (car (org-element-context))) + ;; ;; Link: Open it. + ;; (org-open-at-point-global)) + + ((org-at-heading-p) + ;; Heading: Move to position after entry content. + ;; NOTE: This is probably the most interesting feature of this function. + (let ((heading-start (org-entry-beginning-position))) + (goto-char (org-entry-end-position)) + (cond ((and (org-at-heading-p) + (= heading-start (org-entry-beginning-position))) + ;; Entry ends on its heading; add newline after + (end-of-line) + (insert "\n\n")) + (t + ;; Entry ends after its heading; back up + (forward-line -1) + (end-of-line) + (when (org-at-heading-p) + ;; At the same heading + (forward-line) + (insert "\n") + (forward-line -1)) + ;; FIXME: looking-back is supposed to be called with more arguments. + (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))))) + (insert "\n")) + (forward-line -1))))) + + ((org-at-item-checkbox-p) + ;; Checkbox: Insert new item with checkbox. + (org-insert-todo-heading nil)) + + ((org-in-item-p) + ;; Plain list. Yes, this gets a little complicated... + (let ((context (org-element-context))) + (if (or (eq 'plain-list (car context)) ; First item in list + (and (eq 'item (car context)) + (not (eq (org-element-property :contents-begin context) + (org-element-property :contents-end context)))) + (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link + ;; Non-empty item: Add new item. + (org-insert-item) + ;; Empty item: Close the list. + ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. + (delete-region (line-beginning-position) (line-end-position)) + (insert "\n")))) + + ((when (fboundp 'org-inlinetask-in-task-p) + (org-inlinetask-in-task-p)) + ;; Inline task: Don't insert a new heading. + (org-return)) + + ((org-at-table-p) + (cond ((save-excursion + (beginning-of-line) + ;; See `org-table-next-field'. + (cl-loop with end = (line-end-position) + for cell = (org-element-table-cell-parser) + always (equal (org-element-property :contents-begin cell) + (org-element-property :contents-end cell)) + while (re-search-forward "|" end t))) + ;; Empty row: end the table. + (delete-region (line-beginning-position) (line-end-position)) + (org-return)) + (t + ;; Non-empty row: call `org-return'. + (org-return)))) + (t + ;; All other cases: call `org-return'. + (org-return))))) + + (with-eval-after-load 'org + (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim)) #+END_SRC *** Insert blank lines around headers -- cgit 1.4.1-21-gabe81