Saving the current restriction and restoring it while following links

| categories: orgmode, emacs | tags:

On the org-mode mailing list there has been some discussion about following id links. The issue is that if your buffer is narrowed, clicking on the link does not change the restriction to actually take you to the entry. This is debatably desirable. If I click on a link, I want it to go where it points. But, I might also like to go back to my narrowed view. So here consider how to save the state of narrowing, and restore it. We modify the function that opens an id link to save the restriction, and widen the buffer if necessary.

Saving the restriction seems easy, we just save a marker to point, and the point-min and point-max. We save the marker for a convenient way to get the buffer, and perhaps the actual point. We advise the C-c & function to restore the restriction after we leave it. This should fix the restriction in whatever buffer we undid it in.

Here is the code that seems to work for me. Thanks to Rasmus for the idea on saving the restriction data.

(defvar *saved-restriction* nil
 "A global var containing the current restriction.
Returns (current-buffer point-min point-max")

(defun save-current-restriction ()
  "Save the current restriction at point."
  (setq *saved-restriction*
        (if (buffer-narrowed-p)
            (list (current-buffer) (point-min) (point-max))
          nil)))

(defun restore-saved-restriction ()
  "Restore the last saved restriction."
  (when *saved-restriction*
    (set-buffer (car *saved-restriction*))
    (narrow-to-region (nth 1 *saved-restriction*)
                      (nth 2 *saved-restriction*)))
  (setq *saved-restriction* nil))

;' actually modify this function to save the restriction, and widen if needed.
(defun org-id-open (id)
  "Go to the entry with id ID."
  (org-mark-ring-push)
  (let ((m (org-id-find id 'marker))
        cmd)
    (unless m
      (error "Cannot find entry with ID \"%s\"" id))
    ;; Use a buffer-switching command in analogy to finding files
    (setq cmd
          (or
           (cdr
            (assq
             (cdr (assq 'file org-link-frame-setup))
             '((find-file . switch-to-buffer)
               (find-file-other-window . switch-to-buffer-other-window)
               (find-file-other-frame . switch-to-buffer-other-frame))))
           'switch-to-buffer-other-window))
    (if (not (equal (current-buffer) (marker-buffer m)))
        (funcall cmd (marker-buffer m)))
    (save-current-restriction)
    (when (> m (point-max))
      (widen))
    (goto-char m)
    (move-marker m nil)
    (org-show-context)))


;; And we advise the function going back to restore the restriction.
(defadvice org-mark-ring-goto (after restore-my-restriction () activate)
  "Restore narrowing."
  (restore-saved-restriction))
org-mark-ring-goto

This seems to preserve restrictions in the current buffer and in other buffers, as long as I use C-c & to invoke org-mark-ring goto. I am not sure how easy it would be to make this work for all links. Each link has its own function for following so I am not sure we can easily get them all to do this unless there is some high level function to advise like org-mouse-down-mouse or something similar. It also has the limitation that the restoration only occurs using org-mark-ring-goto, unless you specifically run the (restore-saved-restriction) function yourself. That could be made an interactive function for that purpose. Otherwise, this seems like a reasonable approach.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter