From bd9e0ad63deb7fe601bb370cf611405eccae4dde Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Thu, 27 Feb 2014 20:54:46 +0100 Subject: [PATCH] Fix `org-open-at-point' on nested objects * 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 | 325 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 172 insertions(+), 153 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index eb65d0894..762a49253 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -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))) -- 2.11.4.GIT