summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2017-12-18 15:50:51 +0100
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2017-12-18 16:01:41 +0100
commitfdb2eb670165895327e648e3ea7cc1aa196577aa (patch)
tree855cf5dfde31718d6c0c3ab56822c49636118125
parentdce82f7e5c7d993d4a2250cf6c4dbc1b55507bbe (diff)
downloadorg-mode-fdb2eb670165895327e648e3ea7cc1aa196577aa.tar.gz
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.
-rw-r--r--lisp/org-footnote.el383
-rw-r--r--lisp/org-macs.el18
-rw-r--r--lisp/org.el382
-rw-r--r--testing/lisp/test-org-footnote.el92
-rw-r--r--testing/lisp/test-org.el7
5 files changed, 491 insertions, 391 deletions
diff --git a/lisp/org-footnote.el b/lisp/org-footnote.el
index 5df0efc..e555364 100644
--- a/lisp/org-footnote.el
+++ b/lisp/org-footnote.el
@@ -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."
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 5fe322b..cd2c563 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -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
diff --git a/lisp/org.el b/lisp/org.el
index 2cff98a..e66e6d5 100644
--- a/lisp/org.el
+++ b/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)
diff --git a/testing/lisp/test-org-footnote.el b/testing/lisp/test-org-footnote.el
index b2347cb..78ff459 100644
--- a/testing/lisp/test-org-footnote.el
+++ b/testing/lisp/test-org-footnote.el
@@ -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)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index d5d1b4a..aa0c5fc 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -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 ()