Fix subtle differences between overlays and invisible text properties

* lisp/org-clock.el (org-clock-in):
(org-clock-find-position):
(org-clock-out):
* lisp/org.el (org-add-planning-info):
(org-scan-tags):
(org-global-tags-completion-table):
(org-make-tags-matcher):
(org-tags-expand):
(org--property-local-values):
(org-read-date-analyze):
(org-revert-all-org-buffers):
(org-beginning-of-line): Make sure that we inherit invisible state
when inserting text.
(org-sort-entries): Preserve invisible state after replace-match.

(org-log-beginning): Do not try to move by visible lines.

* lisp/org-macs.el (org-preserve-local-variables): Do not try to
preserve overlays.
* lisp/ox.el (org-export--generate-copy-script): Preserve folding
properties in export buffer.
* testing/lisp/test-ob.el (test-ob/preserve-results-indentation): Fix
test failure.
* testing/lisp/test-org.el (test-org/meta-return):
(test-org/custom-properties): Use new folding.
This commit is contained in:
Ihor Radchenko 2022-01-16 15:18:53 +08:00
parent cd83606cfd
commit f63ff07441
No known key found for this signature in database
GPG Key ID: 6470762A7DA11D8B
6 changed files with 367 additions and 340 deletions

View File

@ -1373,14 +1373,14 @@ the default behavior."
(sit-for 2) (sit-for 2)
(throw 'abort nil)) (throw 'abort nil))
(t (t
(insert-before-markers "\n") (insert-before-markers-and-inherit "\n")
(backward-char 1) (backward-char 1)
(when (and (save-excursion (when (and (save-excursion
(end-of-line 0) (end-of-line 0)
(org-in-item-p))) (org-in-item-p)))
(beginning-of-line 1) (beginning-of-line 1)
(indent-line-to (max 0 (- (current-indentation) 2)))) (indent-line-to (max 0 (- (current-indentation) 2))))
(insert org-clock-string " ") (insert-and-inherit org-clock-string " ")
(setq org-clock-effort (org-entry-get (point) org-effort-property)) (setq org-clock-effort (org-entry-get (point) org-effort-property))
(setq org-clock-total-time (org-clock-sum-current-item (setq org-clock-total-time (org-clock-sum-current-item
(org-clock-get-sum-start))) (org-clock-get-sum-start)))
@ -1581,19 +1581,23 @@ line and position cursor in that line."
count (1+ count)))))) count (1+ count))))))
(cond (cond
((null positions) ((null positions)
;; Skip planning line and property drawer, if any. (org-fold-core-ignore-modifications
(org-end-of-meta-data) ;; Skip planning line and property drawer, if any.
(unless (bolp) (insert "\n")) (org-end-of-meta-data)
;; Create a new drawer if necessary. (unless (bolp) (insert-and-inherit "\n"))
(when (and org-clock-into-drawer ;; Create a new drawer if necessary.
(or (not (wholenump org-clock-into-drawer)) (when (and org-clock-into-drawer
(< org-clock-into-drawer 2))) (or (not (wholenump org-clock-into-drawer))
(let ((beg (point))) (< org-clock-into-drawer 2)))
(insert ":" drawer ":\n:END:\n") (let ((beg (point)))
(org-indent-region beg (point)) (insert-and-inherit ":" drawer ":\n:END:\n")
(org-flag-region (org-indent-region beg (point))
(line-end-position -1) (1- (point)) t 'outline) (if (eq org-fold-core-style 'text-properties)
(forward-line -1)))) (org-fold-region
(line-end-position -1) (1- (point)) t 'drawer)
(org-fold-region
(line-end-position -1) (1- (point)) t 'outline))
(forward-line -1)))))
;; When a clock drawer needs to be created because of the ;; When a clock drawer needs to be created because of the
;; number of clock items or simply if it is missing, collect ;; number of clock items or simply if it is missing, collect
;; all clocks in the section and wrap them within the drawer. ;; all clocks in the section and wrap them within the drawer.
@ -1602,28 +1606,29 @@ line and position cursor in that line."
drawer) drawer)
;; Skip planning line and property drawer, if any. ;; Skip planning line and property drawer, if any.
(org-end-of-meta-data) (org-end-of-meta-data)
(let ((beg (point))) (org-fold-core-ignore-modifications
(insert (let ((beg (point)))
(mapconcat (insert-and-inherit
(lambda (p) (mapconcat
(save-excursion (lambda (p)
(goto-char p) (save-excursion
(org-trim (delete-and-extract-region (goto-char p)
(save-excursion (skip-chars-backward " \r\t\n") (org-trim (delete-and-extract-region
(line-beginning-position 2)) (save-excursion (skip-chars-backward " \r\t\n")
(line-beginning-position 2))))) (line-beginning-position 2))
positions "\n") (line-beginning-position 2)))))
"\n:END:\n") positions "\n")
(let ((end (point-marker))) "\n:END:\n")
(goto-char beg) (let ((end (point-marker)))
(save-excursion (insert ":" drawer ":\n")) (goto-char beg)
(org-flag-region (line-end-position) (1- end) t 'outline) (save-excursion (insert-and-inherit ":" drawer ":\n"))
(org-indent-region (point) end) (org-fold-region (line-end-position) (1- end) t 'outline)
(forward-line) (org-indent-region (point) end)
(unless org-log-states-order-reversed (forward-line)
(goto-char end) (unless org-log-states-order-reversed
(beginning-of-line -1)) (goto-char end)
(set-marker end nil)))) (beginning-of-line -1))
(set-marker end nil)))))
(org-log-states-order-reversed (goto-char (car (last positions)))) (org-log-states-order-reversed (goto-char (car (last positions))))
(t (goto-char (car positions)))))))) (t (goto-char (car positions))))))))
@ -1672,24 +1677,25 @@ to, overriding the existing value of `org-clock-out-switch-to-state'."
(if fail-quietly (throw 'exit nil) (error "Clock start time is gone"))) (if fail-quietly (throw 'exit nil) (error "Clock start time is gone")))
(goto-char (match-end 0)) (goto-char (match-end 0))
(delete-region (point) (point-at-eol)) (delete-region (point) (point-at-eol))
(insert "--") (org-fold-core-ignore-modifications
(setq te (org-insert-time-stamp (or at-time now) 'with-hm 'inactive)) (insert-and-inherit "--")
(setq s (org-time-convert-to-integer (setq te (org-insert-time-stamp (or at-time now) 'with-hm 'inactive))
(time-subtract (setq s (org-time-convert-to-integer
(org-time-string-to-time te) (time-subtract
(org-time-string-to-time ts))) (org-time-string-to-time te)
h (floor s 3600) (org-time-string-to-time ts)))
m (floor (mod s 3600) 60)) h (floor s 3600)
(insert " => " (format "%2d:%02d" h m)) m (floor (mod s 3600) 60))
(move-marker org-clock-marker nil) (insert-and-inherit " => " (format "%2d:%02d" h m))
(move-marker org-clock-hd-marker nil) (move-marker org-clock-marker nil)
;; Possibly remove zero time clocks. (move-marker org-clock-hd-marker nil)
(when (and org-clock-out-remove-zero-time-clocks ;; Possibly remove zero time clocks.
(= 0 h m)) (when (and org-clock-out-remove-zero-time-clocks
(setq remove t) (= 0 h m))
(delete-region (line-beginning-position) (setq remove t)
(line-beginning-position 2))) (delete-region (line-beginning-position)
(org-clock-remove-empty-clock-drawer) (line-beginning-position 2)))
(org-clock-remove-empty-clock-drawer))
(when org-clock-mode-line-timer (when org-clock-mode-line-timer
(cancel-timer org-clock-mode-line-timer) (cancel-timer org-clock-mode-line-timer)
(setq org-clock-mode-line-timer nil)) (setq org-clock-mode-line-timer nil))

View File

@ -170,16 +170,8 @@
(when local-variables (when local-variables
(org-with-wide-buffer (org-with-wide-buffer
(goto-char (point-max)) (goto-char (point-max))
;; If last section is folded, make sure to also hide file (unless (bolp) (insert "\n"))
;; local variables after inserting them back. (insert local-variables))))))
(let ((overlay
(cl-find-if (lambda (o)
(eq 'outline (overlay-get o 'invisible)))
(overlays-at (1- (point))))))
(unless (bolp) (insert "\n"))
(insert local-variables)
(when overlay
(move-overlay overlay (overlay-start overlay) (point-max)))))))))
(defmacro org-no-popups (&rest body) (defmacro org-no-popups (&rest body)
"Suppress popup windows and evaluate BODY." "Suppress popup windows and evaluate BODY."

View File

@ -6411,7 +6411,7 @@ odd number. Returns values greater than 0."
(replace-match "# " nil t)) (replace-match "# " nil t))
((= level 1) ((= level 1)
(user-error "Cannot promote to level 0. UNDO to recover if necessary")) (user-error "Cannot promote to level 0. UNDO to recover if necessary"))
(t (replace-match up-head nil t))) (t (replace-match (apply #'propertize up-head (text-properties-at (match-beginning 0))) t)))
(unless (= level 1) (unless (= level 1)
(when org-auto-align-tags (org-align-tags)) (when org-auto-align-tags (org-align-tags))
(when org-adapt-indentation (org-fixup-indentation (- diff)))) (when org-adapt-indentation (org-fixup-indentation (- diff))))
@ -6426,9 +6426,10 @@ odd number. Returns values greater than 0."
(level (save-match-data (funcall outline-level))) (level (save-match-data (funcall outline-level)))
(down-head (concat (make-string (org-get-valid-level level 1) ?*) " ")) (down-head (concat (make-string (org-get-valid-level level 1) ?*) " "))
(diff (abs (- level (length down-head) -1)))) (diff (abs (- level (length down-head) -1))))
(replace-match down-head nil t) (org-fold-core-ignore-fragility-checks
(when org-auto-align-tags (org-align-tags)) (replace-match (apply #'propertize down-head (text-properties-at (match-beginning 0))) t)
(when org-adapt-indentation (org-fixup-indentation diff)) (when org-auto-align-tags (org-align-tags))
(when org-adapt-indentation (org-fixup-indentation diff)))
(run-hooks 'org-after-demote-entry-hook)))) (run-hooks 'org-after-demote-entry-hook))))
(defun org-cycle-level () (defun org-cycle-level ()
@ -8956,7 +8957,15 @@ When called through ELisp, arg is also interpreted in the following way:
this org-state block-reason) this org-state block-reason)
(throw 'exit nil))))) (throw 'exit nil)))))
(store-match-data match-data) (store-match-data match-data)
(replace-match next t t) (org-fold-core-ignore-modifications
(save-excursion
(goto-char (match-beginning 0))
(setf (buffer-substring (match-beginning 0) (match-end 0)) "")
(insert-and-inherit next)
(unless (org-invisible-p (line-beginning-position))
(org-fold-region (line-beginning-position)
(line-end-position)
nil))))
(cond ((and org-state (equal this org-state)) (cond ((and org-state (equal this org-state))
(message "TODO state was already %s" (org-trim next))) (message "TODO state was already %s" (org-trim next)))
((not (pos-visible-in-window-p hl-pos)) ((not (pos-visible-in-window-p hl-pos))
@ -9697,81 +9706,82 @@ of `org-todo-keywords-1'."
"Insert DEADLINE or SCHEDULE information in current entry. "Insert DEADLINE or SCHEDULE information in current entry.
TYPE is either `deadline' or `scheduled'. See `org-deadline' or TYPE is either `deadline' or `scheduled'. See `org-deadline' or
`org-schedule' for information about ARG and TIME arguments." `org-schedule' for information about ARG and TIME arguments."
(let* ((deadline? (eq type 'deadline)) (org-fold-core-ignore-modifications
(keyword (if deadline? org-deadline-string org-scheduled-string)) (let* ((deadline? (eq type 'deadline))
(log (if deadline? org-log-redeadline org-log-reschedule)) (keyword (if deadline? org-deadline-string org-scheduled-string))
(old-date (org-entry-get nil (if deadline? "DEADLINE" "SCHEDULED"))) (log (if deadline? org-log-redeadline org-log-reschedule))
(old-date-time (and old-date (org-time-string-to-time old-date))) (old-date (org-entry-get nil (if deadline? "DEADLINE" "SCHEDULED")))
;; Save repeater cookie from either TIME or current scheduled (old-date-time (and old-date (org-time-string-to-time old-date)))
;; time stamp. We are going to insert it back at the end of ;; Save repeater cookie from either TIME or current scheduled
;; the process. ;; time stamp. We are going to insert it back at the end of
(repeater (or (and (org-string-nw-p time) ;; the process.
;; We use `org-repeat-re' because we need (repeater (or (and (org-string-nw-p time)
;; to tell the difference between a real ;; We use `org-repeat-re' because we need
;; repeater and a time delta, e.g. "+2d". ;; to tell the difference between a real
(string-match org-repeat-re time) ;; repeater and a time delta, e.g. "+2d".
(match-string 1 time)) (string-match org-repeat-re time)
(and (org-string-nw-p old-date) (match-string 1 time))
(string-match "\\([.+-]+[0-9]+[hdwmy]\ (and (org-string-nw-p old-date)
(string-match "\\([.+-]+[0-9]+[hdwmy]\
\\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\)" \\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\)"
old-date) old-date)
(match-string 1 old-date))))) (match-string 1 old-date)))))
(pcase arg (pcase arg
(`(4) (`(4)
(if (not old-date) (if (not old-date)
(message (if deadline? "Entry had no deadline to remove" (message (if deadline? "Entry had no deadline to remove"
"Entry was not scheduled")) "Entry was not scheduled"))
(when (and old-date log) (when (and old-date log)
(org-add-log-setup (if deadline? 'deldeadline 'delschedule) (org-add-log-setup (if deadline? 'deldeadline 'delschedule)
nil old-date log)) nil old-date log))
(org-remove-timestamp-with-keyword keyword) (org-remove-timestamp-with-keyword keyword)
(message (if deadline? "Entry no longer has a deadline." (message (if deadline? "Entry no longer has a deadline."
"Entry is no longer scheduled.")))) "Entry is no longer scheduled."))))
(`(16) (`(16)
(save-excursion (save-excursion
(org-back-to-heading t) (org-back-to-heading t)
(let ((regexp (if deadline? org-deadline-time-regexp (let ((regexp (if deadline? org-deadline-time-regexp
org-scheduled-time-regexp))) org-scheduled-time-regexp)))
(if (not (re-search-forward regexp (line-end-position 2) t)) (if (not (re-search-forward regexp (line-end-position 2) t))
(user-error (if deadline? "No deadline information to update" (user-error (if deadline? "No deadline information to update"
"No scheduled information to update")) "No scheduled information to update"))
(let* ((rpl0 (match-string 1)) (let* ((rpl0 (match-string 1))
(rpl (replace-regexp-in-string " -[0-9]+[hdwmy]" "" rpl0)) (rpl (replace-regexp-in-string " -[0-9]+[hdwmy]" "" rpl0))
(msg (if deadline? "Warn starting from" "Delay until"))) (msg (if deadline? "Warn starting from" "Delay until")))
(replace-match (replace-match
(concat keyword (concat keyword
" <" rpl " <" rpl
(format " -%dd" (format " -%dd"
(abs (- (time-to-days (abs (- (time-to-days
(save-match-data (save-match-data
(org-read-date (org-read-date
nil t nil msg old-date-time))) nil t nil msg old-date-time)))
(time-to-days old-date-time)))) (time-to-days old-date-time))))
">") t t)))))) ">") t t))))))
(_ (_
(org-add-planning-info type time 'closed) (org-add-planning-info type time 'closed)
(when (and old-date (when (and old-date
log log
(not (equal old-date org-last-inserted-timestamp))) (not (equal old-date org-last-inserted-timestamp)))
(org-add-log-setup (if deadline? 'redeadline 'reschedule) (org-add-log-setup (if deadline? 'redeadline 'reschedule)
org-last-inserted-timestamp org-last-inserted-timestamp
old-date old-date
log)) log))
(when repeater (when repeater
(save-excursion (save-excursion
(org-back-to-heading t) (org-back-to-heading t)
(when (re-search-forward (when (re-search-forward
(concat keyword " " org-last-inserted-timestamp) (concat keyword " " org-last-inserted-timestamp)
(line-end-position 2) (line-end-position 2)
t) t)
(goto-char (1- (match-end 0))) (goto-char (1- (match-end 0)))
(insert " " repeater) (insert-and-inherit " " repeater)
(setq org-last-inserted-timestamp (setq org-last-inserted-timestamp
(concat (substring org-last-inserted-timestamp 0 -1) (concat (substring org-last-inserted-timestamp 0 -1)
" " repeater " " repeater
(substring org-last-inserted-timestamp -1)))))) (substring org-last-inserted-timestamp -1))))))
(message (if deadline? "Deadline on %s" "Scheduled to %s") (message (if deadline? "Deadline on %s" "Scheduled to %s")
org-last-inserted-timestamp))))) org-last-inserted-timestamp))))))
(defun org-deadline (arg &optional time) (defun org-deadline (arg &optional time)
"Insert a \"DEADLINE:\" string with a timestamp to make a deadline. "Insert a \"DEADLINE:\" string with a timestamp to make a deadline.
@ -9876,101 +9886,102 @@ among `closed', `deadline', `scheduled' and nil. TIME indicates
the time to use. If none is given, the user is prompted for the time to use. If none is given, the user is prompted for
a date. REMOVE indicates what kind of entries to remove. An old a date. REMOVE indicates what kind of entries to remove. An old
WHAT entry will also be removed." WHAT entry will also be removed."
(let (org-time-was-given org-end-time-was-given default-time default-input) (org-fold-core-ignore-modifications
(when (and (memq what '(scheduled deadline)) (let (org-time-was-given org-end-time-was-given default-time default-input)
(or (not time) (when (and (memq what '(scheduled deadline))
(and (stringp time) (or (not time)
(string-match "^[-+]+[0-9]" time)))) (and (stringp time)
;; Try to get a default date/time from existing timestamp (string-match "^[-+]+[0-9]" time))))
(save-excursion ;; Try to get a default date/time from existing timestamp
(org-back-to-heading t) (save-excursion
(let ((end (save-excursion (outline-next-heading) (point))) ts) (org-back-to-heading t)
(when (re-search-forward (if (eq what 'scheduled) (let ((end (save-excursion (outline-next-heading) (point))) ts)
org-scheduled-time-regexp (when (re-search-forward (if (eq what 'scheduled)
org-deadline-time-regexp) org-scheduled-time-regexp
end t) org-deadline-time-regexp)
(setq ts (match-string 1) end t)
default-time (org-time-string-to-time ts) (setq ts (match-string 1)
default-input (and ts (org-get-compact-tod ts))))))) default-time (org-time-string-to-time ts)
(when what default-input (and ts (org-get-compact-tod ts)))))))
(setq time (when what
(if (stringp time) (setq time
;; This is a string (relative or absolute), set (if (stringp time)
;; proper date. ;; This is a string (relative or absolute), set
(apply #'encode-time ;; proper date.
(org-read-date-analyze (apply #'encode-time
time default-time (decode-time default-time))) (org-read-date-analyze
;; If necessary, get the time from the user time default-time (decode-time default-time)))
(or time (org-read-date nil 'to-time nil ;; If necessary, get the time from the user
(cl-case what (or time (org-read-date nil 'to-time nil
(deadline "DEADLINE") (cl-case what
(scheduled "SCHEDULED") (deadline "DEADLINE")
(otherwise nil)) (scheduled "SCHEDULED")
default-time default-input))))) (otherwise nil))
(org-with-wide-buffer default-time default-input)))))
(org-back-to-heading t) (org-with-wide-buffer
(let ((planning? (save-excursion (org-back-to-heading t)
(forward-line) (let ((planning? (save-excursion
(looking-at-p org-planning-line-re)))) (forward-line)
(cond (looking-at-p org-planning-line-re))))
(planning? (cond
(forward-line) (planning?
;; Move to current indentation. (forward-line)
(skip-chars-forward " \t") ;; Move to current indentation.
;; Check if we have to remove something. (skip-chars-forward " \t")
(dolist (type (if what (cons what remove) remove)) ;; Check if we have to remove something.
(save-excursion (dolist (type (if what (cons what remove) remove))
(when (re-search-forward (save-excursion
(cl-case type (when (re-search-forward
(closed org-closed-time-regexp) (cl-case type
(deadline org-deadline-time-regexp) (closed org-closed-time-regexp)
(scheduled org-scheduled-time-regexp) (deadline org-deadline-time-regexp)
(otherwise (error "Invalid planning type: %s" type))) (scheduled org-scheduled-time-regexp)
(line-end-position) (otherwise (error "Invalid planning type: %s" type)))
t) (line-end-position)
;; Delete until next keyword or end of line. t)
(delete-region ;; Delete until next keyword or end of line.
(match-beginning 0) (delete-region
(if (re-search-forward org-keyword-time-not-clock-regexp
(line-end-position)
t)
(match-beginning 0) (match-beginning 0)
(line-end-position)))))) (if (re-search-forward org-keyword-time-not-clock-regexp
;; If there is nothing more to add and no more keyword is (line-end-position)
;; left, remove the line completely. t)
(if (and (looking-at-p "[ \t]*$") (not what)) (match-beginning 0)
(delete-region (line-end-position 0) (line-end-position))))))
(line-end-position)) ;; If there is nothing more to add and no more keyword is
;; If we removed last keyword, do not leave trailing white ;; left, remove the line completely.
;; space at the end of line. (if (and (looking-at-p "[ \t]*$") (not what))
(let ((p (point))) (delete-region (line-end-position 0)
(save-excursion (line-end-position))
(end-of-line) ;; If we removed last keyword, do not leave trailing white
(unless (= (skip-chars-backward " \t" p) 0) ;; space at the end of line.
(delete-region (point) (line-end-position))))))) (let ((p (point)))
(what (save-excursion
(end-of-line) (end-of-line)
(insert "\n") (unless (= (skip-chars-backward " \t" p) 0)
(when org-adapt-indentation (delete-region (point) (line-end-position)))))))
(indent-to-column (1+ (org-outline-level))))) (what
(t nil))) (end-of-line)
(when what (insert-and-inherit "\n")
;; Insert planning keyword. (when org-adapt-indentation
(insert (cl-case what (indent-to-column (1+ (org-outline-level)))))
(closed org-closed-string) (t nil)))
(deadline org-deadline-string) (when what
(scheduled org-scheduled-string) ;; Insert planning keyword.
(otherwise (error "Invalid planning type: %s" what))) (insert-and-inherit (cl-case what
" ") (closed org-closed-string)
;; Insert associated timestamp. (deadline org-deadline-string)
(let ((ts (org-insert-time-stamp (scheduled org-scheduled-string)
time (otherwise (error "Invalid planning type: %s" what)))
(or org-time-was-given " ")
(and (eq what 'closed) org-log-done-with-time)) ;; Insert associated timestamp.
(eq what 'closed) (let ((ts (org-insert-time-stamp
nil nil (list org-end-time-was-given)))) time
(unless (eolp) (insert " ")) (or org-time-was-given
ts))))) (and (eq what 'closed) org-log-done-with-time))
(eq what 'closed)
nil nil (list org-end-time-was-given))))
(unless (eolp) (insert " "))
ts))))))
(defvar org-log-note-marker (make-marker) (defvar org-log-note-marker (make-marker)
"Marker pointing at the entry where the note is to be inserted.") "Marker pointing at the entry where the note is to be inserted.")
@ -10020,13 +10031,19 @@ narrowing."
(throw 'exit nil)))) (throw 'exit nil))))
;; No drawer found. Create one, if permitted. ;; No drawer found. Create one, if permitted.
(when create (when create
(unless (bolp) (insert "\n")) ;; Avoid situation when we insert drawer right before
(let ((beg (point))) ;; first "*". Otherwise, if the previous heading is
(insert ":" drawer ":\n:END:\n") ;; folded, we are inserting after visible newline at
(org-indent-region beg (point)) ;; the end of the fold, thus breaking the fold
(org-flag-region (line-end-position -1) ;; continuity.
(1- (point)) t 'outline)) (when (org-at-heading-p) (backward-char))
(end-of-line -1))))) (org-fold-core-ignore-modifications
(unless (bolp) (insert-and-inherit "\n"))
(let ((beg (point)))
(insert-and-inherit ":" drawer ":\n:END:\n")
(org-indent-region beg (point))
(org-fold-region (line-end-position -1) (1- (point)) t (if (eq org-fold-core-style 'text-properties) 'drawer 'outline)))))
(end-of-line -1))))
(t (t
(org-end-of-meta-data org-log-state-notes-insert-after-drawers) (org-end-of-meta-data org-log-state-notes-insert-after-drawers)
(skip-chars-forward " \t\n") (skip-chars-forward " \t\n")
@ -10034,7 +10051,7 @@ narrowing."
(unless org-log-states-order-reversed (unless org-log-states-order-reversed
(org-skip-over-state-notes) (org-skip-over-state-notes)
(skip-chars-backward " \t\n") (skip-chars-backward " \t\n")
(forward-line))))) (beginning-of-line 2)))))
(if (bolp) (point) (line-beginning-position 2)))) (if (bolp) (point) (line-beginning-position 2))))
(defun org-add-log-setup (&optional purpose state prev-state how extra) (defun org-add-log-setup (&optional purpose state prev-state how extra)
@ -10160,34 +10177,35 @@ EXTRA is additional text that will be inserted into the notes buffer."
(push note lines)) (push note lines))
(when (and lines (not org-note-abort)) (when (and lines (not org-note-abort))
(with-current-buffer (marker-buffer org-log-note-marker) (with-current-buffer (marker-buffer org-log-note-marker)
(org-with-wide-buffer (org-fold-core-ignore-modifications
;; Find location for the new note. (org-with-wide-buffer
(goto-char org-log-note-marker) ;; Find location for the new note.
(set-marker org-log-note-marker nil) (goto-char org-log-note-marker)
;; Note associated to a clock is to be located right after (set-marker org-log-note-marker nil)
;; the clock. Do not move point. ;; Note associated to a clock is to be located right after
(unless (eq org-log-note-purpose 'clock-out) ;; the clock. Do not move point.
(goto-char (org-log-beginning t))) (unless (eq org-log-note-purpose 'clock-out)
;; Make sure point is at the beginning of an empty line. (goto-char (org-log-beginning t)))
(cond ((not (bolp)) (let ((inhibit-read-only t)) (insert "\n"))) ;; Make sure point is at the beginning of an empty line.
((looking-at "[ \t]*\\S-") (save-excursion (insert "\n")))) (cond ((not (bolp)) (let ((inhibit-read-only t)) (insert-and-inherit "\n")))
;; In an existing list, add a new item at the top level. ((looking-at "[ \t]*\\S-") (save-excursion (insert-and-inherit "\n"))))
;; Otherwise, indent line like a regular one. ;; In an existing list, add a new item at the top level.
(let ((itemp (org-in-item-p))) ;; Otherwise, indent line like a regular one.
(if itemp (let ((itemp (org-in-item-p)))
(indent-line-to (if itemp
(let ((struct (save-excursion (indent-line-to
(goto-char itemp) (org-list-struct)))) (let ((struct (save-excursion
(org-list-get-ind (org-list-get-top-point struct) struct))) (goto-char itemp) (org-list-struct))))
(org-indent-line))) (org-list-get-ind (org-list-get-top-point struct) struct)))
(insert (org-list-bullet-string "-") (pop lines)) (org-indent-line)))
(let ((ind (org-list-item-body-column (line-beginning-position)))) (insert-and-inherit (org-list-bullet-string "-") (pop lines))
(dolist (line lines) (let ((ind (org-list-item-body-column (line-beginning-position))))
(insert "\n") (dolist (line lines)
(indent-line-to ind) (insert-and-inherit "\n")
(insert line))) (indent-line-to ind)
(message "Note stored") (insert-and-inherit line)))
(org-back-to-heading t))))) (message "Note stored")
(org-back-to-heading t))))))
;; Don't add undo information when called from `org-agenda-todo'. ;; Don't add undo information when called from `org-agenda-todo'.
(set-window-configuration org-log-note-window-configuration) (set-window-configuration org-log-note-window-configuration)
(with-current-buffer (marker-buffer org-log-note-return-to) (with-current-buffer (marker-buffer org-log-note-return-to)
@ -11318,34 +11336,35 @@ If TAGS is nil or the empty string, all tags are removed.
This function assumes point is on a headline." This function assumes point is on a headline."
(org-with-wide-buffer (org-with-wide-buffer
(let ((tags (pcase tags (org-fold-core-ignore-modifications
((pred listp) tags) (let ((tags (pcase tags
((pred stringp) (split-string (org-trim tags) ":" t)) ((pred listp) tags)
(_ (error "Invalid tag specification: %S" tags)))) ((pred stringp) (split-string (org-trim tags) ":" t))
(old-tags (org-get-tags nil t)) (_ (error "Invalid tag specification: %S" tags))))
(tags-change? nil)) (old-tags (org-get-tags nil t))
(when (functionp org-tags-sort-function) (tags-change? nil))
(setq tags (sort tags org-tags-sort-function))) (when (functionp org-tags-sort-function)
(setq tags-change? (not (equal tags old-tags))) (setq tags (sort tags org-tags-sort-function)))
(when tags-change? (setq tags-change? (not (equal tags old-tags)))
;; Delete previous tags and any trailing white space. (when tags-change?
(goto-char (if (org-match-line org-tag-line-re) (match-beginning 1) ;; Delete previous tags and any trailing white space.
(line-end-position))) (goto-char (if (org-match-line org-tag-line-re) (match-beginning 1)
(skip-chars-backward " \t") (line-end-position)))
(delete-region (point) (line-end-position)) (skip-chars-backward " \t")
;; Deleting white spaces may break an otherwise empty headline. (delete-region (point) (line-end-position))
;; Re-introduce one space in this case. ;; Deleting white spaces may break an otherwise empty headline.
(unless (org-at-heading-p) (insert " ")) ;; Re-introduce one space in this case.
(when tags (unless (org-at-heading-p) (insert " "))
(save-excursion (insert " " (org-make-tag-string tags))) (when tags
;; When text is being inserted on an invisible region (save-excursion (insert-and-inherit " " (org-make-tag-string tags)))
;; boundary, it can be inadvertently sucked into ;; When text is being inserted on an invisible region
;; invisibility. ;; boundary, it can be inadvertently sucked into
(unless (org-invisible-p (line-beginning-position)) ;; invisibility.
(org-flag-region (point) (line-end-position) nil 'outline)))) (unless (org-invisible-p (line-beginning-position))
;; Align tags, if any. (org-fold-region (point) (line-end-position) nil 'outline))))
(when tags (org-align-tags)) ;; Align tags, if any.
(when tags-change? (run-hooks 'org-after-tags-change-hook))))) (when tags (org-align-tags))
(when tags-change? (run-hooks 'org-after-tags-change-hook))))))
(defun org-change-tag-in-region (beg end tag off) (defun org-change-tag-in-region (beg end tag off)
"Add or remove TAG for each entry in the region. "Add or remove TAG for each entry in the region.
@ -12539,19 +12558,20 @@ decreases scheduled or deadline date by one day."
((member property org-special-properties) ((member property org-special-properties)
(error "The %s property cannot be set with `org-entry-put'" property)) (error "The %s property cannot be set with `org-entry-put'" property))
(t (t
(let* ((range (org-get-property-block beg 'force)) (org-fold-core-ignore-modifications
(end (cdr range)) (let* ((range (org-get-property-block beg 'force))
(case-fold-search t)) (end (cdr range))
(goto-char (car range)) (case-fold-search t))
(if (re-search-forward (org-re-property property nil t) end t) (goto-char (car range))
(progn (delete-region (match-beginning 0) (match-end 0)) (if (re-search-forward (org-re-property property nil t) end t)
(goto-char (match-beginning 0))) (progn (delete-region (match-beginning 0) (match-end 0))
(goto-char end) (goto-char (match-beginning 0)))
(insert "\n") (goto-char end)
(backward-char)) (insert-and-inherit "\n")
(insert ":" property ":") (backward-char))
(when value (insert " " value)) (insert-and-inherit ":" property ":")
(org-indent-line))))) (when value (insert-and-inherit " " value))
(org-indent-line))))))
(run-hook-with-args 'org-property-changed-functions property value)))) (run-hook-with-args 'org-property-changed-functions property value))))
(defun org-buffer-property-keys (&optional specials defaults columns) (defun org-buffer-property-keys (&optional specials defaults columns)
@ -13705,23 +13725,24 @@ stamp will not contribute to the agenda.
PRE and POST are optional strings to be inserted before and after the PRE and POST are optional strings to be inserted before and after the
stamp. stamp.
The command returns the inserted time stamp." The command returns the inserted time stamp."
(let ((fmt (funcall (if with-hm 'cdr 'car) org-time-stamp-formats)) (org-fold-core-ignore-modifications
stamp) (let ((fmt (funcall (if with-hm 'cdr 'car) org-time-stamp-formats))
(when inactive (setq fmt (concat "[" (substring fmt 1 -1) "]"))) stamp)
(insert-before-markers (or pre "")) (when inactive (setq fmt (concat "[" (substring fmt 1 -1) "]")))
(when (listp extra) (insert-before-markers-and-inherit (or pre ""))
(setq extra (car extra)) (when (listp extra)
(if (and (stringp extra) (setq extra (car extra))
(string-match "\\([0-9]+\\):\\([0-9]+\\)" extra)) (if (and (stringp extra)
(setq extra (format "-%02d:%02d" (string-match "\\([0-9]+\\):\\([0-9]+\\)" extra))
(string-to-number (match-string 1 extra)) (setq extra (format "-%02d:%02d"
(string-to-number (match-string 2 extra)))) (string-to-number (match-string 1 extra))
(setq extra nil))) (string-to-number (match-string 2 extra))))
(when extra (setq extra nil)))
(setq fmt (concat (substring fmt 0 -1) extra (substring fmt -1)))) (when extra
(insert-before-markers (setq stamp (format-time-string fmt time))) (setq fmt (concat (substring fmt 0 -1) extra (substring fmt -1))))
(insert-before-markers (or post "")) (insert-before-markers-and-inherit (setq stamp (format-time-string fmt time)))
(setq org-last-inserted-timestamp stamp))) (insert-before-markers-and-inherit (or post ""))
(setq org-last-inserted-timestamp stamp))))
(defun org-toggle-time-stamp-overlays () (defun org-toggle-time-stamp-overlays ()
"Toggle the use of custom time stamp formats." "Toggle the use of custom time stamp formats."
@ -18346,7 +18367,10 @@ Alignment is done according to `org-property-format', which see."
(let ((newtext (concat (match-string 4) (let ((newtext (concat (match-string 4)
(org-trim (org-trim
(format org-property-format (match-string 1) (match-string 3)))))) (format org-property-format (match-string 1) (match-string 3))))))
(setf (buffer-substring (match-beginning 0) (match-end 0)) newtext))))) ;; Do not use `replace-match' here as we want to inherit folding
;; properties if inside fold.
(setf (buffer-substring (match-beginning 0) (match-end 0)) "")
(insert-and-inherit newtext)))))
(defun org-indent-line () (defun org-indent-line ()
"Indent line depending on context. "Indent line depending on context.

View File

@ -2588,7 +2588,9 @@ The function assumes BUFFER's major mode is `org-mode'."
(or (memq var (or (memq var
'(default-directory '(default-directory
buffer-file-name buffer-file-name
buffer-file-coding-system)) buffer-file-coding-system
;; Needed to preserve folding state
char-property-alias-alist))
(assq var bound-variables) (assq var bound-variables)
(string-match "^\\(org-\\|orgtbl-\\)" (string-match "^\\(org-\\|orgtbl-\\)"
(symbol-name var))) (symbol-name var)))

View File

@ -1557,8 +1557,8 @@ echo \"$data\"
(org-test-with-temp-text " #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" (org-test-with-temp-text " #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src"
(org-babel-execute-src-block) (org-babel-execute-src-block)
(let ((case-fold-search t)) (search-forward "RESULTS")) (let ((case-fold-search t)) (search-forward "RESULTS"))
(list (org-get-indentation) (list (current-indentation)
(progn (forward-line) (org-get-indentation)))))) (progn (forward-line) (current-indentation))))))
(should (should
(equal (equal
'(2 2) '(2 2)
@ -1566,8 +1566,8 @@ echo \"$data\"
" #+name: block\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" " #+name: block\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src"
(org-babel-execute-src-block) (org-babel-execute-src-block)
(let ((case-fold-search t)) (search-forward "RESULTS")) (let ((case-fold-search t)) (search-forward "RESULTS"))
(list (org-get-indentation) (list (current-indentation)
(progn (forward-line) (org-get-indentation)))))) (progn (forward-line) (current-indentation))))))
;; Don't get fooled by TAB-based indentation. ;; Don't get fooled by TAB-based indentation.
(should (should
(equal (equal
@ -1577,8 +1577,8 @@ echo \"$data\"
(setq tab-width 4) (setq tab-width 4)
(org-babel-execute-src-block) (org-babel-execute-src-block)
(let ((case-fold-search t)) (search-forward "RESULTS")) (let ((case-fold-search t)) (search-forward "RESULTS"))
(list (org-get-indentation) (list (current-indentation)
(progn (forward-line) (org-get-indentation)))))) (progn (forward-line) (current-indentation))))))
;; Properly indent examplified blocks. ;; Properly indent examplified blocks.
(should (should
(equal (equal

View File

@ -1522,6 +1522,7 @@
(should (should
(org-test-with-temp-text ":MYDRAWER:\n- a\n:END:" (org-test-with-temp-text ":MYDRAWER:\n- a\n:END:"
(forward-line) (forward-line)
(org-fold-reveal)
(org-meta-return) (org-meta-return)
(beginning-of-line) (beginning-of-line)
(looking-at "- $")))) (looking-at "- $"))))
@ -2943,6 +2944,7 @@ Foo Bar
(let ((org-custom-properties '("FOO" "BAR"))) (let ((org-custom-properties '("FOO" "BAR")))
(org-test-with-temp-text (org-test-with-temp-text
"* H\n:PROPERTIES:\n<point>:FOO: val\n:P: 1\n:BAR: baz\n:END:\n" "* H\n:PROPERTIES:\n<point>:FOO: val\n:P: 1\n:BAR: baz\n:END:\n"
(org-fold-reveal)
(org-toggle-custom-properties-visibility) (org-toggle-custom-properties-visibility)
(and (org-invisible-p2) (and (org-invisible-p2)
(not (progn (forward-line) (org-invisible-p2))) (not (progn (forward-line) (org-invisible-p2)))
@ -2963,6 +2965,7 @@ Foo Bar
(let ((org-custom-properties '("A"))) (let ((org-custom-properties '("A")))
(org-test-with-temp-text (org-test-with-temp-text
"* H\n:PROPERTIES:\n:A: 1\n:END:\n\n:PROPERTIES:\n<point>:A: 2\n:END:" "* H\n:PROPERTIES:\n:A: 1\n:END:\n\n:PROPERTIES:\n<point>:A: 2\n:END:"
(org-fold-reveal)
(org-toggle-custom-properties-visibility) (org-toggle-custom-properties-visibility)
(org-invisible-p2))))) (org-invisible-p2)))))