From 653005e1b383b4c7ad9086fc0b7e40d262dab744 Mon Sep 17 00:00:00 2001 Message-Id: <653005e1b383b4c7ad9086fc0b7e40d262dab744.1676215378.git.yantar92@posteo.net> From: Ihor Radchenko Date: Sun, 12 Feb 2023 18:13:36 +0300 Subject: [PATCH] org-fold: Allow customizing commands where invisible edits are checked * lisp/org-fold.el (org-fold-catch-invisible-edits-commands): New custom option. (org-fold-catch-invisible-edits): Mention the new custom option in the docstring. (org-fold-check-before-invisible-edit-maybe): New function checking if edits are safe for `this-command'. (org-fold--advice-edit-commands): New function advising the functions with `org-fold-check-before-invisible-edit-maybe'. * lisp/org.el (org-mode): Advice functions on Org startup. (org-self-insert-command): (org-delete-backward-char): (org-delete-char): (org-meta-return): Do not call `org-fold-check-before-invisible-edit' and rely on the new advise mechanism instead. * etc/ORG-NEWS (Commands affected by ~org-fold-catch-invisible-edits~ can now be customized): Announce the change. * doc/org-manual.org (Catching invisible edits): Mention new customization. --- doc/org-manual.org | 12 +++++++++--- etc/ORG-NEWS | 12 ++++++++++++ lisp/org-fold.el | 45 ++++++++++++++++++++++++++++++++++++++++++++- lisp/org.el | 6 ++---- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index 2d38cf76c..2b836a026 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -622,11 +622,17 @@ *** Catching invisible edits #+cindex: edits, catching invisible #+vindex: org-fold-catch-invisible-edits +#+vindex: org-fold-catch-invisible-edits-commands Sometimes you may inadvertently edit an invisible part of the buffer and be confused on what has been edited and how to undo the mistake. -Setting ~org-fold-catch-invisible-edits~ to non-~nil~ helps preventing -this. See the docstring of this option on how Org should catch -invisible edits and process them. +By default, Org prevents such edits for a limited set of user +commands. Users can control which commands are affected by +customizing ~org-fold-catch-invisible-edits-commands~. + +The strategy used to decide if a given edit is dangerous is controlled +by ~org-fold-catch-invisible-edits~. See the docstring of this option +on the available strategies. Set the option to ~nil~ to disable +catching invisible edits completely. ** Motion :PROPERTIES: diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 87ecd77cd..b85c46758 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -24,6 +24,18 @@ consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode] has been ported to. ** New and changed options +*** Commands affected by ~org-fold-catch-invisible-edits~ can now be customized + +New user option ~org-fold-catch-invisible-edits-commands~ controls +which commands trigger checking for invisible edits. + +The full list of affected commands is: +- ~org-self-insert-command~ +- ~org-delete-backward-char~ +- ~org-delete-char~ +- ~org-meta-return~ +- ~org-return~ (not checked in earlier Org versions) + *** New escape in ~org-beamer-environments-extra~ for labels in Beamer export The escape =%l= in ~org-beamer-environments-extra~ inserts the label obtained from ~org-beamer--get-label~. This is added to the default diff --git a/lisp/org-fold.el b/lisp/org-fold.el index 1b7ca22b0..3d86eb802 100644 --- a/lisp/org-fold.el +++ b/lisp/org-fold.el @@ -189,7 +189,10 @@ (defcustom org-fold-catch-invisible-edits 'smart Never delete a previously invisible character or add in the middle or right after an invisible region. Basically, this allows insertion and backward-delete right before ellipses. - FIXME: maybe in this case we should not even show?" + FIXME: maybe in this case we should not even show? + +This variable only affects commands listed in +`org-fold-catch-invisible-edits-commands'." :group 'org-edit-structure :version "24.1" :type '(choice @@ -199,6 +202,33 @@ (defcustom org-fold-catch-invisible-edits 'smart (const :tag "Show invisible part and do the edit" show) (const :tag "Be smart and do the right thing" smart))) +(defcustom org-fold-catch-invisible-edits-commands + ;; We do not add non-Org commands here by default to avoid advising + ;; globally. See `org-fold--advice-edit-commands'. + '((org-self-insert-command . insert) + (org-delete-backward-char . delete-backward) + (org-delete-char . delete) + (org-meta-return . insert) + (org-return . insert)) + "Alist of commands where Org checks for invisible edits. +Each element is (COMMAND . KIND), where COMMAND is symbol representing +command as stored in `this-command' and KIND is symbol `insert', +symbol `delete', or symbol `delete-backward'. + +The checks are performed around `point'. + +This variable must be set before loading Org in order to take effect. + +Also, see `org-fold-catch-invisible-edits'." + :group 'org-edit-structure + :package-version '("Org" . "9.7") + :type '(alist + :key-type symbol + :value-type (choice + (const insert) + (const delete) + (const delete-backward)))) + ;;; Core functionality ;;; API @@ -901,6 +931,19 @@ (defun org-fold-check-before-invisible-edit (kind) ;; Don't do the edit, make the user repeat it in full visibility (user-error "Edit in invisible region aborted, repeat to confirm with text visible")))))))) +(defun org-fold-check-before-invisible-edit-maybe (&rest _) + "Check before invisible command by `this-command'." + (when (derived-mode-p 'org-mode) + (pcase (alist-get this-command org-fold-catch-invisible-edits-commands) + ((pred null) nil) + (kind (org-fold-check-before-invisible-edit kind))))) + +(defun org-fold--advice-edit-commands () + "Advice editing commands according to `org-fold-catch-invisible-edits-commands'. +The advices are installed in current buffer." + (dolist (command (mapcar #'car org-fold-catch-invisible-edits-commands)) + (advice-add command :before #'org-fold-check-before-invisible-edit-maybe))) + (provide 'org-fold) ;;; org-fold.el ends here diff --git a/lisp/org.el b/lisp/org.el index 4d12084d9..8d88f83e7 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -4839,6 +4839,8 @@ (define-derived-mode org-mode outline-mode "Org" (add-hook 'before-change-functions 'org-before-change-function nil 'local) ;; Check for running clock before killing a buffer (add-hook 'kill-buffer-hook 'org-check-running-clock nil 'local) + ;; Check for invisible edits. + (org-fold--advice-edit-commands) ;; Initialize cache. (org-element-cache-reset) (when (and org-element-cache-persistent @@ -16440,7 +16442,6 @@ (defun org-self-insert-command (N) If the cursor is in a table looking at whitespace, the whitespace is overwritten, and the table is not marked as requiring realignment." (interactive "p") - (org-fold-check-before-invisible-edit 'insert) (cond ((and org-use-speed-commands (let ((kv (this-command-keys-vector))) @@ -16510,7 +16511,6 @@ (defun org-delete-backward-char (N) because, in this case the deletion might narrow the column." (interactive "p") (save-match-data - (org-fold-check-before-invisible-edit 'delete-backward) (if (and (= N 1) (not overwrite-mode) (not (org-region-active-p)) @@ -16530,7 +16530,6 @@ (defun org-delete-char (N) because, in this case the deletion might narrow the column." (interactive "p") (save-match-data - (org-fold-check-before-invisible-edit 'delete) (cond ((or (/= N 1) (eq (char-after) ?|) @@ -17944,7 +17943,6 @@ (defun org-meta-return (&optional arg) `org-table-wrap-region', depending on context. When called with an argument, unconditionally call `org-insert-heading'." (interactive "P") - (org-fold-check-before-invisible-edit 'insert) (or (run-hook-with-args-until-success 'org-metareturn-hook) (call-interactively (cond (arg #'org-insert-heading) ((org-at-table-p) #'org-table-wrap-region) -- 2.39.1