summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2012-03-10 11:37:13 +0100
committerNicolas Goaziou <n.goaziou@gmail.com>2012-03-10 13:42:46 +0100
commitc7203b414284a109db2ab329ce6134caa2ed59ad (patch)
tree640edffe535b5f2e6d7e6a1bf7991b011e1be916
parent86131a8b5017cd2cec2167a76a8c6233ee58db2c (diff)
downloadorg-mode-c7203b414284a109db2ab329ce6134caa2ed59ad.tar.gz
org-export: Change source code handling API
* contrib/lisp/org-element.el (org-element-example-block-parser, org-element-src-block-parser): Add `:number-lines', `:preserve-indent, `:retain-labels', `:use-labels' and `:label-fmt' properties. * contrib/lisp/org-export.el (org-export-resolve-coderef, org-export-get-loc): Apply changes to src-block and example-block elements' properties. (org-export-unravel-code, org-export-format-code, org-export-format-code-default): New functions. (org-export-handle-code): Removed function. * EXPERIMENTAL/org-e-latex.el (org-e-latex-example-block): Use new function. (org-e-latex-src-block): Use new API. Better handling of numbered lines with special packages. * EXPERIMENTAL/org-e-ascii.el (org-e-ascii-example-block, org-e-ascii-src-block): Use new functions. * testing/lisp/test-org-element.el: Add tests. * testing/lisp/test-org-export.el: Add tests.
-rw-r--r--EXPERIMENTAL/org-e-ascii.el9
-rw-r--r--EXPERIMENTAL/org-e-latex.el136
-rw-r--r--contrib/lisp/org-element.el80
-rw-r--r--contrib/lisp/org-export.el251
-rw-r--r--testing/lisp/test-org-element.el106
-rw-r--r--testing/lisp/test-org-export.el98
6 files changed, 517 insertions, 163 deletions
diff --git a/EXPERIMENTAL/org-e-ascii.el b/EXPERIMENTAL/org-e-ascii.el
index c930091..a4b5cbf 100644
--- a/EXPERIMENTAL/org-e-ascii.el
+++ b/EXPERIMENTAL/org-e-ascii.el
@@ -50,6 +50,7 @@
(declare-function org-export-collect-tables "org-export" (info))
(declare-function org-export-data "org-export" (data backend info))
(declare-function org-export-expand-macro "org-export" (macro info))
+(declare-function org-export-format-code-default "org-export" (element info))
(declare-function org-export-get-coderef-format "org-export" (path desc))
(declare-function org-export-get-footnote-number "org-export" (footnote info))
(declare-function org-export-get-headline-number "org-export" (headline info))
@@ -57,8 +58,6 @@
(element info &optional types predicate))
(declare-function org-export-get-parent-headline "org-export" (blob info))
(declare-function org-export-get-relative-level "org-export" (headline info))
-(declare-function org-export-handle-code
- "org-export" (element info &optional num-fmt ref-fmt delayed))
(declare-function org-export-included-file "org-export" (keyword backend info))
(declare-function org-export-low-level-p "org-export" (headline info))
(declare-function org-export-output-file-name "org-export"
@@ -1082,7 +1081,8 @@ contextual information."
(defun org-e-ascii-example-block (example-block contents info)
"Transcode a EXAMPLE-BLOCK element from Org to ASCII.
CONTENTS is nil. INFO is a plist holding contextual information."
- (org-e-ascii--box-string (org-export-handle-code example-block info) info))
+ (org-e-ascii--box-string
+ (org-export-format-code-default example-block info) info))
;;;; Export Snippet
@@ -1546,7 +1546,8 @@ contextual information."
(let ((caption (org-e-ascii--build-caption src-block info)))
(concat
(when (and caption org-e-ascii-caption-above) (concat caption "\n"))
- (org-e-ascii--box-string (org-export-handle-code src-block info) info)
+ (org-e-ascii--box-string
+ (org-export-format-code-default src-block info) info)
(when (and caption (not org-e-ascii-caption-above))
(concat "\n" caption)))))
diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el
index c83d000..ac11c1a 100644
--- a/EXPERIMENTAL/org-e-latex.el
+++ b/EXPERIMENTAL/org-e-latex.el
@@ -54,13 +54,16 @@
(declare-function org-export-first-sibling-p "org-export" (headline info))
(declare-function org-export-footnote-first-reference-p "org-export"
(footnote-reference info))
+(declare-function org-export-format-code "org-export"
+ (code fun &optional num-lines ref-alist))
+(declare-function org-export-format-code-default "org-export" (element info))
(declare-function org-export-get-coderef-format "org-export" (path desc))
(declare-function org-export-get-footnote-definition "org-export"
(footnote-reference info))
(declare-function org-export-get-footnote-number "org-export" (footnote info))
(declare-function org-export-get-previous-element "org-export" (blob info))
(declare-function org-export-get-relative-level "org-export" (headline info))
-(declare-function org-export-handle-code
+(declare-function org-export-unravel-code
"org-export" (element info &optional num-fmt ref-fmt delayed))
(declare-function org-export-included-file "org-export" (keyword backend info))
(declare-function org-export-inline-image-p "org-export"
@@ -959,12 +962,13 @@ contextual information."
;;;; Example Block
(defun org-e-latex-example-block (example-block contents info)
- "Transcode a EXAMPLE-BLOCK element from Org to LaTeX.
-CONTENTS is nil. INFO is a plist holding contextual information."
- (let* ((options (or (org-element-property :options example-block) ""))
- (value (org-export-handle-code example-block info)))
- (org-e-latex--wrap-label
- example-block (format "\\begin{verbatim}\n%s\\end{verbatim}" value))))
+ "Transcode an EXAMPLE-BLOCK element from Org to LaTeX.
+CONTENTS is nil. INFO is a plist holding contextual
+information."
+ (org-e-latex--wrap-label
+ example-block
+ (format "\\begin{verbatim}\n%s\\end{verbatim}"
+ (org-export-format-code-default example-block info))))
;;;; Export Snippet
@@ -1674,39 +1678,68 @@ holding contextual information."
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(let* ((lang (org-element-property :language src-block))
- (code (org-export-handle-code src-block info))
(caption (org-element-property :caption src-block))
(label (org-element-property :name src-block))
(custom-env (and lang
(cadr (assq (intern lang)
- org-e-latex-custom-lang-environments)))))
+ org-e-latex-custom-lang-environments))))
+ (num-start (case (org-element-property :number-lines src-block)
+ (continued (org-export-get-loc src-block info))
+ (new 0)))
+ (retain-labels (org-element-property :retain-labels src-block)))
(cond
- ;; No source fontification.
+ ;; Case 1. No source fontification.
((not org-e-latex-listings)
- (let ((caption-str (org-e-latex--caption/label-string
- caption label info))
+ (let ((caption-str (org-e-latex--caption/label-string caption label info))
(float-env (when caption "\\begin{figure}[H]\n%s\n\\end{figure}")))
- (format (or float-env "%s")
- (concat
- caption-str
- (format "\\begin{verbatim}\n%s\\end{verbatim}" code)))))
- ;; Custom environment.
- (custom-env
- (format "\\begin{%s}\n%s\\end{%s}\n" custom-env code custom-env))
- ;; Use minted package.
+ (format
+ (or float-env "%s")
+ (concat caption-str
+ (format "\\begin{verbatim}\n%s\\end{verbatim}"
+ (org-export-format-code-default src-block info))))))
+ ;; Case 2. Custom environment.
+ (custom-env (format "\\begin{%s}\n%s\\end{%s}\n"
+ custom-env
+ (org-export-format-code-default src-block info)
+ custom-env))
+ ;; Case 3. Use minted package.
((eq org-e-latex-listings 'minted)
- (let* ((mint-lang (or (cadr (assq (intern lang) org-e-latex-minted-langs))
- lang))
- (float-env (when (or label caption)
- (format "\\begin{listing}[H]\n%%s\n%s\\end{listing}"
- (org-e-latex--caption/label-string
- caption label info))))
- (body (format "\\begin{minted}[%s]{%s}\n%s\\end{minted}"
- (org-e-latex--make-option-string
- org-e-latex-minted-options)
- mint-lang code)))
+ (let ((float-env (when (or label caption)
+ (format "\\begin{listing}[H]\n%%s\n%s\\end{listing}"
+ (org-e-latex--caption/label-string
+ caption label info))))
+ (body
+ (format
+ "\\begin{minted}[%s]{%s}\n%s\\end{minted}"
+ ;; Options.
+ (org-e-latex--make-option-string
+ (if (not num-start) org-e-latex-minted-options
+ (append `(("linenos")
+ ("firstnumber" ,(number-to-string (1+ num-start))))
+ org-e-latex-minted-options)))
+ ;; Language.
+ (or (cadr (assq (intern lang) org-e-latex-minted-langs)) lang)
+ ;; Source code.
+ (let* ((code-info (org-export-unravel-code src-block contents))
+ (max-width
+ (apply 'max
+ (mapcar 'length
+ (org-split-string (car code-info) "\n")))))
+ (org-export-format-code
+ (car code-info)
+ (lambda (loc num ref)
+ (concat
+ loc
+ (when ref
+ ;; Ensure references are flushed to the right,
+ ;; separated with 6 spaces from the widest line
+ ;; of code.
+ (concat (make-string (+ (- max-width (length loc)) 6) ? )
+ (format "(%s)" ref)))))
+ nil (and retain-labels (cdr code-info)))))))
+ ;; Return value.
(if float-env (format float-env body) body)))
- ;; Use listings package.
+ ;; Case 4. Use listings package.
(t
(let ((lst-lang
(or (cadr (assq (intern lang) org-e-latex-listings-langs)) lang))
@@ -1719,14 +1752,39 @@ contextual information."
"{[%s]%s}"
(org-export-secondary-string (cdr caption) 'e-latex info)
main))))))
- (concat (format "\\lstset{%s}\n"
- (org-e-latex--make-option-string
- (append org-e-latex-listings-options
- `(("language" ,lst-lang))
- (when label `(("label" ,label)))
- (when caption-str
- `(("caption" ,caption-str))))))
- (format "\\begin{lstlisting}\n%s\\end{lstlisting}" code)))))))
+ (concat
+ ;; Options.
+ (format "\\lstset{%s}\n"
+ (org-e-latex--make-option-string
+ (append org-e-latex-listings-options
+ `(("language" ,lst-lang))
+ (when label `(("label" ,label)))
+ (when caption-str `(("caption" ,caption-str)))
+ (cond ((not num-start) '(("numbers" "none")))
+ ((zerop num-start) '(("numbers" "left")))
+ (t `(("numbers" "left")
+ ("firstnumber"
+ ,(number-to-string (1+ num-start)))))))))
+ ;; Source code.
+ (format
+ "\\begin{lstlisting}\n%s\\end{lstlisting}"
+ (let* ((code-info (org-export-unravel-code src-block info))
+ (max-width
+ (apply 'max
+ (mapcar 'length
+ (org-split-string (car code-info) "\n")))))
+ (org-export-format-code
+ (car code-info)
+ (lambda (loc num ref)
+ (concat
+ loc
+ (when ref
+ ;; Ensure references are flushed to the right,
+ ;; separated with 6 spaces from the widest line of
+ ;; code
+ (concat (make-string (+ (- max-width (length loc)) 6) ? )
+ (format "(%s)" ref)))))
+ nil (and retain-labels (cdr code-info)))))))))))
;;;; Statistics Cookie
diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el
index 098c312..36e0408 100644
--- a/contrib/lisp/org-element.el
+++ b/contrib/lisp/org-element.el
@@ -991,22 +991,43 @@ CONTENTS is nil."
(defun org-element-example-block-parser ()
"Parse an example block.
-Return a list whose car is `example' and cdr is a plist
-containing `:begin', `:end', `:options', `:hiddenp', `:value' and
-`:post-blank' keywords."
+Return a list whose car is `example-block' and cdr is a plist
+containing `:begin', `:end', `:number-lines', `:preserve-indent',
+`:retain-labels', `:use-labels', `:label-fmt', `:hiddenp',
+`:switches', `:value' and `:post-blank' keywords."
(save-excursion
(end-of-line)
(let* ((case-fold-search t)
(switches (progn
(re-search-backward
- "^[ \t]*#\\+begin_example\\(?: +\\(.*\\)\\)?" nil t)
+ "^[ \t]*#\\+BEGIN_EXAMPLE\\(?: +\\(.*\\)\\)?" nil t)
(org-match-string-no-properties 1)))
+ ;; Switches analysis
+ (number-lines (cond ((not switches) nil)
+ ((string-match "-n\\>" switches) 'new)
+ ((string-match "+n\\>" switches) 'continued)))
+ (preserve-indent (and switches (string-match "-i\\>" switches)))
+ ;; Should labels be retained in (or stripped from) example
+ ;; blocks?
+ (retain-labels
+ (or (not switches)
+ (not (string-match "-r\\>" switches))
+ (and number-lines (string-match "-k\\>" switches))))
+ ;; What should code-references use - labels or
+ ;; line-numbers?
+ (use-labels
+ (or (not switches)
+ (and retain-labels (not (string-match "-k\\>" switches)))))
+ (label-fmt (and switches
+ (string-match "-l +\"\\([^\"\n]+\\)\"" switches)
+ (match-string 1 switches)))
+ ;; Standard block parsing.
(keywords (org-element-collect-affiliated-keywords))
(begin (car keywords))
(contents-begin (progn (forward-line) (point)))
(hidden (org-truely-invisible-p))
(contents-end (progn
- (re-search-forward "^[ \t]*#\\+end_example" nil t)
+ (re-search-forward "^[ \t]*#\\+END_EXAMPLE" nil t)
(point-at-bol)))
(value (buffer-substring-no-properties contents-begin contents-end))
(pos-before-blank (progn (forward-line) (point)))
@@ -1017,6 +1038,11 @@ containing `:begin', `:end', `:options', `:hiddenp', `:value' and
:end ,end
:value ,value
:switches ,switches
+ :number-lines ,number-lines
+ :preserve-indent ,preserve-indent
+ :retain-labels ,retain-labels
+ :use-labels ,use-labels
+ :label-fmt ,label-fmt
:hiddenp ,hidden
:post-blank ,(count-lines pos-before-blank end)
,@(cadr keywords))))))
@@ -1341,31 +1367,52 @@ CONTENTS is nil."
Return a list whose car is `src-block' and cdr is a plist
containing `:language', `:switches', `:parameters', `:begin',
-`:end', `:hiddenp', `:contents-begin', `:contents-end', `:value'
-and `:post-blank' keywords."
+`:end', `:hiddenp', `:contents-begin', `:contents-end',
+`:number-lines', `:retain-labels', `:use-labels', `:label-fmt',
+`:preserve-indent', `:value' and `:post-blank' keywords."
(save-excursion
(end-of-line)
(let* ((case-fold-search t)
;; Get position at beginning of block.
(contents-begin
(re-search-backward
- (concat "^[ \t]*#\\+begin_src"
- "\\(?: +\\(\\S-+\\)\\)?" ; language
- "\\(\\(?: +[-+][A-Za-z]\\)*\\)" ; switches
- "\\(.*\\)[ \t]*$") ; arguments
+ (concat
+ "^[ \t]*#\\+BEGIN_SRC"
+ "\\(?: +\\(\\S-+\\)\\)?" ; language
+ "\\(\\(?: +\\(?:-l \".*?\"\\|[-+][A-Za-z]\\)\\)*\\)" ; switches
+ "\\(.*\\)[ \t]*$") ; parameters
nil t))
;; Get language as a string.
(language (org-match-string-no-properties 1))
- ;; Get switches.
- (switches (org-match-string-no-properties 2))
;; Get parameters.
(parameters (org-trim (org-match-string-no-properties 3)))
+ ;; Get switches.
+ (switches (org-match-string-no-properties 2))
+ ;; Switches analysis
+ (number-lines (cond ((not switches) nil)
+ ((string-match "-n\\>" switches) 'new)
+ ((string-match "+n\\>" switches) 'continued)))
+ (preserve-indent (and switches (string-match "-i\\>" switches)))
+ (label-fmt (and switches
+ (string-match "-l +\"\\([^\"\n]+\\)\"" switches)
+ (match-string 1 switches)))
+ ;; Should labels be retained in (or stripped from) src
+ ;; blocks?
+ (retain-labels
+ (or (not switches)
+ (not (string-match "-r\\>" switches))
+ (and number-lines (string-match "-k\\>" switches))))
+ ;; What should code-references use - labels or
+ ;; line-numbers?
+ (use-labels
+ (or (not switches)
+ (and retain-labels (not (string-match "-k\\>" switches)))))
;; Get affiliated keywords.
(keywords (org-element-collect-affiliated-keywords))
;; Get beginning position.
(begin (car keywords))
;; Get position at end of block.
- (contents-end (progn (re-search-forward "^[ \t]*#\\+end_src" nil t)
+ (contents-end (progn (re-search-forward "^[ \t]*#\\+END_SRC" nil t)
(forward-line)
(point)))
;; Retrieve code.
@@ -1387,6 +1434,11 @@ and `:post-blank' keywords."
:parameters ,parameters
:begin ,begin
:end ,end
+ :number-lines ,number-lines
+ :preserve-indent ,preserve-indent
+ :retain-labels ,retain-labels
+ :use-labels ,use-labels
+ :label-fmt ,label-fmt
:hiddenp ,hidden
:value ,value
:post-blank ,(count-lines contents-end end)
diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el
index 22a7699..6d3b02a 100644
--- a/contrib/lisp/org-export.el
+++ b/contrib/lisp/org-export.el
@@ -2801,30 +2801,24 @@ INFO is a plist used as a communication channel.
Return associated line number in source code, or REF itself,
depending on src-block or example element's switches."
(org-element-map
- (plist-get info :parse-tree) '(src-block example)
+ (plist-get info :parse-tree) '(example-block src-block)
(lambda (el)
- (let ((switches (or (org-element-property :switches el) "")))
- (with-temp-buffer
- (insert (org-trim (org-element-property :value el)))
- ;; Build reference regexp.
- (let* ((label
- (or (and (string-match "-l +\"\\([^\"\n]+\\)\"" switches)
- (match-string 1 switches))
- org-coderef-label-format))
- (ref-re
- (format "^.*?\\S-.*?\\([ \t]*\\(%s\\)\\)[ \t]*$"
- (replace-regexp-in-string "%s" ref label nil t))))
- ;; Element containing REF is found. Only associate REF to
- ;; a line number if element has "+n" or "-n" and "-k" or
- ;; "-r" as switches. When it has "+n", count accumulated
- ;; locs before, too.
- (when (re-search-backward ref-re nil t)
- (cond
- ((not (string-match "-[kr]\\>" switches)) ref)
- ((string-match "-n\\>" switches) (line-number-at-pos))
- ((string-match "\\+n\\>" switches)
- (+ (org-export-get-loc el info) (line-number-at-pos)))
- (t ref)))))))
+ (with-temp-buffer
+ (insert (org-trim (org-element-property :value el)))
+ (let* ((label-fmt (or (org-element-property :label-fmt el)
+ org-coderef-label-format))
+ (ref-re
+ (format "^.*?\\S-.*?\\([ \t]*\\(%s\\)\\)[ \t]*$"
+ (regexp-quote
+ (replace-regexp-in-string "%s" ref label-fmt nil t)))))
+ ;; Element containing REF is found. Resolve it to either
+ ;; a label or a line number, as needed.
+ (when (re-search-backward ref-re nil t)
+ (cond
+ ((org-element-property :use-labels el) ref)
+ ((eq (org-element-property :number-lines el) 'continued)
+ (+ (org-export-get-loc el info) (line-number-at-pos)))
+ (t (line-number-at-pos)))))))
info 'first-match))
@@ -2933,8 +2927,21 @@ objects of the same type."
;; src-block or example-block elements with a "+n" switch until
;; a given element, excluded. Note: "-n" switches reset that count.
-;; `org-export-handle-code' takes care of line numbering and reference
-;; cleaning in source code, when appropriate.
+;; `org-export-unravel-code' extracts source code (along with a code
+;; references alist) from an `element-block' or `src-block' type
+;; element.
+
+;; `org-export-format-code' applies a formatting function to each line
+;; of code, providing relative line number and code reference when
+;; appropriate. Since it doesn't access the original element from
+;; which the source code is coming, it expects from the code calling
+;; it to know if lines should be numbered and if code references
+;; should appear.
+
+;; Eventually, `org-export-format-code-default' is a higher-level
+;; function (it makes use of the two previous functions) which handles
+;; line numbering and code references inclusion, and returns source
+;; code in a format suitable for plain text or verbatim output.
(defun org-export-get-loc (element info)
"Return accumulated lines of code up to ELEMENT.
@@ -2953,111 +2960,145 @@ ELEMENT is excluded from count."
;; Only count lines from src-block and example-block elements
;; with a "+n" or "-n" switch. A "-n" switch resets counter.
((not (memq (org-element-type el) '(src-block example-block))) nil)
- ((let ((switches (org-element-property :switches el)))
- (when (and switches (string-match "\\([-+]\\)n\\>" switches))
+ ((let ((linums (org-element-property :number-lines el)))
+ (when linums
;; Accumulate locs or reset them.
- (let ((accumulatep (string= (match-string 1 switches) "-"))
- (lines (org-count-lines
+ (let ((lines (org-count-lines
(org-trim (org-element-property :value el)))))
- (setq loc (if accumulatep lines (+ loc lines))))))
+ (setq loc (if (eq linums 'new) lines (+ loc lines))))))
;; Return nil to stay in the loop.
nil)))
info 'first-match)
;; Return value.
loc))
-(defun org-export-handle-code (element info &optional num-fmt ref-fmt delayed)
- "Handle line numbers and code references in ELEMENT.
+(defun org-export-unravel-code (element info)
+ "Clean source code and extract references out of it.
ELEMENT has either a `src-block' an `example-block' type. INFO
is a plist used as a communication channel.
-If optional argument NUM-FMT is a string, it will be used as
-a format string for numbers at beginning of each line.
-
-If optional argument REF-FMT is a string, it will be used as
-a format string for each line of code containing a reference.
-
-When optional argument DELAYED is non-nil, `org-loc' and
-`org-coderef' properties, set to an adequate value, are applied
-to, respectively, numbered lines and lines with a reference. No
-line numbering is done and all references are stripped from the
-resulting string. Both NUM-FMT and REF-FMT arguments are ignored
-in that situation.
-
-Return new code as a string."
- (let* ((switches (or (org-element-property :switches element) ""))
- (code (org-element-property :value element))
- (numberp (string-match "[-+]n\\>" switches))
- (accumulatep (string-match "\\+n\\>" switches))
- ;; Initialize loc counter when any kind of numbering is
- ;; active.
- (total-LOC (cond
- (accumulatep (org-export-get-loc element info))
- (numberp 0)))
- ;; Get code and clean it. Remove blank lines at its
- ;; beginning and end. Also remove protective commas.
- (preserve-indent-p (or org-src-preserve-indentation
- (string-match "-i\\>" switches)))
- (replace-labels (when (string-match "-r\\>" switches)
- (if (string-match "-k\\>" switches) 'keep t)))
+Return a cons cell whose CAR is the source code, cleaned from any
+reference and protective comma and CDR is an alist between
+relative line number (integer) and name of code reference on that
+line (string)."
+ (let* ((line 0) refs
+ ;; Get code and clean it. Remove blank lines at its beginning
+ ;; and end. Also remove protective commas.
(code (let ((c (replace-regexp-in-string
"\\`\\([ \t]*\n\\)+" ""
(replace-regexp-in-string
- "\\(:?[ \t]*\n\\)*[ \t]*\\'" "\n" code))))
+ "\\(:?[ \t]*\n\\)*[ \t]*\\'" "\n"
+ (org-element-property :value element)))))
;; If appropriate, remove global indentation.
- (unless preserve-indent-p (setq c (org-remove-indentation c)))
+ (unless (or org-src-preserve-indentation
+ (org-element-property :preserve-indent element))
+ (setq c (org-remove-indentation c)))
;; Free up the protected lines. Note: Org blocks
;; have commas at the beginning or every line.
- (if (string=
- (or (org-element-property :language element) "")
- "org")
+ (if (string= (org-element-property :language element) "org")
(replace-regexp-in-string "^," "" c)
(replace-regexp-in-string
"^\\(,\\)\\(:?\\*\\|[ \t]*#\\+\\)" "" c nil nil 1))))
- ;; Split code to process it line by line.
- (code-lines (org-split-string code "\n"))
- ;; If numbering is active, ensure line numbers will be
- ;; correctly padded before applying the format string.
- (num-fmt
- (when (and (not delayed) numberp)
- (format (if (stringp num-fmt) num-fmt "%s: ")
- (format "%%%ds"
- (length (number-to-string
- (+ (length code-lines) total-LOC)))))))
;; Get format used for references.
- (label-fmt (or (and (string-match "-l +\"\\([^\"\n]+\\)\"" switches)
- (match-string 1 switches))
- org-coderef-label-format))
+ (label-fmt (or (org-element-property :label-fmt element)
+ org-coderef-label-format))
;; Build a regexp matching a loc with a reference.
- (with-ref-re (format "^.*?\\S-.*?\\([ \t]*\\(%s\\)\\)[ \t]*$"
- (replace-regexp-in-string
- "%s" "\\([-a-zA-Z0-9_ ]+\\)" label-fmt nil t))))
+ (with-ref-re
+ (format "^.*?\\S-.*?\\([ \t]*\\(%s\\)[ \t]*\\)$"
+ (regexp-quote
+ (replace-regexp-in-string
+ "%s" "\\([-a-zA-Z0-9_ ]+\\)" label-fmt nil t)))))
+ ;; Return value.
+ (cons
+ ;; Code with references removed.
+ (org-element-normalize-string
+ (mapconcat
+ (lambda (loc)
+ (incf line)
+ (if (not (string-match with-ref-re loc)) loc
+ ;; Ref line: remove ref, and signal its position in REFS.
+ (push (cons line (match-string 3 loc)) refs)
+ (replace-match "" nil nil loc 1)))
+ (org-split-string code "\n") "\n"))
+ ;; Reference alist.
+ refs)))
+
+(defun org-export-format-code (code fun &optional num-lines ref-alist)
+ "Format CODE by applying FUN line-wise and return it.
+
+CODE is a string representing the code to format. FUN is
+a function. It must accept three arguments: a line of
+code (string), the current line number (integer) or nil and the
+reference associated to the current line (string) or nil.
+
+Optional argument NUM-LINES can be an integer representing the
+number of code lines accumulated until the current code. Line
+numbers passed to FUN will take it into account. If it is nil,
+FUN's second argument will always be nil. This number can be
+obtained with `org-export-get-loc' function.
+
+Optional argument REF-ALIST can be an alist between relative line
+number (i.e. ignoring NUM-LINES) and the name of the code
+reference on it. If it is nil, FUN's third argument will always
+be nil. It can be obtained through the use of
+`org-export-unravel-code' function."
+ (let ((--locs (org-split-string code "\n"))
+ (--line 0))
(org-element-normalize-string
(mapconcat
- (lambda (loc)
- ;; Maybe add line number to current line of code (LOC).
- (when numberp
- (incf total-LOC)
- (setq loc (if delayed (org-add-props loc nil 'org-loc total-LOC)
- (concat (format num-fmt total-LOC) loc))))
- ;; Take action if at a ref line.
- (when (string-match with-ref-re loc)
- (let ((ref (match-string 3 loc)))
- (setq loc
- ;; Option "-r" without "-k" removes labels.
- ;; A non-nil DELAYED removes labels unconditionally.
- (if (or delayed
- (and replace-labels (not (eq replace-labels 'keep))))
- (replace-match "" nil nil loc 1)
- (replace-match (format "(%s)" ref) nil nil loc 2)))
- ;; Store REF in `org-coderef' property if DELAYED asks to.
- (cond (delayed (setq loc (org-add-props loc nil 'org-coderef ref)))
- ;; If REF-FMT is defined, apply it to current LOC.
- ((stringp ref-fmt) (setq loc (format ref-fmt loc))))))
- ;; Return updated LOC for concatenation.
- loc)
- code-lines "\n"))))
+ (lambda (--loc)
+ (incf --line)
+ (let ((--ref (cdr (assq --line ref-alist))))
+ (funcall fun --loc (and num-lines (+ num-lines --line)) --ref)))
+ --locs "\n"))))
+
+(defun org-export-format-code-default (element info)
+ "Return source code from ELEMENT, formatted in a standard way.
+
+ELEMENT is either a `src-block' or `example-block' element. INFO
+is a plist used as a communication channel.
+
+This function takes care of line numbering and code references
+inclusion. Line numbers, when applicable, appear at the
+beginning of the line, separated from the code by two white
+spaces. Code references, on the other hand, appear flushed to
+the right, separated by six white spaces from the widest line of
+code."
+ ;; Extract code and references.
+ (let* ((code-info (org-export-unravel-code element info))
+ (code (car code-info))
+ (code-lines (org-split-string code "\n"))
+ (refs (and (org-element-property :retain-labels element)
+ (cdr code-info)))
+ ;; Handle line numbering.
+ (num-start (case (org-element-property :number-lines element)
+ (continued (org-export-get-loc element info))
+ (new 0)))
+ (num-fmt
+ (and num-start
+ (format "%%%ds "
+ (length (number-to-string
+ (+ (length code-lines) num-start))))))
+ ;; Prepare references display, if required. Any reference
+ ;; should start six columns after the widest line of code,
+ ;; wrapped with parenthesis.
+ (max-width
+ (+ (apply 'max (mapcar 'length code-lines))
+ (if (not num-start) 0 (length (format num-fmt num-start))))))
+ (org-export-format-code
+ code
+ (lambda (loc line-num ref)
+ (let ((number-str (and num-fmt (format num-fmt line-num))))
+ (concat
+ number-str
+ loc
+ (and ref
+ (concat (make-string
+ (- (+ 6 max-width)
+ (+ (length loc) (length number-str))) ? )
+ (format "(%s)" ref))))))
+ num-start refs)))
;;;; For Tables
diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el
index b321ffa..bae0a9d 100644
--- a/testing/lisp/test-org-element.el
+++ b/testing/lisp/test-org-element.el
@@ -29,6 +29,7 @@
;;; Tests:
+
;;;; Headlines
(ert-deftest test-org-element/headline-quote-keyword ()
@@ -100,6 +101,109 @@
+;;;; Example-blocks and Src-blocks
+
+(ert-deftest test-org-element/block-switches ()
+ "Test `example-block' and `src-block' switches parsing."
+ (let ((org-coderef-label-format "(ref:%s)"))
+ ;; 1. Test "-i" switch.
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should-not (org-element-property :preserve-indent element))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -i\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (org-element-property :preserve-indent element))))
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should-not (org-element-property :preserve-indent element))))
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE -i\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (org-element-property :preserve-indent element))))
+ ;; 2. "-n -r -k" combination should number lines, retain labels but
+ ;; not use them in coderefs.
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE -n -r -k\nText.\N#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (not (org-element-property :use-labels element))))))
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -n -r -k\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (not (org-element-property :use-labels element))))))
+ ;; 3. "-n -r" combination should number-lines remove labels and not
+ ;; use them in coderefs.
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE -n -r\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (not (org-element-property :retain-labels element))
+ (not (org-element-property :use-labels element))))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (not (org-element-property :retain-labels element))
+ (not (org-element-property :use-labels element))))))
+ ;; 4. "-n" or "+n" should number lines, retain labels and use them
+ ;; in coderefs.
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE -n\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE +n\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp +n\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (org-element-property :number-lines element)
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ ;; 5. No switch should not number lines, but retain labels and use
+ ;; them in coderefs.
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (not (org-element-property :number-lines element))
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (not (org-element-property :number-lines element))
+ (org-element-property :retain-labels element)
+ (org-element-property :use-labels element)))))
+ ;; 6. "-r" switch only: do not number lines, remove labels, and
+ ;; don't use labels in coderefs.
+ (org-test-with-temp-text "#+BEGIN_EXAMPLE -r\nText.\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should (and (not (org-element-property :number-lines element))
+ (not (org-element-property :retain-labels element))
+ (not (org-element-property :use-labels element))))))
+ (org-test-with-temp-text "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1)\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should (and (not (org-element-property :number-lines element))
+ (not (org-element-property :retain-labels element))
+ (not (org-element-property :use-labels element))))))
+ ;; 7. Recognize coderefs with user-defined syntax.
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText [ref:text]\n#+END_EXAMPLE"
+ (let ((element (org-element-current-element)))
+ (should
+ (equal (org-element-property :coderef-fmt element) "[ref:%s]"))))
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -l \"[ref:%s]\"\n(+ 1 1) [ref:text]\n#+END_SRC"
+ (let ((element (org-element-current-element)))
+ (should
+ (equal (org-element-property :coderef-fmt element) "[ref:%s]"))))))
+
+
+
;;; Navigation tools.
(ert-deftest test-org-element/forward-element ()
@@ -312,7 +416,7 @@ Outside."
(org-element-up)
(should (looking-at "\\* Top"))))
-(ert-deftest test-org-elemnet/down-element ()
+(ert-deftest test-org-element/down-element ()
"Test `org-element-down' specifications."
;; 1. Error when the element hasn't got a recursive type.
(org-test-with-temp-text "Paragraph."
diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el
index 3a86dcb..a61242f 100644
--- a/testing/lisp/test-org-export.el
+++ b/testing/lisp/test-org-export.el
@@ -470,3 +470,101 @@ body\n")))
(lambda (link)
(org-export-get-ordinal
(org-export-resolve-fuzzy-link link info) info)) info t))))))
+
+(defun test-org-export/resolve-coderef ()
+ "Test `org-export-resolve-coderef' specifications."
+ (let ((org-coderef-label-format "(ref:%s)"))
+ ;; 1. A link to a "-n -k -r" block returns line number.
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ ;; 2. A link to a "-n -r" block returns line number.
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ ;; 3. A link to a "-n" block returns coderef.
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
+ "coderef"))))
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
+ "coderef"))))
+ ;; 4. A link to a "-r" block returns line number.
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
+ ;; 5. A link to a block without a switch returns coderef.
+ (org-test-with-temp-text
+ "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
+ "coderef"))))
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should
+ (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
+ "coderef"))))
+ ;; 6. Correctly handle continued line numbers. A "+n" switch
+ ;; should resume numbering from previous block with numbered
+ ;; lines, ignoring blocks not numbering lines in the process.
+ ;; A "-n" switch resets count.
+ (org-test-with-temp-text "
+#+BEGIN_EXAMPLE -n
+Text.
+#+END_EXAMPLE
+
+#+BEGIN_SRC emacs-lisp
+\(- 1 1)
+#+END_SRC
+
+#+BEGIN_SRC emacs-lisp +n -r
+\(+ 1 1) (ref:addition)
+#+END_SRC
+
+#+BEGIN_EXAMPLE -n -r
+Another text. (ref:text)
+#+END_EXAMPLE"
+ (let* ((tree (org-element-parse-buffer))
+ (info `(:parse-tree ,tree)))
+ (should (= (org-export-resolve-coderef "addition" info) 2))
+ (should (= (org-export-resolve-coderef "text" info) 1))))
+ ;; 7. Recognize coderef with user-specified syntax.
+ (org-test-with-temp-text
+ "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE"
+ (let ((tree (org-element-parse-buffer)))
+ (should (equal (org-export-resolve-coderef "text" `(:parse-tree ,tree))
+ "text"))))))
+
+
+
+(provide 'test-org-export)
+;;; test-org-export.el end here