emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [patch, ox] #+INCLUDE resolves links
@ 2014-09-21  0:51 Rasmus
  2014-09-21 11:46 ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-21  0:51 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 791 bytes --]

Hi,

This patch allows INCLUDE to have intuitive links as resolved by
`org-link'-search'.  A couple of examples:

#+INCLUDE: file.org::#custom_id :noheadline :lines "3-"
#+INCLUDE: file.org::*headline :lines "-10"

:noheading tries to get rid of the first headline, and immediately
subsequent drawer and property-drawer, if present.  :noheading is only
interpret when a headline argument is present.  :lines is interpreted
relatively, if coupled with a headline link.

I should work for other types of links as well though it could be
limited to headlines only.

Perhaps it would even make sense to let it take a no-file argument to
locate things within the same buffer.  This would be useful for
including, say, tables in babel/code-appendices.  

What do you think?

—Rasmus

--
Hooray!

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 5253 bytes --]

From 727b20f454cb4f1582874f1b35d5e5f53ec44f79 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sat, 20 Sep 2014 22:22:15 +0200
Subject: [PATCH] ox: Allow links with #+INCLUDE-keyword

* ox.el (org-export--prepare-file-contents,
org-export-expand-include-keyword): Handle links and add option
no-heading.
---
 lisp/ox.el | 64 ++++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 44 insertions(+), 20 deletions(-)

diff --git a/lisp/ox.el b/lisp/ox.el
index f01f951..e78743a 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3321,13 +3321,23 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 headline
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
-			      (setq value (replace-match "" nil nil value)))))
+			    (let ((matched (save-match-data
+					     (org-split-string (match-string 1 value) "::"))))
+			      (setq headline (car-safe (cdr-safe matched)))
+			      (prog1 (expand-file-name
+				      (org-remove-double-quotes
+				       (car matched))
+				      dir)
+				(setq value (replace-match "" nil nil value))))))
+
+		 (no-headline
+		  (and (string-match
+			":\\(no-?headline[[:space:]]*\\(?:'t\\|true\\|yes\\)?\\)" value)
+		       (prog1 t
+		 	 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3370,18 +3380,18 @@ paths."
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (arg-str (if (stringp src-args)
-				  (format " %s" src-args)
-				""))
+				    (format " %s" src-args)
+				  ""))
 		       (contents
 			(org-escape-code-in-string
-			 (org-export--prepare-file-contents file lines))))
+			 (org-export--prepare-file-contents file headline no-headline lines))))
 		   (format "%s#+BEGIN_%s%s\n%s%s#+END_%s\n"
 			   ind-str block arg-str contents ind-str block))))
 	       ((stringp block)
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (contents
-			 (org-export--prepare-file-contents file lines)))
+			(org-export--prepare-file-contents file headline no-headline lines)))
 		   (format "%s#+BEGIN_%s\n%s%s#+END_%s\n"
 			   ind-str block contents ind-str block))))
 	       (t
@@ -3390,7 +3400,7 @@ paths."
 		   (let ((org-inhibit-startup t)) (org-mode))
 		   (insert
 		    (org-export--prepare-file-contents
-		     file lines ind minlevel
+		     file headline no-headline lines ind minlevel
 		     (or (gethash file file-prefix)
 			 (puthash file (incf current-prefix) file-prefix))))
 		   (org-export-expand-include-keyword
@@ -3398,7 +3408,7 @@ paths."
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
-(defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
+(defun org-export--prepare-file-contents (file &optional headline no-headline lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
 When optional argument LINES is a string specifying a range of
@@ -3420,6 +3430,20 @@ This is useful to avoid conflicts when more than one Org file
 with footnotes is included in a document."
   (with-temp-buffer
     (insert-file-contents file)
+    (org-mode)
+    (when headline
+      (org-link-search headline)
+      (narrow-to-region
+       (org-element-property
+	(if no-headline :contents-begin :begin) (org-element-at-point))
+       (org-element-property :end (org-element-at-point)))
+      ;; get rid of drawers and properties
+      (when no-headline
+	(let ((element (org-element-at-point)))
+	  (while (member (org-element-type element) '(drawer property-drawer))
+	    (delete-region (org-element-property :begin element)
+			   (org-element-property :end   element))
+	    (setq element (org-element-at-point))))))
     (when lines
       (let* ((lines (split-string lines "-"))
 	     (lbeg (string-to-number (car lines)))
@@ -3492,15 +3516,15 @@ with footnotes is included in a document."
 		    ((org-string-match-p "\\`[0-9]+\\'" label)
 		     (insert (format "fn:%d-" id)))
 		    (t (forward-char 3) (insert (format "%d-" id)))))))))
-    (org-element-normalize-string (buffer-string))))
-
-(defun org-export-execute-babel-code ()
-  "Execute every Babel code in the visible part of current buffer."
-  ;; Get a pristine copy of current buffer so Babel references can be
-  ;; properly resolved.
-  (let ((reference (org-export-copy-buffer)))
-    (unwind-protect (org-babel-exp-process-buffer reference)
-      (kill-buffer reference))))
+    (org-element-normalize-string (buffer-string)))
+
+  (defun org-export-execute-babel-code ()
+    "Execute every Babel code in the visible part of current buffer."
+    ;; Get a pristine copy of current buffer so Babel references can be
+    ;; properly resolved.
+    (let ((reference (org-export-copy-buffer)))
+      (unwind-protect (org-babel-exp-process-buffer reference)
+	(kill-buffer reference)))))
 
 (defun org-export--copy-to-kill-ring-p ()
   "Return a non-nil value when output should be added to the kill ring.
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-21  0:51 [patch, ox] #+INCLUDE resolves links Rasmus
@ 2014-09-21 11:46 ` Rasmus
  2014-09-21 13:53   ` Nicolas Goaziou
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-21 11:46 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1120 bytes --]

Hi,

Rasmus <rasmus@gmx.us> writes:

> This patch allows INCLUDE to have intuitive links as resolved by
> `org-link'-search'.  A couple of examples:
>
> #+INCLUDE: file.org::#custom_id :noheadline :lines "3-"
> #+INCLUDE: file.org::*headline :lines "-10"
>
> :noheading tries to get rid of the first headline, and immediately
> subsequent drawer and property-drawer, if present.  :noheading is only
> interpret when a headline argument is present.  :lines is interpreted
> relatively, if coupled with a headline link.
>
> I should work for other types of links as well though it could be
> limited to headlines only.
>
> Perhaps it would even make sense to let it take a no-file argument to
> locate things within the same buffer.  This would be useful for
> including, say, tables in babel/code-appendices.

Ups, I think the previous patch had a paredit mistake.

The attached patch includes documentation, better error handling and
enforces consistency when using `org-link-search'.  I used the patch
this morning and I think it makes INCLUDE more enjoyable to use.

Cheers,
Rasmus

--
It was you, Jezebel, it was you

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 8716 bytes --]

From 6925403a72db7216b9deca56acc3f72f6d179f22 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sun, 21 Sep 2014 13:35:11 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files): Updated.
* org.texi (Include files): Updated.
---
 doc/org.texi      | 18 ++++++++++++++++
 doc/orgguide.texi |  9 ++++++--
 lisp/org.el       |  8 ++++---
 lisp/ox.el        | 63 +++++++++++++++++++++++++++++++++++++++++++------------
 4 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..9414314 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,24 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link, see @ref{External links}, to extract an
+object as matched by @code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named
+elements.}.  If the keyword @code{:only-contents} is used, only the contents
+of the element in included.  For headlines, drawers and properties
+immediately following the headline will not be included when using
+@code{:only-contents}.  The @code{:lines} keyword is local to the
+element in question.  Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include tabel with name and caption.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..d3cee0c 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpret as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :no-contents
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index 4ffe1e8..86a1bf9 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20520,9 +20520,11 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index f01f951..f5b8fcc 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -1,5 +1,4 @@
 ;;; ox.el --- Generic Export Engine for Org Mode
-
 ;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
 
 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
