Browse Source

org-element: Fix parsing error in `org-element-context'

* lisp/org-element.el (org-element-text-markup-successor,
  org-element-latex-or-entity-successor,
  org-element-export-snippet-successor,
  org-element-footnote-reference-successor,
  org-element-inline-babel-call-successor,
  org-element-inline-src-block-successor,
  org-element-line-break-successor, org-element-link-successor,
  org-element-plain-link-successor, org-element-macro-successor,
  org-element-radio-target-successor,
  org-element-statistics-cookie-successor,
  org-element-sub/superscript-successor,
  org-element-table-cell-successor, org-element-target-successor,
  org-element-timestamp-successor): Remove LIMIT argument.
(org-element--parse-objects, org-element--get-next-object-candidates):
Apply signature change to successors.
(org-element-context): Narrow buffer around object containers so
parsing of objects when using this function is done under the same
restrictions as in buffer parsing.
* testing/lisp/test-org-element.el: Add test.
Nicolas Goaziou 11 years ago
parent
commit
026b99ecb8
2 changed files with 194 additions and 218 deletions
  1. 186 216
      lisp/org-element.el
  2. 8 2
      testing/lisp/test-org-element.el

+ 186 - 216
lisp/org-element.el

@@ -2560,17 +2560,15 @@ Assume point is at the first star marker."
 CONTENTS is the contents of the object."
   (format "*%s*" contents))
 
-(defun org-element-text-markup-successor (limit)
+(defun org-element-text-markup-successor ()
   "Search for the next text-markup object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is a symbol among `bold',
 `italic', `underline', `strike-through', `code' and `verbatim'
 and CDR is beginning position."
   (save-excursion
     (unless (bolp) (backward-char))
-    (when (re-search-forward org-emph-re limit t)
+    (when (re-search-forward org-emph-re nil t)
       (let ((marker (match-string 3)))
 	(cons (cond
 	       ((equal marker "*") 'bold)
@@ -2652,11 +2650,9 @@ CONTENTS is nil."
 	  (org-element-property :name entity)
 	  (when (org-element-property :use-brackets-p entity) "{}")))
 
-(defun org-element-latex-or-entity-successor (limit)
+(defun org-element-latex-or-entity-successor ()
   "Search for the next latex-fragment or entity object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `entity' or
 `latex-fragment' and CDR is beginning position."
   (save-excursion
@@ -2670,7 +2666,7 @@ Return value is a cons cell whose CAR is `entity' or
 	     (concat (mapconcat (lambda (e) (nth 1 (assoc e org-latex-regexps)))
 				matchers "\\|")
 		     "\\|" entity-re)
-	     limit t)
+	     nil t)
 	(goto-char (match-beginning 0))
 	(if (looking-at entity-re)
 	    ;; Determine if it's a real entity or a LaTeX command.
@@ -2722,18 +2718,16 @@ CONTENTS is nil."
 	  (org-element-property :back-end export-snippet)
 	  (org-element-property :value export-snippet)))
 
-(defun org-element-export-snippet-successor (limit)
+(defun org-element-export-snippet-successor ()
   "Search for the next export-snippet object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `export-snippet' and CDR
 its beginning position."
   (save-excursion
     (let (beg)
-      (when (and (re-search-forward "@@[-A-Za-z0-9]+:" limit t)
+      (when (and (re-search-forward "@@[-A-Za-z0-9]+:" nil t)
 		 (setq beg (match-beginning 0))
-		 (search-forward "@@" limit t))
+		 (search-forward "@@" nil t))
 	(cons 'export-snippet beg)))))
 
 
@@ -2789,21 +2783,19 @@ CONTENTS is nil."
 	     (concat ":" (org-element-interpret-data inline-def))))))
     (format "[%s]" (concat label def))))
 
-(defun org-element-footnote-reference-successor (limit)
+(defun org-element-footnote-reference-successor ()
   "Search for the next footnote-reference object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `footnote-reference' and
 CDR is beginning position."
   (save-excursion
     (catch 'exit
-      (while (re-search-forward org-footnote-re limit t)
+      (while (re-search-forward org-footnote-re nil t)
 	(save-excursion
 	  (let ((beg (match-beginning 0))
 		(count 1))
 	    (backward-char)
-	    (while (re-search-forward "[][]" limit t)
+	    (while (re-search-forward "[][]" nil t)
 	      (if (equal (match-string 0) "[") (incf count) (decf count))
 	      (when (zerop count)
 		(throw 'exit (cons 'footnote-reference beg))))))))))
@@ -2846,11 +2838,9 @@ CONTENTS is nil."
 	      main-source)
 	    (and post-options (format "[%s]" post-options)))))
 
-(defun org-element-inline-babel-call-successor (limit)
+(defun org-element-inline-babel-call-successor ()
   "Search for the next inline-babel-call object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `inline-babel-call' and
 CDR is beginning position."
   (save-excursion
@@ -2858,7 +2848,7 @@ CDR is beginning position."
     ;; `org-babel-inline-lob-one-liner-regexp'.
     (when (re-search-forward
 	   "call_\\([^()\n]+?\\)\\(?:\\[.*?\\]\\)?([^\n]*?)\\(\\[.*?\\]\\)?"
-	   limit t)
+	   nil t)
       (cons 'inline-babel-call (match-beginning 0)))))
 
 
@@ -2867,8 +2857,6 @@ CDR is beginning position."
 (defun org-element-inline-src-block-parser ()
   "Parse inline source block at point.
 
-LIMIT bounds the search.
-
 Return a list whose CAR is `inline-src-block' and CDR a plist
 with `:begin', `:end', `:language', `:value', `:parameters' and
 `:post-blank' as keywords.
@@ -2903,16 +2891,14 @@ CONTENTS is nil."
 	    (if arguments (format "[%s]" arguments) "")
 	    body)))
 
-(defun org-element-inline-src-block-successor (limit)
+(defun org-element-inline-src-block-successor ()
   "Search for the next inline-babel-call element.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `inline-babel-call' and
 CDR is beginning position."
   (save-excursion
     (unless (bolp) (backward-char))
-    (when (re-search-forward org-babel-inline-src-block-regexp limit t)
+    (when (re-search-forward org-babel-inline-src-block-regexp nil t)
       (cons 'inline-src-block (match-beginning 1)))))
 
 ;;;; Italic
@@ -3006,15 +2992,13 @@ Assume point is at the beginning of the line break."
 CONTENTS is nil."
   "\\\\\n")
 
-(defun org-element-line-break-successor (limit)
+(defun org-element-line-break-successor ()
   "Search for the next line-break object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `line-break' and CDR is
 beginning position."
   (save-excursion
-    (let ((beg (and (re-search-forward "[^\\\\]\\(\\\\\\\\\\)[ \t]*$" limit t)
+    (let ((beg (and (re-search-forward "[^\\\\]\\(\\\\\\\\\\)[ \t]*$" nil t)
 		    (goto-char (match-beginning 1)))))
       ;; A line break can only happen on a non-empty line.
       (when (and beg (re-search-backward "\\S-" (point-at-bol) t))
@@ -3127,28 +3111,24 @@ CONTENTS is the contents of the object, or nil."
 	      raw-link
 	      (if contents (format "[%s]" contents) "")))))
 
-(defun org-element-link-successor (limit)
+(defun org-element-link-successor ()
   "Search for the next link object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `link' and CDR is
 beginning position."
   (save-excursion
     (let ((link-regexp
 	   (if (not org-target-link-regexp) org-any-link-re
 	     (concat org-any-link-re "\\|" org-target-link-regexp))))
-      (when (re-search-forward link-regexp limit t)
+      (when (re-search-forward link-regexp nil t)
 	(cons 'link (match-beginning 0))))))
 
-(defun org-element-plain-link-successor (limit)
+(defun org-element-plain-link-successor ()
   "Search for the next plain link object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `link' and CDR is
 beginning position."
-  (and (save-excursion (re-search-forward org-plain-link-re limit t))
+  (and (save-excursion (re-search-forward org-plain-link-re nil t))
        (cons 'link (match-beginning 0))))
 
 
@@ -3196,17 +3176,15 @@ Assume point is at the macro."
 CONTENTS is nil."
   (org-element-property :value macro))
 
-(defun org-element-macro-successor (limit)
+(defun org-element-macro-successor ()
   "Search for the next macro object.
 
-LIMIT bounds the search.
-
 Return value is cons cell whose CAR is `macro' and CDR is
 beginning position."
   (save-excursion
     (when (re-search-forward
 	   "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}"
-	   limit t)
+	   nil t)
       (cons 'macro (match-beginning 0)))))
 
 
@@ -3242,15 +3220,13 @@ Assume point is at the radio target."
 CONTENTS is the contents of the object."
   (concat "<<<" contents ">>>"))
 
-(defun org-element-radio-target-successor (limit)
+(defun org-element-radio-target-successor ()
   "Search for the next radio-target object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `radio-target' and CDR
 is beginning position."
   (save-excursion
-    (when (re-search-forward org-radio-target-regexp limit t)
+    (when (re-search-forward org-radio-target-regexp nil t)
       (cons 'radio-target (match-beginning 0)))))
 
 
@@ -3282,15 +3258,13 @@ Assume point is at the beginning of the statistics-cookie."
 CONTENTS is nil."
   (org-element-property :value statistics-cookie))
 
-(defun org-element-statistics-cookie-successor (limit)
+(defun org-element-statistics-cookie-successor ()
   "Search for the next statistics cookie object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `statistics-cookie' and
 CDR is beginning position."
   (save-excursion
-    (when (re-search-forward "\\[[0-9]*\\(%\\|/[0-9]*\\)\\]" limit t)
+    (when (re-search-forward "\\[[0-9]*\\(%\\|/[0-9]*\\)\\]" nil t)
       (cons 'statistics-cookie (match-beginning 0)))))
 
 
@@ -3363,16 +3337,14 @@ CONTENTS is the contents of the object."
    (if (org-element-property :use-brackets-p subscript) "_{%s}" "_%s")
    contents))
 
-(defun org-element-sub/superscript-successor  (limit)
+(defun org-element-sub/superscript-successor ()
   "Search for the next sub/superscript object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is either `subscript' or
 `superscript' and CDR is beginning position."
   (save-excursion
     (unless (bolp) (backward-char))
-    (when (re-search-forward org-match-substring-regexp limit t)
+    (when (re-search-forward org-match-substring-regexp nil t)
       (cons (if (string= (match-string 2) "_") 'subscript 'superscript)
 	    (match-beginning 2)))))
 
@@ -3439,11 +3411,9 @@ and `:post-blank' keywords."
 CONTENTS is the contents of the cell, or nil."
   (concat  " " contents " |"))
 
-(defun org-element-table-cell-successor (limit)
+(defun org-element-table-cell-successor ()
   "Search for the next table-cell object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `table-cell' and CDR is
 beginning position."
   (when (looking-at "[ \t]*.*?[ \t]*|") (cons 'table-cell (point))))
@@ -3476,15 +3446,13 @@ Assume point is at the target."
 CONTENTS is nil."
   (format "<<%s>>" (org-element-property :value target)))
 
-(defun org-element-target-successor (limit)
+(defun org-element-target-successor ()
   "Search for the next target object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `target' and CDR is
 beginning position."
   (save-excursion
-    (when (re-search-forward org-target-regexp limit t)
+    (when (re-search-forward org-target-regexp nil t)
       (cons 'target (match-beginning 0)))))
 
 
@@ -3662,11 +3630,9 @@ CONTENTS is nil."
 		       (eq type 'active-range)
 		       (and hour-end minute-end)))))))))
 
-(defun org-element-timestamp-successor (limit)
+(defun org-element-timestamp-successor ()
   "Search for the next timestamp object.
 
-LIMIT bounds the search.
-
 Return value is a cons cell whose CAR is `timestamp' and CDR is
 beginning position."
   (save-excursion
@@ -3676,7 +3642,7 @@ beginning position."
 		   "\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
 		   "\\|"
 		   "\\(?:<%%\\(?:([^>\n]+)\\)>\\)")
-	   limit t)
+	   nil t)
       (cons 'timestamp (match-beginning 0)))))
 
 
@@ -3758,14 +3724,14 @@ CONTENTS is nil."
   (limit &optional granularity special structure)
   "Parse the element starting at point.
 
-LIMIT bounds the search.
-
 Return value is a list like (TYPE PROPS) where TYPE is the type
 of the element and PROPS a plist of properties associated to the
 element.
 
 Possible types are defined in `org-element-all-elements'.
 
+LIMIT bounds the search.
+
 Optional argument GRANULARITY determines the depth of the
 recursion.  Allowed values are `headline', `greater-element',
 `element', `object' or nil.  When it is broader than `object' (or
@@ -4314,57 +4280,56 @@ RESTRICTION is a list of object successors which are allowed in
 the current object."
   (let ((candidates 'initial))
     (save-excursion
-      (goto-char beg)
-      (while (and (< (point) end)
-		  (setq candidates (org-element--get-next-object-candidates
-				    end restriction candidates)))
-	(let ((next-object
-	       (let ((pos (apply 'min (mapcar 'cdr candidates))))
-		 (save-excursion
-		   (goto-char pos)
-		   (funcall (intern (format "org-element-%s-parser"
-					    (car (rassq pos candidates)))))))))
-	  ;; 1. Text before any object.  Untabify it.
-	  (let ((obj-beg (org-element-property :begin next-object)))
-	    (unless (= (point) obj-beg)
-	      (setq acc
-		    (org-element-adopt-elements
-		     acc
-		     (replace-regexp-in-string
-		      "\t" (make-string tab-width ? )
-		      (buffer-substring-no-properties (point) obj-beg))))))
-	  ;; 2. Object...
-	  (let ((obj-end (org-element-property :end next-object))
-		(cont-beg (org-element-property :contents-begin next-object)))
-	    ;; Fill contents of NEXT-OBJECT by side-effect, if it has
-	    ;; a recursive type.
-	    (when (and cont-beg
-		       (memq (car next-object) org-element-recursive-objects))
-	      (save-restriction
-		(narrow-to-region
-		 cont-beg
-		 (org-element-property :contents-end next-object))
+      (save-restriction
+	(narrow-to-region beg end)
+	(goto-char (point-min))
+	(while (and (not (eobp))
+		    (setq candidates
+			  (org-element--get-next-object-candidates
+			   restriction candidates)))
+	  (let ((next-object
+		 (let ((pos (apply 'min (mapcar 'cdr candidates))))
+		   (save-excursion
+		     (goto-char pos)
+		     (funcall (intern (format "org-element-%s-parser"
+					      (car (rassq pos candidates)))))))))
+	    ;; 1. Text before any object.  Untabify it.
+	    (let ((obj-beg (org-element-property :begin next-object)))
+	      (unless (= (point) obj-beg)
+		(setq acc
+		      (org-element-adopt-elements
+		       acc
+		       (replace-regexp-in-string
+			"\t" (make-string tab-width ? )
+			(buffer-substring-no-properties (point) obj-beg))))))
+	    ;; 2. Object...
+	    (let ((obj-end (org-element-property :end next-object))
+		  (cont-beg (org-element-property :contents-begin next-object)))
+	      ;; Fill contents of NEXT-OBJECT by side-effect, if it has
+	      ;; a recursive type.
+	      (when (and cont-beg
+			 (memq (car next-object) org-element-recursive-objects))
 		(org-element--parse-objects
-		 (point-min) (point-max) next-object
-		 (org-element-restriction next-object))))
-	    (setq acc (org-element-adopt-elements acc next-object))
-	    (goto-char obj-end))))
-      ;; 3. Text after last object.  Untabify it.
-      (unless (= (point) end)
-	(setq acc
-	      (org-element-adopt-elements
-	       acc
-	       (replace-regexp-in-string
-		"\t" (make-string tab-width ? )
-		(buffer-substring-no-properties (point) end)))))
-      ;; Result.
-      acc)))
-
-(defun org-element--get-next-object-candidates (limit restriction objects)
+		 cont-beg (org-element-property :contents-end next-object)
+		 next-object (org-element-restriction next-object)))
+	      (setq acc (org-element-adopt-elements acc next-object))
+	      (goto-char obj-end))))
+	;; 3. Text after last object.  Untabify it.
+	(unless (eobp)
+	  (setq acc
+		(org-element-adopt-elements
+		 acc
+		 (replace-regexp-in-string
+		  "\t" (make-string tab-width ? )
+		  (buffer-substring-no-properties (point) end)))))
+	;; Result.
+	acc))))
+
+(defun org-element--get-next-object-candidates (restriction objects)
   "Return an alist of candidates for the next object.
 
-LIMIT bounds the search, and RESTRICTION narrows candidates to
-some object successors.
+RESTRICTION is a list of object types, as symbols.  Only
+candidates with such types are looked after.
 
 OBJECTS is the previous candidates alist.  If it is set to
 `initial', no search has been done before, and all symbols in
@@ -4379,7 +4344,7 @@ beginning position."
        ;; allowed in RESTRICTION.
        (mapcar
 	(lambda (res)
-	  (funcall (intern (format "org-element-%s-successor" res)) limit))
+	  (funcall (intern (format "org-element-%s-successor" res))))
 	restriction)
      ;; Focus on objects returned during last search.  Keep those
      ;; still after point.  Search again objects before it.
@@ -4390,8 +4355,7 @@ beginning position."
 		 (succ (or (cdr (assq type org-element-object-successor-alist))
 			   type)))
 	    (and succ
-		 (funcall (intern (format "org-element-%s-successor" succ))
-			  limit)))))
+		 (funcall (intern (format "org-element-%s-successor" succ)))))))
       objects))))
 
 
@@ -4763,103 +4727,109 @@ object type, but always include `:begin', `:end', `:parent' and
 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."
-  (org-with-wide-buffer
-   (let* ((origin (point))
-	  (element (or element (org-element-at-point)))
-	  (type (org-element-type element))
-	  end)
-     ;; Check if point is inside an element containing objects or at
-     ;; a secondary string.  In that case, move to beginning of the
-     ;; element or secondary string and set END to the other side.
-     (if (not (or (let ((post (org-element-property :post-affiliated element)))
-		    (and post (> post origin)
-			 (< (org-element-property :begin element) origin)
-			 (progn (beginning-of-line)
-				(looking-at org-element--affiliated-re)
-				(member (upcase (match-string 1))
-					org-element-parsed-keywords))
-			 ;; We're at an affiliated keyword.  Change
-			 ;; type to retrieve correct restrictions.
-			 (setq type 'keyword)
-			 ;; Determine if we're at main or dual value.
-			 (if (and (match-end 2) (<= origin (match-end 2)))
-			     (progn (goto-char (match-beginning 2))
-				    (setq end (match-end 2)))
-			   (goto-char (match-end 0))
-			   (setq end (line-end-position)))))
-		  (and (eq type 'item)
-		       (let ((tag (org-element-property :tag element)))
-			 (and tag
-			      (progn
-				(beginning-of-line)
-				(search-forward tag (point-at-eol))
-				(goto-char (match-beginning 0))
-				(and (>= origin (point))
-				     (<= origin
-					 ;; `1+' is required so some
-					 ;; successors can match
-					 ;; properly their object.
-					 (setq end (1+ (match-end 0)))))))))
-		  (and (memq type '(headline inlinetask))
-		       (progn (beginning-of-line)
-			      (skip-chars-forward "* ")
-			      (setq end (point-at-eol))))
-		  (and (memq type '(paragraph table-row verse-block))
-		       (let ((cbeg (org-element-property
-				    :contents-begin element))
-			     (cend (org-element-property
-				    :contents-end element)))
-			 (and cbeg cend ; cbeg is nil for table rules
-			      (>= origin cbeg)
-			      (<= origin cend)
-			      (progn (goto-char cbeg) (setq end cend)))))
-		  (and (eq type 'keyword)
-		       (let ((key (org-element-property :key element)))
-			 (and (member key org-element-document-properties)
-			      (progn (beginning-of-line)
-				     (search-forward key (line-end-position) t)
-				     (forward-char)
-				     (setq end (line-end-position))))))))
-	 element
+  (catch 'objects-forbidden
+    (org-with-wide-buffer
+     (let* ((origin (point))
+            (element (or element (org-element-at-point)))
+            (type (org-element-type element))
+            context)
+       ;; Check if point is inside an element containing objects or at
+       ;; a secondary string.  In that case, move to beginning of the
+       ;; element or secondary string and set END to the other side.
+       (cond
+	;; At a parsed affiliated keyword, check if we're inside main
+	;; or dual value and limit parsing to that area.
+	((let ((post (org-element-property :post-affiliated element)))
+	   (and post (< origin post)))
+	 (beginning-of-line)
+	 (looking-at org-element--affiliated-re)
+	 (cond
+	  ((not (member (upcase (match-string 1)) org-element-parsed-keywords))
+	   (throw 'objects-forbidden element))
+	  ((< (match-end 0) origin)
+	   (narrow-to-region (match-end 0) (line-end-position)))
+	  ((and (match-beginning 2)
+		(>= origin (match-beginning 2))
+		(< origin (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 (not tag) (throw 'objects-forbidden element)
+	     (beginning-of-line)
+	     (search-forward tag (line-end-position))
+	     (goto-char (match-beginning 0))
+	     (if (and (>= origin (point)) (< origin (match-end 0)))
+		 (narrow-to-region (point) (match-end 0))
+	       (throw 'objects-forbidden element)))))
+	;; At an headline or inlinetask, objects are located within
+	;; their title.
+	((memq type '(headline inlinetask))
+	 (goto-char (org-element-property :begin element))
+	 (skip-chars-forward "* ")
+	 (if (and (>= origin (point)) (< origin (line-end-position)))
+	     (narrow-to-region (point) (line-end-position))
+	   (throw 'objects-forbidden element)))
+	;; 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 (>= origin cbeg) (< origin cend))
+	       (narrow-to-region cbeg cend)
+	     (throw 'objects-forbidden element))))
+	;; At a parsed keyword, objects are located within value.
+	((eq type 'keyword)
+	 (if (not (member (org-element-property :key element)
+			  org-element-document-properties))
+	     (throw 'objects-forbidden element)
+	   (beginning-of-line)
+	   (search-forward ":")
+	   (if (and (>= origin (point)) (< origin (line-end-position)))
+	       (narrow-to-region (point) (line-end-position))
+	     (throw 'objects-forbidden element))))
+	(t (throw 'objects-forbidden element)))
+       (goto-char (point-min))
        (let ((restriction (org-element-restriction type))
-	     (parent element)
-	     (candidates 'initial))
-	 (catch 'exit
-	   (while (setq candidates (org-element--get-next-object-candidates
-				    end restriction candidates))
-	     (let ((closest-cand (rassq (apply 'min (mapcar 'cdr candidates))
-					candidates)))
-	       ;; If ORIGIN is before next object in element, there's
-	       ;; no point in looking further.
-	       (if (> (cdr closest-cand) origin) (throw 'exit parent)
-		 (let* ((object
-			 (progn (goto-char (cdr closest-cand))
-				(funcall (intern (format "org-element-%s-parser"
-							 (car closest-cand))))))
-			(cbeg (org-element-property :contents-begin object))
-			(cend (org-element-property :contents-end object))
-			(obj-end (org-element-property :end object)))
-		   (cond
-		    ;; ORIGIN is after OBJECT, so skip it.
-		    ((<= obj-end origin)
-		     (if (/= obj-end end) (goto-char obj-end)
-		       (throw 'exit
-			      (org-element-put-property
-			       object :parent parent))))
-		    ;; ORIGIN is within a non-recursive object or at
-		    ;; an object boundaries: Return that object.
-		    ((or (not cbeg) (> cbeg origin) (< cend origin))
-		     (throw 'exit
-			    (org-element-put-property object :parent parent)))
-		    ;; Otherwise, move within current object and
-		    ;; restrict search to the end of its contents.
-		    (t (goto-char cbeg)
-		       (org-element-put-property object :parent parent)
-		       (setq parent object
-			     restriction (org-element-restriction object)
-			     candidates 'initial
-			     end cend)))))))
-	   parent))))))
+             (parent element)
+             (candidates 'initial))
+         (catch 'exit
+           (while (setq candidates
+			(org-element--get-next-object-candidates
+			 restriction candidates))
+             (let ((closest-cand (rassq (apply 'min (mapcar 'cdr candidates))
+                                        candidates)))
+               ;; If ORIGIN is before next object in element, there's
+               ;; no point in looking further.
+               (if (> (cdr closest-cand) origin) (throw 'exit parent)
+                 (let* ((object
+                         (progn (goto-char (cdr closest-cand))
+                                (funcall (intern (format "org-element-%s-parser"
+                                                         (car closest-cand))))))
+                        (cbeg (org-element-property :contents-begin object))
+                        (cend (org-element-property :contents-end object))
+                        (obj-end (org-element-property :end object)))
+                   (cond
+                    ;; ORIGIN is after OBJECT, so skip it.
+                    ((<= obj-end origin) (goto-char obj-end))
+                    ;; ORIGIN is within a non-recursive object or at
+                    ;; an object boundaries: Return that object.
+                    ((or (not cbeg) (< origin cbeg) (>= origin cend))
+                     (throw 'exit
+                            (org-element-put-property object :parent parent)))
+                    ;; Otherwise, move within current object and
+                    ;; restrict search to the end of its contents.
+                    (t (goto-char cbeg)
+                       (narrow-to-region (point) cend)
+                       (org-element-put-property object :parent parent)
+                       (setq parent object
+                             restriction (org-element-restriction object)
+                             candidates 'initial)))))))
+           parent))))))
 
 (defun org-element-nested-p (elem-A elem-B)
   "Non-nil when elements ELEM-A and ELEM-B are nested."

+ 8 - 2
testing/lisp/test-org-element.el

@@ -501,7 +501,13 @@ Some other text
 	    '(("test" "test" nil "test" "test" "test" "test"))))
        (org-test-with-temp-text "\\test"
 	 (org-element-map (org-element-parse-buffer) 'entity 'identity nil t))))
-    "test")))
+    "test"))
+  ;; Special case: entity at the end of a container.
+  (should
+   (eq 'entity
+       (org-test-with-temp-text "*\\alpha \\beta*"
+	 (search-forward "be")
+	 (org-element-type (org-element-context))))))
 
 
 ;;;; Example Block
@@ -1351,7 +1357,7 @@ e^{i\\pi}+1=0
 	  (org-test-with-temp-text
 	      "#+LINK: orgmode http://www.orgmode.org/\n[[orgmode:#docs]]"
 	    (progn (org-mode-restart)
-		   (goto-char (point-max))
+		   (goto-char (1- (point-max)))
 		   (org-element-property :type (org-element-context))))))
   ;; Link abbreviation in a secondary string.
   (should