emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Aaron Ecay <aaronecay@gmail.com>
To: emacs-orgmode@gnu.org
Subject: [PATCH 2/3] Introduce machinery to ox.el for concordance generation
Date: Sun, 31 Mar 2013 23:15:00 -0400	[thread overview]
Message-ID: <1364786101-16603-3-git-send-email-aaronecay@gmail.com> (raw)
In-Reply-To: <1364786101-16603-1-git-send-email-aaronecay@gmail.com>

* lisp/ox.el (org-export-concordance): New buffer-local variable
(org-export-with-concordance): New user option
(org-export--concordance-propertize),
(org-export--concordance-propertize-pre),
(org-export--build-concordance),
(org-export--read-concordance): New functions
(org-export-data): Call org-export–concordance-propertize where
appropriate
(org-export-as),
(org-export-to-buffer),
(org-export-to-file): Handle concordance generation

The general idea is as follows:
1) Before copying the buffer for export, call org-export--propertize-pre
   to add line-number properties to the buffer
2) The parser sees these properties during export (previous commit)
3) org-export-data adds text properties to the strings it generates,
   indicating which lines they originated from (using function
   org-export--propertize)
4) These properties survive into the output buffer (because export no
   longer calls org-no-properties)
5) After export is finished, org-export-build-concordance walks the
   result buffer, calculating a concordance of source lines and output
   lines.
6) This value is stored in the org buffer’s org-export-concordance local
   variable
---
 lisp/ox.el | 151 +++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 121 insertions(+), 30 deletions(-)

diff --git a/lisp/ox.el b/lisp/ox.el
index ff6407b..e1c76bd 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -302,6 +302,13 @@ and its CDR is a list of export options.")
 This marker will be used with `C-u C-c C-e' to make sure export repetition
 uses the same subtree if the previous command was restricted to a subtree.")
 
+(defvar org-export-concordance nil
+  "The concordance resulting from the last export operation.
+
+The variable is always buffer-local, and only manipulated if
+`org-export-with-concordance' is set.")
+(make-variable-buffer-local 'org-export-concordance)
+
 \f
 ;;; User-configurable Variables
 ;;
@@ -351,6 +358,16 @@ e.g. \"c:t\"."
   :group 'org-export-general
   :type 'boolean)
 
+(defcustom org-export-with-concordance nil
+  "Non-nil means to generate a concordance.
+
+This is currently useful only for the LaTeX backend (and derived
+backends), which can use it to patch the SyncTeX file generated
+by LaTeX, so that it is possible to jump back and forth between
+the org file and resulting pdf."
+  :group 'org-export-general
+  :type 'boolean)
+
 (defcustom org-export-with-creator 'comment
   "Non-nil means the postamble should contain a creator sentence.
 
@@ -2040,6 +2057,61 @@ INFO is a plist containing export directives."
       (let ((transcoder (cdr (assq type (plist-get info :translate-alist)))))
 	(and (functionp transcoder) transcoder)))))
 
