Browse Source

Coderefs: Implement line numbering and code references

This patch covers the much of the new line numbering and code line
referencing.

We introduce a new function with the longish name
`org-export-replace-src-segments-and-examples'.  It is an extension of
the earlier `org-export-replace-src-segments'.  It now also covers
EXAMPLE blocks, not only source code blocks.  This was necessary to
allow line numbering also in EXAMPLE blocks.

Both clock types now accept a string with options which will be given
in the BEGIN line.  Options currently recognized are -n, +n, and -r.

A bit unsatisfactory about this patch may be that LaTeX-specific code
is now defined in org-exp.el, maybe eventually we want to think of a
way to move this code back to org-export-latex.el.  Really, all it
does is adding line numbers and wrapping them into a verbatim
environment.

Besides adding line numbers in a backend specific way, this patch also
adds code to search or special cookies in source code, like "((1))" or
"((name))".  These are labels that can be addressed by links as
references to specific line in source code.
Carsten Dominik 16 years ago
parent
commit
88a78204c3
1 changed files with 140 additions and 55 deletions
  1. 140 55
      lisp/org-exp.el

+ 140 - 55
lisp/org-exp.el

@@ -1449,6 +1449,8 @@ translations.  There is currently no way for users to extend this.")
 
 (defvar org-export-target-aliases nil
   "Alist of targets with invisible aliases.")
+(defvar org-export-code-refs nil
+  "Alist of code references and line numbers")
 
 (defun org-export-preprocess-string (string &rest parameters)
   "Cleanup STRING so that that the true exported has a more consistent source.
@@ -1469,6 +1471,7 @@ on this string to produce the exported version."
 	 target-alist rtn)
 
     (setq org-export-target-aliases nil)
+    (setq org-export-code-refs nil)
 
     (with-current-buffer (get-buffer-create " org-mode-tmp")
       (erase-buffer)
@@ -1494,7 +1497,7 @@ on this string to produce the exported version."
 				     (plist-get parameters :exclude-tags))
 
       ;; Handle source code snippets
-      (org-export-replace-src-segments)
+      (org-export-replace-src-segments-and-examples backend)
 
       ;; Find all headings and compute the targets for them
       (setq target-alist (org-export-define-heading-targets target-alist))
@@ -1524,9 +1527,6 @@ on this string to produce the exported version."
       ;; but mark them as targets that should be invisible
       (setq target-alist (org-export-handle-invisible-targets target-alist))
 
-      ;; Protect examples
-      (org-export-protect-examples (if asciip 'indent nil))
-
       ;; Protect backend specific stuff, throw away the others.
       (org-export-select-backend-specific-text backend)
 
@@ -2234,70 +2234,155 @@ in the list) and remove property and value from the list in LISTVAR."
       (if s (symbol-name s) s)
     s))
 
-;;; Fontification of code
+;;; Fontification and line numbers for code examples
+
+(defvar org-export-last-code-line-counter-value 0)
+
 ;; Currently only for the HTML backend, but who knows....
-(defun org-export-replace-src-segments ()
+(defun org-export-replace-src-segments-and-examples (backend)
   "Replace source code segments with special code for export."
+  (setq org-export-last-code-line-counter-value 0)
   (let ((case-fold-search t)
-	lang code trans)
+	lang code trans opts)
     (goto-char (point-min))
     (while (re-search-forward
-	    "^#\\+BEGIN_SRC:?[ \t]+\\([^ \t\n]+\\)[ \t]*\n\\([^\000]+?\n\\)#\\+END_SRC.*"
+	    "\\(^#\\+BEGIN_SRC:?[ \t]+\\([^ \t\n]+\\)\\(.*\\)\n\\([^\000]+?\n\\)#\\+END_SRC.*\\)\\|\\(^#\\+BEGIN_EXAMPLE:?[ \t]+\\(.*\\)\n\\([^\000]+?\n\\)#\\+END_EXAMPLE.*\\)"
 	    nil t)
-      (setq lang (match-string 1) code (match-string 2)
-	    trans (org-export-format-source-code lang code))
+      (if (match-end 1)
+	  ;; src segments
+	  (setq lang (match-string 2)
+		opts (match-string 3)
+		code (match-string 4))
+	(setq lang nil
+	      opts (match-string 6)
+	      code (match-string 7)))
+
+      (setq trans (org-export-format-source-code-or-example
+		   backend lang code opts))
       (replace-match trans t t))))
 
