Fix `org-open-at-point' on nested objects
authorNicolas Goaziou <n.goaziou@gmail.com>
Thu, 27 Feb 2014 19:54:46 +0000 (27 20:54 +0100)
committerNicolas Goaziou <n.goaziou@gmail.com>
Thu, 27 Feb 2014 19:54:46 +0000 (27 20:54 +0100)
* lisp/org.el (org-open-at-point): On an unsupported object nested
  within another object, try to open the parent object.

This should correctly open the following link, with point on the
verbatim object:

  [[http://orgmode.org][=verbatim=]]

lisp/org.el

index eb65d08..762a492 100644 (file)
@@ -10459,159 +10459,178 @@ is used internally by `org-open-link-from-string'."
     (move-marker org-open-link-marker (point))
     (setq org-window-config-before-follow-link (current-window-configuration))
     (org-remove-occur-highlights nil nil t)
-    (let* ((context (org-element-context))
-           (type (org-element-type context)))
-      (cond
-       ;; On a headline or an inlinetask, but not on a timestamp,
-       ;; a link or on tags.
-       ((and (org-at-heading-p)
-             (not (memq type '(timestamp link)))
-             ;; Not on tags.
-             (save-excursion (beginning-of-line)
-                             (looking-at org-complex-heading-regexp)
-                             (or (not (match-beginning 5))
-                                 (< (point) (match-beginning 5)))))
-        (let* ((data (org-offer-links-in-entry (current-buffer) (point) arg))
-              (links (car data))
-              (links-end (cdr data)))
-         (if links
-             (dolist (link (if (stringp links) (list links) links))
-               (search-forward link nil links-end)
-               (goto-char (match-beginning 0))
-               (org-open-at-point))
-           (require 'org-attach)
-           (org-attach-reveal 'if-exists))))
-       ((run-hook-with-args-until-success 'org-open-at-point-functions))
-       ;; Do nothing on white spaces after an object.
-       ((let ((end (org-element-property :end context)))
-         (= (save-excursion
-              ;; Make sure we're not on invisible text, as it would
-              ;; make the check unpredictable on object's borders.
-              (when (invisible-p (point))
-                (goto-char
-                 (next-single-property-change (point) 'invisible nil end)))
-              (skip-chars-forward " \t" end) (point))
-            end))
-       (user-error "No link found"))
-       ((eq type 'timestamp) (org-follow-timestamp-link))
-       ;; On tags within a headline or an inlinetask.
-       ((save-excursion (beginning-of-line)
-                        (and (looking-at org-complex-heading-regexp)
-                             (match-beginning 5)
-                             (>= (point) (match-beginning 5))))
-        (org-tags-view arg (substring (match-string 5) 0 -1)))
-       ((eq type 'link)
-        (let ((type (org-element-property :type context))
-              (path (org-element-property :path context)))
-          ;; Switch back to REFERENCE-BUFFER needed when called in
-          ;; a temporary buffer through `org-open-link-from-string'.
-          (with-current-buffer (or reference-buffer (current-buffer))
-            (cond
-             ((equal type "file")
-              (if (string-match "[*?{]" (file-name-nondirectory path))
-                  (dired path)
-                (apply
-                (or (let ((app (org-element-property :application context)))
-                      (nth 1 (assoc (concat "file" (and app (concat "+" app)))
-                                    org-link-protocols)))
-                    #'org-open-file)
-                path arg
-                (let ((option (org-element-property :search-option context)))
-                  (cond ((not option) nil)
-                        ((org-string-match-p "\\`[0-9]+\\'" option)
-                         (list (string-to-number option)))
-                        (t (list nil option)))))))
-            ((assoc type org-link-protocols)
-             (funcall (nth 1 (assoc type org-link-protocols)) path))
-             ((equal type "help")
-              (let ((f-or-v (intern path)))
-                (cond ((fboundp f-or-v) (describe-function f-or-v))
-                      ((boundp f-or-v) (describe-variable f-or-v))
-                      (t (error "Not a known function or variable")))))
-             ((equal type "mailto")
-              (let ((cmd (car org-link-mailto-program))
-                    (args (cdr org-link-mailto-program))
-                    (spec
-                     (format-spec-make
-                      ?a path           ; %a is address.
-                      ?s (let ((option  ; %s is subject.
-                                (org-element-property :search-option context)))
-                           (if (not option) "" (org-link-escape option)))))
-                    final-args)
-                (apply cmd
-                       (dolist (arg args (nreverse final-args))
-                         (if (not (stringp arg)) (push arg final-args)
-                           (push (format-spec arg spec) final-args))))))
-             ((member type '("http" "https" "ftp" "news"))
-              (browse-url (org-link-escape-browser (concat type ":" path))))
-             ((equal type "doi")
-              (browse-url
-               (org-link-escape-browser (concat org-doi-server-url path))))
-             ((equal type "message") (browse-url (concat type ":" path)))
-             ((equal type "shell")
-              (let ((buf (generate-new-buffer "*Org Shell Output"))
-                    (cmd path))
-                (if (or (and (org-string-nw-p org-confirm-shell-link-not-regexp)
-                             (string-match org-confirm-shell-link-not-regexp cmd))
-                        (not org-confirm-shell-link-function)
-                        (funcall org-confirm-shell-link-function
-                                 (format "Execute \"%s\" in shell? "
-                                         (org-add-props cmd nil
-                                           'face 'org-warning))))
-                    (progn
-                      (message "Executing %s" cmd)
-                      (shell-command cmd buf)
-                      (when (featurep 'midnight)
-                        (setq clean-buffer-list-kill-buffer-names
-                              (cons buf clean-buffer-list-kill-buffer-names))))
-                  (error "Abort"))))
-             ((equal type "elisp")
-              (let ((cmd path))
-                (if (or (and (org-string-nw-p org-confirm-elisp-link-not-regexp)
-                             (org-string-match-p
-                              org-confirm-elisp-link-not-regexp cmd))
-                        (not org-confirm-elisp-link-function)
-                        (funcall org-confirm-elisp-link-function
-                                 (format "Execute \"%s\" as elisp? "
-                                         (org-add-props cmd nil
-                                           'face 'org-warning))))
-                    (message "%s => %s" cmd
-                             (if (eq (string-to-char cmd) ?\() (eval (read cmd))
-                               (call-interactively (read cmd))))
-                  (error "Abort"))))
-             ((equal type "id")
-              (require 'ord-id)
-              (funcall (nth 1 (assoc "id" org-link-protocols)) path))
-             ((member type '("coderef" "custom-id" "fuzzy" "radio"))
-              (unless (run-hook-with-args-until-success
-                      'org-open-link-functions path)
-               (if (not arg) (org-mark-ring-push)
-                 (switch-to-buffer-other-window
-                  (org-get-buffer-for-internal-link (current-buffer))))
-               (let ((cmd `(org-link-search
-                            ,(org-element-property :raw-link context)
-                            ,(cond ((equal arg '(4)) ''occur)
-                                   ((equal arg '(16)) ''org-occur))
-                            ,(org-element-property :begin context))))
-                 (condition-case nil
-                     (let ((org-link-search-inhibit-query t))
-                       (eval cmd))
-                   (error (progn (widen) (eval cmd)))))))
-             (t (browse-url-at-point))))))
-       ;; On a footnote reference or at a footnote definition's label.
-       ((or (eq type 'footnote-reference)
-           (and (eq type 'footnote-definition)
-                (save-excursion
-                  ;; Do not validate action when point is on the
-                  ;; spaces right after the footnote label, in order
-                  ;; to be on par with behaviour on links.
-                  (skip-chars-forward " \t")
-                  (let ((begin (org-element-property :contents-begin context)))
-                    (if begin (< (point) begin)
-                      (= (line-beginning-position)
-                         (org-element-property :post-affiliated context)))))))
-        (org-footnote-action))
-       (t (user-error "No link found"))))
+    (unless (run-hook-with-args-until-success 'org-open-at-point-functions)
+      (let* ((context (org-element-context))
+            (type (org-element-type context)))
+       ;; On an unsupported object type, check if it is contained
+       ;; within a support one.  Bail out if we find an element
+       ;; instead.
+       (when (memq type '(bold code entity export-snippet inline-babel-call
+                               inline-src-block italic line-break
+                               latex-fragment macro radio-target
+                               statistics-cookie strike-through subscript
+                               superscript table-cell underline verbatim))
+         (while (and (setq context (org-element-property :parent context))
+                     (not (memq (setq type (org-element-type context))
+                                '(link footnote-reference paragraph verse-block
+                                       table-cell))))))
+       (cond
+        ;; On a headline or an inlinetask, but not on a timestamp,
+        ;; a link or on tags.
+        ((and (org-at-heading-p)
+              (not (memq type '(timestamp link)))
+              ;; Not on tags.
+              (save-excursion (beginning-of-line)
+                              (looking-at org-complex-heading-regexp)
+                              (or (not (match-beginning 5))
+                                  (< (point) (match-beginning 5)))))
+         (let* ((data (org-offer-links-in-entry (current-buffer) (point) arg))
+                (links (car data))
+                (links-end (cdr data)))
+           (if links
+               (dolist (link (if (stringp links) (list links) links))
+                 (search-forward link nil links-end)
+                 (goto-char (match-beginning 0))
+                 (org-open-at-point))
+             (require 'org-attach)
+             (org-attach-reveal 'if-exists))))
+        ;; Do nothing on white spaces after an object.
+        ((let ((end (org-element-property :end context)))
+           (= (save-excursion
+                ;; Make sure we're not on invisible text, as it would
+                ;; make the check unpredictable on object's borders.
+                (when (invisible-p (point))
+                  (goto-char
+                   (next-single-property-change (point) 'invisible nil end)))
+                (skip-chars-forward " \t" end) (point))
+              end))
+         (user-error "No link found"))
+        ((eq type 'timestamp) (org-follow-timestamp-link))
+        ;; On tags within a headline or an inlinetask.
+        ((save-excursion (beginning-of-line)
+                         (and (looking-at org-complex-heading-regexp)
+                              (match-beginning 5)
+                              (>= (point) (match-beginning 5))))
+         (org-tags-view arg (substring (match-string 5) 0 -1)))
+        ((eq type 'link)
+         (let ((type (org-element-property :type context))
+               (path (org-element-property :path context)))
+           ;; Switch back to REFERENCE-BUFFER needed when called in
+           ;; a temporary buffer through `org-open-link-from-string'.
+           (with-current-buffer (or reference-buffer (current-buffer))
+             (cond
+              ((equal type "file")
+               (if (string-match "[*?{]" (file-name-nondirectory path))
+                   (dired path)
+                 (apply
+                  (or (let ((app (org-element-property :application context)))
+                        (nth 1 (assoc (concat "file" (and app (concat "+" app)))
+                                      org-link-protocols)))
+                      #'org-open-file)
+                  path arg
+                  (let ((option (org-element-property :search-option context)))
+                    (cond ((not option) nil)
+                          ((org-string-match-p "\\`[0-9]+\\'" option)
+                           (list (string-to-number option)))
+                          (t (list nil option)))))))
+              ((assoc type org-link-protocols)
+               (funcall (nth 1 (assoc type org-link-protocols)) path))
+              ((equal type "help")
+               (let ((f-or-v (intern path)))
+                 (cond ((fboundp f-or-v) (describe-function f-or-v))
+                       ((boundp f-or-v) (describe-variable f-or-v))
+                       (t (error "Not a known function or variable")))))
+              ((equal type "mailto")
+               (let ((cmd (car org-link-mailto-program))
+                     (args (cdr org-link-mailto-program))
+                     (spec
+                      (format-spec-make
+                       ?a path          ; %a is address.
+                       ?s (let ((option ; %s is subject.
+                                 (org-element-property
+                                  :search-option context)))
+                            (if (not option) "" (org-link-escape option)))))
+                     final-args)
+                 (apply cmd
+                        (dolist (arg args (nreverse final-args))
+                          (if (not (stringp arg)) (push arg final-args)
+                            (push (format-spec arg spec) final-args))))))
+              ((member type '("http" "https" "ftp" "news"))
+               (browse-url (org-link-escape-browser (concat type ":" path))))
+              ((equal type "doi")
+               (browse-url
+                (org-link-escape-browser (concat org-doi-server-url path))))
+              ((equal type "message") (browse-url (concat type ":" path)))
+              ((equal type "shell")
+               (let ((buf (generate-new-buffer "*Org Shell Output"))
+                     (cmd path))
+                 (if (or (and (org-string-nw-p
+                               org-confirm-shell-link-not-regexp)
+                              (string-match
+                               org-confirm-shell-link-not-regexp cmd))
+                         (not org-confirm-shell-link-function)
+                         (funcall org-confirm-shell-link-function
+                                  (format "Execute \"%s\" in shell? "
+                                          (org-add-props cmd nil
+                                            'face 'org-warning))))
+                     (progn
+                       (message "Executing %s" cmd)
+                       (shell-command cmd buf)
+                       (when (featurep 'midnight)
+                         (setq clean-buffer-list-kill-buffer-names
+                               (cons buf
+                                     clean-buffer-list-kill-buffer-names))))
+                   (error "Abort"))))
+              ((equal type "elisp")
+               (let ((cmd path))
+                 (if (or (and (org-string-nw-p
+                               org-confirm-elisp-link-not-regexp)
+                              (org-string-match-p
+                               org-confirm-elisp-link-not-regexp cmd))
+                         (not org-confirm-elisp-link-function)
+                         (funcall org-confirm-elisp-link-function
+                                  (format "Execute \"%s\" as elisp? "
+                                          (org-add-props cmd nil
+                                            'face 'org-warning))))
+                     (message "%s => %s" cmd
+                              (if (eq (string-to-char cmd) ?\()
+                                  (eval (read cmd))
+                                (call-interactively (read cmd))))
+                   (error "Abort"))))
+              ((equal type "id")
+               (require 'ord-id)
+               (funcall (nth 1 (assoc "id" org-link-protocols)) path))
+              ((member type '("coderef" "custom-id" "fuzzy" "radio"))
+               (unless (run-hook-with-args-until-success
+                        'org-open-link-functions path)
+                 (if (not arg) (org-mark-ring-push)
+                   (switch-to-buffer-other-window
+                    (org-get-buffer-for-internal-link (current-buffer))))
+                 (let ((cmd `(org-link-search
+                              ,(org-element-property :raw-link context)
+                              ,(cond ((equal arg '(4)) ''occur)
+                                     ((equal arg '(16)) ''org-occur))
+                              ,(org-element-property :begin context))))
+                   (condition-case nil
+                       (let ((org-link-search-inhibit-query t))
+                         (eval cmd))
+                     (error (progn (widen) (eval cmd)))))))
+              (t (browse-url-at-point))))))
+        ;; On a footnote reference or at a footnote definition's label.
+        ((or (eq type 'footnote-reference)
+             (and (eq type 'footnote-definition)
+                  (save-excursion
+                    ;; Do not validate action when point is on the
+                    ;; spaces right after the footnote label, in order
+                    ;; to be on par with behaviour on links.
+                    (skip-chars-forward " \t")
+                    (let ((begin
+                           (org-element-property :contents-begin context)))
+                      (if begin (< (point) begin)
+                        (= (org-element-property :post-affiliated context)
+                           (line-beginning-position)))))))
+         (org-footnote-action))
+        (t (user-error "No link found")))))
     (move-marker org-open-link-marker nil)
     (run-hook-with-args 'org-follow-link-hook)))