浏览代码

org-open-at-point: Do not list links under headline that cannot be opened

* lisp/org-element.el (org-element-context): Do not alter match-data.
* lisp/org.el (org-open-at-point): Update docstring listing that
`org-open-at-point' opens src-blocks and citations.
(org-offer-links-in-entry): Do not display links within invalid contexts.

Reported in https://list.orgmode.org/PAXPR06MB77609E8C8E769CD7D769FA4BC6199@PAXPR06MB7760.eurprd06.prod.outlook.com/
Ihor Radchenko 3 年之前
父节点
当前提交
057df6cce2
共有 2 个文件被更改,包括 119 次插入109 次删除
  1. 108 107
      lisp/org-element.el
  2. 11 2
      lisp/org.el

+ 108 - 107
lisp/org-element.el

@@ -7880,115 +7880,116 @@ the beginning of any other object, return that object.
 Optional argument ELEMENT, when non-nil, is the closest element
 containing point, as returned by `org-element-at-point'.
 Providing it allows for quicker computation."
-  (catch 'objects-forbidden
-    (org-with-wide-buffer
-     (let* ((pos (point))
-	    (element (or element (org-element-at-point)))
-	    (type (org-element-type element))
-	    (post (org-element-property :post-affiliated element)))
-       ;; If point is inside an element containing objects or
-       ;; a secondary string, narrow buffer to the container and
-       ;; proceed with parsing.  Otherwise, return ELEMENT.
-       (cond
-	;; At a parsed affiliated keyword, check if we're inside main
-	;; or dual value.
-	((and post (< pos post))
-	 (beginning-of-line)
-	 (let ((case-fold-search t)) (looking-at org-element--affiliated-re))
-	 (cond
-	  ((not (member-ignore-case (match-string 1)
+  (save-match-data
+    (catch 'objects-forbidden
+      (org-with-wide-buffer
+       (let* ((pos (point))
+	      (element (or element (org-element-at-point)))
+	      (type (org-element-type element))
+	      (post (org-element-property :post-affiliated element)))
+         ;; If point is inside an element containing objects or
+         ;; a secondary string, narrow buffer to the container and
+         ;; proceed with parsing.  Otherwise, return ELEMENT.
+         (cond
+	  ;; At a parsed affiliated keyword, check if we're inside main
+	  ;; or dual value.
+	  ((and post (< pos post))
+	   (beginning-of-line)
+	   (let ((case-fold-search t)) (looking-at org-element--affiliated-re))
+	   (cond
+	    ((not (member-ignore-case (match-string 1)
 				    org-element-parsed-keywords))
-	   (throw 'objects-forbidden element))
-	  ((< (match-end 0) pos)
-	   (narrow-to-region (match-end 0) (line-end-position)))
-	  ((and (match-beginning 2)
-		(>= pos (match-beginning 2))
-		(< pos (match-end 2)))
-	   (narrow-to-region (match-beginning 2) (match-end 2)))
+	     (throw 'objects-forbidden element))
+	    ((< (match-end 0) pos)
+	     (narrow-to-region (match-end 0) (line-end-position)))
+	    ((and (match-beginning 2)
+		  (>= pos (match-beginning 2))
+		  (< pos (match-end 2)))
+	     (narrow-to-region (match-beginning 2) (match-end 2)))
+	    (t (throw 'objects-forbidden element)))
+	   ;; Also change type to retrieve correct restrictions.
+	   (setq type 'keyword))
+	  ;; At an item, objects can only be located within tag, if any.
+	  ((eq type 'item)
+	   (let ((tag (org-element-property :tag element)))
+	     (if (or (not tag) (/= (line-beginning-position) post))
+	         (throw 'objects-forbidden element)
+	       (beginning-of-line)
+	       (search-forward tag (line-end-position))
+	       (goto-char (match-beginning 0))
+	       (if (and (>= pos (point)) (< pos (match-end 0)))
+		   (narrow-to-region (point) (match-end 0))
+	         (throw 'objects-forbidden element)))))
+	  ;; At an headline or inlinetask, objects are in title.
+	  ((memq type '(headline inlinetask))
+	   (let ((case-fold-search nil))
+	     (goto-char (org-element-property :begin element))
+	     (looking-at org-complex-heading-regexp)
+	     (let ((end (match-end 4)))
+	       (if (not end) (throw 'objects-forbidden element)
+	         (goto-char (match-beginning 4))
+	         (when (looking-at org-element-comment-string)
+		   (goto-char (match-end 0)))
+	         (if (>= (point) end) (throw 'objects-forbidden element)
+		   (narrow-to-region (point) end))))))
+	  ;; At a paragraph, a table-row or a verse block, objects are
+	  ;; located within their contents.
+	  ((memq type '(paragraph table-row verse-block))
+	   (let ((cbeg (org-element-property :contents-begin element))
+	         (cend (org-element-property :contents-end element)))
+	     ;; CBEG is nil for table rules.
+	     (if (and cbeg cend (>= pos cbeg)
+		      (or (< pos cend) (and (= pos cend) (eobp))))
+	         (narrow-to-region cbeg cend)
+	       (throw 'objects-forbidden element))))
 	  (t (throw 'objects-forbidden element)))
-	 ;; Also change type to retrieve correct restrictions.
-	 (setq type 'keyword))
-	;; At an item, objects can only be located within tag, if any.
-	((eq type 'item)
-	 (let ((tag (org-element-property :tag element)))
-	   (if (or (not tag) (/= (line-beginning-position) post))
-	       (throw 'objects-forbidden element)
-	     (beginning-of-line)
-	     (search-forward tag (line-end-position))
-	     (goto-char (match-beginning 0))
-	     (if (and (>= pos (point)) (< pos (match-end 0)))
-		 (narrow-to-region (point) (match-end 0))
-	       (throw 'objects-forbidden element)))))
-	;; At an headline or inlinetask, objects are in title.
-	((memq type '(headline inlinetask))
-	 (let ((case-fold-search nil))
-	   (goto-char (org-element-property :begin element))
-	   (looking-at org-complex-heading-regexp)
-	   (let ((end (match-end 4)))
-	     (if (not end) (throw 'objects-forbidden element)
-	       (goto-char (match-beginning 4))
-	       (when (looking-at org-element-comment-string)
-		 (goto-char (match-end 0)))
-	       (if (>= (point) end) (throw 'objects-forbidden element)
-		 (narrow-to-region (point) end))))))
-	;; At a paragraph, a table-row or a verse block, objects are
-	;; located within their contents.
-	((memq type '(paragraph table-row verse-block))
-	 (let ((cbeg (org-element-property :contents-begin element))
-	       (cend (org-element-property :contents-end element)))
-	   ;; CBEG is nil for table rules.
-	   (if (and cbeg cend (>= pos cbeg)
-		    (or (< pos cend) (and (= pos cend) (eobp))))
-	       (narrow-to-region cbeg cend)
-	     (throw 'objects-forbidden element))))
-	(t (throw 'objects-forbidden element)))
-       (goto-char (point-min))
-       (let ((restriction (org-element-restriction type))
-	     (parent element)
-	     last)
-	 (catch 'exit
-	   (while t
-	     (let ((next (org-element--object-lex restriction)))
-	       (when next (org-element-put-property next :parent parent))
-	       ;; Process NEXT, if any, in order to know if we need to
-	       ;; skip it, return it or move into it.
-	       (if (or (not next) (> (org-element-property :begin next) pos))
-		   (throw 'exit (or last parent))
-		 (let ((end (org-element-property :end next))
-		       (cbeg (org-element-property :contents-begin next))
-		       (cend (org-element-property :contents-end next)))
-		   (cond
-		    ;; Skip objects ending before point.  Also skip
-		    ;; objects ending at point unless it is also the
-		    ;; end of buffer, since we want to return the
-		    ;; innermost object.
-		    ((and (<= end pos) (/= (point-max) end))
-		     (goto-char end)
-		     ;; For convenience, when object ends at POS,
-		     ;; without any space, store it in LAST, as we
-		     ;; will return it if no object starts here.
-		     (when (and (= end pos)
-				(not (memq (char-before) '(?\s ?\t))))
-		       (setq last next)))
-		    ;; If POS is within a container object, move into
-		    ;; that object.
-		    ((and cbeg cend
-			  (>= pos cbeg)
-			  (or (< pos cend)
-			      ;; At contents' end, if there is no
-			      ;; space before point, also move into
-			      ;; object, for consistency with
-			      ;; convenience feature above.
-			      (and (= pos cend)
-				   (or (= (point-max) pos)
-				       (not (memq (char-before pos)
-					        '(?\s ?\t)))))))
-		     (goto-char cbeg)
-		     (narrow-to-region (point) cend)
-		     (setq parent next)
-		     (setq restriction (org-element-restriction next)))
-		    ;; Otherwise, return NEXT.
-		    (t (throw 'exit next)))))))))))))
+         (goto-char (point-min))
+         (let ((restriction (org-element-restriction type))
+	       (parent element)
+	       last)
+	   (catch 'exit
+	     (while t
+	       (let ((next (org-element--object-lex restriction)))
+	         (when next (org-element-put-property next :parent parent))
+	         ;; Process NEXT, if any, in order to know if we need to
+	         ;; skip it, return it or move into it.
+	         (if (or (not next) (> (org-element-property :begin next) pos))
+		     (throw 'exit (or last parent))
+		   (let ((end (org-element-property :end next))
+		         (cbeg (org-element-property :contents-begin next))
+		         (cend (org-element-property :contents-end next)))
+		     (cond
+		      ;; Skip objects ending before point.  Also skip
+		      ;; objects ending at point unless it is also the
+		      ;; end of buffer, since we want to return the
+		      ;; innermost object.
+		      ((and (<= end pos) (/= (point-max) end))
+		       (goto-char end)
+		       ;; For convenience, when object ends at POS,
+		       ;; without any space, store it in LAST, as we
+		       ;; will return it if no object starts here.
+		       (when (and (= end pos)
+				  (not (memq (char-before) '(?\s ?\t))))
+		         (setq last next)))
+		      ;; If POS is within a container object, move into
+		      ;; that object.
+		      ((and cbeg cend
+			    (>= pos cbeg)
+			    (or (< pos cend)
+			        ;; At contents' end, if there is no
+			        ;; space before point, also move into
+			        ;; object, for consistency with
+			        ;; convenience feature above.
+			        (and (= pos cend)
+				     (or (= (point-max) pos)
+				         (not (memq (char-before pos)
+					            '(?\s ?\t)))))))
+		       (goto-char cbeg)
+		       (narrow-to-region (point) cend)
+		       (setq parent next)
+		       (setq restriction (org-element-restriction next)))
+		      ;; Otherwise, return NEXT.
+		      (t (throw 'exit next))))))))))))))
 
 (defun org-element-lineage (datum &optional types with-self)
   "List all ancestors of a given element or object.

+ 11 - 2
lisp/org.el

@@ -8200,7 +8200,9 @@ a link at point.  If they don't find anything interesting at point,
 they must return nil.")
 
 (defun org-open-at-point (&optional arg)
-  "Open link, timestamp, footnote or tags at point.
+  "Open thing at point.
+The thing can be a link, citation, timestamp, footnote, src-block or
+tags.
 
 When point is on a link, follow it.  Normally, files will be
 opened by an appropriate application.  If the optional prefix
@@ -8215,6 +8217,10 @@ When point is a footnote definition, move to the first reference
 found.  If it is on a reference, move to the associated
 definition.
 
+When point is on a src-block of inline src-block, open its result.
+
+When point is on a citation, follow it.
+
 When point is on a headline, display a list of every link in the
 entry, so it is possible to pick one, or all, of them.  If point
 is on a tag, call `org-tags-view' instead.
@@ -8333,7 +8339,10 @@ there is one, return it."
 	 (org-back-to-heading t)
 	 (setq end (save-excursion (outline-next-heading) (point)))
 	 (while (re-search-forward org-link-any-re end t)
-	   (push (match-string 0) links))
+           ;; Only consider valid links or links openable via
+           ;; `org-open-at-point'.
+           (when (memq (org-element-type (org-element-context)) '(link comment comment-block node-property keyword))
+	     (push (match-string 0) links)))
 	 (setq links (org-uniquify (reverse links))))
        (cond
 	((null links)