diff options
author | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2017-05-01 23:35:39 +0200 |
---|---|---|
committer | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2017-05-01 23:35:39 +0200 |
commit | 500abcd7fbe24abc887300faec2058905a13fa33 (patch) | |
tree | 2fbac89fe496a5254654c7090935edd2485e88f6 | |
parent | 823c821bc8ec11988440fdfa3ab48fdfa1955f26 (diff) | |
download | org-mode-500abcd7fbe24abc887300faec2058905a13fa33.tar.gz |
ob-core: Fix `org-babel-balanced-split'release_9.0.6
* lisp/ob-core.el (org-babel-balanced-split): Rewrite function.
Reported-by: Moritz Heidkamp <moritz@twoticketsplease.de>
<http://permalink.gmane.org/gmane.emacs.orgmode/113204>
-rw-r--r-- | lisp/ob-core.el | 103 | ||||
-rw-r--r-- | testing/lisp/test-ob.el | 16 |
2 files changed, 87 insertions, 32 deletions
diff --git a/lisp/ob-core.el b/lisp/ob-core.el index 03a187e..8a4c1ba 100644 --- a/lisp/ob-core.el +++ b/lisp/ob-core.el @@ -1432,36 +1432,79 @@ specified in the properties of the current outline entry." (defun org-babel-balanced-split (string alts) "Split STRING on instances of ALTS. -ALTS is a cons of two character options where each option may be -either the numeric code of a single character or a list of -character alternatives. For example to split on balanced -instances of \"[ \t]:\" set ALTS to ((32 9) . 58)." - (let* ((matches (lambda (ch spec) (if (listp spec) (member ch spec) (equal spec ch)))) - (matched (lambda (ch last) - (if (consp alts) - (and (funcall matches ch (cdr alts)) - (funcall matches last (car alts))) - (funcall matches ch alts)))) - (balance 0) (last 0) - quote partial lst) - (mapc (lambda (ch) ; split on [], (), "" balanced instances of [ \t]: - (setq balance (+ balance - (cond ((or (equal 91 ch) (equal 40 ch)) 1) - ((or (equal 93 ch) (equal 41 ch)) -1) - (t 0)))) - (when (and (equal 34 ch) (not (equal 92 last))) - (setq quote (not quote))) - (setq partial (cons ch partial)) - (when (and (= balance 0) (not quote) (funcall matched ch last)) - (setq lst (cons (apply #'string (nreverse - (if (consp alts) - (cddr partial) - (cdr partial)))) - lst)) - (setq partial nil)) - (setq last ch)) - (string-to-list string)) - (nreverse (cons (apply #'string (nreverse partial)) lst)))) +ALTS is a character, or cons of two character options where each +option may be either the numeric code of a single character or +a list of character alternatives. For example, to split on +balanced instances of \"[ \t]:\", set ALTS to ((32 9) . 58)." + (with-temp-buffer + (insert string) + (goto-char (point-min)) + (let ((splitp (lambda (past next) + ;; Non-nil when there should be a split after NEXT + ;; character. PAST is the character before NEXT. + (pcase alts + (`(,(and first (pred consp)) . ,(and second (pred consp))) + (and (memq past first) (memq next second))) + (`(,first . ,(and second (pred consp))) + (and (eq past first) (memq next second))) + (`(,(and first (pred consp)) . ,second) + (and (memq past first) (eq next second))) + (`(,first . ,second) + (and (eq past first) (eq next second))) + ((pred (eq next)) t) + (_ nil)))) + (partial nil) + (result nil)) + (while (not (eobp)) + (cond + ((funcall splitp (char-before) (char-after)) + ;; There is a split after point. If ALTS is two-folds, + ;; remove last parsed character as it belongs to ALTS. + (when (consp alts) (pop partial)) + ;; Include elements parsed so far in RESULTS and flush + ;; partial parsing. + (when partial + (push (apply #'string (nreverse partial)) result) + (setq partial nil)) + (forward-char)) + ((memq (char-after) '(?\( ?\[)) + ;; Include everything between balanced brackets. + (let* ((origin (point)) + (after (char-after)) + (openings (list after))) + (forward-char) + (while (and openings (re-search-forward "[]()]" nil t)) + (pcase (char-before) + ((and match (or ?\[ ?\()) (push match openings)) + (?\] (when (eq ?\[ (car openings)) (pop openings))) + (_ (when (eq ?\( (car openings)) (pop openings))))) + (if (null openings) + (setq partial + (nconc (nreverse (string-to-list + (buffer-substring origin (point)))) + partial)) + ;; Un-balanced bracket. Backtrack. + (push after partial) + (goto-char (1+ origin))))) + ((and (eq ?\" (char-after)) (not (eq ?\\ (char-before)))) + ;; Include everything between non-escaped double quotes. + (push ?\" partial) + (let ((origin (point))) + (condition-case nil + ;; Use `read' since it is fast and takes care of + ;; escaped quotes already. + (setq partial + (nconc (cons ?\" + (nreverse (string-to-list + (read (current-buffer))))) + partial)) + ;; No closing double quote found. Backtrack. + (end-of-file (goto-char (1+ origin)))))) + (t (push (char-after) partial) + (forward-char)))) + ;; Add pending parsing and return result. + (when partial (push (apply #'string (nreverse partial)) result)) + (nreverse result)))) (defun org-babel-join-splits-near-ch (ch list) "Join splits where \"=\" is on either end of the split." diff --git a/testing/lisp/test-ob.el b/testing/lisp/test-ob.el index fe74c0c..fe4ecb8 100644 --- a/testing/lisp/test-ob.el +++ b/testing/lisp/test-ob.el @@ -701,11 +701,23 @@ x (should (= 2 (length (org-babel-ref-split-args "a=\"this, no work\", b=1")))))) -(ert-deftest test-ob/org-babel-balanced-split () +(ert-deftest test-ob/balanced-split () + "Test `org-babel-balanced-split' specifications." (should (equal '(":a 1" "b [2 3]" "c (4 :d (5 6))") (org-babel-balanced-split ":a 1 :b [2 3] :c (4 :d (5 6))" - '((32 9) . 58))))) + '((32 9) . 58)))) + ;; Handle un-balanced parens. + (should + (equal '(":foo ((6)" "bar 1") + (org-babel-balanced-split ":foo ((6) :bar 1" '((32 9) . 58)))) + (should + (equal '(":foo \"(foo\"" "bar 2") + (org-babel-balanced-split ":foo \"(foo\" :bar 2" '((32 9) . 58)))) + ;; Handle un-balanced quotes. + (should + (equal '(":foo \"1" "bar 3") + (org-babel-balanced-split ":foo \"1 :bar 3" '((32 9) . 58))))) (ert-deftest test-ob/commented-last-block-line-no-var () (org-test-with-temp-text-in-file " |