From fac86b03fe19d5bb6fe018c3cbc3becac6263b0e Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 30 Sep 2012 17:20:27 +0200 Subject: [PATCH 1/2] Normalize comma-escaping of src-blocks and example-blocks * lisp/org-src.el (org-escape-code-in-string, org-unescape-code-in-string, org-escape-code-in-region, org-unescape-code-in-region): New functions. (org-edit-src-code, org-edit-src-exit): Use new functions. * lisp/org.el (org-strip-protective-commas): Removed function. * lisp/org-exp.el (org-export-select-backend-specific-text): Use new function. * lisp/ob.el (org-babel-parse-src-block-match, org-babel-parse-inline-src-block-match, org-babel-insert-result): Always escape produced blocks, independently on the language of the block, if any. Use new functions. * doc/org.texi: Update documentation. * testing/lisp/test-ob.el: Update test. --- doc/org.texi | 20 ++++++------- lisp/ob.el | 28 +++++------------- lisp/org-exp.el | 3 +- lisp/org-src.el | 63 +++++++++++++++++++++++++---------------- lisp/org.el | 16 ----------- testing/lisp/test-ob.el | 2 +- 6 files changed, 59 insertions(+), 73 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index 5d407084d..72e8d1198 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -9387,16 +9387,16 @@ so often, shortcuts are provided using the Easy Templates facility @item C-c ' Edit the source code example at point in its native mode. This works by switching to a temporary buffer with the source code. You need to exit by -pressing @kbd{C-c '} again@footnote{Upon exit, lines starting with @samp{*} -or @samp{#} will get a comma prepended, to keep them from being interpreted -by Org as outline nodes or special comments. These commas will be stripped -for editing with @kbd{C-c '}, and also for export.}. The edited version will -then replace the old version in the Org buffer. Fixed-width regions -(where each line starts with a colon followed by a space) will be edited -using @code{artist-mode}@footnote{You may select a different-mode with the -variable @code{org-edit-fixed-width-region-mode}.} to allow creating ASCII -drawings easily. Using this command in an empty line will create a new -fixed-width region. +pressing @kbd{C-c '} again@footnote{Upon exit, lines starting with @samp{*}, +@samp{,*}, @samp{#+} and @samp{,#+} will get a comma prepended, to keep them +from being interpreted by Org as outline nodes or special syntax. These +commas will be stripped for editing with @kbd{C-c '}, and also for export.}. +The edited version will then replace the old version in the Org buffer. +Fixed-width regions (where each line starts with a colon followed by a space) +will be edited using @code{artist-mode}@footnote{You may select +a different-mode with the variable @code{org-edit-fixed-width-region-mode}.} +to allow creating ASCII drawings easily. Using this command in an empty line +will create a new fixed-width region. @kindex C-c l @item C-c l Calling @code{org-store-link} while editing a source code example in a diff --git a/lisp/ob.el b/lisp/ob.el index f15457d68..0f3a3efb1 100644 --- a/lisp/ob.el +++ b/lisp/ob.el @@ -39,7 +39,6 @@ (declare-function show-all "outline" ()) (declare-function org-reduce "org" (CL-FUNC CL-SEQ &rest CL-KEYS)) (declare-function org-mark-ring-push "org" (&optional pos buffer)) -(declare-function org-strip-protective-commas "org" (beg end)) (declare-function tramp-compat-make-temp-file "tramp-compat" (filename &optional dir-flag)) (declare-function tramp-dissect-file-name "tramp" (name &optional nodefault)) @@ -64,7 +63,6 @@ (declare-function org-cycle "org" (&optional arg)) (declare-function org-uniquify "org" (list)) (declare-function org-current-level "org" ()) -(declare-function org-strip-protective-commas "org" (beg end)) (declare-function org-table-import "org-table" (file arg)) (declare-function org-add-hook "org-compat" (hook function &optional append local)) @@ -87,10 +85,10 @@ (declare-function org-list-struct "org-list" ()) (declare-function org-list-prevs-alist "org-list" (struct)) (declare-function org-list-get-list-end "org-list" (item struct prevs)) -(declare-function org-strip-protective-commas "org" (beg end)) (declare-function org-remove-if "org" (predicate seq)) (declare-function org-completing-read "org" (&rest args)) -(declare-function org-add-protective-commas "org-src" (beg end)) +(declare-function org-escape-code-in-region "org-src" (beg end)) +(declare-function org-unescape-code-in-string "org-src" (s)) (defgroup org-babel nil "Code block evaluation and management in `org-mode' documents." @@ -1241,7 +1239,7 @@ may be specified in the properties of the current outline entry." ;; get block body less properties, protective commas, and indentation (with-temp-buffer (save-match-data - (insert (org-babel-strip-protective-commas body lang)) + (insert (org-unescape-code-in-string body)) (unless preserve-indentation (org-do-remove-indentation)) (buffer-string))) (org-babel-merge-params @@ -1258,8 +1256,7 @@ may be specified in the properties of the current outline entry." (let* ((lang (org-no-properties (match-string 2))) (lang-headers (intern (concat "org-babel-default-header-args:" lang)))) (list lang - (org-babel-strip-protective-commas - (org-no-properties (match-string 5)) lang) + (org-unescape-code-in-string (org-no-properties (match-string 5))) (org-babel-merge-params org-babel-default-inline-header-args (org-babel-params-from-properties lang) @@ -1937,10 +1934,10 @@ code ---- the results are extracted in the syntax of the source ((member "prepend" result-params)))) ; already there (setq results-switches (if results-switches (concat " " results-switches) "")) - (let ((wrap (lambda (start finish &optional escape) + (let ((wrap (lambda (start finish) (goto-char end) (insert (concat finish "\n")) (goto-char beg) (insert (concat start "\n")) - (if escape (org-add-protective-commas (point) end)) + (org-escape-code-in-region (point) end) (goto-char end) (goto-char (point-at-eol)) (setq end (point-marker)))) (proper-list-p (lambda (it) (and (listp it) (null (cdr (last it))))))) @@ -1987,7 +1984,7 @@ code ---- the results are extracted in the syntax of the source ((member "latex" result-params) (funcall wrap "#+BEGIN_LaTeX" "#+END_LaTeX")) ((member "org" result-params) - (funcall wrap "#+BEGIN_SRC org" "#+END_SRC" 'escape)) + (funcall wrap "#+BEGIN_SRC org" "#+END_SRC")) ((member "code" result-params) (funcall wrap (format "#+BEGIN_SRC %s%s" (or lang "none") results-switches) "#+END_SRC")) @@ -2370,17 +2367,6 @@ block but are passed literally to the \"example-block\"." (funcall nb-add (buffer-substring index (point-max)))) new-body)) -(defun org-babel-strip-protective-commas (body &optional lang) - "Strip protective commas from bodies of source blocks." - (with-temp-buffer - (insert body) - (if (and lang (string= lang "org")) - (progn (goto-char (point-min)) - (while (re-search-forward "^[ \t]*\\(,\\)" nil t) - (replace-match "" nil nil nil 1))) - (org-strip-protective-commas (point-min) (point-max))) - (buffer-string))) - (defun org-babel-script-escape (str &optional force) "Safely convert tables into elisp lists." (let (in-single in-double out) diff --git a/lisp/org-exp.el b/lisp/org-exp.el index 6b506cd12..cac03afb4 100644 --- a/lisp/org-exp.el +++ b/lisp/org-exp.el @@ -48,6 +48,7 @@ (declare-function org-table-colgroup-line-p "org-table" (line)) (declare-function org-pop-to-buffer-same-window "org-compat" (&optional buffer-or-name norecord label)) +(declare-function org-unescape-code-in-region "org-src" (beg end)) (autoload 'org-export-generic "org-export-generic" "Export using the generic exporter" t) @@ -1790,7 +1791,7 @@ from the buffer." beg-content end-content `(org-protected t original-indentation ,ind org-native-text t)) ;; strip protective commas - (org-strip-protective-commas beg-content end-content) + (org-unescape-code-in-region beg-content end-content) (delete-region (match-beginning 0) (match-end 0)) (save-excursion (goto-char beg) diff --git a/lisp/org-src.el b/lisp/org-src.el index 9d6bc1aa2..2a7c27237 100644 --- a/lisp/org-src.el +++ b/lisp/org-src.el @@ -41,10 +41,8 @@ (declare-function org-at-table.el-p "org" ()) (declare-function org-get-indentation "org" (&optional line)) (declare-function org-switch-to-buffer-other-window "org" (&rest args)) -(declare-function org-strip-protective-commas "org" (beg end)) (declare-function org-pop-to-buffer-same-window "org-compat" (&optional buffer-or-name norecord label)) -(declare-function org-strip-protective-commas "org" (beg end)) (declare-function org-base-buffer "org" (buffer)) (defcustom org-edit-src-region-extra nil @@ -311,13 +309,8 @@ buffer." (error "Language mode `%s' fails with: %S" lang-f (nth 1 e))))) (dolist (pair transmitted-variables) (org-set-local (car pair) (cadr pair))) - (if (derived-mode-p 'org-mode) - (progn - (goto-char (point-min)) - (while (re-search-forward "^," nil t) - (if (eq (org-current-line) line) (setq total-nindent (1+ total-nindent))) - (replace-match ""))) - (org-strip-protective-commas (point-min) (point-max))) + ;; Remove protecting commas from visible part of buffer. + (org-unescape-code-in-region (point-min) (point-max)) (when markline (org-goto-line (1+ (- markline begline))) (org-move-to-column @@ -590,20 +583,38 @@ the language, a switch telling if the content should be in a single line." (goto-char pos) (org-get-indentation))) -(defun org-add-protective-commas (beg end &optional line) - "Add protective commas in region. -Return the delta in size of the region." +(defun org-escape-code-in-region (beg end) + "Escape lines between BEG and END. +Escaping happens when a line starts with \"*\", \"#+\", \",*\" or +\",#+\" by appending a comma to it." (interactive "r") - (let ((org-re "^\\(.\\)") - (other-re "^\\([*]\\|[ \t]*#\\+\\)") - (delta 0)) - (save-excursion - (goto-char beg) - (while (re-search-forward (if (derived-mode-p 'org-mode) org-re other-re) - end t) - (if (and line (eq (org-current-line) line)) (setq delta (1+ delta))) - (replace-match ",\\1"))) - delta)) + (save-excursion + (goto-char beg) + (while (re-search-forward "^[ \t]*,?\\(\\*\\|#\\+\\)" end t) + (replace-match ",\\1" nil nil nil 1)))) + +(defun org-escape-code-in-string (s) + "Escape lines in string S. +Escaping happens when a line starts with \"*\", \"#+\", \",*\" or +\",#+\" by appending a comma to it." + (replace-regexp-in-string "^[ \t]*,?\\(\\*\\|#\\+\\)" ",\\1" s nil nil 1)) + +(defun org-unescape-code-in-region (beg end) + "Un-escape lines between BEG and END. +Un-escaping happens by removing the first comma on lines starting +with \",*\", \",#+\", \",,*\" and \",,#+\"." + (interactive "r") + (save-excursion + (goto-char beg) + (while (re-search-forward "^[ \t]*,?\\(,\\)\\(?:\\*\\|#\\+\\)" end t) + (replace-match "" nil nil nil 1)))) + +(defun org-unescape-code-in-string (s) + "Un-escape lines in string S. +Un-escaping happens by removing the first comma on lines starting +with \",*\", \",#+\", \",,*\" and \",,#+\"." + (replace-regexp-in-string + "^[ \t]*,?\\(,\\)\\(?:\\*\\|#\\+\\)" "" s nil nil 1)) (defun org-edit-src-exit (&optional context) "Exit special edit and protect problematic lines." @@ -649,8 +660,12 @@ Return the delta in size of the region." (goto-char (point-min)) (if (looking-at "\\s-*") (replace-match " "))) (when (org-bound-and-true-p org-edit-src-from-org-mode) - (setq delta (+ delta (org-add-protective-commas - (point-min) (point-max) line)))) + (org-escape-code-in-region (point-min) (point-max)) + (setq delta (+ delta + (save-excursion + (org-goto-line line) + (if (looking-at "[ \t]*\\(,,\\)?\\(\\*\\|#+\\)") 1 + 0))))) (when (org-bound-and-true-p org-edit-src-picture) (setq preserve-indentation nil) (untabify (point-min) (point-max)) diff --git a/lisp/org.el b/lisp/org.el index 1d103ff1e..37f6f3a81 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -5560,22 +5560,6 @@ by a #." t) (t nil)))))) -(defun org-strip-protective-commas (beg end) - "Strip protective commas between BEG and END in the current buffer." - (interactive "r") - (save-excursion - (save-match-data - (goto-char beg) - (let ((front-line (save-excursion - (re-search-forward - "[^[:space:]]" end t) - (goto-char (match-beginning 0)) - (current-column)))) - (while (re-search-forward "^[ \t]*\\(,\\)\\([*]\\|#\\)" end t) - (goto-char (match-beginning 1)) - (when (= (current-column) front-line) - (replace-match "" nil nil nil 1))))))) - (defun org-activate-angle-links (limit) "Run through the buffer and add overlays to links." (if (re-search-forward org-angle-link-re limit t) diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el index 0565043c0..08de70296 100644 --- a/testing/lisp/test-ob.el +++ b/testing/lisp/test-ob.el @@ -932,7 +932,7 @@ hello there "#+BEGIN_SRC org ,* heading ,** subheading -,content +content #+END_SRC" "* org-babel-remove-result From 3bd22fb0456ff71b941b1965497df605ac1689e1 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 30 Sep 2012 17:45:21 +0200 Subject: [PATCH 2/2] org-element: Store value of example-blocks and src-blocks unescaped * lisp/org-element.el (org-element-example-block-parser, org-element-src-block-parser): Store value of example-blocks and src-blocks unescaped. (org-element-example-block-interpreter, org-element-src-block-interpreter): Escape value again when storing it. * contrib/lisp/org-export.el (org-export-unravel-code): Don't clean commas from code since org-element already took care of it. * testing/lisp/test-org-export.el: Update test. * testing/lisp/test-org-element.el: Add tests. --- contrib/lisp/org-export.el | 15 +++++---------- lisp/org-element.el | 12 ++++++++---- testing/lisp/test-org-element.el | 30 ++++++++++++++++++++++++++---- testing/lisp/test-org-export.el | 15 +-------------- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index 4f01b7ee4..be7f14dea 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -3516,22 +3516,17 @@ 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. + ;; beginning and end. (code (let ((c (replace-regexp-in-string "\\`\\([ \t]*\n\\)+" "" (replace-regexp-in-string "\\(:?[ \t]*\n\\)*[ \t]*\\'" "\n" (org-element-property :value element))))) ;; If appropriate, remove global indentation. - (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= (org-element-property :language element) "org") - (replace-regexp-in-string "^," "" c) - (replace-regexp-in-string - "^\\(,\\)\\(:?\\*\\|[ \t]*#\\+\\)" "" c nil nil 1)))) + (if (or org-src-preserve-indentation + (org-element-property :preserve-indent element)) + c + (org-remove-indentation c)))) ;; Get format used for references. (label-fmt (regexp-quote (or (org-element-property :label-fmt element) diff --git a/lisp/org-element.el b/lisp/org-element.el index 21628b502..1d1f3bf51 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -1504,7 +1504,9 @@ containing `:begin', `:end', `:number-lines', `:preserve-indent', (begin (car keywords)) (contents-begin (progn (forward-line) (point))) (hidden (org-invisible-p2)) - (value (buffer-substring-no-properties contents-begin contents-end)) + (value (org-unescape-code-in-string + (buffer-substring-no-properties + contents-begin contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -1531,7 +1533,8 @@ CONTENTS is nil." (let ((switches (org-element-property :switches example-block))) (concat "#+BEGIN_EXAMPLE" (and switches (concat " " switches)) "\n" (org-remove-indentation - (org-element-property :value example-block)) + (org-escape-code-in-string + (org-element-property :value example-block))) "#+END_EXAMPLE"))) @@ -2032,7 +2035,8 @@ Assume point is at the beginning of the block." ;; Get visibility status. (hidden (progn (forward-line) (org-invisible-p2))) ;; Retrieve code. - (value (buffer-substring-no-properties (point) contents-end)) + (value (org-unescape-code-in-string + (buffer-substring-no-properties (point) contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -2080,7 +2084,7 @@ CONTENTS is nil." (concat (and lang (concat " " lang)) (and switches (concat " " switches)) (and params (concat " " params)))) - value + (org-escape-code-in-string value) "#+END_SRC"))) diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index 6fc3d4361..0b636731a 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -465,7 +465,13 @@ CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] => 0:01" (should-not (org-test-with-temp-text "#+BEGIN_EXAMPLE" (org-element-map - (org-element-parse-buffer) 'example-block 'identity nil t)))) + (org-element-parse-buffer) 'example-block 'identity nil t))) + ;; Properly un-escape code. + (should + (equal "* Headline\n #+keyword\nText\n" + (org-test-with-temp-text + "#+BEGIN_EXAMPLE\n,* Headline\n ,#+keyword\nText\n#+END_EXAMPLE" + (org-element-property :value (org-element-at-point)))))) (ert-deftest test-org-element/block-switches () "Test `example-block' and `src-block' switches parsing." @@ -1450,7 +1456,13 @@ Outside list" ;; Ignore incomplete block. (should-not (org-test-with-temp-text "#+BEGIN_SRC" - (org-element-map (org-element-parse-buffer) 'src-block 'identity)))) + (org-element-map (org-element-parse-buffer) 'src-block 'identity))) + ;; Properly un-escape code. + (should + (equal "* Headline\n #+keyword\nText\n" + (org-test-with-temp-text + "#+BEGIN_SRC org\n,* Headline\n ,#+keyword\nText\n#+END_SRC" + (org-element-property :value (org-element-at-point)))))) ;;;; Statistics Cookie @@ -1911,7 +1923,12 @@ CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] => 0:01")) (should (equal (org-test-parse-and-interpret "#+BEGIN_EXAMPLE -n -k\n(+ 1 1)\n#+END_EXAMPLE") - "#+BEGIN_EXAMPLE -n -k\n(+ 1 1)\n#+END_EXAMPLE\n"))) + "#+BEGIN_EXAMPLE -n -k\n(+ 1 1)\n#+END_EXAMPLE\n")) + ;; Preserve code escaping. + (should + (equal (org-test-parse-and-interpret + "#+BEGIN_EXAMPLE\n,* Headline\n ,#+keyword\nText #+END_EXAMPLE") + "#+BEGIN_EXAMPLE\n,* Headline\n ,#+keyword\nText #+END_EXAMPLE\n"))) (ert-deftest test-org-element/export-block-interpreter () "Test export block interpreter." @@ -1975,7 +1992,12 @@ CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>\n")))) (equal (let ((org-edit-src-content-indentation 2)) (org-test-parse-and-interpret "#+BEGIN_SRC emacs-lisp -n -k\n(+ 1 1)\n#+END_SRC")) - "#+BEGIN_SRC emacs-lisp -n -k\n (+ 1 1)\n#+END_SRC\n"))) + "#+BEGIN_SRC emacs-lisp -n -k\n (+ 1 1)\n#+END_SRC\n")) + ;; Preserve code escaping. + (should + (equal (org-test-parse-and-interpret + "#+BEGIN_SRC org\n,* Headline\n ,#+keyword\nText #+END_SRC") + "#+BEGIN_SRC org\n,* Headline\n ,#+keyword\nText #+END_SRC\n"))) (ert-deftest test-org-element/table-interpreter () "Test table, table-row and table-cell interpreters." diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el index 7ef578964..87fc306e9 100644 --- a/testing/lisp/test-org-export.el +++ b/testing/lisp/test-org-export.el @@ -1068,20 +1068,7 @@ Another text. (ref:text) #+END_EXAMPLE" (goto-line 5) (should (equal (org-export-unravel-code (org-element-at-point)) - '("(+ 2 2)\n(+ 3 3)\n" (2 . "one"))))) - ;; 5. Free up comma-protected lines. - ;; - ;; 5.1. In an Org source block, every line is protected. - (org-test-with-temp-text - "#+BEGIN_SRC org\n,* Test\n,# comment\n,Text\n#+END_SRC" - (should (equal (org-export-unravel-code (org-element-at-point)) - '("* Test\n# comment\nText\n")))) - ;; 5.2. In other blocks, only headlines, comments and keywords are - ;; protected. - (org-test-with-temp-text - "#+BEGIN_EXAMPLE\n,* Headline\n, * Not headline\n,Keep\n#+END_EXAMPLE" - (should (equal (org-export-unravel-code (org-element-at-point)) - '("* Headline\n, * Not headline\n,Keep\n")))))) + '("(+ 2 2)\n(+ 3 3)\n" (2 . "one")))))))