From d66d6f55e02dd75e308ea105a08feee522ff7c86 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Wed, 2 Sep 2015 21:02:41 +0200 Subject: [PATCH] ox: Fix smart inner quotes * lisp/ox.el (org-export--smart-quote-status): Fix inner smart quotes. * testing/lisp/test-ox.el (test-org-export/activate-smart-quotes): Add tests. Reported-by: "T.F. Torrey" --- lisp/ox.el | 91 +++++++++++++++++++++-------------------- testing/lisp/test-ox.el | 15 +++++++ 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/lisp/ox.el b/lisp/ox.el index 0b9f4f0cb..d3179cc9a 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -5174,62 +5174,65 @@ INFO is the current export state, as a plist." table))) (value (gethash parent cache 'missing-data))) (if (not (eq value 'missing-data)) (cdr (assq s value)) - (let (level1-open level2-open full-status) + (let (level1-open full-status) (org-element-map parent 'plain-text (lambda (text) (let ((start 0) current-status) (while (setq start (string-match "['\"]" text start)) - (incf start) (push (cond ((equal (match-string 0 text) "\"") (setf level1-open (not level1-open)) - (setf level2-open nil) (if level1-open 'opening-double-quote 'closing-double-quote)) ;; Not already in a level 1 quote: this is an ;; apostrophe. ((not level1-open) 'apostrophe) - ;; Apostrophe. - ((org-string-match-p "\\S-'\\S-" text) 'apostrophe) - ;; Apostrophe at the beginning of a string. Check - ;; white space at the end of the last object. - ((and (org-string-match-p "\\`'\\S-" text) - (let ((p (org-export-get-previous-element text info))) - (and p - (if (stringp p) - (not (org-string-match-p "[ \t]\\'" p)) - (memq (org-element-property :post-blank p) - '(0 nil)))))) - 'apostrophe) - ;; Apostrophe at the end of a string. Check white - ;; space at the beginning of the next object, which - ;; can only happen if that object is a string. - ((and (org-string-match-p "\\S-'\\'" text) - (let ((n (org-export-get-next-element text info))) - (and n - (not (and (stringp n) - (org-string-match-p "\\`[ \t]" n)))))) - 'apostrophe) - ;; Lonesome apostrophe. Check white space around - ;; both ends. - ((and (equal text "'") - (let ((p (org-export-get-previous-element text info))) - (and p - (if (stringp p) - (not (org-string-match-p "[ \t]\\'" p)) - (memq (org-element-property :post-blank p) - '(0 nil))) - (let ((n (org-export-get-next-element text info))) - (and n - (not (and (stringp n) - (org-string-match-p "\\`[ \t]" - n)))))))) - 'apostrophe) - ;; Else, consider it as a level 2 quote. - (t (setf level2-open (not level2-open)) - (if level2-open 'opening-single-quote - 'closing-single-quote))) - current-status)) + ;; Extract previous char and next char. As + ;; a special case, they can also be set to `blank', + ;; `no-blank' or nil. Then determine if current + ;; match is allowed as an opening quote or a closing + ;; quote. + (t + (let* ((previous + (if (> start 0) (substring text (1- start) start) + (let ((p (org-export-get-previous-element + text info))) + (cond ((not p) nil) + ((stringp p) (substring p (1- (length p)))) + ((memq (org-element-property :post-blank p) + '(0 nil)) + 'no-blank) + (t 'blank))))) + (next + (if (< (1+ start) (length text)) + (substring text (1+ start) (+ start 2)) + (let ((n (org-export-get-next-element text info))) + (cond ((not n) nil) + ((stringp n) (substring n 0 1)) + (t 'no-blank))))) + (allow-open + (and (if (stringp previous) + (string-match "\\s\"\\|\\s-\\|\\s(" + previous) + (memq previous '(blank nil))) + (if (stringp next) + (string-match "\\w\\|\\s.\\|\\s_" next) + (eq next 'no-blank)))) + (allow-close + (and (if (stringp previous) + (string-match "\\w\\|\\s.\\|\\s_" previous) + (eq previous 'no-blank)) + (if (stringp next) + (string-match "\\s-\\|\\s)\\|\\s.\\|\\s\"" + next) + (memq next '(blank nil)))))) + (cond + ((and allow-open allow-close) (error "Should not happen")) + (allow-open 'opening-single-quote) + (allow-close 'closing-single-quote) + (t 'apostrophe))))) + current-status) + (setq start (1+ start))) (when current-status (push (cons text (nreverse current-status)) full-status)))) info nil org-element-recursive-objects) diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index ed4a483d9..2c97cef78 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -2821,6 +2821,21 @@ Another text. (ref:text) (org-element-map tree 'plain-text (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) info))))) + ;; Inner quotes: close to special symbols. + (should + (equal '("« outer (« inner ») outer »") + (let ((org-export-default-language "fr")) + (org-test-with-parsed-data "\"outer ('inner') outer\"" + (org-element-map tree 'plain-text + (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) + info))))) + (should + (equal '("« « inner » »") + (let ((org-export-default-language "fr")) + (org-test-with-parsed-data "\"'inner'\"" + (org-element-map tree 'plain-text + (lambda (s) (org-export-activate-smart-quotes s :utf-8 info)) + info))))) ;; Apostrophe: standard test. (should (equal '("It « shouldn’t » fail")