mirror of
https://git.savannah.gnu.org/git/emacs/org-mode.git
synced 2025-01-05 11:45:52 +00:00
Preserve file local variables during some operations
* lisp/org-macs.el (org-preserve-local-variables): New macro. * lisp/org-footnote.el (org-footnote--clear-footnote-section): (org-footnote--goto-local-insertion-point): (org-footnote-create-definition): (org-footnote-delete): (org-footnote-renumber-fn:N): (org-footnote-sort): (org-footnote-normalize): * lisp/org.el (org-move-subtree-down): (org-copy-subtree): (org-sort-entries): (org-refile): Use new macro. * testing/lisp/test-org-footnote.el (test-org-footnote/normalize): (test-org-footnote/delete): (test-org-footnote/sort): (test-org-footnote/normalize): * testing/lisp/test-org.el (test-org/sort-entries): Add tests. Operations affected include copying, killing, refiling, archiving and moving subtrees. It also affects sorting, creating and deleting footnotes.
This commit is contained in:
parent
dce82f7e5c
commit
fdb2eb6701
@ -313,23 +313,23 @@ otherwise."
|
||||
|
||||
(defun org-footnote--clear-footnote-section ()
|
||||
"Remove all footnote sections in buffer and create a new one.
|
||||
New section is created at the end of the buffer, before any file
|
||||
local variable definition. Leave point within the new section."
|
||||
New section is created at the end of the buffer. Leave point
|
||||
within the new section."
|
||||
(when org-footnote-section
|
||||
(goto-char (point-min))
|
||||
(let ((regexp
|
||||
(format "^\\*+ +%s[ \t]*$"
|
||||
(regexp-quote org-footnote-section))))
|
||||
(let ((regexp (format "^\\*+ +%s[ \t]*$"
|
||||
(regexp-quote org-footnote-section))))
|
||||
(while (re-search-forward regexp nil t)
|
||||
(delete-region
|
||||
(match-beginning 0)
|
||||
(progn (org-end-of-subtree t t)
|
||||
(if (not (eobp)) (point)
|
||||
(org-footnote--goto-local-insertion-point)
|
||||
(skip-chars-forward " \t\n")
|
||||
(if (eobp) (point) (line-beginning-position)))))))
|
||||
(org-end-of-subtree t t))))
|
||||
(goto-char (point-max))
|
||||
(org-footnote--goto-local-insertion-point)
|
||||
;; Clean-up blank lines at the end of the buffer.
|
||||
(skip-chars-backward " \r\t\n")
|
||||
(unless (bobp)
|
||||
(forward-line)
|
||||
(when (eolp) (insert "\n")))
|
||||
(delete-region (point) (point-max))
|
||||
(when (and (cdr (assq 'heading org-blank-before-new-entry))
|
||||
(zerop (save-excursion (org-back-over-empty-lines))))
|
||||
(insert "\n"))
|
||||
@ -448,14 +448,8 @@ while collecting them."
|
||||
"Find insertion point for footnote, just before next outline heading.
|
||||
Assume insertion point is within currently accessible part of the buffer."
|
||||
(org-with-limited-levels (outline-next-heading))
|
||||
;; Skip file local variables. See `modify-file-local-variable'.
|
||||
(when (eobp)
|
||||
(let ((case-fold-search t))
|
||||
(re-search-backward "^[ \t]*# +Local Variables:"
|
||||
(max (- (point-max) 3000) (point-min))
|
||||
t)))
|
||||
(skip-chars-backward " \t\n")
|
||||
(forward-line)
|
||||
(unless (bobp) (forward-line))
|
||||
(unless (bolp) (insert "\n")))
|
||||
|
||||
|
||||
@ -676,21 +670,22 @@ Return buffer position at the beginning of the definition. This
|
||||
function doesn't move point."
|
||||
(let ((label (org-footnote-normalize-label label))
|
||||
electric-indent-mode) ; Prevent wrong indentation.
|
||||
(org-with-wide-buffer
|
||||
(cond
|
||||
((not org-footnote-section) (org-footnote--goto-local-insertion-point))
|
||||
((save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward
|
||||
(concat "^\\*+[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$")
|
||||
nil t))
|
||||
(goto-char (match-end 0))
|
||||
(forward-line)
|
||||
(unless (bolp) (insert "\n")))
|
||||
(t (org-footnote--clear-footnote-section)))
|
||||
(when (zerop (org-back-over-empty-lines)) (insert "\n"))
|
||||
(insert "[fn:" label "] \n")
|
||||
(line-beginning-position 0))))
|
||||
(org-preserve-local-variables
|
||||
(org-with-wide-buffer
|
||||
(cond
|
||||
((not org-footnote-section) (org-footnote--goto-local-insertion-point))
|
||||
((save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward
|
||||
(concat "^\\*+[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$")
|
||||
nil t))
|
||||
(goto-char (match-end 0))
|
||||
(forward-line)
|
||||
(unless (bolp) (insert "\n")))
|
||||
(t (org-footnote--clear-footnote-section)))
|
||||
(when (zerop (org-back-over-empty-lines)) (insert "\n"))
|
||||
(insert "[fn:" label "] \n")
|
||||
(line-beginning-position 0)))))
|
||||
|
||||
(defun org-footnote-delete-references (label)
|
||||
"Delete every reference to footnote LABEL.
|
||||
@ -733,31 +728,32 @@ and all references of a footnote label.
|
||||
|
||||
If LABEL is non-nil, delete that footnote instead."
|
||||
(catch 'done
|
||||
(let* ((nref 0) (ndef 0) x
|
||||
;; 1. Determine LABEL of footnote at point.
|
||||
(label (cond
|
||||
;; LABEL is provided as argument.
|
||||
(label)
|
||||
;; Footnote reference at point. If the footnote is
|
||||
;; anonymous, delete it and exit instead.
|
||||
((setq x (org-footnote-at-reference-p))
|
||||
(or (car x)
|
||||
(progn
|
||||
(delete-region (nth 1 x) (nth 2 x))
|
||||
(message "Anonymous footnote removed")
|
||||
(throw 'done t))))
|
||||
;; Footnote definition at point.
|
||||
((setq x (org-footnote-at-definition-p))
|
||||
(car x))
|
||||
(t (error "Don't know which footnote to remove")))))
|
||||
;; 2. Now that LABEL is non-nil, find every reference and every
|
||||
;; definition, and delete them.
|
||||
(setq nref (org-footnote-delete-references label)
|
||||
ndef (org-footnote-delete-definitions label))
|
||||
;; 3. Verify consistency of footnotes and notify user.
|
||||
(org-footnote-auto-adjust-maybe)
|
||||
(message "%d definition(s) of and %d reference(s) of footnote %s removed"
|
||||
ndef nref label))))
|
||||
(org-preserve-local-variables
|
||||
(let* ((nref 0) (ndef 0) x
|
||||
;; 1. Determine LABEL of footnote at point.
|
||||
(label (cond
|
||||
;; LABEL is provided as argument.
|
||||
(label)
|
||||
;; Footnote reference at point. If the footnote is
|
||||
;; anonymous, delete it and exit instead.
|
||||
((setq x (org-footnote-at-reference-p))
|
||||
(or (car x)
|
||||
(progn
|
||||
(delete-region (nth 1 x) (nth 2 x))
|
||||
(message "Anonymous footnote removed")
|
||||
(throw 'done t))))
|
||||
;; Footnote definition at point.
|
||||
((setq x (org-footnote-at-definition-p))
|
||||
(car x))
|
||||
(t (error "Don't know which footnote to remove")))))
|
||||
;; 2. Now that LABEL is non-nil, find every reference and every
|
||||
;; definition, and delete them.
|
||||
(setq nref (org-footnote-delete-references label)
|
||||
ndef (org-footnote-delete-definitions label))
|
||||
;; 3. Verify consistency of footnotes and notify user.
|
||||
(org-footnote-auto-adjust-maybe)
|
||||
(message "%d definition(s) of and %d reference(s) of footnote %s removed"
|
||||
ndef nref label)))))
|
||||
|
||||
|
||||
;;;; Sorting, Renumbering, Normalizing
|
||||
@ -765,28 +761,25 @@ If LABEL is non-nil, delete that footnote instead."
|
||||
(defun org-footnote-renumber-fn:N ()
|
||||
"Order numbered footnotes into a sequence in the document."
|
||||
(interactive)
|
||||
(let ((references (org-footnote--collect-references)))
|
||||
(unwind-protect
|
||||
(let* ((c 0)
|
||||
(references (cl-remove-if-not
|
||||
(lambda (r) (string-match-p "\\`[0-9]+\\'" (car r)))
|
||||
references))
|
||||
(alist (mapcar (lambda (l) (cons l (number-to-string (cl-incf c))))
|
||||
(delete-dups (mapcar #'car references)))))
|
||||
(org-with-wide-buffer
|
||||
;; Re-number references.
|
||||
(dolist (ref references)
|
||||
(goto-char (nth 1 ref))
|
||||
(org-footnote--set-label (cdr (assoc (nth 0 ref) alist))))
|
||||
;; Re-number definitions.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^\\[fn:\\([0-9]+\\)\\]" nil t)
|
||||
(replace-match (or (cdr (assoc (match-string 1) alist))
|
||||
;; Un-referenced definitions get
|
||||
;; higher numbers.
|
||||
(number-to-string (cl-incf c)))
|
||||
nil nil nil 1))))
|
||||
(dolist (r references) (set-marker (nth 1 r) nil)))))
|
||||
(let* ((c 0)
|
||||
(references (cl-remove-if-not
|
||||
(lambda (r) (string-match-p "\\`[0-9]+\\'" (car r)))
|
||||
(org-footnote--collect-references)))
|
||||
(alist (mapcar (lambda (l) (cons l (number-to-string (cl-incf c))))
|
||||
(delete-dups (mapcar #'car references)))))
|
||||
(org-with-wide-buffer
|
||||
;; Re-number references.
|
||||
(dolist (ref references)
|
||||
(goto-char (nth 1 ref))
|
||||
(org-footnote--set-label (cdr (assoc (nth 0 ref) alist))))
|
||||
;; Re-number definitions.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^\\[fn:\\([0-9]+\\)\\]" nil t)
|
||||
(replace-match (or (cdr (assoc (match-string 1) alist))
|
||||
;; Un-referenced definitions get higher
|
||||
;; numbers.
|
||||
(number-to-string (cl-incf c)))
|
||||
nil nil nil 1)))))
|
||||
|
||||
(defun org-footnote-sort ()
|
||||
"Rearrange footnote definitions in the current buffer.
|
||||
@ -795,129 +788,121 @@ references. Also relocate definitions at the end of their
|
||||
relative section or within a single footnote section, according
|
||||
to `org-footnote-section'. Inline definitions are ignored."
|
||||
(let ((references (org-footnote--collect-references)))
|
||||
(unwind-protect
|
||||
(let ((definitions (org-footnote--collect-definitions 'delete)))
|
||||
(org-with-wide-buffer
|
||||
(org-footnote--clear-footnote-section)
|
||||
;; Insert footnote definitions at the appropriate location,
|
||||
;; separated by a blank line. Each definition is inserted
|
||||
;; only once throughout the buffer.
|
||||
(let (inserted)
|
||||
(dolist (cell references)
|
||||
(let ((label (car cell))
|
||||
(nested (not (nth 2 cell)))
|
||||
(inline (nth 3 cell)))
|
||||
(unless (or (member label inserted) inline)
|
||||
(push label inserted)
|
||||
(unless (or org-footnote-section nested)
|
||||
;; If `org-footnote-section' is non-nil, or
|
||||
;; reference is nested, point is already at the
|
||||
;; correct position. Otherwise, move at the
|
||||
;; appropriate location within the section
|
||||
;; containing the reference.
|
||||
(goto-char (nth 1 cell))
|
||||
(org-footnote--goto-local-insertion-point))
|
||||
(insert "\n"
|
||||
(or (cdr (assoc label definitions))
|
||||
(format "[fn:%s] DEFINITION NOT FOUND." label))
|
||||
"\n"))))
|
||||
;; Insert un-referenced footnote definitions at the end.
|
||||
(let ((unreferenced
|
||||
(cl-remove-if (lambda (d) (member (car d) inserted))
|
||||
definitions)))
|
||||
(dolist (d unreferenced) (insert "\n" (cdr d) "\n"))))))
|
||||
;; Clear dangling markers in the buffer.
|
||||
(dolist (r references) (set-marker (nth 1 r) nil)))))
|
||||
(org-preserve-local-variables
|
||||
(let ((definitions (org-footnote--collect-definitions 'delete)))
|
||||
(org-with-wide-buffer
|
||||
(org-footnote--clear-footnote-section)
|
||||
;; Insert footnote definitions at the appropriate location,
|
||||
;; separated by a blank line. Each definition is inserted
|
||||
;; only once throughout the buffer.
|
||||
(let (inserted)
|
||||
(dolist (cell references)
|
||||
(let ((label (car cell))
|
||||
(nested (not (nth 2 cell)))
|
||||
(inline (nth 3 cell)))
|
||||
(unless (or (member label inserted) inline)
|
||||
(push label inserted)
|
||||
(unless (or org-footnote-section nested)
|
||||
;; If `org-footnote-section' is non-nil, or
|
||||
;; reference is nested, point is already at the
|
||||
;; correct position. Otherwise, move at the
|
||||
;; appropriate location within the section
|
||||
;; containing the reference.
|
||||
(goto-char (nth 1 cell))
|
||||
(org-footnote--goto-local-insertion-point))
|
||||
(insert "\n"
|
||||
(or (cdr (assoc label definitions))
|
||||
(format "[fn:%s] DEFINITION NOT FOUND." label))
|
||||
"\n"))))
|
||||
;; Insert un-referenced footnote definitions at the end.
|
||||
(pcase-dolist (`(,label . ,definition) definitions)
|
||||
(unless (member label inserted)
|
||||
(insert "\n" definition "\n")))))))))
|
||||
|
||||
(defun org-footnote-normalize ()
|
||||
"Turn every footnote in buffer into a numbered one."
|
||||
(interactive)
|
||||
(let ((references (org-footnote--collect-references 'anonymous)))
|
||||
(unwind-protect
|
||||
(let ((n 0)
|
||||
(translations nil)
|
||||
(definitions nil))
|
||||
(org-with-wide-buffer
|
||||
;; Update label for reference. We need to do this before
|
||||
;; clearing definitions in order to rename nested footnotes
|
||||
;; before they are deleted.
|
||||
(dolist (cell references)
|
||||
(let* ((label (car cell))
|
||||
(anonymous (not label))
|
||||
(new
|
||||
(cond
|
||||
;; In order to differentiate anonymous
|
||||
;; references from regular ones, set their
|
||||
;; labels to integers, not strings.
|
||||
(anonymous (setcar cell (cl-incf n)))
|
||||
((cdr (assoc label translations)))
|
||||
(t (let ((l (number-to-string (cl-incf n))))
|
||||
(push (cons label l) translations)
|
||||
l)))))
|
||||
(goto-char (nth 1 cell)) ; Move to reference's start.
|
||||
(org-footnote--set-label
|
||||
(if anonymous (number-to-string new) new))
|
||||
(let ((size (nth 3 cell)))
|
||||
;; Transform inline footnotes into regular references
|
||||
;; and retain their definition for later insertion as
|
||||
;; a regular footnote definition.
|
||||
(when size
|
||||
(let ((def (concat
|
||||
(format "[fn:%s] " new)
|
||||
(org-trim
|
||||
(substring
|
||||
(delete-and-extract-region
|
||||
(point) (+ (point) size 1))
|
||||
1)))))
|
||||
(push (cons (if anonymous new label) def) definitions)
|
||||
(when org-footnote-fill-after-inline-note-extraction
|
||||
(org-fill-paragraph)))))))
|
||||
;; Collect definitions. Update labels according to ALIST.
|
||||
(let ((definitions
|
||||
(nconc definitions
|
||||
(org-footnote--collect-definitions 'delete)))
|
||||
(inserted))
|
||||
(org-footnote--clear-footnote-section)
|
||||
(dolist (cell references)
|
||||
(let* ((label (car cell))
|
||||
(anonymous (integerp label))
|
||||
(pos (nth 1 cell)))
|
||||
;; Move to appropriate location, if required. When
|
||||
;; there is a footnote section or reference is
|
||||
;; nested, point is already at the expected location.
|
||||
(unless (or org-footnote-section (not (nth 2 cell)))
|
||||
(goto-char pos)
|
||||
(org-footnote--goto-local-insertion-point))
|
||||
;; Insert new definition once label is updated.
|
||||
(unless (member label inserted)
|
||||
(push label inserted)
|
||||
(let ((stored (cdr (assoc label definitions)))
|
||||
;; Anonymous footnotes' label is already
|
||||
;; up-to-date.
|
||||
(new (if anonymous label
|
||||
(cdr (assoc label translations)))))
|
||||
(insert "\n"
|
||||
(cond
|
||||
((not stored)
|
||||
(format "[fn:%s] DEFINITION NOT FOUND." new))
|
||||
(anonymous stored)
|
||||
(t
|
||||
(replace-regexp-in-string
|
||||
"\\`\\[fn:\\(.*?\\)\\]" new stored nil nil 1)))
|
||||
"\n")))))
|
||||
;; Insert un-referenced footnote definitions at the end.
|
||||
(let ((unreferenced
|
||||
(cl-remove-if (lambda (d) (member (car d) inserted))
|
||||
definitions)))
|
||||
(dolist (d unreferenced)
|
||||
(insert "\n"
|
||||
(replace-regexp-in-string
|
||||
org-footnote-definition-re
|
||||
(format "[fn:%d]" (cl-incf n))
|
||||
(cdr d))
|
||||
"\n"))))))
|
||||
;; Clear dangling markers.
|
||||
(dolist (r references) (set-marker (nth 1 r) nil)))))
|
||||
(org-preserve-local-variables
|
||||
(let ((n 0)
|
||||
(translations nil)
|
||||
(definitions nil)
|
||||
(references (org-footnote--collect-references 'anonymous)))
|
||||
(org-with-wide-buffer
|
||||
;; Update label for reference. We need to do this before
|
||||
;; clearing definitions in order to rename nested footnotes
|
||||
;; before they are deleted.
|
||||
(dolist (cell references)
|
||||
(let* ((label (car cell))
|
||||
(anonymous (not label))
|
||||
(new
|
||||
(cond
|
||||
;; In order to differentiate anonymous references
|
||||
;; from regular ones, set their labels to integers,
|
||||
;; not strings.
|
||||
(anonymous (setcar cell (cl-incf n)))
|
||||
((cdr (assoc label translations)))
|
||||
(t (let ((l (number-to-string (cl-incf n))))
|
||||
(push (cons label l) translations)
|
||||
l)))))
|
||||
(goto-char (nth 1 cell)) ; Move to reference's start.
|
||||
(org-footnote--set-label
|
||||
(if anonymous (number-to-string new) new))
|
||||
(let ((size (nth 3 cell)))
|
||||
;; Transform inline footnotes into regular references and
|
||||
;; retain their definition for later insertion as
|
||||
;; a regular footnote definition.
|
||||
(when size
|
||||
(let ((def (concat
|
||||
(format "[fn:%s] " new)
|
||||
(org-trim
|
||||
(substring
|
||||
(delete-and-extract-region
|
||||
(point) (+ (point) size 1))
|
||||
1)))))
|
||||
(push (cons (if anonymous new label) def) definitions)
|
||||
(when org-footnote-fill-after-inline-note-extraction
|
||||
(org-fill-paragraph)))))))
|
||||
;; Collect definitions. Update labels according to ALIST.
|
||||
(let ((definitions
|
||||
(nconc definitions
|
||||
(org-footnote--collect-definitions 'delete)))
|
||||
(inserted))
|
||||
(org-footnote--clear-footnote-section)
|
||||
(dolist (cell references)
|
||||
(let* ((label (car cell))
|
||||
(anonymous (integerp label))
|
||||
(pos (nth 1 cell)))
|
||||
;; Move to appropriate location, if required. When there
|
||||
;; is a footnote section or reference is nested, point is
|
||||
;; already at the expected location.
|
||||
(unless (or org-footnote-section (not (nth 2 cell)))
|
||||
(goto-char pos)
|
||||
(org-footnote--goto-local-insertion-point))
|
||||
;; Insert new definition once label is updated.
|
||||
(unless (member label inserted)
|
||||
(push label inserted)
|
||||
(let ((stored (cdr (assoc label definitions)))
|
||||
;; Anonymous footnotes' label is already
|
||||
;; up-to-date.
|
||||
(new (if anonymous label
|
||||
(cdr (assoc label translations)))))
|
||||
(insert "\n"
|
||||
(cond
|
||||
((not stored)
|
||||
(format "[fn:%s] DEFINITION NOT FOUND." new))
|
||||
(anonymous stored)
|
||||
(t
|
||||
(replace-regexp-in-string
|
||||
"\\`\\[fn:\\(.*?\\)\\]" new stored nil nil 1)))
|
||||
"\n")))))
|
||||
;; Insert un-referenced footnote definitions at the end.
|
||||
(pcase-dolist (`(,label . ,definition) definitions)
|
||||
(unless (member label inserted)
|
||||
(insert "\n"
|
||||
(replace-regexp-in-string org-footnote-definition-re
|
||||
(format "[fn:%d]" (cl-incf n))
|
||||
definition)
|
||||
"\n"))))))))
|
||||
|
||||
(defun org-footnote-auto-adjust-maybe ()
|
||||
"Renumber and/or sort footnotes according to user settings."
|
||||
|
@ -169,6 +169,24 @@ point nowhere."
|
||||
"Load FILE with optional arguments NOERROR and MUSTSUFFIX."
|
||||
`(load ,file 'noerror nil nil 'mustsuffix))
|
||||
|
||||
(defmacro org-preserve-local-variables (&rest body)
|
||||
"Execute BODY while preserving local variables."
|
||||
(declare (debug (body)))
|
||||
`(let ((local-variables
|
||||
(org-with-wide-buffer
|
||||
(goto-char (point-max))
|
||||
(let ((case-fold-search t))
|
||||
(and (re-search-backward "^[ \t]*# +Local Variables:"
|
||||
(max (- (point) 3000) 1)
|
||||
t)
|
||||
(delete-and-extract-region (point) (point-max)))))))
|
||||
(unwind-protect (progn ,@body)
|
||||
(when local-variables
|
||||
(org-with-wide-buffer
|
||||
(goto-char (point-max))
|
||||
(unless (bolp) (insert "\n"))
|
||||
(insert local-variables))))))
|
||||
|
||||
|
||||
|
||||
;;; Buffer
|
||||
|
382
lisp/org.el
382
lisp/org.el
@ -8085,80 +8085,81 @@ case."
|
||||
"Move the current subtree down past ARG headlines of the same level."
|
||||
(interactive "p")
|
||||
(setq arg (prefix-numeric-value arg))
|
||||
(let ((movfunc (if (> arg 0) 'org-get-next-sibling
|
||||
'org-get-last-sibling))
|
||||
(ins-point (make-marker))
|
||||
(cnt (abs arg))
|
||||
(col (current-column))
|
||||
beg beg0 end txt folded ne-beg ne-end ne-ins ins-end)
|
||||
;; Select the tree
|
||||
(org-back-to-heading)
|
||||
(setq beg0 (point))
|
||||
(save-excursion
|
||||
(setq ne-beg (org-back-over-empty-lines))
|
||||
(setq beg (point)))
|
||||
(save-match-data
|
||||
(save-excursion (outline-end-of-heading)
|
||||
(setq folded (org-invisible-p)))
|
||||
(progn (org-end-of-subtree nil t)
|
||||
(unless (eobp) (backward-char))))
|
||||
(outline-next-heading)
|
||||
(setq ne-end (org-back-over-empty-lines))
|
||||
(setq end (point))
|
||||
(goto-char beg0)
|
||||
(when (and (> arg 0) (org-first-sibling-p) (< ne-end ne-beg))
|
||||
;; include less whitespace
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(forward-line (- ne-beg ne-end))
|
||||
(setq beg (point))))
|
||||
;; Find insertion point, with error handling
|
||||
(while (> cnt 0)
|
||||
(or (and (funcall movfunc) (looking-at org-outline-regexp))
|
||||
(progn (goto-char beg0)
|
||||
(user-error "Cannot move past superior level or buffer limit")))
|
||||
(setq cnt (1- cnt)))
|
||||
(when (> arg 0)
|
||||
;; Moving forward - still need to move over subtree
|
||||
(org-end-of-subtree t t)
|
||||
(save-excursion
|
||||
(org-back-over-empty-lines)
|
||||
(or (bolp) (newline))))
|
||||
(setq ne-ins (org-back-over-empty-lines))
|
||||
(move-marker ins-point (point))
|
||||
(setq txt (buffer-substring beg end))
|
||||
(org-save-markers-in-region beg end)
|
||||
(delete-region beg end)
|
||||
(org-remove-empty-overlays-at beg)
|
||||
(or (= beg (point-min)) (outline-flag-region (1- beg) beg nil))
|
||||
(or (bobp) (outline-flag-region (1- (point)) (point) nil))
|
||||
(and (not (bolp)) (looking-at "\n") (forward-char 1))
|
||||
(let ((bbb (point)))
|
||||
(insert-before-markers txt)
|
||||
(org-reinstall-markers-in-region bbb)
|
||||
(move-marker ins-point bbb))
|
||||
(or (bolp) (insert "\n"))
|
||||
(setq ins-end (point))
|
||||
(goto-char ins-point)
|
||||
(org-skip-whitespace)
|
||||
(when (and (< arg 0)
|
||||
(org-first-sibling-p)
|
||||
(> ne-ins ne-beg))
|
||||
;; Move whitespace back to beginning
|
||||
(save-excursion
|
||||
(goto-char ins-end)
|
||||
(let ((kill-whole-line t))
|
||||
(kill-line (- ne-ins ne-beg)) (point)))
|
||||
(insert (make-string (- ne-ins ne-beg) ?\n)))
|
||||
(move-marker ins-point nil)
|
||||
(if folded
|
||||
(outline-hide-subtree)
|
||||
(org-show-entry)
|
||||
(org-show-children)
|
||||
(org-cycle-hide-drawers 'children))
|
||||
(org-clean-visibility-after-subtree-move)
|
||||
;; move back to the initial column we were at
|
||||
(move-to-column col)))
|
||||
(org-preserve-local-variables
|
||||
(let ((movfunc (if (> arg 0) 'org-get-next-sibling
|
||||
'org-get-last-sibling))
|
||||
(ins-point (make-marker))
|
||||
(cnt (abs arg))
|
||||
(col (current-column))
|
||||
beg beg0 end txt folded ne-beg ne-end ne-ins ins-end)
|
||||
;; Select the tree
|
||||
(org-back-to-heading)
|
||||
(setq beg0 (point))
|
||||
(save-excursion
|
||||
(setq ne-beg (org-back-over-empty-lines))
|
||||
(setq beg (point)))
|
||||
(save-match-data
|
||||
(save-excursion (outline-end-of-heading)
|
||||
(setq folded (org-invisible-p)))
|
||||
(progn (org-end-of-subtree nil t)
|
||||
(unless (eobp) (backward-char))))
|
||||
(outline-next-heading)
|
||||
(setq ne-end (org-back-over-empty-lines))
|
||||
(setq end (point))
|
||||
(goto-char beg0)
|
||||
(when (and (> arg 0) (org-first-sibling-p) (< ne-end ne-beg))
|
||||
;; include less whitespace
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(forward-line (- ne-beg ne-end))
|
||||
(setq beg (point))))
|
||||
;; Find insertion point, with error handling
|
||||
(while (> cnt 0)
|
||||
(or (and (funcall movfunc) (looking-at org-outline-regexp))
|
||||
(progn (goto-char beg0)
|
||||
(user-error "Cannot move past superior level or buffer limit")))
|
||||
(setq cnt (1- cnt)))
|
||||
(when (> arg 0)
|
||||
;; Moving forward - still need to move over subtree
|
||||
(org-end-of-subtree t t)
|
||||
(save-excursion
|
||||
(org-back-over-empty-lines)
|
||||
(unless (bolp) (insert "\n"))))
|
||||
(setq ne-ins (org-back-over-empty-lines))
|
||||
(move-marker ins-point (point))
|
||||
(setq txt (buffer-substring beg end))
|
||||
(org-save-markers-in-region beg end)
|
||||
(delete-region beg end)
|
||||
(org-remove-empty-overlays-at beg)
|
||||
(or (= beg (point-min)) (outline-flag-region (1- beg) beg nil))
|
||||
(or (bobp) (outline-flag-region (1- (point)) (point) nil))
|
||||
(and (not (bolp)) (looking-at "\n") (forward-char 1))
|
||||
(let ((bbb (point)))
|
||||
(insert-before-markers txt)
|
||||
(org-reinstall-markers-in-region bbb)
|
||||
(move-marker ins-point bbb))
|
||||
(or (bolp) (insert "\n"))
|
||||
(setq ins-end (point))
|
||||
(goto-char ins-point)
|
||||
(org-skip-whitespace)
|
||||
(when (and (< arg 0)
|
||||
(org-first-sibling-p)
|
||||
(> ne-ins ne-beg))
|
||||
;; Move whitespace back to beginning.
|
||||
(save-excursion
|
||||
(goto-char ins-end)
|
||||
(let ((kill-whole-line t))
|
||||
(kill-line (- ne-ins ne-beg)) (point)))
|
||||
(insert (make-string (- ne-ins ne-beg) ?\n)))
|
||||
(move-marker ins-point nil)
|
||||
(if folded
|
||||
(outline-hide-subtree)
|
||||
(org-show-entry)
|
||||
(org-show-children)
|
||||
(org-cycle-hide-drawers 'children))
|
||||
(org-clean-visibility-after-subtree-move)
|
||||
;; Move back to the initial column we were at.
|
||||
(move-to-column col))))
|
||||
|
||||
(defvar org-subtree-clip ""
|
||||
"Clipboard for cut and paste of subtrees.
|
||||
@ -8185,35 +8186,36 @@ If FORCE-STORE-MARKERS is non-nil, store the relative locations
|
||||
of some markers in the region, even if CUT is non-nil. This is
|
||||
useful if the caller implements cut-and-paste as copy-then-paste-then-cut."
|
||||
(interactive "p")
|
||||
(let (beg end folded (beg0 (point)))
|
||||
(if (called-interactively-p 'any)
|
||||
(org-back-to-heading nil) ; take what looks like a subtree
|
||||
(org-back-to-heading t)) ; take what is really there
|
||||
(setq beg (point))
|
||||
(skip-chars-forward " \t\r\n")
|
||||
(save-match-data
|
||||
(if nosubtrees
|
||||
(outline-next-heading)
|
||||
(save-excursion (outline-end-of-heading)
|
||||
(setq folded (org-invisible-p)))
|
||||
(ignore-errors (org-forward-heading-same-level (1- n) t))
|
||||
(org-end-of-subtree t t)))
|
||||
;; Include the end of an inlinetask
|
||||
(when (and (featurep 'org-inlinetask)
|
||||
(looking-at-p (concat (org-inlinetask-outline-regexp)
|
||||
"END[ \t]*$")))
|
||||
(end-of-line))
|
||||
(setq end (point))
|
||||
(goto-char beg0)
|
||||
(when (> end beg)
|
||||
(setq org-subtree-clip-folded folded)
|
||||
(when (or cut force-store-markers)
|
||||
(org-save-markers-in-region beg end))
|
||||
(if cut (kill-region beg end) (copy-region-as-kill beg end))
|
||||
(setq org-subtree-clip (current-kill 0))
|
||||
(message "%s: Subtree(s) with %d characters"
|
||||
(if cut "Cut" "Copied")
|
||||
(length org-subtree-clip)))))
|
||||
(org-preserve-local-variables
|
||||
(let (beg end folded (beg0 (point)))
|
||||
(if (called-interactively-p 'any)
|
||||
(org-back-to-heading nil) ; take what looks like a subtree
|
||||
(org-back-to-heading t)) ; take what is really there
|
||||
(setq beg (point))
|
||||
(skip-chars-forward " \t\r\n")
|
||||
(save-match-data
|
||||
(if nosubtrees
|
||||
(outline-next-heading)
|
||||
(save-excursion (outline-end-of-heading)
|
||||
(setq folded (org-invisible-p)))
|
||||
(ignore-errors (org-forward-heading-same-level (1- n) t))
|
||||
(org-end-of-subtree t t)))
|
||||
;; Include the end of an inlinetask
|
||||
(when (and (featurep 'org-inlinetask)
|
||||
(looking-at-p (concat (org-inlinetask-outline-regexp)
|
||||
"END[ \t]*$")))
|
||||
(end-of-line))
|
||||
(setq end (point))
|
||||
(goto-char beg0)
|
||||
(when (> end beg)
|
||||
(setq org-subtree-clip-folded folded)
|
||||
(when (or cut force-store-markers)
|
||||
(org-save-markers-in-region beg end))
|
||||
(if cut (kill-region beg end) (copy-region-as-kill beg end))
|
||||
(setq org-subtree-clip (current-kill 0))
|
||||
(message "%s: Subtree(s) with %d characters"
|
||||
(if cut "Cut" "Copied")
|
||||
(length org-subtree-clip))))))
|
||||
|
||||
(defun org-paste-subtree (&optional level tree for-yank remove)
|
||||
"Paste the clipboard as a subtree, with modification of headline level.
|
||||
@ -8691,88 +8693,91 @@ function is being called interactively."
|
||||
(dcst (downcase sorting-type))
|
||||
(case-fold-search nil)
|
||||
(now (current-time)))
|
||||
(sort-subr
|
||||
(/= dcst sorting-type)
|
||||
;; This function moves to the beginning character of the "record" to
|
||||
;; be sorted.
|
||||
(lambda nil
|
||||
(if (re-search-forward re nil t)
|
||||
(goto-char (match-beginning 0))
|
||||
(goto-char (point-max))))
|
||||
;; This function moves to the last character of the "record" being
|
||||
;; sorted.
|
||||
(lambda nil
|
||||
(save-match-data
|
||||
(condition-case nil
|
||||
(outline-forward-same-level 1)
|
||||
(error
|
||||
(goto-char (point-max))))))
|
||||
;; This function returns the value that gets sorted against.
|
||||
(lambda nil
|
||||
(cond
|
||||
((= dcst ?n)
|
||||
(if (looking-at org-complex-heading-regexp)
|
||||
(string-to-number (org-sort-remove-invisible (match-string 4)))
|
||||
nil))
|
||||
((= dcst ?a)
|
||||
(if (looking-at org-complex-heading-regexp)
|
||||
(funcall case-func (org-sort-remove-invisible (match-string 4)))
|
||||
nil))
|
||||
((= dcst ?k)
|
||||
(or (get-text-property (point) :org-clock-minutes) 0))
|
||||
((= dcst ?t)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (or (re-search-forward org-ts-regexp end t)
|
||||
(re-search-forward org-ts-regexp-both end t))
|
||||
(org-time-string-to-seconds (match-string 0))
|
||||
(float-time now))))
|
||||
((= dcst ?c)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward
|
||||
(concat "^[ \t]*\\[" org-ts-regexp1 "\\]")
|
||||
end t)
|
||||
(org-time-string-to-seconds (match-string 0))
|
||||
(float-time now))))
|
||||
((= dcst ?s)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward org-scheduled-time-regexp end t)
|
||||
(org-time-string-to-seconds (match-string 1))
|
||||
(float-time now))))
|
||||
((= dcst ?d)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward org-deadline-time-regexp end t)
|
||||
(org-time-string-to-seconds (match-string 1))
|
||||
(float-time now))))
|
||||
((= dcst ?p)
|
||||
(if (re-search-forward org-priority-regexp (point-at-eol) t)
|
||||
(string-to-char (match-string 2))
|
||||
org-default-priority))
|
||||
((= dcst ?r)
|
||||
(or (org-entry-get nil property) ""))
|
||||
((= dcst ?o)
|
||||
(when (looking-at org-complex-heading-regexp)
|
||||
(let* ((m (match-string 2))
|
||||
(s (if (member m org-done-keywords) '- '+)))
|
||||
(- 99 (funcall s (length (member m org-todo-keywords-1)))))))
|
||||
((= dcst ?f)
|
||||
(if getkey-func
|
||||
(progn
|
||||
(setq tmp (funcall getkey-func))
|
||||
(when (stringp tmp) (setq tmp (funcall case-func tmp)))
|
||||
tmp)
|
||||
(error "Invalid key function `%s'" getkey-func)))
|
||||
(t (error "Invalid sorting type `%c'" sorting-type))))
|
||||
nil
|
||||
(cond
|
||||
((= dcst ?a) 'string<)
|
||||
((= dcst ?f)
|
||||
(or compare-func
|
||||
(and interactive?
|
||||
(org-read-function
|
||||
(concat "Function for comparing keys "
|
||||
"(empty for default `sort-subr' predicate): ")
|
||||
'allow-empty))))
|
||||
((member dcst '(?p ?t ?s ?d ?c ?k)) '<)))
|
||||
(org-preserve-local-variables
|
||||
(sort-subr
|
||||
(/= dcst sorting-type)
|
||||
;; This function moves to the beginning character of the
|
||||
;; "record" to be sorted.
|
||||
(lambda nil
|
||||
(if (re-search-forward re nil t)
|
||||
(goto-char (match-beginning 0))
|
||||
(goto-char (point-max))))
|
||||
;; This function moves to the last character of the "record" being
|
||||
;; sorted.
|
||||
(lambda nil
|
||||
(save-match-data
|
||||
(condition-case nil
|
||||
(outline-forward-same-level 1)
|
||||
(error
|
||||
(goto-char (point-max))))))
|
||||
;; This function returns the value that gets sorted against.
|
||||
(lambda ()
|
||||
(cond
|
||||
((= dcst ?n)
|
||||
(if (looking-at org-complex-heading-regexp)
|
||||
(string-to-number
|
||||
(org-sort-remove-invisible (match-string 4)))
|
||||
nil))
|
||||
((= dcst ?a)
|
||||
(if (looking-at org-complex-heading-regexp)
|
||||
(funcall case-func
|
||||
(org-sort-remove-invisible (match-string 4)))
|
||||
nil))
|
||||
((= dcst ?k)
|
||||
(or (get-text-property (point) :org-clock-minutes) 0))
|
||||
((= dcst ?t)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (or (re-search-forward org-ts-regexp end t)
|
||||
(re-search-forward org-ts-regexp-both end t))
|
||||
(org-time-string-to-seconds (match-string 0))
|
||||
(float-time now))))
|
||||
((= dcst ?c)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward
|
||||
(concat "^[ \t]*\\[" org-ts-regexp1 "\\]")
|
||||
end t)
|
||||
(org-time-string-to-seconds (match-string 0))
|
||||
(float-time now))))
|
||||
((= dcst ?s)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward org-scheduled-time-regexp end t)
|
||||
(org-time-string-to-seconds (match-string 1))
|
||||
(float-time now))))
|
||||
((= dcst ?d)
|
||||
(let ((end (save-excursion (outline-next-heading) (point))))
|
||||
(if (re-search-forward org-deadline-time-regexp end t)
|
||||
(org-time-string-to-seconds (match-string 1))
|
||||
(float-time now))))
|
||||
((= dcst ?p)
|
||||
(if (re-search-forward org-priority-regexp (point-at-eol) t)
|
||||
(string-to-char (match-string 2))
|
||||
org-default-priority))
|
||||
((= dcst ?r)
|
||||
(or (org-entry-get nil property) ""))
|
||||
((= dcst ?o)
|
||||
(when (looking-at org-complex-heading-regexp)
|
||||
(let* ((m (match-string 2))
|
||||
(s (if (member m org-done-keywords) '- '+)))
|
||||
(- 99 (funcall s (length (member m org-todo-keywords-1)))))))
|
||||
((= dcst ?f)
|
||||
(if getkey-func
|
||||
(progn
|
||||
(setq tmp (funcall getkey-func))
|
||||
(when (stringp tmp) (setq tmp (funcall case-func tmp)))
|
||||
tmp)
|
||||
(error "Invalid key function `%s'" getkey-func)))
|
||||
(t (error "Invalid sorting type `%c'" sorting-type))))
|
||||
nil
|
||||
(cond
|
||||
((= dcst ?a) 'string<)
|
||||
((= dcst ?f)
|
||||
(or compare-func
|
||||
(and interactive?
|
||||
(org-read-function
|
||||
(concat "Function for comparing keys "
|
||||
"(empty for default `sort-subr' predicate): ")
|
||||
'allow-empty))))
|
||||
((member dcst '(?p ?t ?s ?d ?c ?k)) '<))))
|
||||
(when restore-clock?
|
||||
(move-marker org-clock-marker
|
||||
(1+ (next-single-property-change
|
||||
@ -11552,9 +11557,10 @@ prefix argument (`C-u C-u C-u C-c C-w')."
|
||||
(unless org-refile-keep
|
||||
(if regionp
|
||||
(delete-region (point) (+ (point) (- region-end region-start)))
|
||||
(delete-region
|
||||
(and (org-back-to-heading t) (point))
|
||||
(min (1+ (buffer-size)) (org-end-of-subtree t t) (point)))))
|
||||
(org-preserve-local-variables
|
||||
(delete-region
|
||||
(and (org-back-to-heading t) (point))
|
||||
(min (1+ (buffer-size)) (org-end-of-subtree t t) (point))))))
|
||||
(when (featurep 'org-inlinetask)
|
||||
(org-inlinetask-remove-END-maybe))
|
||||
(setq org-markers-to-move nil)
|
||||
|
@ -112,7 +112,33 @@
|
||||
(org-footnote-new))
|
||||
(buffer-substring-no-properties
|
||||
(line-beginning-position -1)
|
||||
(line-beginning-position 4))))))
|
||||
(line-beginning-position 4)))))
|
||||
;; Do not alter file local variables when inserting new definition
|
||||
;; label.
|
||||
(should
|
||||
(equal "Paragraph[fn:1]
|
||||
|
||||
\[fn:1]
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(org-test-with-temp-text
|
||||
"Paragraph<point>\n# Local Variables:\n# foo: t\n# End:"
|
||||
(let ((org-footnote-section nil)) (org-footnote-new))
|
||||
(buffer-string))))
|
||||
(should
|
||||
(equal "Paragraph[fn:1]
|
||||
|
||||
* Footnotes
|
||||
|
||||
\[fn:1]
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(org-test-with-temp-text
|
||||
"Paragraph<point>\n# Local Variables:\n# foo: t\n# End:"
|
||||
(let ((org-footnote-section "Footnotes")) (org-footnote-new))
|
||||
(buffer-string)))))
|
||||
|
||||
(ert-deftest test-org-footnote/delete ()
|
||||
"Test `org-footnote-delete' specifications."
|
||||
@ -175,7 +201,15 @@
|
||||
(org-test-with-temp-text
|
||||
"Text[fn:1]\n\n[fn:1] Definition.\n\n\nOther text."
|
||||
(org-footnote-delete "1")
|
||||
(buffer-string))))))
|
||||
(buffer-string)))))
|
||||
;; Preserve file local variables when deleting a footnote.
|
||||
(should
|
||||
(equal
|
||||
"Paragraph\n# Local Variables:\n# foo: t\n# End:"
|
||||
(org-test-with-temp-text
|
||||
"Paragraph[fn:1]\n[fn:1] Def 1\n# Local Variables:\n# foo: t\n# End:"
|
||||
(let ((org-footnote-section nil)) (org-footnote-delete "1"))
|
||||
(buffer-string)))))
|
||||
|
||||
(ert-deftest test-org-footnote/goto-definition ()
|
||||
"Test `org-footnote-goto-definition' specifications."
|
||||
@ -454,7 +488,32 @@ Text[fn:1][fn:4]
|
||||
"
|
||||
(org-test-with-temp-text "Text[fn:9]\n\n[fn:1] A\n[fn:9] B"
|
||||
(let ((org-footnote-section nil)) (org-footnote-sort))
|
||||
(buffer-string)))))
|
||||
(buffer-string))))
|
||||
;; When sorting, preserve file local variables.
|
||||
(should
|
||||
(equal "
|
||||
Paragraph[fn:1][fn:2]
|
||||
|
||||
\[fn:1] Def 1
|
||||
|
||||
\[fn:2] Def 2
|
||||
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(org-test-with-temp-text
|
||||
"
|
||||
Paragraph[fn:1][fn:2]
|
||||
|
||||
\[fn:2] Def 2
|
||||
|
||||
\[fn:1] Def 1
|
||||
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(let ((org-footnote-section nil)) (org-footnote-sort))
|
||||
(buffer-string)))))
|
||||
|
||||
(ert-deftest test-org-footnote/renumber-fn:N ()
|
||||
"Test `org-footnote-renumber-fn:N' specifications."
|
||||
@ -569,7 +628,32 @@ Text[fn:1][fn:4]
|
||||
"Test[fn:1]\nNext\n\n[fn:1] def\n\n[fn:2] A\n"
|
||||
(org-test-with-temp-text "Test[fn::def]\nNext\n[fn:unref] A"
|
||||
(let ((org-footnote-section nil)) (org-footnote-normalize))
|
||||
(buffer-string)))))
|
||||
(buffer-string))))
|
||||
;; Preserve file local variables when normalizing.
|
||||
(should
|
||||
(equal "
|
||||
Paragraph[fn:1][fn:2]
|
||||
|
||||
\[fn:1] Def 1
|
||||
|
||||
\[fn:2] Def 2
|
||||
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(org-test-with-temp-text
|
||||
"
|
||||
Paragraph[fn:foo][fn:bar]
|
||||
|
||||
\[fn:bar] Def 2
|
||||
|
||||
\[fn:foo] Def 1
|
||||
|
||||
# Local Variables:
|
||||
# foo: t
|
||||
# End:"
|
||||
(let ((org-footnote-section nil)) (org-footnote-normalize))
|
||||
(buffer-string)))))
|
||||
|
||||
|
||||
(provide 'test-org-footnote)
|
||||
|
@ -2918,6 +2918,13 @@ SCHEDULED: <2017-05-06 Sat>
|
||||
:END:
|
||||
"
|
||||
(org-sort-entries nil ?k)
|
||||
(buffer-string))))
|
||||
;; Preserve file local variables when sorting.
|
||||
(should
|
||||
(equal "\n* A\n* B\n# Local Variables:\n# foo: t\n# End:\n"
|
||||
(org-test-with-temp-text
|
||||
"\n* B\n* A\n# Local Variables:\n# foo: t\n# End:"
|
||||
(org-sort-entries nil ?a)
|
||||
(buffer-string)))))
|
||||
|
||||
(ert-deftest test-org/file-contents ()
|
||||
|
Loading…
Reference in New Issue
Block a user