Hi, This small series keeps the original narrowing visible while reading a new heading title.
`org-edit-headline' widens the buffer in order to find and update the current heading. However, the interactive prompt does not need to run in the widened buffer. The first patch temporarily restores the original narrowing while reading the new heading text. The second patch handles the column view ITEM path separately. Column view calls `org-edit-headline' through `org-with-point-at', which widens the buffer before the command runs. Therefore, the ITEM value has to be read before moving to the heading marker. Best, -- Slawomir Grochowski
>From d4e5d2ee3990157cd623efe4d7c332c6d634a534 Mon Sep 17 00:00:00 2001 From: Slawomir Grochowski <[email protected]> Date: Tue, 23 Jun 2026 09:49:54 +0200 Subject: [PATCH 1/2] ; org: Preserve narrowing while editing heading * lisp/org.el (org-edit-headline): Temporarily restore narrowing while reading new heading text. * testing/lisp/test-org.el (test-org/edit-headline): Test it. --- lisp/org.el | 33 ++++++++++++++++++++------------- testing/lisp/test-org.el | 18 +++++++++++++++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 04eed3088..da8ae6588 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -6852,19 +6852,26 @@ This is a list with the following elements: "Edit the current headline. Set it to HEADING when provided." (interactive nil org-mode) - (org-with-wide-buffer - (org-back-to-heading t) - (let ((case-fold-search nil)) - (when (looking-at org-complex-heading-regexp) - (let* ((old (match-string-no-properties 4)) - (new (save-match-data - (org-trim (or heading (read-string "Edit: " old)))))) - (unless (equal old new) - (if old (replace-match new t t nil 4) - (goto-char (or (match-end 3) (match-end 2) (match-end 1))) - (insert " " new)) - (when org-auto-align-tags (org-align-tags)) - (when (looking-at "[ \t]*$") (replace-match "")))))))) + (let ((beg (point-min)) + (end (point-max))) + (org-with-wide-buffer + (org-back-to-heading t) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (let* ((old (match-string-no-properties 4)) + (new (save-match-data + (org-trim + (or heading + (save-excursion + (save-restriction + (narrow-to-region beg end) + (read-string "Edit: " old)))))))) + (unless (equal old new) + (if old (replace-match new t t nil 4) + (goto-char (or (match-end 3) (match-end 2) (match-end 1))) + (insert " " new)) + (when org-auto-align-tags (org-align-tags)) + (when (looking-at "[ \t]*$") (replace-match ""))))))))) (defun org-insert-heading-after-current () "Insert a new heading with same level as current, after current subtree." diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index aabf18bfa..1b0bcbf3d 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -3283,7 +3283,23 @@ More text (equal "* B :tag:" (org-test-with-temp-text "* A :tag:" (let ((org-tags-column 4)) (org-edit-headline "B")) - (buffer-string))))) + (buffer-string)))) + ;; Preserve narrowing while reading a new heading. + (should + (equal "B" + (org-test-with-temp-text "* Top +** <point>A +*** Child +* Other" + (org-narrow-to-subtree) + (let ((min (point-min))) + (cl-letf (((symbol-function 'read-string) + (lambda (&rest _) + (should (buffer-narrowed-p)) + (should (= (point-min) min)) + "B"))) + (org-edit-headline)) + (org-get-heading t t t t)))))) -- 2.39.5
>From ebd1b0717a7bd8b123f2d619de0ed0cd09e760b1 Mon Sep 17 00:00:00 2001 From: Slawomir Grochowski <[email protected]> Date: Tue, 23 Jun 2026 09:59:52 +0200 Subject: [PATCH 2/2] ; org-colview: Preserve narrowing editing ITEM * lisp/org-colview.el (org-columns-edit-value): Read a new ITEM value before moving to the heading marker. * testing/lisp/test-org-colview.el (test-org-colview/columns-edit-value): Test editing ITEM through column view. --- lisp/org-colview.el | 8 +++++++- testing/lisp/test-org-colview.el | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 37cfbe937..97e874274 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -835,7 +835,13 @@ Where possible, use the standard interface for changing this line." ("BEAMER_ENV" (command-action #'org-beamer-select-environment)) ("CLOCKSUM" (user-error "This special column cannot be edited")) ("DEADLINE" (command-action #'org-deadline)) - ("ITEM" (command-action #'org-edit-headline)) + ("ITEM" + (let* ((value (org-with-point-at pom + (or (nth 4 (org-heading-components)) ""))) + (nval (org-trim (read-string "Edit: " value)))) + (and (not (equal nval value)) + (lambda () + (org-with-point-at pom (org-edit-headline nval)))))) ("PRIORITY" (command-action #'org-priority)) ("SCHEDULED" (command-action #'org-schedule)) ("TAGS" diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el index 6a1f9d21b..8d7dc7692 100644 --- a/testing/lisp/test-org-colview.el +++ b/testing/lisp/test-org-colview.el @@ -1706,6 +1706,25 @@ https://list.orgmode.org/[email protected]/T/#u." (lambda (&rest _) "new"))) (org-columns-edit-value)) (org-entry-get (point) "A")))) + ;; Edit ITEM while preserving narrowing at the prompt. + (should + (equal "B" + (org-test-with-temp-text "* Top +** <point>A +*** Child +* Other" + (org-narrow-to-subtree) + (let ((min (point-min)) + (org-columns-default-format "%ITEM")) + (org-columns) + (cl-letf (((symbol-function 'read-string) + (lambda (_prompt initial &rest _) + (should (equal initial "A")) + (should (buffer-narrowed-p)) + (should (= (point-min) min)) + "B"))) + (org-columns-edit-value "ITEM")) + (org-get-heading t t t t))))) ;; When the new value matches the current one, the property is ;; left untouched. (should -- 2.39.5