-(defvar htmlp)  ;; dynamically scoped from org-exp.el
+(defvar htmlp)  ;; dynamically scoped
+(defvar latexp)  ;; dynamically scoped
 
-(defun org-export-format-source-code (lang code)
+(defun org-export-format-source-code-or-example (backend
+						 lang code &optional opts)
   "Format CODE from language LANG and return it formatted for export.
-Currently, this only does something for HTML export, for all other
-backends, it converts the segment into an EXAMPLE segment."
+If LANG is nil, do not add any fontification.
+OPTS contains formatting optons, like `-n' for triggering numbering lines,
+and `+n' for continuing previous numering.
+Code formatting according to language currently only works for HTML.
+Numbering lines works for all three major backends (html, latex, and ascii)."
   (save-match-data
-    (cond
-     (htmlp
-      ;; We are exporting to HTML
-      (require 'htmlize nil t)
-      (if (not (fboundp 'htmlize-region-for-paste))
-	  (progn
+    (let (num cont rtn named rpllbl keepp)
+      (setq num (string-match "[-+]n\\>" (or opts ""))
+	    cont (string-match "\\+n\\>" (or opts ""))
+	    rpllbl (string-match "-r\\>" (or opts "")))
+      (setq rtn code)
+      (when (equal lang "org")
+	(setq rtn (with-temp-buffer
+		    (insert rtn)
+		    ;; Free up the protected lines
+		    (goto-char (point-min))
+		    (while (re-search-forward "^," nil t)
+		      (replace-match "")
+		      (end-of-line 1))
+		    (buffer-string))))
+      ;; Now backend-specific coding
+      (cond
+       ((eq backend 'html)
+	;; We are exporting to HTML
+	(when lang
+	  (require 'htmlize nil t)
+	  (when (not (fboundp 'htmlize-region-for-paste))
 	    ;; we do not have htmlize.el, or an old version of it
 	    (message
-	     "htmlize.el 1.34 or later is needed for source code formatting")
-	    (concat "#+BEGIN_EXAMPLE\n" code
-		    (if (string-match "\n\\'" code) "" "\n")
-		    "#+END_EXAMPLE\n"))
-	;; ok, we are good to go
-	(let* ((mode (and lang (intern (concat lang "-mode"))))
-	       (org-inhibit-startup t)
-	       (org-startup-folded nil)
-	       (htmltext
-		(with-temp-buffer
-		  (insert code)
-		  ;; Free up the protected stuff
-		  (goto-char (point-min))
-		  (while (re-search-forward "^," nil t)
-		    (replace-match "")
-		    (end-of-line 1))
-		  (if (functionp mode)
-		      (funcall mode)
-		    (fundamental-mode))
-		  (font-lock-fontify-buffer)
-		  (org-export-htmlize-region-for-paste
-		   (point-min) (point-max)))))
-	  (if (string-match "<pre\\([^>]*\\)>\n?" htmltext)
-	      (setq htmltext (replace-match
-			      (format "<pre class=\"src src-%s\">\n" lang)
-			      t t htmltext)))
-	  (concat "\n#+BEGIN_HTML\n" htmltext "\n#+END_HTML\n\n"))))
-     (t
-      ;; This is not HTML, so just make it an example.
-      (when (equal lang "org")
-	(while (string-match "^," code)
-	  (setq code (replace-match "" t t code))))
-      (concat "#+BEGIN_EXAMPLE\n" code
-	      (if (string-match "\n\\'" code) "" "\n")
-	      "#+END_EXAMPLE\n")))))
+	     "htmlize.el 1.34 or later is needed for source code formatting")))
+
+	(if lang
+	    (let* ((mode (and lang (intern (concat lang "-mode"))))
+		   (org-inhibit-startup t)
+		   (org-startup-folded nil))
+	      (setq rtn
+		    (with-temp-buffer
+		      (insert rtn)
+		      (if (functionp mode)
+			  (funcall mode)
+			(fundamental-mode))
+		      (font-lock-fontify-buffer)
+		      (org-export-htmlize-region-for-paste
+		       (point-min) (point-max))))
+	      (if (string-match "<pre\\([^>]*\\)>\n?" rtn)
+		  (setq rtn (replace-match
+			     (format "<pre class=\"src src-%s\">\n" lang)
+			     t t rtn))))
+	  (setq rtn (concat "<pre class=\"example\">\n" rtn "</pre>\n")))
+	(setq rtn (org-export-number-lines rtn 'html 1 1 num cont rpllbl))
+	(concat "\n#+BEGIN_HTML\n" rtn "\n#+END_HTML\n\n"))
+       ((eq backend 'latex)
+	(setq rtn (org-export-number-lines rtn 'latex 0 0 num cont rpllbl))
+	(concat "\n#+BEGIN_LaTeX\n\\begin{verbatim}\n" rtn
+		"\n\\end{verbatim}\n#+END_LaTeX\n\n"))
+       ((eq backend 'ascii)
+	;; This is not HTML or LaTeX, so just make it an example.
+	(setq rtn (org-export-number-lines rtn 'ascii 0 0 num cont rpllbl))
+	(concat "#+BEGIN_ASCII\n"
+		(mapconcat
+		 (lambda (l) (concat "  " l))
+		 (org-split-string rtn "\n")
+		 "\n")
+		"\n#+END_ASCII\n"))))))
+
+(defun org-export-number-lines (text backend
+				     &optional skip1 skip2 number cont
+				     replace-labels)
+  (if (not number) (setq replace-labels nil)) ;; must use names if no numbers
+  (setq skip1 (or skip1 0) skip2 (or skip2 0))
+  (if (not cont) (setq org-export-last-code-line-counter-value 0))
+  (with-temp-buffer
+    (insert text)
+    (goto-char (point-max))
+    (skip-chars-backward " \t\n\r")
+    (delete-region (point) (point-max))
+    (beginning-of-line (- 1 skip2))
+    (let* ((last (org-current-line))
+	   (n org-export-last-code-line-counter-value)
+	   (nmax (+ n (- last skip1)))
+	   (fmt (format "%%%dd:  " (length (number-to-string nmax))))
+	   (fm
+	    (cond
+	     ((eq backend 'html) (format "<span class=\"linenr\">%s</span>"
+					 fmt))
+	     ((eq backend 'ascii) fmt)
+	     ((eq backend 'latex) fmt)
+	     (t "")))
+	   ref)
+
+      (goto-line (1+ skip1))
+      (while (and (re-search-forward "^" nil t) (not (eobp)) (< n nmax))
+	(if number
+	    (insert (format fm (incf n)))
+	  (forward-char 1))
+	(when (looking-at ".*?\\([ \t]+\\(((\\(.*?\\)))\\)\\)")
+	  (setq ref (match-string 3))
+	  (if replace-labels
+	      (progn
+		(delete-region (match-beginning 1) (match-end 1))
+		(push (cons ref n) org-export-code-refs))
+	    (goto-char (match-beginning 2))
+	    (delete-region (match-beginning 2) (match-end 2))
+	    (insert "(" ref ")")
+	    (push (cons ref (concat "(" ref ")")) org-export-code-refs))
+	  (when (eq backend 'html)
+	    (save-excursion
+	      (beginning-of-line 1)
+	      (insert (format "<span id=\"coderef-%s\" class=\"coderef-off\">"
+			      ref))
+	      (end-of-line 1)
+	      (insert "</span>")))))
+      (setq org-export-last-code-line-counter-value n)
+      (goto-char (point-max))
+      (newline)
+      (buffer-string))))
 
 ;;; ASCII export