+(defun org-export--concordance-propertize (data string)
+  "Add line number text properties to STRING, based on DATA.
+
+This will allow the construction of a concordance from the
+completed string."
+  (let ((len (length string)))
+    (when (> len 1)
+      (put-text-property 0 1 'org-line-num
+			 (org-element-property :begin-line data)
+			 string)
+      (put-text-property (1- len) len 'org-line-num
+			 (org-element-property :end-line data)
+			 string)))
+  string)
+
+(defun org-export--concordance-propertize-pre ()
+  "Put line-number text properties on a buffer.
+
+Each line gets a org-line-num-pre property, which is its line
+number in the buffer before any export operations have changed
+the buffer."
+  ;; This is called from `org-export-as', which has issued
+  ;; `save-restriction'.
+  (widen)
+  (while (= 0 (forward-line 1))
+    (put-text-property (point) (point-at-eol) 'org-line-num-pre
+		       (line-number-at-pos))))
+
+(defun org-export--build-concordance ()
+  "Build a concordance, based on text props in an exported buffer."
+  (save-excursion
+    (let ((res '())
+	  next)
+      (goto-char (point-min))
+      (while (setq next (next-single-property-change (point) 'org-line-num))
+	(goto-char next)
+	(setq res (cons (cons (line-number-at-pos)
+			      (get-text-property (point) 'org-line-num))
+			res))
+	(forward-char 1))
+      (setq res (nreverse res))
+      (setq next res)
+      (while (cdr next)
+	(if (equal (caar next) (caadr next))
+	    (setcdr next (cddr next))
+	  (setq next (cdr next))))
+      res)))
+
+(defun org-export--read-concordance (concordance src-line)
+  "Get the original line number from CONCORDANCE for output line SRC-LINE."
+  (while (and (caadr concordance)
+	      (<= (caadr concordance) src-line))
+    (setq concordance (cdr concordance)))
+  (cdar concordance))
+
 (defun org-export-data (data info)
   "Convert DATA into current back-end format.
 
@@ -2056,11 +2128,16 @@ Return transcoded string."
 	       ((memq data (plist-get info :ignore-list)) nil)
 	       ;; Plain text.
 	       ((eq type 'plain-text)
-		(org-export-filter-apply-functions
-		 (plist-get info :filter-plain-text)
-		 (let ((transcoder (org-export-transcoder data info)))
-		   (if transcoder (funcall transcoder data info) data))
-		 info))
+		(let* ((transcoder (org-export-transcoder data info))
+		       (transcoded-string (if transcoder
+					      (funcall transcoder data info)
+					    data))
+		       (propertized-string (org-export--concordance-propertize
+					    data transcoded-string)))
+		  (org-export-filter-apply-functions
+		   (plist-get info :filter-plain-text)
+		   propertized-string
+		   info)))
 	       ;; Uninterpreted element/object: change it back to Org
 	       ;; syntax and export again resulting raw string.
 	       ((not (org-export--interpret-p data info))
@@ -2081,15 +2158,18 @@ Return transcoded string."
 		    (and (eq type 'headline)
 			 (eq (plist-get info :with-archived-trees) 'headline)
 			 (org-element-property :archivedp data)))
-		(let ((transcoder (org-export-transcoder data info)))
-		  (or (and (functionp transcoder)
-			   (funcall transcoder data nil info))
-		      ;; Export snippets never return a nil value so
-		      ;; that white spaces following them are never
-		      ;; ignored.
-		      (and (eq type 'export-snippet) ""))))
-	       ;; Element/Object with contents.
-	       (t
+		(let* ((transcoder (org-export-transcoder data info))
+                       (transcoded-string
+                        (or (and (functionp transcoder)
+                                 (funcall transcoder data nil info))
+                            ;; Export snippets never return a nil value so
+                            ;; that white spaces following them are never
+                            ;; ignored.
+                            (and (eq type 'export-snippet) ""))))
+                  (and transcoded-string
+                       (org-export--concordance-propertize data transcoded-string))))
+               ;; Element/Object with contents.
+               (t
 		(let ((transcoder (org-export-transcoder data info)))
 		  (when transcoder
 		    (let* ((greaterp (memq type org-element-greater-elements))
@@ -2118,11 +2198,13 @@ Return transcoded string."
 					  data)
 				      (memq (org-element-type parent)
 					    '(footnote-definition item))))))))
-			     "")))
-		      (funcall transcoder data
-			       (if (not greaterp) contents
-				 (org-element-normalize-string contents))
-			       info))))))))
+			     ""))
+			   (transcoded-string
+			    (funcall transcoder data
+				     (if (not greaterp) contents
+				       (org-element-normalize-string contents))
+				     info)))
+		      (org-export--concordance-propertize data transcoded-string))))))))
 	;; Final result will be memoized before being returned.
 	(puthash
 	 data
@@ -2893,6 +2975,8 @@ Return code as a string."
 	;; created, where include keywords, macros are expanded and
 	;; code blocks are evaluated.
 	(org-export-with-buffer-copy
+	 (when org-export-with-concordance
+	   (org-export--concordance-propertize-pre))
 	 ;; Run first hook with current back-end as argument.
 	 (run-hook-with-args 'org-export-before-processing-hook backend)
 	 (org-export-expand-include-keyword)
@@ -2953,15 +3037,12 @@ Return code as a string."
 			     (funcall inner-template body info)))
 		(template (cdr (assq 'template
 				     (plist-get info :translate-alist)))))
-	   ;; Remove all text properties since they cannot be
-	   ;; retrieved from an external process.  Finally call
-	   ;; final-output filter and return result.
-	   (org-no-properties
-	    (org-export-filter-apply-functions
-	     (plist-get info :filter-final-output)
-	     (if (or (not (functionp template)) body-only) full-body
-	       (funcall template full-body info))
-	     info))))))))
+	   ;; Call final-output filter and return result.
+	   (org-export-filter-apply-functions
+	    (plist-get info :filter-final-output)
+	    (if (or (not (functionp template)) body-only) full-body
+	      (funcall template full-body info))
+	    info)))))))
 
 ;;;###autoload
 (defun org-export-to-buffer
@@ -2980,11 +3061,16 @@ see.
 Depending on `org-export-copy-to-kill-ring', add buffer contents
 to kill ring.  Return buffer."
   (let ((out (org-export-as backend subtreep visible-only body-only ext-plist))
-	(buffer (get-buffer-create buffer)))
+	(buffer (get-buffer-create buffer))
+	concordance)
     (with-current-buffer buffer
       (erase-buffer)
       (insert out)
+      (when org-export-with-concordance
+	(setq concordance (org-export--build-concordance)))
       (goto-char (point-min)))
+    (when concordance
+      (setq org-export-concordance concordance))
     ;; Maybe add buffer contents to kill ring.
     (when (and (org-export--copy-to-kill-ring-p) (org-string-nw-p out))
       (org-kill-new out))
@@ -3009,11 +3095,16 @@ to kill ring.  Return output file's name."
   ;; we'd rather avoid needless transcoding of parse tree.
   (unless (file-writable-p file) (error "Output file not writable"))
   ;; Insert contents to a temporary buffer and write it to FILE.
-  (let ((out (org-export-as backend subtreep visible-only body-only ext-plist)))
+  (let ((out (org-export-as backend subtreep visible-only body-only ext-plist))
+	concordance)
     (with-temp-buffer
       (insert out)
+      (when org-export-with-concordance
+	(setq concordance (org-export--build-concordance)))
       (let ((coding-system-for-write org-export-coding-system))
 	(write-file file)))
+    (when concordance
+      (setq org-export-concordance concordance))
     ;; Maybe add file contents to kill ring.
     (when (and (org-export--copy-to-kill-ring-p) (org-string-nw-p out))
       (org-kill-new out)))
-- 
1.8.2

  parent reply	other threads:[~2013-04-01  3:15 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-01  3:14 [PATCH 0/3] synctex support for pdf export Aaron Ecay
2013-04-01  3:14 ` [PATCH 1/3] Add :begin-line and :end-line to parser results Aaron Ecay
2013-04-01  3:15 ` Aaron Ecay [this message]
2013-04-01  3:15 ` [PATCH 3/3] Add synctex modification based on concordance Aaron Ecay
2013-04-01  9:15 ` [PATCH 0/3] synctex support for pdf export Nicolas Goaziou
2013-04-01 15:33   ` Aaron Ecay
2013-04-04 13:19     ` Nicolas Goaziou
2013-04-18  8:29       ` Aaron Ecay
2013-04-18 16:27         ` Rasmus
2013-04-15  9:33   ` Andreas Leha
2013-04-15 11:50     ` Alan Schmitt
2013-04-15 15:37       ` Bastien

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1364786101-16603-3-git-send-email-aaronecay@gmail.com \
    --to=aaronecay@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).