@@ -3321,13 +3320,23 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
-			      (setq value (replace-match "" nil nil value)))))
+			    (let ((matched (save-match-data
+					     (org-split-string (match-string 1 value) "::"))))
+			      (setq location (car-safe (cdr-safe matched)))
+			      (prog1 (expand-file-name
+				      (org-remove-double-quotes
+				       (car matched))
+				      dir)
+				(setq value (replace-match "" nil nil value))))))
+
+		 (only-contents
+		  (and (string-match
+			":\\(only-?contents?[[:space:]]*\\(?:'t\\|true\\|yes\\)?\\)" value)
+		       (prog1 t
+		 	 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3370,18 +3379,18 @@ paths."
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (arg-str (if (stringp src-args)
-				  (format " %s" src-args)
-				""))
+				    (format " %s" src-args)
+				  ""))
 		       (contents
 			(org-escape-code-in-string
-			 (org-export--prepare-file-contents file lines))))
+			 (org-export--prepare-file-contents file location only-contents lines))))
 		   (format "%s#+BEGIN_%s%s\n%s%s#+END_%s\n"
 			   ind-str block arg-str contents ind-str block))))
 	       ((stringp block)
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (contents
-			 (org-export--prepare-file-contents file lines)))
+			(org-export--prepare-file-contents file location only-contents lines)))
 		   (format "%s#+BEGIN_%s\n%s%s#+END_%s\n"
 			   ind-str block contents ind-str block))))
 	       (t
@@ -3390,7 +3399,7 @@ paths."
 		   (let ((org-inhibit-startup t)) (org-mode))
 		   (insert
 		    (org-export--prepare-file-contents
-		     file lines ind minlevel
+		     file location only-contents lines ind minlevel
 		     (or (gethash file file-prefix)
 			 (puthash file (incf current-prefix) file-prefix))))
 		   (org-export-expand-include-keyword
@@ -3398,9 +3407,17 @@ paths."
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
-(defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
+(defun org-export--prepare-file-contents (file &optional location only-contents lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
+When optional argument LOCATION is a string the matching element
+identified using `org-link-search' is returned.  Note that
+`org-link-search-must-match-exact-headline' is locally set to
+non-nil.  When ONLY-CONTENTS is non-nil only the contents of the
+matched element in included.  If LOCATION is a headline and
+ONLY-CONTENTS is non-nil, drawers and property-drawers
+immediately following the first headline are also removed.
+
 When optional argument LINES is a string specifying a range of
 lines, include only those lines.
 
@@ -3420,6 +3437,26 @@ This is useful to avoid conflicts when more than one Org file
 with footnotes is included in a document."
   (with-temp-buffer
     (insert-file-contents file)
+    (org-mode)
+    (when location
+      (condition-case err
+	  ;; enforce consistency in search.
+	  (let ((org-link-search-must-match-exact-headline t))
+	    (org-link-search location))
+	;; helpful error messages
+	(error (error (format "%s for %s::%s"
+			      (error-message-string err) file location))))
+      (narrow-to-region
+       (org-element-property
+	(if only-contents :contents-begin :begin) (org-element-at-point))
+       (org-element-property (if only-contents :contents-end :end) (org-element-at-point)))
+      ;; get rid of drawers and properties
+      (when only-contents
+	(let ((element (org-element-at-point)))
+	  (while (member (org-element-type element) '(drawer property-drawer))
+	    (delete-region (org-element-property :begin element)
+			   (org-element-property :end   element))
+	    (setq element (org-element-at-point))))))
     (when lines
       (let* ((lines (split-string lines "-"))
 	     (lbeg (string-to-number (car lines)))
@@ -3495,7 +3532,7 @@ with footnotes is included in a document."
     (org-element-normalize-string (buffer-string))))
 
 (defun org-export-execute-babel-code ()
-  "Execute every Babel code in the visible part of current buffer."
+  "ExecUte every Babel code in the visible part of current buffer."
   ;; Get a pristine copy of current buffer so Babel references can be
   ;; properly resolved.
   (let ((reference (org-export-copy-buffer)))
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-21 11:46 ` Rasmus
@ 2014-09-21 13:53   ` Nicolas Goaziou
  2014-09-21 14:46     ` Rasmus
  2014-09-23 23:25     ` Rasmus
  0 siblings, 2 replies; 21+ messages in thread
From: Nicolas Goaziou @ 2014-09-21 13:53 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> Rasmus <rasmus@gmx.us> writes:
>
>> This patch allows INCLUDE to have intuitive links as resolved by
>> `org-link'-search'.  A couple of examples:

Thanks for the patch. Some comments follow.

>> #+INCLUDE: file.org::#custom_id :noheadline :lines "3-"

Is it `:only-contents' or `:no-headline'? Also ":kwd1 :kbd2 value" is
usually a shortcut for ":kwd1 nil :kbd2 value" (at least in export
attributes). Your example is thus confusing, you should include the
expected value.

  #+INCLUDE: "file.org::#custom_id" :only-contents t :lines "3-"

> +elements.}.  If the keyword @code{:only-contents} is used, only the contents
> +of the element in included.  For headlines, drawers and properties
                  ^^

> +assumed to be in Org mode format and will be processed normally.  File-links
> +will be interpret as well:
           ^^^^^^^^^

>  ;;; ox.el --- Generic Export Engine for Org Mode
> -
>  ;; Copyright (C) 2012-2014 Free Software Foundation, Inc.

You can remove this chunk.

> +		 (only-contents
> +		  (and (string-match
> +			":\\(only-?contents?[[:space:]]*\\(?:'t\\|true\\|yes\\)?\\)" value)

This should be ":only-contents t" or ":only-contents nil".
":only-contents" alone can be tolerated as a shortcut for
":only-contents nil", but that's all.

> +		       (prog1 t
> +		 	 (setq value (replace-match "" nil nil value)))))

Since `replace-match' cannot return nil here, you can remove

  (prog1 t ...) 

wrapper. If you insist on ONLY-CONTENTS being t, then

  (progn (setq ...) t)

is better.

> +			 (org-export--prepare-file-contents file location only-contents lines))))

Couldn't location, only-contents and lines be merged into a single
argument? At the moment, you are either short-circuiting or breaking
guard against circular inclusions (which relies on a combination of
file-name and lines).

IOW, each include keyword could be defined as a triplet of file name,
beginning and ending global positions. You could implement a helper
function to translate FILE LOCATION and ONLY-CONTENTS into this triplet,
which would then be passed to `org-export--prepare-file-contents'.

> -(defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
> +(defun org-export--prepare-file-contents (file &optional location only-contents lines ind minlevel id)
>    "Prepare the contents of FILE for inclusion and return them as a string.
>  
> +When optional argument LOCATION is a string the matching element
> +identified using `org-link-search' is returned.  Note that
> +`org-link-search-must-match-exact-headline' is locally set to
> +non-nil.  When ONLY-CONTENTS is non-nil only the contents of the
> +matched element in included.  If LOCATION is a headline and
> +ONLY-CONTENTS is non-nil, drawers and property-drawers
> +immediately following the first headline are also removed.
> +
>  When optional argument LINES is a string specifying a range of
>  lines, include only those lines.
>  
> @@ -3420,6 +3437,26 @@ This is useful to avoid conflicts when more than one Org file
>  with footnotes is included in a document."
>    (with-temp-buffer
>      (insert-file-contents file)
> +    (org-mode)

You cannot enforce `org-mode' as the current major mode since you can
include other file types.

> +    (when location
> +      (condition-case err
> +	  ;; enforce consistency in search.
> +	  (let ((org-link-search-must-match-exact-headline t))
> +	    (org-link-search location))
> +	;; helpful error messages
> +	(error (error (format "%s for %s::%s"
> +			      (error-message-string err) file location))))
> +      (narrow-to-region
> +       (org-element-property
> +	(if only-contents :contents-begin :begin) (org-element-at-point))
> +       (org-element-property (if only-contents :contents-end :end) (org-element-at-point)))
> +      ;; get rid of drawers and properties
> +      (when only-contents
> +	(let ((element (org-element-at-point)))
> +	  (while (member (org-element-type element) '(drawer property-drawer))
> +	    (delete-region (org-element-property :begin element)
> +			   (org-element-property :end   element))
> +	    (setq element (org-element-at-point))))))

This could be handled when building the triplet. However, please do not
skip drawers (property drawers are fine), as you cannot tell what the
contents are.

>      (when lines
>        (let* ((lines (split-string lines "-"))
>  	     (lbeg (string-to-number (car lines)))
> @@ -3495,7 +3532,7 @@ with footnotes is included in a document."
>      (org-element-normalize-string (buffer-string))))
>  
>  (defun org-export-execute-babel-code ()
> -  "Execute every Babel code in the visible part of current buffer."
> +  "ExecUte every Babel code in the visible part of current buffer."

You can remove this chunk too.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-21 13:53   ` Nicolas Goaziou
@ 2014-09-21 14:46     ` Rasmus
  2014-09-21 19:51       ` Nicolas Goaziou
  2014-09-23 23:25     ` Rasmus
  1 sibling, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-21 14:46 UTC (permalink / raw)
  To: emacs-orgmode

Hi,

A short comment. 

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

>> +			 (org-export--prepare-file-contents file location only-contents lines))))
>
> Couldn't location, only-contents and lines be merged into a single
> argument? At the moment, you are either short-circuiting or breaking
> guard against circular inclusions (which relies on a combination of
> file-name and lines).

Yeah, you are right.  I will look into that.

>> @@ -3420,6 +3437,26 @@ This is useful to avoid conflicts when more than one Org file
>>  with footnotes is included in a document."
>>    (with-temp-buffer
>>      (insert-file-contents file)
>> +    (org-mode)
>
> You cannot enforce `org-mode' as the current major mode since you can
> include other file types.

But then I can't use org-element-at-point:

(with-temp-buffer
  (text-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))

(with-temp-buffer
  (org-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))

Thanks,
Rasmus

-- 
Send from my Emacs

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-21 14:46     ` Rasmus
@ 2014-09-21 19:51       ` Nicolas Goaziou
  0 siblings, 0 replies; 21+ messages in thread
From: Nicolas Goaziou @ 2014-09-21 19:51 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Rasmus <rasmus@gmx.us> writes:

>> You cannot enforce `org-mode' as the current major mode since you can
>> include other file types.
>
> But then I can't use org-element-at-point:
>
> (with-temp-buffer
>   (text-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))
>
> (with-temp-buffer
>   (org-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))

`org-export--prepare-file-contents' is not called with the same number
of arguments when it is an Org file. You can activate `org-mode' if you
are in this situation (look for (when ind ...) and (when minlevel ...)
in the function).


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-21 13:53   ` Nicolas Goaziou
  2014-09-21 14:46     ` Rasmus
@ 2014-09-23 23:25     ` Rasmus
  2014-09-24 21:22       ` Nicolas Goaziou
  1 sibling, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-23 23:25 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 6978 bytes --]

Hi,

Okay, I hope I got a better patch here.  I do — for sure — present
another dreadfully long email!  Let's start.

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

>>> #+INCLUDE: file.org::#custom_id :noheadline :lines "3-"
>
> Is it `:only-contents' or `:no-headline'? Also ":kwd1 :kbd2 value" is
> usually a shortcut for ":kwd1 nil :kbd2 value" (at least in export
> attributes). Your example is thus confusing, you should include the
> expected value.
>
>   #+INCLUDE: "file.org::#custom_id" :only-contents t :lines "3-"

Well it's only-contents now.  Why?  It's more precise in terms of the
org-element terminology and it makes more sense when you include, say,
a table.

>> +elements.}.  If the keyword @code{:only-contents} is used, only the
>> contents
>> +of the element in included.  For headlines, drawers and properties
>                   ^^
>
>> +assumed to be in Org mode format and will be processed normally.
>> File-links
>> +will be interpret as well:
>            ^^^^^^^^^

Sorry about that.  Fixed.

>>  ;;; ox.el --- Generic Export Engine for Org Mode
>> -
>>  ;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
>
> You can remove this chunk.

As above.  [I should somehow disable whitespace cleanup when in source repos].

>> +		 (only-contents
>> +		  (and (string-match
>> + ":\\(only-?contents?[[:space:]]*\\(?:'t\\|true\\|yes\\)?\\)"
>> value)
>
> This should be ":only-contents t" or ":only-contents nil".
> ":only-contents" alone can be tolerated as a shortcut for
> ":only-contents nil", but that's all.

Okay, I hope I got it now.  It's a rather forgiving regexp in terms of
mistakes.  Is that OK?


>> +		       (prog1 t
>> + (setq value (replace-match "" nil nil value)))))
>
> Since `replace-match' cannot return nil here, you can remove

I did it in another way in the new patch now since to allow for nil
values.

>   (prog1 t ...)
>
> wrapper. If you insist on ONLY-CONTENTS being t, then

No, it's changed.

>> + (org-export--prepare-file-contents file location only-contents
>> lines))))
>
> Couldn't location, only-contents and lines be merged into a single
> argument? At the moment, you are either short-circuiting or breaking
> guard against circular inclusions (which relies on a combination of
> file-name and lines).

I try to do that now.  So the arguments of
`org-export--prepare-file-contents' are now unchanged compared to
master.

Note that lonely property drawers are now unconditionally discard if
they are in the beginning of the buffer — including just after an
initial drawer.

> IOW, each include keyword could be defined as a triplet of file name,
> beginning and ending global positions. You could implement a helper
> function to translate FILE LOCATION and ONLY-CONTENTS into this
> triplet,
> which would then be passed to `org-export--prepare-file-contents'.

I just pass a string of line numbers like before.

>> -(defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
>> +(defun org-export--prepare-file-contents (file &optional location only-contents lines ind minlevel id)
>>    "Prepare the contents of FILE for inclusion and return them as a string.
>>
>> +When optional argument LOCATION is a string the matching element
>> +identified using `org-link-search' is returned.  Note that
>> +`org-link-search-must-match-exact-headline' is locally set to
>> +non-nil.  When ONLY-CONTENTS is non-nil only the contents of the
>> +matched element in included.  If LOCATION is a headline and
>> +ONLY-CONTENTS is non-nil, drawers and property-drawers
>> +immediately following the first headline are also removed.
>> +
>>  When optional argument LINES is a string specifying a range of
>>  lines, include only those lines.
>>
>> @@ -3420,6 +3437,26 @@ This is useful to avoid conflicts when more than one Org file
>>  with footnotes is included in a document."
>>    (with-temp-buffer
>>      (insert-file-contents file)
>> +    (org-mode)
>
> You cannot enforce `org-mode' as the current major mode since you can
> include other file types.
>
>> +    (when location
>> +      (condition-case err
>> + ;; enforce consistency in search.
>> + (let ((org-link-search-must-match-exact-headline t))
>> +	    (org-link-search location))
>> +	;; helpful error messages
>> + (error (error (format "%s for %s::%s"
>> + (error-message-string err) file location))))
>> +      (narrow-to-region
>> +       (org-element-property
>> + (if only-contents :contents-begin :begin) (org-element-at-point))
>> + (org-element-property (if only-contents :contents-end :end)
>> (org-element-at-point)))
>> + ;; get rid of drawers and properties
>> +      (when only-contents
>> + (let ((element (org-element-at-point)))
>> + (while (member (org-element-type element) '(drawer
>> property-drawer))
>> + (delete-region (org-element-property :begin element)
>> + (org-element-property :end element))
>> + (setq element (org-element-at-point))))))
>
> This could be handled when building the triplet. However, please do
> not
> skip drawers (property drawers are fine), as you cannot tell what the
> contents are.

OK.

>>      (when lines
>>        (let* ((lines (split-string lines "-"))
>>  	     (lbeg (string-to-number (car lines)))
>> @@ -3495,7 +3532,7 @@ with footnotes is included in a document."
>>      (org-element-normalize-string (buffer-string))))
>>
>>  (defun org-export-execute-babel-code ()
>> -  "Execute every Babel code in the visible part of current buffer."
>> +  "ExecUte every Babel code in the visible part of current buffer."
>
> You can remove this chunk too.

Sorry.

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rasmus <rasmus@gmx.us> writes:
>
>>> You cannot enforce `org-mode' as the current major mode since you can
>>> include other file types.
>>
>> But then I can't use org-element-at-point:
>>
>> (with-temp-buffer
>>   (text-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))
>>
>> (with-temp-buffer
>>   (org-mode) (insert "* test\nmy txt") (goto-char (point-min)) (org-element-at-point))
>
> `org-export--prepare-file-contents' is not called with the same number
> of arguments when it is an Org file. You can activate `org-mode' if you
> are in this situation (look for (when ind ...) and (when minlevel ...)
> in the function).

I hope it's OK now.  In the new `org-export--inclusion-absolute-lines'
I trigger org-mode when a location is given (e.g. file::#h3).  This
should be OK as only org files should be specified with this sort of
link (I believe) and further it's currently only called in the end of
`org-export-expand-include-keyword' when we know it's an org file.

It doesn't make sense to apply this to source files, right?

I will document the functions better before a final patch.

Thanks,
Rasmus

--
Don't panic!!!

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 10238 bytes --]

From 29892a30446c58c42736b426d7385783d73579be Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Tue, 23 Sep 2014 22:16:45 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files)
org.texi (Include files): Updated.
---
 doc/org.texi      |  18 +++++++++
 doc/orgguide.texi |   9 ++++-
 lisp/org.el       |   8 ++--
 lisp/ox.el        | 115 ++++++++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 129 insertions(+), 21 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..c3dd1c1 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,24 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link, see @ref{External links}, to extract an
+object as matched by @code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named
+elements.}.  If the keyword @code{:only-contents} is used, only the contents
+of the included element.  For headlines, drawers and properties
+immediately following the headline will not be included when using
+@code{:only-contents}.  The @code{:lines} keyword is local to the
+element in question.  Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include tabel with name and caption.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..8f0098e 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpreted as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :no-contents
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index 4ffe1e8..86a1bf9 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20520,9 +20520,11 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index f01f951..122e62a 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3321,13 +3321,24 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
-			      (setq value (replace-match "" nil nil value)))))
+			    (let ((matched (save-match-data
+					     (org-split-string
+					      (org-remove-double-quotes (match-string 1 value)) "::"))))
+			      (setq location (car-safe (cdr-safe matched)))
+			      (prog1 (expand-file-name
+				      (car matched)
+				      dir)
+				(setq value (replace-match "" nil nil value))))))
+
+		 (only-contents
+		  (and (string-match
+			":only-?contents?[[:space:]]*\"?\\(t\\|true\\|yes\\)?\"?"
+			value)
+		       (prog1 (and (match-string 1 value) t)
+			 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3370,34 +3381,92 @@ paths."
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (arg-str (if (stringp src-args)
-				  (format " %s" src-args)
-				""))
+				    (format " %s" src-args)
+				  ""))
 		       (contents
 			(org-escape-code-in-string
-			 (org-export--prepare-file-contents file lines))))
+			 (org-export--prepare-file-contents file nil nil lines))))
 		   (format "%s#+BEGIN_%s%s\n%s%s#+END_%s\n"
 			   ind-str block arg-str contents ind-str block))))
 	       ((stringp block)
 		(insert
 		 (let ((ind-str (make-string ind ? ))
 		       (contents
-			 (org-export--prepare-file-contents file lines)))
+			(org-export--prepare-file-contents file lines)))
 		   (format "%s#+BEGIN_%s\n%s%s#+END_%s\n"
 			   ind-str block contents ind-str block))))
 	       (t
 		(insert
 		 (with-temp-buffer
-		   (let ((org-inhibit-startup t)) (org-mode))
-		   (insert
-		    (org-export--prepare-file-contents
-		     file lines ind minlevel
-		     (or (gethash file file-prefix)
-			 (puthash file (incf current-prefix) file-prefix))))
+		   (let ((org-inhibit-startup t)
+			 (lines (org-export--inclusion-absolute-lines
+				 file  location only-contents lines)))
+		     (org-mode)
+		     (insert
+		      (org-export--prepare-file-contents
+		       file lines ind minlevel
+		       (or (gethash file file-prefix)
+			   (puthash file (incf current-prefix) file-prefix)))))
 		   (org-export-expand-include-keyword
 		    (cons (list file lines) included)
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
+(defun org-export--inclusion-absolute-lines (file &optional location only-contents lines)
+  "Resolve absolute lines for an INCLUDEd file given arguments.
+
+Inputs are the FILE to be included, the LOCATION in FILE, whether
+to ONLY-CONTENTS should be included and lines.
+
+Return absolute lines as a string."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (when location
+      ;; locations are only defined for org files so
+      ;; OK to start org-mode.
+      (condition-case err
+	  ;; enforce consistency in search.
+	  (let ((org-link-search-must-match-exact-headline t))
+	    (org-link-search location))
+	;; helpful error messages
+	(error
+	 (error (format "%s for %s::%s" (error-message-string err) file location))))
+      (org-mode)
+      (narrow-to-region
+       (org-element-property
+	(if only-contents :contents-begin :begin) (org-element-at-point))
+       (org-element-property (if only-contents :contents-end :end) (org-element-at-point))))
+    (when only-contents
+      ;; skip drawers and property-drawers
+      ;; these are removed as needed in `org-export--prepare-file-contents'
+      ;; TODO: How to actually do this?  Only line numbers are send to
+      ;; `org-export--prepare-file-contents'.  split in two?
+      (goto-char (point-min))
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let ((element (org-element-at-point)))
+	(while (memq (org-element-type element) '(drawer property-drawer))
+	  (goto-char (org-element-property :end element))
+	  (setq element (org-element-at-point)))))
+    (when lines
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let* ((lines (split-string lines "-"))
+	     (lbeg (string-to-number (car lines)))
+	     (lend (string-to-number (cadr lines)))
+	     (beg (if (zerop lbeg) (point-min)
+		    (goto-char (point-min))
+		    (forward-line (1- lbeg))
+		    (point)))
+	     (end (if (zerop lend) (point-max)
+		    (goto-char (point-min))
+		    (forward-line (1- lend))
+		    (point))))
+	(narrow-to-region beg end)))
+    (apply (lambda (beg end) (format "%s-%s" beg end))
+	   ;; `line-number-at-pos' returns the narrowed line-number
+	   (mapcar 'line-number-at-pos (prog1  (list (point-min) (point-max)) (widen))))))
+
 (defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
@@ -3419,7 +3488,8 @@ each footnote definition and reference if FILE is an Org file.
 This is useful to avoid conflicts when more than one Org file
 with footnotes is included in a document."
   (with-temp-buffer
-    (insert-file-contents file)
+   (switch-to-buffer-other-window (current-buffer))
+   (insert-file-contents file)
     (when lines
       (let* ((lines (split-string lines "-"))
 	     (lbeg (string-to-number (car lines)))
@@ -3449,6 +3519,19 @@ with footnotes is included in a document."
     (when ind
       (unless (eq major-mode 'org-mode)
 	(let ((org-inhibit-startup t)) (org-mode)))
+      ;; make sure that there is not an immediate "lonely"
+      ;; property-drawer.  Normal drawers are OK but property-drawers
+      ;; may follow normal drawers.
+      (goto-char (point-min))
+      (let ((element (org-element-at-point)))
+	(while (memq (org-element-type element) '(drawer property-drawer))
+	  ;; entering here the first element is not a heading so it's
+	  ;; safe to get rid of property-drawers.
+	  (if (eq (org-element-type element) 'property-drawer)
+	      (delete-region (org-element-property :begin element)
+			     (org-element-property :end element))
+	  (goto-char (org-element-property :end element)))
+	  (setq element (org-element-at-point))))
       (goto-char (point-min))
       (let ((ind-str (make-string ind ? )))
 	(while (not (or (eobp) (looking-at org-outline-regexp-bol)))
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-23 23:25     ` Rasmus
@ 2014-09-24 21:22       ` Nicolas Goaziou
  2014-09-28 19:32         ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Nicolas Goaziou @ 2014-09-24 21:22 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> Okay, I hope I got a better patch here.

Thank you. Some comments follow.

> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>> This should be ":only-contents t" or ":only-contents nil".
>> ":only-contents" alone can be tolerated as a shortcut for
>> ":only-contents nil", but that's all.
>
> Okay, I hope I got it now.  It's a rather forgiving regexp in terms of
> mistakes.  Is that OK?

Please no ":only-contents yes", ":only-contents true", ":only-contents
of_course!" in the regexp. If :only-contents is followed by anything but
nil or another keyword, its value is non-nil. See below.

> It doesn't make sense to apply this to source files, right?

Right.

> +elements.}.  If the keyword @code{:only-contents} is used, only the contents
> +of the included element.

The sentence is not complete. Also, it should be something like "If you
set @code{:only-contents} property to a non-nil value, only...".

> For headlines, drawers and properties
> +immediately following the headline will not be included when using
> +@code{:only-contents}.

This is not true anymore about the drawers. This should be merged with
the previous phrase to avoid duplicating "@code{:only-contents}" (e.g.,
only the contents of the matched element are inserted, without any
planning line or property drawer).

>  Some examples:
> +
> +@example
> +#+INCLUDE: "./paper.org::#theory" :only-contents

  #+INCLUDE: "./paper.org::#theory" :only-contents t

> +@smallexample
> +#+INCLUDE: "./otherfile.org::#my_custom_id" :no-contents
> +@end smallexample

  #+INCLUDE: "./otherfile.org::#my_custom_id" :only-contents t

> +			    (let ((matched (save-match-data
> +					     (org-split-string
> +					      (org-remove-double-quotes (match-string 1 value)) "::"))))

There's no reason to use `org-split-string' here since you only want to
match the last "::". You can use the same regexp used by
"org-element.el", i.e.

  (when (string-match "::\\(.*\\)\\'" value)
    (setq location (match-string 1 value)
          value (replace-match "" nil nil value)))

> +		 (only-contents
> +		  (and (string-match
> +			":only-?contents?[[:space:]]*\"?\\(t\\|true\\|yes\\)?\"?"
> +			value)
> +		       (prog1 (and (match-string 1 value) t)
> +			 (setq value (replace-match "" nil nil value)))))

  (only-contents
   (and (string-match ":only-contents +\\([^: \r\t\n]\\S-*\\)" value)
        (org-not-nil (match-string 1 value))))

> +		   (let ((org-inhibit-startup t)
> +			 (lines (org-export--inclusion-absolute-lines
> +				 file  location only-contents lines)))

Lines are local to the element only if LOCATION is provided, right?

  (lines (if location
             (org-export--inclusion-absolute-lines ...)
           lines))

> +  (with-temp-buffer
> +    (insert-file-contents file)
> +    (when location

This check is useless since we know location is defined (otherwise,
this function wouldn't be called).

> +      ;; locations are only defined for org files so
> +      ;; OK to start org-mode.

You can remove this.

> +      (condition-case err
> +	  ;; enforce consistency in search.

"Enforce"

> +	  (let ((org-link-search-must-match-exact-headline t))
> +	    (org-link-search location))
> +	;; helpful error messages

You can remove this.

> +	(error
> +	 (error (format "%s for %s::%s" (error-message-string err) file location))))
> +      (org-mode)

Shouldn't (org-mode) be moved before calling `org-link-search'? At some
point, `org-link-search' may use Elements.

> +      (narrow-to-region
> +       (org-element-property
> +	(if only-contents :contents-begin :begin) (org-element-at-point))
> +       (org-element-property (if only-contents :contents-end :end) (org-element-at-point))))

  (let ((element (org-element-at-point)))
    (let ((contents-beg
           (and only-contents
                (org-element-property :contents-begin element))))
      (narrow-to-region
       (or contents-beg (org-element-property :begin element))
       (org-element-property (if contents-beg :contents-end :end) element))))

> +    (when only-contents
> +      ;; skip drawers and property-drawers
> +      ;; these are removed as needed in `org-export--prepare-file-contents'
> +      ;; TODO: How to actually do this?  Only line numbers are send to
> +      ;; `org-export--prepare-file-contents'.  split in two?
> +      (goto-char (point-min))
> +      (org-skip-whitespace)
> +      (beginning-of-line)
> +      (let ((element (org-element-at-point)))
> +	(while (memq (org-element-type element) '(drawer property-drawer))
> +	  (goto-char (org-element-property :end element))
> +	  (setq element (org-element-at-point)))))

Regular drawers are not expected to be skipped. Also, the following
should be better

  (when (and only-contents
             (memq (org-element-type element) '(headline inlinetask)))
    (goto-char (point-min))
    (when (org-looking-at-p org-planning-line-re) (forward-line))
    (when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
    (unless (bolp) (forward-line)))

This should be obviously included within the previous `let'.

> +	(narrow-to-region beg end)))

This is not needed.

> +    (apply (lambda (beg end) (format "%s-%s" beg end))
> +	   ;; `line-number-at-pos' returns the narrowed line-number
> +	   (mapcar 'line-number-at-pos (prog1  (list (point-min) (point-max)) (widen))))))

This is inefficient because `line-number-at-pos' will start counting
twice from line 1.

  (goto-char beg)
  (widen)
  (let ((start-line (line-number-at-pos)))
    (format "%d-%d"
            start-line
            (+ start-line
               (let ((c 0)) (while (< (point) end) (incf c) (forward-line)) c))))

I didn't check, there may an off-by-one error. Anyway, all this needs
tests.

> +   (switch-to-buffer-other-window (current-buffer))
> +   (insert-file-contents file)

Why is it needed?

>      (when ind
>        (unless (eq major-mode 'org-mode)
>  	(let ((org-inhibit-startup t)) (org-mode)))
> +      ;; make sure that there is not an immediate "lonely"
> +      ;; property-drawer.  Normal drawers are OK but property-drawers
> +      ;; may follow normal drawers.
> +      (goto-char (point-min))
> +      (let ((element (org-element-at-point)))
> +	(while (memq (org-element-type element) '(drawer property-drawer))
> +	  ;; entering here the first element is not a heading so it's
> +	  ;; safe to get rid of property-drawers.
> +	  (if (eq (org-element-type element) 'property-drawer)
> +	      (delete-region (org-element-property :begin element)
> +			     (org-element-property :end element))
> +	  (goto-char (org-element-property :end element)))
> +	  (setq element (org-element-at-point))))

Why do you need to skip anything since function above already took care
of that?


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-24 21:22       ` Nicolas Goaziou
@ 2014-09-28 19:32         ` Rasmus
  2014-09-30  8:07           ` Nicolas Goaziou
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-28 19:32 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 5293 bytes --]

Hi,

Thanks for the comments.  I hope I addressed the previous comments and
did not introduce new reasons bugs.
I added tests.

Comments on comments follow.

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

>> Okay, I hope I got it now.  It's a rather forgiving regexp in terms of
>> mistakes.  Is that OK?
>
> Please no ":only-contents yes", ":only-contents true", ":only-contents
> of_course!" in the regexp. If :only-contents is followed by anything but
> nil or another keyword, its value is non-nil. See below.

Good catch; I added explicit support for

     ¡?[oO][fF][-_]?[cC][Oo][uU][rR][sS][\.!¡]?

in the regexp!  ¡Gotta catch 'em all!

> The sentence is not complete. Also, it should be something like "If you
> set @code{:only-contents} property to a non-nil value, only...".
>> [...]
> This is not true anymore about the drawers. This should be merged with
> the previous phrase to avoid duplicating "@code{:only-contents}" (e.g.,
> only the contents of the matched element are inserted, without any
> planning line or property drawer).

Fixed this and other documentation bugs — hopefully.  Let me know if
it's clear.

>> +			    (let ((matched (save-match-data
>> +					     (org-split-string
>> + (org-remove-double-quotes (match-string 1 value)) "::"))))
>
> There's no reason to use `org-split-string' here since you only want to
> match the last "::". You can use the same regexp used by
> "org-element.el", i.e.
>
>   (when (string-match "::\\(.*\\)\\'" value)
>     (setq location (match-string 1 value)
>           value (replace-match "" nil nil value)))


 Custom_ID is very flexible.  I've use a similar regexp.

>> +		 (only-contents
>> +		  (and (string-match
>> + ":only-?contents?[[:space:]]*\"?\\(t\\|true\\|yes\\)?\"?"
>> +			value)
>> +		       (prog1 (and (match-string 1 value) t)
>> +			 (setq value (replace-match "" nil nil value)))))
>
>   (only-contents
>    (and (string-match ":only-contents +\\([^: \r\t\n]\\S-*\\)" value)
>         (org-not-nil (match-string 1 value))))

I have removed flexibility in speling.

>> +      (narrow-to-region
>> +       (org-element-property
>> +	(if only-contents :contents-begin :begin) (org-element-at-point))
>> + (org-element-property (if only-contents :contents-end :end)
>> (org-element-at-point))))
>
>   (let ((element (org-element-at-point)))
>     (let ((contents-beg
>            (and only-contents
>                 (org-element-property :contents-begin element))))
>       (narrow-to-region
>        (or contents-beg (org-element-property :begin element))
>        (org-element-property (if contents-beg :contents-end :end) element))))

Just out of curiosity, what is an example of a element that can be
named and does not have a :contents-begin?

>> +    (when only-contents
>> +      ;; skip drawers and property-drawers
>> +      ;; these are removed as needed in `org-export--prepare-file-contents'
>> +      ;; TODO: How to actually do this?  Only line numbers are send to
>> +      ;; `org-export--prepare-file-contents'.  split in two?
>> +      (goto-char (point-min))
>> +      (org-skip-whitespace)
>> +      (beginning-of-line)
>> +      (let ((element (org-element-at-point)))
>> +	(while (memq (org-element-type element) '(drawer property-drawer))
>> +	  (goto-char (org-element-property :end element))
>> +	  (setq element (org-element-at-point)))))
>
> Regular drawers are not expected to be skipped. Also, the following
> should be better
>
>   (when (and only-contents
>              (memq (org-element-type element) '(headline inlinetask)))
>     (goto-char (point-min))
>     (when (org-looking-at-p org-planning-line-re) (forward-line))
>     (when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
>     (unless (bolp) (forward-line)))
>
> This should be obviously included within the previous `let'.

Okay, there's a lot of improvements in that suggestion.  However, it
misses this case which created using only "official" shortcuts

     * head
     SCHEDULED: <2014-09-28 sun>
     :LOGBOOK:
     - Note taken on [2014-09-28 sat 12:21] \\
       a drawer
     :END:
     :PROPERTIES:
     :CUSTOM_ID: h
     :END:

The patch handles something like this now cf. the last test.

>> +    (apply (lambda (beg end) (format "%s-%s" beg end))
>> +	   ;; `line-number-at-pos' returns the narrowed line-number
>> + (mapcar 'line-number-at-pos (prog1 (list (point-min) (point-max))
>> (widen))))))
>
> This is inefficient because `line-number-at-pos' will start counting
> twice from line 1.
>
>   (goto-char beg)
>   (widen)
>   (let ((start-line (line-number-at-pos)))
>     (format "%d-%d"
>             start-line
>             (+ start-line
>                (let ((c 0)) (while (< (point) end) (incf c) (forward-line)) c))))
>
> I didn't check, there may an off-by-one error. Anyway, all this needs
> tests.

Fine with me.  It's a bit less elegant IMO, but you are right.  I had
to do it slightly differently since the line number needs to appear
irrespective of whether lines are included in the call initially.
That being said, I could have very well overlooked some obvioues way
of doing it.

—Rasmus

-- 
Hooray!

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 12731 bytes --]

From 0c0b4a4b9ea0db7ce3f8c08c9a2947db85c54470 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sun, 28 Sep 2014 21:05:17 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files)
org.texi (Include files): Updated.
* testing/examples/include.org: New examples.
* test-ox.el (test-org-export/expand-include): New tests.
---
 doc/org.texi                 |  18 +++++++
 doc/orgguide.texi            |   9 +++-
 lisp/org.el                  |   8 +--
 lisp/ox.el                   | 117 +++++++++++++++++++++++++++++++++++++++----
 testing/examples/include.org |  25 +++++++++
 testing/lisp/test-ox.el      |  57 ++++++++++++++++++++-
 6 files changed, 218 insertions(+), 16 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..5a9c0c5 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,24 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link, see @ref{search option in file links}, to
+extract an object as matched by @code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named
+elements.}.  If the @code{:only-contents} property is non-nil, only the
+contents of the requested element will be included, omitting any
+property-drawers, planning-lines, attributes, captions etc.  The
+@code{:lines} keyword operates locally with respect to the requested element.
+Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents t
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include named element.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..4feeaca 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpreted as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :only-contents t
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index b09e72d..c70e44a 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20522,9 +20522,11 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index 59091fc..ed1024b 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3321,13 +3321,25 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
+			    (prog1
+				(save-match-data
+				  (let ((matched (match-string 1 value)))
+				    (when (string-match "\\(::\\(.*?\\)\\)\"?\\'" matched)
+				      (setq location (match-string 2 matched))
+				      (setq matched
+					    (replace-match "" nil nil  matched 1)))
+				    (expand-file-name
+				     (org-remove-double-quotes
+				      matched)
+				     dir)))
 			      (setq value (replace-match "" nil nil value)))))
+		 (only-contents
+		  (and (string-match ":only-contents +\\([^: \r\t\n]\\S-*\\)" value)
+		       (prog1 (org-not-nil (match-string 1 value))
+			 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3387,17 +3399,88 @@ paths."
 	       (t
 		(insert
 		 (with-temp-buffer
-		   (let ((org-inhibit-startup t)) (org-mode))
-		   (insert
-		    (org-export--prepare-file-contents
-		     file lines ind minlevel
-		     (or (gethash file file-prefix)
-			 (puthash file (incf current-prefix) file-prefix))))
+		   (let ((org-inhibit-startup t)
+			 (lines
+			  (if location
+			      (org-export--inclusion-absolute-lines
+			       file  location only-contents lines)
+			    lines)))
+		     (org-mode)
+		     (insert
+		      (org-export--prepare-file-contents
+		       file lines ind minlevel
+		       (or (gethash file file-prefix)
+			   (puthash file (incf current-prefix) file-prefix)))))
 		   (org-export-expand-include-keyword
 		    (cons (list file lines) included)
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
+(defun org-export--inclusion-absolute-lines (file location only-contents lines)
+  "Resolve absolute lines for an included file with file-link.
+
+FILE is string file-name of the file to include.  LOCATION is a
+string name within FILE to be included (located via
+`org-link-search').  If ONLY-CONTENTS is non-nil only the
+contents of the named element will be included, as determined
+Org-Element.  If LINES is non-nil only those lines are included.
+
+Return a string of lines to be included in the format expected by
+`org-export--prepare-file-contents'."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (unless (eq major-mode 'org-mode)
+      (let ((org-inhibit-startup t)) (org-mode)))
+    (condition-case err
+	;; Enforce consistent search.
+	(let ((org-link-search-must-match-exact-headline t))
+	  (org-link-search location))
+      (error
+       (error (format "%s for %s::%s" (error-message-string err) file location))))
+    (let* ((element (org-element-at-point))
+	   (contents-begin
+	    (and only-contents (org-element-property :contents-begin element))))
+      (narrow-to-region
+       (or contents-begin (org-element-property :begin element))
+       (org-element-property (if contents-begin :contents-end :end) element))
+      (when (and only-contents
+		 (memq (org-element-type element) '(headline inlinetask)))
+	;; skip planning line and property-drawer.  If a normal drawer
+	;; precedes a property-drawer both will be included.
+	;; Remaining property-drawers are removed as needed in
+	;; `org-export--prepare-file-contents'
+	(goto-char (point-min))
+	(when (org-looking-at-p org-planning-line-re) (forward-line))
+	(when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
+	(unless (bolp) (forward-line))
+	(narrow-to-region (point) (point-max))))
+    (when lines
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let* ((lines (split-string lines "-"))
+	     (lbeg (string-to-number (car lines)))
+	     (lend (string-to-number (cadr lines)))
+	     (beg (if (zerop lbeg) (point-min)
+		    (goto-char (point-min))
+		    (forward-line (1- lbeg))
+		    (point)))
+	     (end (if (zerop lend) (point-max)
+		    (goto-char beg)
+		    (forward-line (1-  lend))
+		    (point))))
+	(narrow-to-region beg end)))
+    (let ((end (point-max)))
+      (goto-char (point-min))
+      (widen)
+      (let ((start-line (line-number-at-pos)))
+	(format "%d-%d"
+		start-line
+		(save-excursion
+		  (+ start-line
+		     (let ((counter 0))
+		       (while (< (point) end) (incf counter) (forward-line))
+		       counter))))))))
+
 (defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
@@ -3444,6 +3527,20 @@ with footnotes is included in a document."
     (skip-chars-backward " \r\t\n")
     (forward-line)
     (delete-region (point) (point-max))
+    ;; Remove property-drawers after drawers.
+    (when (or ind minlevel)
+      (unless (eq major-mode 'org-mode)
+    	(let ((org-inhibit-startup t)) (org-mode)))
+      (goto-char (point-min))
+      (when (looking-at org-drawer-regexp)
+	(goto-char (match-end 0))
+	(search-forward-regexp org-drawer-regexp)
+	(forward-line 1)
+	(beginning-of-line))
+      (when (looking-at org-property-drawer-re)
+	(delete-region (match-beginning 0) (match-end 0))
+	(beginning-of-line))
+      (delete-region (point) (save-excursion (and (org-skip-whitespace) (point)))))
     ;; If IND is set, preserve indentation of include keyword until
     ;; the first headline encountered.
     (when ind
diff --git a/testing/examples/include.org b/testing/examples/include.org
index 186facb..c04c942 100644
--- a/testing/examples/include.org
+++ b/testing/examples/include.org
@@ -8,3 +8,28 @@ Small Org file with an include keyword.
 
 * Heading
 body
+
+* Another heading
+:PROPERTIES:
+:CUSTOM_ID: ah
+:END:
+1
+2
+3
+
+* A headline with a table
+:PROPERTIES:
+:CUSTOM_ID: ht
+:END:
+#+CAPTION: a table
+#+NAME: tbl
+| 1 |
+
+* drawer-headline
+:LOGBOOK:
+drawer
+:END:
+:PROPERTIES:
+:CUSTOM_ID: dh
+:END:
+content
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 4af3510..4706ec2 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -918,7 +918,62 @@ Footnotes[fn:1], [fn:test] and [fn:inline:anonymous footnote].
 		(org-export-expand-include-keyword)
 		(org-element-map (org-element-parse-buffer)
 		    'footnote-reference
-		  (lambda (ref) (org-element-property :label ref))))))))))))
+		  (lambda (ref) (org-element-property :label ref)))))))))))
+  ;; If only-contents is non-nil only include contents of element
+  (should
+   (equal
+    "body\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::*Heading\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Headings can be included via CUSTOM_ID
+  (should
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir)
+     (org-export-expand-include-keyword)
+     (goto-char (point-min))
+     (looking-at "* Another heading")))
+  ;; Named objects can be included
+  (should
+   (equal
+    "| 1 |\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Including non-existing elements should result in an error
+  (should-error
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir)
+     (org-export-expand-include-keyword)))
+  ;; Lines work relatively to an included element
+  (should
+   (equal
+    "2\n3\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t :lines \"2-3\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped from headlines
+  (should
+   (equal
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped, drawers should not be
+  (should
+   (equal
+    ":LOGBOOK:\ndrawer\n:END:\ncontent\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string)))))
 
 (ert-deftest test-org-export/expand-macro ()
   "Test macro expansion in an Org buffer."
-- 
2.1.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-28 19:32         ` Rasmus
@ 2014-09-30  8:07           ` Nicolas Goaziou
  2014-09-30 10:18             ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Nicolas Goaziou @ 2014-09-30  8:07 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> Thanks for the comments.  I hope I addressed the previous comments and
> did not introduce new reasons bugs.
> I added tests.

Great.

> Just out of curiosity, what is an example of a element that can be
> named and does not have a :contents-begin?

  #+name: empty-drawer
  :DRAWER:
  :END:

> Okay, there's a lot of improvements in that suggestion.  However, it
> misses this case which created using only "official" shortcuts
>
>      * head
>      SCHEDULED: <2014-09-28 sun>
>      :LOGBOOK:
>      - Note taken on [2014-09-28 sat 12:21] \\
>        a drawer
>      :END:
>      :PROPERTIES:
>      :CUSTOM_ID: h
>      :END:

This was done on purpose, anticipating a patch I'm working on. Anyway,
it doesn't matter much. I'll revert it once my work is ready.

> +elements.}.  If the @code{:only-contents} property is non-nil, only the
> +contents of the requested element will be included, omitting any
> +property-drawers, planning-lines, attributes, captions etc.  The

properties (or property) drawer, planning line.

Do you think it's worth specifying that captions and attributes will be
ignored? It seems pretty obvious to me as they do not belong to contents
(unlike to planning and properties drawers).

> +		 (only-contents
> +		  (and (string-match ":only-contents +\\([^: \r\t\n]\\S-*\\)" value)
> +		       (prog1 (org-not-nil (match-string 1 value))
> +			 (setq value (replace-match "" nil nil value)))))

Why do you need to remove match from VALUE? AFAICT, the only match that
needs to be removed is the file name. Others are regular.

> +	;; skip planning line and property-drawer.  If a normal drawer
> +	;; precedes a property-drawer both will be included.
> +	;; Remaining property-drawers are removed as needed in
> +	;; `org-export--prepare-file-contents'

  ;; Skip planning line and properties drawer.

> +  ;; If only-contents is non-nil only include contents of element
> +  (should
> +   (equal
> +    "body\n"
> +    (org-test-with-temp-text
> +	(format "#+INCLUDE: \"%s/examples/include.org::*Heading\" :only-contents t" org-test-dir)
> +      (org-export-expand-include-keyword)

[...]

> +  ;; Properties should be dropped, drawers should not be
> +  (should
> +   (equal
> +    ":LOGBOOK:\ndrawer\n:END:\ncontent\n"
> +    (org-test-with-temp-text
> +	(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
> +      (org-export-expand-include-keyword)
> +      (buffer-string)))))

Mind the 80th column in your tests, and full stop at end of comments.

Besides the minor issues above, it looks good. Feel free to push it
whenever you want.

Thank you for that work.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-30  8:07           ` Nicolas Goaziou
@ 2014-09-30 10:18             ` Rasmus
  2014-09-30 14:29               ` Nicolas Goaziou
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-30 10:18 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

>> Just out of curiosity, what is an example of a element that can be
>> named and does not have a :contents-begin?
>
>   #+name: empty-drawer
>   :DRAWER:
>   :END:

Thanks. 

>> Okay, there's a lot of improvements in that suggestion.  However, it
>> misses this case which created using only "official" shortcuts
>>
>>      * head
>>      SCHEDULED: <2014-09-28 sun>
>>      :LOGBOOK:
>>      - Note taken on [2014-09-28 sat 12:21] \\
>>        a drawer
>>      :END:
>>      :PROPERTIES:
>>      :CUSTOM_ID: h
>>      :END:
>
> This was done on purpose, anticipating a patch I'm working on. Anyway,
> it doesn't matter much. I'll revert it once my work is ready.

Okay.  If you discussed on the list I missed it.

>> +elements.}.  If the @code{:only-contents} property is non-nil, only the
>> +contents of the requested element will be included, omitting any
>> +property-drawers, planning-lines, attributes, captions etc.  The
>
> properties (or property) drawer, planning line.
>
> Do you think it's worth specifying that captions and attributes will be
> ignored? It seems pretty obvious to me as they do not belong to contents
> (unlike to planning and properties drawers).

It's two extra words and maybe it's helpful to make the concept clear
to people unfamilar with org-element.  The statement "contents of the
requested element", while technically clear, is only barely
comprehensible for the uninvited.

However, it's only tweo extra words, so I'm happy to remove them if
you have strong feeling about this.


>> +		 (only-contents
>> + (and (string-match ":only-contents +\\([^: \r\t\n]\\S-*\\)" value)
>> +		       (prog1 (org-not-nil (match-string 1 value))
>> +			 (setq value (replace-match "" nil nil value)))))
>
> Why do you need to remove match from VALUE? AFAICT, the only match that
> needs to be removed is the file name. Others are regular.

I don't know what you mean with regular.  Anyway, my reasoning is that
this INCLUDE-command

        #+INCLUDE: "/tmp/test0.org" :my-random-prop "foo"

Will give me something like:

\#+\textsc{begin}\(_{\text{my}}\)-random-prop
\section{test0}
\label{sec-2}
1
\#+\textsc{end}\(_{\text{my}}\)-random-prop

In casual testing similar stuff happened when I did not remove the
match.  It could have been it was a bit too casual and that I
misspelling the property.  I will test this properly later.

> Mind the 80th column in your tests, and full stop at end of comments.

OK, I'll fix it.

> Besides the minor issues above, it looks good. Feel free to push it
> whenever you want.

Don't have push.

—Rasmus

-- 
Lasciate ogni speranza o voi che entrate: siete nella mani di'machellaio

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-30 10:18             ` Rasmus
@ 2014-09-30 14:29               ` Nicolas Goaziou
  2014-09-30 21:48                 ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Nicolas Goaziou @ 2014-09-30 14:29 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Rasmus <rasmus@gmx.us> writes:

> It's two extra words and maybe it's helpful to make the concept clear
> to people unfamilar with org-element.  The statement "contents of the
> requested element", while technically clear, is only barely
> comprehensible for the uninvited.
>
> However, it's only tweo extra words, so I'm happy to remove them if
> you have strong feeling about this.

I have no strong feeling. Though, I admit the "etc" these two words
imply annoys me. Anyway, you will be the final judge.

> I don't know what you mean with regular.  Anyway, my reasoning is that
> this INCLUDE-command
>
>         #+INCLUDE: "/tmp/test0.org" :my-random-prop "foo"
>
> Will give me something like:
>
> \#+\textsc{begin}\(_{\text{my}}\)-random-prop
> \section{test0}
> \label{sec-2}
> 1
> \#+\textsc{end}\(_{\text{my}}\)-random-prop
>
> In casual testing similar stuff happened when I did not remove the
> match.  It could have been it was a bit too casual and that I
> misspelling the property.  I will test this properly later.

OK. I wait for your report.


Regards,

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-30 14:29               ` Nicolas Goaziou
@ 2014-09-30 21:48                 ` Rasmus
  2014-10-01 20:03                   ` Nicolas Goaziou
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-09-30 21:48 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 2422 bytes --]

Hi,

Changes are one sentence in the documentations, casing, and I changed
the regexp so that :only-contents is valid (it's nil).

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Rasmus <rasmus@gmx.us> writes:
>
>> It's two extra words and maybe it's helpful to make the concept clear
>> to people unfamilar with org-element.  The statement "contents of the
>> requested element", while technically clear, is only barely
>> comprehensible for the uninvited.
>>
>> However, it's only tweo extra words, so I'm happy to remove them if
>> you have strong feeling about this.
>
> I have no strong feeling. Though, I admit the "etc" these two words
> imply annoys me. Anyway, you will be the final judge.

Is it better now?

>> I don't know what you mean with regular.  Anyway, my reasoning is that
>> this INCLUDE-command
>>
>>         #+INCLUDE: "/tmp/test0.org" :my-random-prop "foo"
>>
>> Will give me something like:
>>
>> \#+\textsc{begin}\(_{\text{my}}\)-random-prop
>> \section{test0}
>> \label{sec-2}
>> 1
>> \#+\textsc{end}\(_{\text{my}}\)-random-prop
>>
>> In casual testing similar stuff happened when I did not remove the
>> match.  It could have been it was a bit too casual and that I
>> misspelling the property.  I will test this properly later.
>
> OK. I wait for your report.

Block sucks up whatever remains, so the match must be removed:

(block (and (string-match "\\<\\(\\S-+\\)\\>" value)
			     (match-string 1 value)))


I want to discuss one more important potential issue before having the
patch applied.  Currently, location is ignored if the included part is
not an env (line 3381) and not a block (3392).  I'm not sure this is
right.  I could do one of the following:

   1. Nothing (current state)
   2. Throw an error if location and env or block are combined.
   3. Try to use location even if block is set.  Recall, though, that
      location is resolved using org-mode. 
   4. Let location be a general regexp if env or block is non-nil.
      But then we are breaking with the org file-link idea.
   5. Make location work for org files when env or block, otherwise
      throw an error.

WDYT?

Less important.  Should the <I "speedkey" (I don't know if that's the
right term) prompt for a location, or change the cursor position to
after the filename?

—Rasmus

-- 
Governments should be afraid of their people

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 12757 bytes --]

From 44e20962a3c16e1f79a2d5bbc8420f00f93db637 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sun, 28 Sep 2014 21:05:17 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files)
org.texi (Include files): Updated.
* testing/examples/include.org: New examples.
* test-ox.el (test-org-export/expand-include): New tests.
---
 doc/org.texi                 |  17 +++++++
 doc/orgguide.texi            |   9 +++-
 lisp/org.el                  |   9 ++--
 lisp/ox.el                   | 117 +++++++++++++++++++++++++++++++++++++++----
 testing/examples/include.org |  25 +++++++++
 testing/lisp/test-ox.el      |  59 +++++++++++++++++++++-
 6 files changed, 220 insertions(+), 16 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..296e289 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,23 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link, see @ref{search option in file links}, to
+extract an object as matched by @code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named
+elements.}.  If the @code{:only-contents} property is non-nil, only the
+contents of the requested element will be included, omitting properties
+drawer and planning-line if present.  The @code{:lines} keyword
+operates locally with respect to the requested element.  Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents t
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include named element.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..4feeaca 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpreted as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :only-contents t
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index 9815eb4..0910296 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20524,9 +20524,12 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string
+			    (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index 90c623e..dc36c70 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3325,13 +3325,25 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
+			    (prog1
+				(save-match-data
+				  (let ((matched (match-string 1 value)))
+				    (when (string-match "\\(::\\(.*?\\)\\)\"?\\'" matched)
+				      (setq location (match-string 2 matched))
+				      (setq matched
+					    (replace-match "" nil nil  matched 1)))
+				    (expand-file-name
+				     (org-remove-double-quotes
+				      matched)
+				     dir)))
 			      (setq value (replace-match "" nil nil value)))))
+		 (only-contents
+		  (and (string-match ":only-contents *?\\([^: \r\t\n]\\S-*\\)?" value)
+		       (prog1 (org-not-nil (match-string 1 value))
+			 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3391,17 +3403,88 @@ paths."
 	       (t
 		(insert
 		 (with-temp-buffer
-		   (let ((org-inhibit-startup t)) (org-mode))
-		   (insert
-		    (org-export--prepare-file-contents
-		     file lines ind minlevel
-		     (or (gethash file file-prefix)
-			 (puthash file (incf current-prefix) file-prefix))))
+		   (let ((org-inhibit-startup t)
+			 (lines
+			  (if location
+			      (org-export--inclusion-absolute-lines
+			       file  location only-contents lines)
+			    lines)))
+		     (org-mode)
+		     (insert
+		      (org-export--prepare-file-contents
+		       file lines ind minlevel
+		       (or (gethash file file-prefix)
+			   (puthash file (incf current-prefix) file-prefix)))))
 		   (org-export-expand-include-keyword
 		    (cons (list file lines) included)
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
+(defun org-export--inclusion-absolute-lines (file location only-contents lines)
+  "Resolve absolute lines for an included file with file-link.
+
+FILE is string file-name of the file to include.  LOCATION is a
+string name within FILE to be included (located via
+`org-link-search').  If ONLY-CONTENTS is non-nil only the
+contents of the named element will be included, as determined
+Org-Element.  If LINES is non-nil only those lines are included.
+
+Return a string of lines to be included in the format expected by
+`org-export--prepare-file-contents'."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (unless (eq major-mode 'org-mode)
+      (let ((org-inhibit-startup t)) (org-mode)))
+    (condition-case err
+	;; Enforce consistent search.
+	(let ((org-link-search-must-match-exact-headline t))
+	  (org-link-search location))
+      (error
+       (error (format "%s for %s::%s" (error-message-string err) file location))))
+    (let* ((element (org-element-at-point))
+	   (contents-begin
+	    (and only-contents (org-element-property :contents-begin element))))
+      (narrow-to-region
+       (or contents-begin (org-element-property :begin element))
+       (org-element-property (if contents-begin :contents-end :end) element))
+      (when (and only-contents
+		 (memq (org-element-type element) '(headline inlinetask)))
+	;; Skip planning line and property-drawer.  If a normal drawer
+	;; precedes a property-drawer both will be included.
+	;; Remaining property-drawers are removed as needed in
+	;; `org-export--prepare-file-contents'
+	(goto-char (point-min))
+	(when (org-looking-at-p org-planning-line-re) (forward-line))
+	(when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
+	(unless (bolp) (forward-line))
+	(narrow-to-region (point) (point-max))))
+    (when lines
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let* ((lines (split-string lines "-"))
+	     (lbeg (string-to-number (car lines)))
+	     (lend (string-to-number (cadr lines)))
+	     (beg (if (zerop lbeg) (point-min)
+		    (goto-char (point-min))
+		    (forward-line (1- lbeg))
+		    (point)))
+	     (end (if (zerop lend) (point-max)
+		    (goto-char beg)
+		    (forward-line (1-  lend))
+		    (point))))
+	(narrow-to-region beg end)))
+    (let ((end (point-max)))
+      (goto-char (point-min))
+      (widen)
+      (let ((start-line (line-number-at-pos)))
+	(format "%d-%d"
+		start-line
+		(save-excursion
+		  (+ start-line
+		     (let ((counter 0))
+		       (while (< (point) end) (incf counter) (forward-line))
+		       counter))))))))
+
 (defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
@@ -3448,6 +3531,20 @@ with footnotes is included in a document."
     (skip-chars-backward " \r\t\n")
     (forward-line)
     (delete-region (point) (point-max))
+    ;; Remove property-drawers after drawers.
+    (when (or ind minlevel)
+      (unless (eq major-mode 'org-mode)
+    	(let ((org-inhibit-startup t)) (org-mode)))
+      (goto-char (point-min))
+      (when (looking-at org-drawer-regexp)
+	(goto-char (match-end 0))
+	(search-forward-regexp org-drawer-regexp)
+	(forward-line 1)
+	(beginning-of-line))
+      (when (looking-at org-property-drawer-re)
+	(delete-region (match-beginning 0) (match-end 0))
+	(beginning-of-line))
+      (delete-region (point) (save-excursion (and (org-skip-whitespace) (point)))))
     ;; If IND is set, preserve indentation of include keyword until
     ;; the first headline encountered.
     (when ind
diff --git a/testing/examples/include.org b/testing/examples/include.org
index 186facb..c04c942 100644
--- a/testing/examples/include.org
+++ b/testing/examples/include.org
@@ -8,3 +8,28 @@ Small Org file with an include keyword.
 
 * Heading
 body
+
+* Another heading
+:PROPERTIES:
+:CUSTOM_ID: ah
+:END:
+1
+2
+3
+
+* A headline with a table
+:PROPERTIES:
+:CUSTOM_ID: ht
+:END:
+#+CAPTION: a table
+#+NAME: tbl
+| 1 |
+
+* drawer-headline
+:LOGBOOK:
+drawer
+:END:
+:PROPERTIES:
+:CUSTOM_ID: dh
+:END:
+content
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 4af3510..0b7df02 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -918,7 +918,64 @@ Footnotes[fn:1], [fn:test] and [fn:inline:anonymous footnote].
 		(org-export-expand-include-keyword)
 		(org-element-map (org-element-parse-buffer)
 		    'footnote-reference
-		  (lambda (ref) (org-element-property :label ref))))))))))))
+		  (lambda (ref) (org-element-property :label ref)))))))))))
+  ;; If only-contents is non-nil only include contents of element
+  (should
+   (equal
+    "body\n"
+    (org-test-with-temp-text
+     (concat
+      (format "#+INCLUDE: \"%s/examples/include.org::*Heading\" " org-test-dir)
+      ":only-contents t")
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Headings can be included via CUSTOM_ID
+  (should
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir)
+     (org-export-expand-include-keyword)
+     (goto-char (point-min))
+     (looking-at "* Another heading")))
+  ;; Named objects can be included
+  (should
+   (equal
+    "| 1 |\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Including non-existing elements should result in an error
+  (should-error
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir)
+     (org-export-expand-include-keyword)))
+  ;; Lines work relatively to an included element
+  (should
+   (equal
+    "2\n3\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t :lines \"2-3\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped from headlines
+  (should
+   (equal
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped, drawers should not be
+  (should
+   (equal
+    ":LOGBOOK:\ndrawer\n:END:\ncontent\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string)))))
 
 (ert-deftest test-org-export/expand-macro ()
   "Test macro expansion in an Org buffer."
-- 
2.1.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-09-30 21:48                 ` Rasmus
@ 2014-10-01 20:03                   ` Nicolas Goaziou
  2014-10-01 21:27                     ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Nicolas Goaziou @ 2014-10-01 20:03 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> Changes are one sentence in the documentations, casing, and I changed
> the regexp so that :only-contents is valid (it's nil).

Thank you.

It isn't very important, but you forgot full stops at the end of
comments in the test file.

> Is it better now?

I think so.

> I want to discuss one more important potential issue before having the
> patch applied.  Currently, location is ignored if the included part is
> not an env (line 3381) and not a block (3392).  I'm not sure this is
> right.  I could do one of the following:
>
>    1. Nothing (current state)
>    2. Throw an error if location and env or block are combined.
>    3. Try to use location even if block is set.  Recall, though, that
>       location is resolved using org-mode. 
>    4. Let location be a general regexp if env or block is non-nil.
>       But then we are breaking with the org file-link idea.
>    5. Make location work for org files when env or block, otherwise
>       throw an error.
>
> WDYT?

I think option 1 is perfect. If a block with org contents is needed, one
can always do

  #+begin_center
  #+include: "file.org::*headline"
  #+end_center

Block and environments are really meant for literal insertion, where
locations do not apply.

> Less important.  Should the <I "speedkey" (I don't know if that's the
> right term) prompt for a location, or change the cursor position to
> after the filename?

The "right term" is structure template. Since those are configurable,
I think the default, simple, behaviour is preferable.

> +		 (only-contents
> +		  (and (string-match ":only-contents *?\\([^: \r\t\n]\\S-*\\)?" value)

Is the shy *? necessary?


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-01 20:03                   ` Nicolas Goaziou
@ 2014-10-01 21:27                     ` Rasmus
  2014-10-02  7:29                       ` Xavier Garrido
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-10-01 21:27 UTC (permalink / raw)
  To: mail, emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1558 bytes --]

Hi,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> It isn't very important, but you forgot full stops at the end of
> comments in the test file.

Fixed. 

>> I want to discuss one more important potential issue before having the
>> patch applied.  Currently, location is ignored if the included part is
>> not an env (line 3381) and not a block (3392).  I'm not sure this is
>> right.  I could do one of the following:
>>
>>    1. Nothing (current state)
>>    2. Throw an error if location and env or block are combined.
>>    3. Try to use location even if block is set.  Recall, though, that
>>       location is resolved using org-mode. 
>>    4. Let location be a general regexp if env or block is non-nil.
>>       But then we are breaking with the org file-link idea.
>>    5. Make location work for org files when env or block, otherwise
>>       throw an error.
>>
>> WDYT?
>
> I think option 1 is perfect. If a block with org contents is needed, one
> can always do
>
>   #+begin_center
>   #+include: "file.org::*headline"
>   #+end_center
>
> Block and environments are really meant for literal insertion, where
> locations do not apply.

Great!  Less work.  

We can always tune it later as necessary.

>> +		 (only-contents
>> + (and (string-match ":only-contents *?\\([^: \r\t\n]\\S-*\\)?"
>> value)
>
> Is the shy *? necessary?

No!  Only the * is necessary to catch an implicit nil.  Thanks.

Feel free to commit the attached patch if there's nothing else.

—Rasmus 

-- 
Hooray!

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 12764 bytes --]

From 35fdda4c4fe160bee2963687d368104febb2b627 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sun, 28 Sep 2014 21:05:17 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files)
org.texi (Include files): Updated.
* testing/examples/include.org: New examples.
* test-ox.el (test-org-export/expand-include): New tests.
---
 doc/org.texi                 |  17 +++++++
 doc/orgguide.texi            |   9 +++-
 lisp/org.el                  |   9 ++--
 lisp/ox.el                   | 117 +++++++++++++++++++++++++++++++++++++++----
 testing/examples/include.org |  25 +++++++++
 testing/lisp/test-ox.el      |  59 +++++++++++++++++++++-
 6 files changed, 220 insertions(+), 16 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..296e289 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,23 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link, see @ref{search option in file links}, to
+extract an object as matched by @code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named
+elements.}.  If the @code{:only-contents} property is non-nil, only the
+contents of the requested element will be included, omitting properties
+drawer and planning-line if present.  The @code{:lines} keyword
+operates locally with respect to the requested element.  Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents t
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include named element.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..4feeaca 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpreted as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :only-contents t
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index 9815eb4..0910296 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20524,9 +20524,12 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string
+			    (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index 90c623e..06eb8eb 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3325,13 +3325,25 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
+			    (prog1
+				(save-match-data
+				  (let ((matched (match-string 1 value)))
+				    (when (string-match "\\(::\\(.*?\\)\\)\"?\\'" matched)
+				      (setq location (match-string 2 matched))
+				      (setq matched
+					    (replace-match "" nil nil  matched 1)))
+				    (expand-file-name
+				     (org-remove-double-quotes
+				      matched)
+				     dir)))
 			      (setq value (replace-match "" nil nil value)))))
+		 (only-contents
+		  (and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?" value)
+		       (prog1 (org-not-nil (match-string 1 value))
+			 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3391,17 +3403,88 @@ paths."
 	       (t
 		(insert
 		 (with-temp-buffer
-		   (let ((org-inhibit-startup t)) (org-mode))
-		   (insert
-		    (org-export--prepare-file-contents
-		     file lines ind minlevel
-		     (or (gethash file file-prefix)
-			 (puthash file (incf current-prefix) file-prefix))))
+		   (let ((org-inhibit-startup t)
+			 (lines
+			  (if location
+			      (org-export--inclusion-absolute-lines
+			       file  location only-contents lines)
+			    lines)))
+		     (org-mode)
+		     (insert
+		      (org-export--prepare-file-contents
+		       file lines ind minlevel
+		       (or (gethash file file-prefix)
+			   (puthash file (incf current-prefix) file-prefix)))))
 		   (org-export-expand-include-keyword
 		    (cons (list file lines) included)
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
+(defun org-export--inclusion-absolute-lines (file location only-contents lines)
+  "Resolve absolute lines for an included file with file-link.
+
+FILE is string file-name of the file to include.  LOCATION is a
+string name within FILE to be included (located via
+`org-link-search').  If ONLY-CONTENTS is non-nil only the
+contents of the named element will be included, as determined
+Org-Element.  If LINES is non-nil only those lines are included.
+
+Return a string of lines to be included in the format expected by
+`org-export--prepare-file-contents'."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (unless (eq major-mode 'org-mode)
+      (let ((org-inhibit-startup t)) (org-mode)))
+    (condition-case err
+	;; Enforce consistent search.
+	(let ((org-link-search-must-match-exact-headline t))
+	  (org-link-search location))
+      (error
+       (error (format "%s for %s::%s" (error-message-string err) file location))))
+    (let* ((element (org-element-at-point))
+	   (contents-begin
+	    (and only-contents (org-element-property :contents-begin element))))
+      (narrow-to-region
+       (or contents-begin (org-element-property :begin element))
+       (org-element-property (if contents-begin :contents-end :end) element))
+      (when (and only-contents
+		 (memq (org-element-type element) '(headline inlinetask)))
+	;; Skip planning line and property-drawer.  If a normal drawer
+	;; precedes a property-drawer both will be included.
+	;; Remaining property-drawers are removed as needed in
+	;; `org-export--prepare-file-contents'.
+	(goto-char (point-min))
+	(when (org-looking-at-p org-planning-line-re) (forward-line))
+	(when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
+	(unless (bolp) (forward-line))
+	(narrow-to-region (point) (point-max))))
+    (when lines
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let* ((lines (split-string lines "-"))
+	     (lbeg (string-to-number (car lines)))
+	     (lend (string-to-number (cadr lines)))
+	     (beg (if (zerop lbeg) (point-min)
+		    (goto-char (point-min))
+		    (forward-line (1- lbeg))
+		    (point)))
+	     (end (if (zerop lend) (point-max)
+		    (goto-char beg)
+		    (forward-line (1-  lend))
+		    (point))))
+	(narrow-to-region beg end)))
+    (let ((end (point-max)))
+      (goto-char (point-min))
+      (widen)
+      (let ((start-line (line-number-at-pos)))
+	(format "%d-%d"
+		start-line
+		(save-excursion
+		  (+ start-line
+		     (let ((counter 0))
+		       (while (< (point) end) (incf counter) (forward-line))
+		       counter))))))))
+
 (defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
@@ -3448,6 +3531,20 @@ with footnotes is included in a document."
     (skip-chars-backward " \r\t\n")
     (forward-line)
     (delete-region (point) (point-max))
+    ;; Remove property-drawers after drawers.
+    (when (or ind minlevel)
+      (unless (eq major-mode 'org-mode)
+    	(let ((org-inhibit-startup t)) (org-mode)))
+      (goto-char (point-min))
+      (when (looking-at org-drawer-regexp)
+	(goto-char (match-end 0))
+	(search-forward-regexp org-drawer-regexp)
+	(forward-line 1)
+	(beginning-of-line))
+      (when (looking-at org-property-drawer-re)
+	(delete-region (match-beginning 0) (match-end 0))
+	(beginning-of-line))
+      (delete-region (point) (save-excursion (and (org-skip-whitespace) (point)))))
     ;; If IND is set, preserve indentation of include keyword until
     ;; the first headline encountered.
     (when ind
diff --git a/testing/examples/include.org b/testing/examples/include.org
index 186facb..c04c942 100644
--- a/testing/examples/include.org
+++ b/testing/examples/include.org
@@ -8,3 +8,28 @@ Small Org file with an include keyword.
 
 * Heading
 body
+
+* Another heading
+:PROPERTIES:
+:CUSTOM_ID: ah
+:END:
+1
+2
+3
+
+* A headline with a table
+:PROPERTIES:
+:CUSTOM_ID: ht
+:END:
+#+CAPTION: a table
+#+NAME: tbl
+| 1 |
+
+* drawer-headline
+:LOGBOOK:
+drawer
+:END:
+:PROPERTIES:
+:CUSTOM_ID: dh
+:END:
+content
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 4af3510..915a5a6 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -918,7 +918,64 @@ Footnotes[fn:1], [fn:test] and [fn:inline:anonymous footnote].
 		(org-export-expand-include-keyword)
 		(org-element-map (org-element-parse-buffer)
 		    'footnote-reference
-		  (lambda (ref) (org-element-property :label ref))))))))))))
+		  (lambda (ref) (org-element-property :label ref)))))))))))
+  ;; If only-contents is non-nil only include contents of element.
+  (should
+   (equal
+    "body\n"
+    (org-test-with-temp-text
+     (concat
+      (format "#+INCLUDE: \"%s/examples/include.org::*Heading\" " org-test-dir)
+      ":only-contents t")
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Headings can be included via CUSTOM_ID.
+  (should
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir)
+     (org-export-expand-include-keyword)
+     (goto-char (point-min))
+     (looking-at "* Another heading")))
+  ;; Named objects can be included.
+  (should
+   (equal
+    "| 1 |\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Including non-existing elements should result in an error.
+  (should-error
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir)
+     (org-export-expand-include-keyword)))
+  ;; Lines work relatively to an included element.
+  (should
+   (equal
+    "2\n3\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t :lines \"2-3\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped from headlines.
+  (should
+   (equal
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped, drawers should not be.
+  (should
+   (equal
+    ":LOGBOOK:\ndrawer\n:END:\ncontent\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string)))))
 
 (ert-deftest test-org-export/expand-macro ()
   "Test macro expansion in an Org buffer."
-- 
2.1.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-01 21:27                     ` Rasmus
@ 2014-10-02  7:29                       ` Xavier Garrido
  2014-10-02  8:55                         ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Xavier Garrido @ 2014-10-02  7:29 UTC (permalink / raw)
  To: Rasmus, mail; +Cc: emacs-orgmode

Hi,

Following the discussion here 
http://article.gmane.org/gmane.emacs.orgmode/91322/match=improved+way, I 
have quickly tested the patch and it perfectly fits my needs. So thanks 
again.

Just one remark : there is an undefined @ref tag in org.texi (line 
10011) file that should referred to "search option in file link". 
Compilation fails due to error in documentation generation.

Cheers,
Xavier

Le 01/10/2014 23:27, Rasmus a écrit :
> Hi,
>
> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>
>> It isn't very important, but you forgot full stops at the end of
>> comments in the test file.
>
> Fixed.
>
>>> I want to discuss one more important potential issue before having the
>>> patch applied.  Currently, location is ignored if the included part is
>>> not an env (line 3381) and not a block (3392).  I'm not sure this is
>>> right.  I could do one of the following:
>>>
>>>     1. Nothing (current state)
>>>     2. Throw an error if location and env or block are combined.
>>>     3. Try to use location even if block is set.  Recall, though, that
>>>        location is resolved using org-mode.
>>>     4. Let location be a general regexp if env or block is non-nil.
>>>        But then we are breaking with the org file-link idea.
>>>     5. Make location work for org files when env or block, otherwise
>>>        throw an error.
>>>
>>> WDYT?
>>
>> I think option 1 is perfect. If a block with org contents is needed, one
>> can always do
>>
>>    #+begin_center
>>    #+include: "file.org::*headline"
>>    #+end_center
>>
>> Block and environments are really meant for literal insertion, where
>> locations do not apply.
>
> Great!  Less work.
>
> We can always tune it later as necessary.
>
>>> +		 (only-contents
>>> + (and (string-match ":only-contents *?\\([^: \r\t\n]\\S-*\\)?"
>>> value)
>>
>> Is the shy *? necessary?
>
> No!  Only the * is necessary to catch an implicit nil.  Thanks.
>
> Feel free to commit the attached patch if there's nothing else.
>
> —Rasmus
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02  7:29                       ` Xavier Garrido
@ 2014-10-02  8:55                         ` Rasmus
  2014-10-02 16:30                           ` Aaron Ecay
  2014-10-02 16:53                           ` Nicolas Goaziou
  0 siblings, 2 replies; 21+ messages in thread
From: Rasmus @ 2014-10-02  8:55 UTC (permalink / raw)
  To: xavier.garrido; +Cc: emacs-orgmode, mail

[-- Attachment #1: Type: text/plain, Size: 719 bytes --]

Xavier Garrido <xavier.garrido@gmail.com> writes:

> Following the discussion here
> http://article.gmane.org/gmane.emacs.orgmode/91322/match=improved+way,
> I have quickly tested the patch and it perfectly fits my needs. So
> thanks again.

I'm happy that it works well for you.

> Just one remark : there is an undefined @ref tag in org.texi (line
> 10011) file that should referred to "search option in file
> link". Compilation fails due to error in documentation generation.

Shoot.  Fixed in attached (tested with texi2pdf).

—Rasmus

PS: How can I get commit access?  I'm starting to feel like the Nick K
of the org-list.

-- 
Sådan en god dansk lagereddike kan man slet ikke bruge mere

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Allow-file-links-with-INCLUDE-keyword.patch --]
[-- Type: text/x-diff, Size: 12745 bytes --]

From 17a647c28f07a505670a7e0d0cc341d700f7bb87 Mon Sep 17 00:00:00 2001
From: Rasmus <rasmus@gmx.us>
Date: Sun, 28 Sep 2014 21:05:17 +0200
Subject: [PATCH] ox: Allow file-links with #+INCLUDE-keyword

* org.el (org-edit-special): Handle file-links for INCLUDE.
* ox.el (org-export--prepare-file-contents): Handle links and
add option no-heading.
* ox.el (org-export-expand-include-keyword): Resolve headline
links and add option :only-contents.
* orgguide.texi (Include files)
org.texi (Include files): Updated.
* testing/examples/include.org: New examples.
* test-ox.el (test-org-export/expand-include): New tests.
---
 doc/org.texi                 |  17 +++++++
 doc/orgguide.texi            |   9 +++-
 lisp/org.el                  |   9 ++--
 lisp/ox.el                   | 117 +++++++++++++++++++++++++++++++++++++++----
 testing/examples/include.org |  25 +++++++++
 testing/lisp/test-ox.el      |  59 +++++++++++++++++++++-
 6 files changed, 220 insertions(+), 16 deletions(-)

diff --git a/doc/org.texi b/doc/org.texi
index 7d98d51..537b21b 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -10008,6 +10008,23 @@ to use the obvious defaults.
 #+INCLUDE: "~/.emacs" :lines "10-"    @r{Include lines from 10 to EOF}
 @end example
 
+Finally, you may use a file-link to extract an object as matched by
+@code{org-link-search}@footnote{Note that
+@code{org-link-search-must-match-exact-headline} is locally bound to non-nil.
+Therefore, @code{org-link-search} only matches headlines and named elements.}
+(@pxref{Search options}).  If the @code{:only-contents} property is non-nil,
+only the contents of the requested element will be included, omitting
+properties drawer and planning-line if present.  The @code{:lines} keyword
+operates locally with respect to the requested element.  Some examples:
+
+@example
+#+INCLUDE: "./paper.org::#theory" :only-contents t
+   @r{Include the body of the heading with the custom id @code{theory}}
+#+INCLUDE: "./paper.org::mytable"  @r{Include named element.}
+#+INCLUDE: "./paper.org::*conclusion" :lines 1-20
+   @r{Include the first 20 lines of the headline named conclusion.}
+@end example
+
 @table @kbd
 @kindex C-c '
 @item C-c '
diff --git a/doc/orgguide.texi b/doc/orgguide.texi
index ca8e052..4feeaca 100644
--- a/doc/orgguide.texi
+++ b/doc/orgguide.texi
@@ -2264,8 +2264,13 @@ include your @file{.emacs} file, you could use:
 The optional second and third parameter are the markup (i.e., @samp{example}
 or @samp{src}), and, if the markup is @samp{src}, the language for formatting
 the contents.  The markup is optional, if it is not given, the text will be
-assumed to be in Org mode format and will be processed normally. @kbd{C-c '}
-will visit the included file.
+assumed to be in Org mode format and will be processed normally.  File-links
+will be interpreted as well:
+@smallexample
+#+INCLUDE: "./otherfile.org::#my_custom_id" :only-contents t
+@end smallexample
+@noindent
+@kbd{C-c '} will visit the included file.
 
 @node Embedded @LaTeX{},  , Include files, Markup
 @section Embedded @LaTeX{}
diff --git a/lisp/org.el b/lisp/org.el
index 9a243b5..9228ba0 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20525,9 +20525,12 @@ Otherwise, return a user error."
 		       session params))))))
       (keyword
        (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
-           (find-file-other-window
-            (org-remove-double-quotes
-             (car (org-split-string (org-element-property :value element)))))
+           (org-open-link-from-string
+	    (format "[[%s]]"
+		    (expand-file-name
+		     (org-remove-double-quotes
+		      (car (org-split-string
+			    (org-element-property :value element)))))))
          (user-error "No special environment to edit here")))
       (table
        (if (eq (org-element-property :type element) 'table.el)
diff --git a/lisp/ox.el b/lisp/ox.el
index 90c623e..bc3792d 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -3325,13 +3325,25 @@ paths."
 	  ;; Extract arguments from keyword's value.
 	  (let* ((value (org-element-property :value element))
 		 (ind (org-get-indentation))
+		 location
 		 (file (and (string-match
 			     "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
-			    (prog1 (expand-file-name
-				    (org-remove-double-quotes
-				     (match-string 1 value))
-				    dir)
+			    (prog1
+				(save-match-data
+				  (let ((matched (match-string 1 value)))
+				    (when (string-match "\\(::\\(.*?\\)\\)\"?\\'" matched)
+				      (setq location (match-string 2 matched))
+				      (setq matched
+					    (replace-match "" nil nil  matched 1)))
+				    (expand-file-name
+				     (org-remove-double-quotes
+				      matched)
+				     dir)))
 			      (setq value (replace-match "" nil nil value)))))
+		 (only-contents
+		  (and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?" value)
+		       (prog1 (org-not-nil (match-string 1 value))
+			 (setq value (replace-match "" nil nil value)))))
 		 (lines
 		  (and (string-match
 			":lines +\"\\(\\(?:[0-9]+\\)?-\\(?:[0-9]+\\)?\\)\""
@@ -3391,17 +3403,88 @@ paths."
 	       (t
 		(insert
 		 (with-temp-buffer
-		   (let ((org-inhibit-startup t)) (org-mode))
-		   (insert
-		    (org-export--prepare-file-contents
-		     file lines ind minlevel
-		     (or (gethash file file-prefix)
-			 (puthash file (incf current-prefix) file-prefix))))
+		   (let ((org-inhibit-startup t)
+			 (lines
+			  (if location
+			      (org-export--inclusion-absolute-lines
+			       file  location only-contents lines)
+			    lines)))
+		     (org-mode)
+		     (insert
+		      (org-export--prepare-file-contents
+		       file lines ind minlevel
+		       (or (gethash file file-prefix)
+			   (puthash file (incf current-prefix) file-prefix)))))
 		   (org-export-expand-include-keyword
 		    (cons (list file lines) included)
 		    (file-name-directory file))
 		   (buffer-string)))))))))))))
 
+(defun org-export--inclusion-absolute-lines (file location only-contents lines)
+  "Resolve absolute lines for an included file with file-link.
+
+FILE is string file-name of the file to include.  LOCATION is a
+string name within FILE to be included (located via
+`org-link-search').  If ONLY-CONTENTS is non-nil only the
+contents of the named element will be included, as determined
+Org-Element.  If LINES is non-nil only those lines are included.
+
+Return a string of lines to be included in the format expected by
+`org-export--prepare-file-contents'."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (unless (eq major-mode 'org-mode)
+      (let ((org-inhibit-startup t)) (org-mode)))
+    (condition-case err
+	;; Enforce consistent search.
+	(let ((org-link-search-must-match-exact-headline t))
+	  (org-link-search location))
+      (error
+       (error (format "%s for %s::%s" (error-message-string err) file location))))
+    (let* ((element (org-element-at-point))
+	   (contents-begin
+	    (and only-contents (org-element-property :contents-begin element))))
+      (narrow-to-region
+       (or contents-begin (org-element-property :begin element))
+       (org-element-property (if contents-begin :contents-end :end) element))
+      (when (and only-contents
+		 (memq (org-element-type element) '(headline inlinetask)))
+	;; Skip planning line and property-drawer.  If a normal drawer
+	;; precedes a property-drawer both will be included.
+	;; Remaining property-drawers are removed as needed in
+	;; `org-export--prepare-file-contents'.
+	(goto-char (point-min))
+	(when (org-looking-at-p org-planning-line-re) (forward-line))
+	(when (looking-at org-property-drawer-re) (goto-char (match-end 0)))
+	(unless (bolp) (forward-line))
+	(narrow-to-region (point) (point-max))))
+    (when lines
+      (org-skip-whitespace)
+      (beginning-of-line)
+      (let* ((lines (split-string lines "-"))
+	     (lbeg (string-to-number (car lines)))
+	     (lend (string-to-number (cadr lines)))
+	     (beg (if (zerop lbeg) (point-min)
+		    (goto-char (point-min))
+		    (forward-line (1- lbeg))
+		    (point)))
+	     (end (if (zerop lend) (point-max)
+		    (goto-char beg)
+		    (forward-line (1-  lend))
+		    (point))))
+	(narrow-to-region beg end)))
+    (let ((end (point-max)))
+      (goto-char (point-min))
+      (widen)
+      (let ((start-line (line-number-at-pos)))
+	(format "%d-%d"
+		start-line
+		(save-excursion
+		  (+ start-line
+		     (let ((counter 0))
+		       (while (< (point) end) (incf counter) (forward-line))
+		       counter))))))))
+
 (defun org-export--prepare-file-contents (file &optional lines ind minlevel id)
   "Prepare the contents of FILE for inclusion and return them as a string.
 
@@ -3448,6 +3531,20 @@ with footnotes is included in a document."
     (skip-chars-backward " \r\t\n")
     (forward-line)
     (delete-region (point) (point-max))
+    ;; Remove property-drawers after drawers.
+    (when (or ind minlevel)
+      (unless (eq major-mode 'org-mode)
+	(let ((org-inhibit-startup t)) (org-mode)))
+      (goto-char (point-min))
+      (when (looking-at org-drawer-regexp)
+	(goto-char (match-end 0))
+	(search-forward-regexp org-drawer-regexp)
+	(forward-line 1)
+	(beginning-of-line))
+      (when (looking-at org-property-drawer-re)
+	(delete-region (match-beginning 0) (match-end 0))
+	(beginning-of-line))
+      (delete-region (point) (save-excursion (and (org-skip-whitespace) (point)))))
     ;; If IND is set, preserve indentation of include keyword until
     ;; the first headline encountered.
     (when ind
diff --git a/testing/examples/include.org b/testing/examples/include.org
index 186facb..c04c942 100644
--- a/testing/examples/include.org
+++ b/testing/examples/include.org
@@ -8,3 +8,28 @@ Small Org file with an include keyword.
 
 * Heading
 body
+
+* Another heading
+:PROPERTIES:
+:CUSTOM_ID: ah
+:END:
+1
+2
+3
+
+* A headline with a table
+:PROPERTIES:
+:CUSTOM_ID: ht
+:END:
+#+CAPTION: a table
+#+NAME: tbl
+| 1 |
+
+* drawer-headline
+:LOGBOOK:
+drawer
+:END:
+:PROPERTIES:
+:CUSTOM_ID: dh
+:END:
+content
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 4af3510..915a5a6 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -918,7 +918,64 @@ Footnotes[fn:1], [fn:test] and [fn:inline:anonymous footnote].
 		(org-export-expand-include-keyword)
 		(org-element-map (org-element-parse-buffer)
 		    'footnote-reference
-		  (lambda (ref) (org-element-property :label ref))))))))))))
+		  (lambda (ref) (org-element-property :label ref)))))))))))
+  ;; If only-contents is non-nil only include contents of element.
+  (should
+   (equal
+    "body\n"
+    (org-test-with-temp-text
+     (concat
+      (format "#+INCLUDE: \"%s/examples/include.org::*Heading\" " org-test-dir)
+      ":only-contents t")
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Headings can be included via CUSTOM_ID.
+  (should
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir)
+     (org-export-expand-include-keyword)
+     (goto-char (point-min))
+     (looking-at "* Another heading")))
+  ;; Named objects can be included.
+  (should
+   (equal
+    "| 1 |\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Including non-existing elements should result in an error.
+  (should-error
+   (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir)
+     (org-export-expand-include-keyword)))
+  ;; Lines work relatively to an included element.
+  (should
+   (equal
+    "2\n3\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t :lines \"2-3\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped from headlines.
+  (should
+   (equal
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::tbl\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
+  ;; Properties should be dropped, drawers should not be.
+  (should
+   (equal
+    ":LOGBOOK:\ndrawer\n:END:\ncontent\n"
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string)))))
 
 (ert-deftest test-org-export/expand-macro ()
   "Test macro expansion in an Org buffer."
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02  8:55                         ` Rasmus
@ 2014-10-02 16:30                           ` Aaron Ecay
  2014-10-02 16:53                           ` Nicolas Goaziou
  1 sibling, 0 replies; 21+ messages in thread
From: Aaron Ecay @ 2014-10-02 16:30 UTC (permalink / raw)
  To: Rasmus, xavier.garrido; +Cc: emacs-orgmode, mail

Hi Rasmus,

Thanks for all the work on this patch – I haven’t had anything to say
about it really, but it looks like a really useful feature.

2014ko urriak 2an, Rasmus-ek idatzi zuen:

[...]


> PS: How can I get commit access?  I'm starting to feel like the Nick K
> of the org-list.

TLDR: email your public ssh key to someone.  Details:

http://orgmode.org/worg/org-contribute.html#devs

-- 
Aaron Ecay

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02  8:55                         ` Rasmus
  2014-10-02 16:30                           ` Aaron Ecay
@ 2014-10-02 16:53                           ` Nicolas Goaziou
  2014-10-02 17:47                             ` Rasmus
  1 sibling, 1 reply; 21+ messages in thread
From: Nicolas Goaziou @ 2014-10-02 16:53 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode, xavier.garrido

Hello,

Rasmus <rasmus@gmx.us> writes:

> Shoot.  Fixed in attached (tested with texi2pdf).

Applied. Thank you for this patch.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02 16:53                           ` Nicolas Goaziou
@ 2014-10-02 17:47                             ` Rasmus
  2014-10-02 19:11                               ` Achim Gratz
  0 siblings, 1 reply; 21+ messages in thread
From: Rasmus @ 2014-10-02 17:47 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 275 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Hello,
>
> Rasmus <rasmus@gmx.us> writes:
>
>> Shoot.  Fixed in attached (tested with texi2pdf).
>
> Applied. Thank you for this patch.

Please also apply this patch to make "make test" pass.

—Rasmus

-- 
Don't panic!!!

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-test-ox.el-Fix-test-error-from-986037a.patch --]
[-- Type: text/x-diff, Size: 2287 bytes --]

From 04d90414a848a55ff602cfbc0b5f300299830732 Mon Sep 17 00:00:00 2001
From: rasmus <rasmus@pank.eu>
Date: Thu, 2 Oct 2014 19:41:31 +0200
Subject: [PATCH] test-ox.el: Fix test-error from 986037a.

* test-ox.el (test-org-export/expand-include): Updated tests.
---
 testing/lisp/test-ox.el | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index 915a5a6..2642f4a 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -804,18 +804,18 @@ text
    (org-test-with-temp-text "#+INCLUDE: dummy.org"
      (org-export-expand-include-keyword)))
   ;; Full insertion with recursive inclusion.
-  (org-test-with-temp-text
-      (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir)
-    (org-export-expand-include-keyword)
-    (should (equal (buffer-string)
-		   "Small Org file with an include keyword.
-
-#+BEGIN_SRC emacs-lisp :exports results\n(+ 2 1)\n#+END_SRC
-
-Success!
-
-* Heading
-body\n")))
+  (should
+   (equal
+    (with-temp-buffer
+      (insert-file
+       (expand-file-name "examples/include.org" org-test-dir))
+      (replace-regexp-in-string
+       (regexp-quote "#+INCLUDE: \"include2.org\"")
+       "Success!" (buffer-string)))
+    (org-test-with-temp-text
+	(format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir)
+      (org-export-expand-include-keyword)
+      (buffer-string))))
   ;; Localized insertion.
   (org-test-with-temp-text
       (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\""
@@ -829,7 +829,7 @@ body\n")))
     "* Top heading\n** Heading\nbody\n"
     (org-test-with-temp-text
 	(format
-	 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\""
+	 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-11\""
 	 org-test-dir)
       (org-export-expand-include-keyword)
       (buffer-string))))
@@ -838,7 +838,7 @@ body\n")))
     "* Top heading\n* Heading\nbody\n"
     (org-test-with-temp-text
 	(format
-	 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\" :minlevel 1"
+	 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-11\" :minlevel 1"
 	 org-test-dir)
       (org-export-expand-include-keyword)
       (buffer-string))))
-- 
2.1.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02 17:47                             ` Rasmus
@ 2014-10-02 19:11                               ` Achim Gratz
  2014-10-02 20:58                                 ` Rasmus
  0 siblings, 1 reply; 21+ messages in thread
From: Achim Gratz @ 2014-10-02 19:11 UTC (permalink / raw)
  To: emacs-orgmode

Rasmus writes:
> Please also apply this patch to make "make test" pass.

Applied.  Please always run all tests before committing.


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

Factory and User Sound Singles for Waldorf Blofeld:
http://Synth.Stromeko.net/Downloads.html#WaldorfSounds

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [patch, ox] #+INCLUDE resolves links
  2014-10-02 19:11                               ` Achim Gratz
@ 2014-10-02 20:58                                 ` Rasmus
  0 siblings, 0 replies; 21+ messages in thread
From: Rasmus @ 2014-10-02 20:58 UTC (permalink / raw)
  To: emacs-orgmode

Achim Gratz <Stromeko@nexgo.de> writes:

> Rasmus writes:
>> Please also apply this patch to make "make test" pass.
>
> Applied.  Please always run all tests before committing.

Thanks.  I only learned about it now from Aaron's link.

-- 
Together we will make the possible totalllly impossible!

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-10-02 20:58 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-21  0:51 [patch, ox] #+INCLUDE resolves links Rasmus
2014-09-21 11:46 ` Rasmus
2014-09-21 13:53   ` Nicolas Goaziou
2014-09-21 14:46     ` Rasmus
2014-09-21 19:51       ` Nicolas Goaziou
2014-09-23 23:25     ` Rasmus
2014-09-24 21:22       ` Nicolas Goaziou
2014-09-28 19:32         ` Rasmus
2014-09-30  8:07           ` Nicolas Goaziou
2014-09-30 10:18             ` Rasmus
2014-09-30 14:29               ` Nicolas Goaziou
2014-09-30 21:48                 ` Rasmus
2014-10-01 20:03                   ` Nicolas Goaziou
2014-10-01 21:27                     ` Rasmus
2014-10-02  7:29                       ` Xavier Garrido
2014-10-02  8:55                         ` Rasmus
2014-10-02 16:30                           ` Aaron Ecay
2014-10-02 16:53                           ` Nicolas Goaziou
2014-10-02 17:47                             ` Rasmus
2014-10-02 19:11                               ` Achim Gratz
2014-10-02 20:58                                 ` Rasmus

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).