Implement :pre-blank property for items and footnotes definitions

* lisp/org-element.el (org-element-footnote-definition-parser):
(org-element-item-parser): Add `:pre-blank' property.
(org-element-footnote-definition-interpreter):
(org-element-item-interpreter):
(org-element-interpret-data):
* lisp/ox.el (org-export-data): Use new property.
* testing/lisp/test-org-element.el (test-org-element/footnote-definition-parser):
(test-org-element/item-parser): Add tests.
This commit is contained in:
Nicolas Goaziou 2017-11-09 22:47:35 +01:00
parent 8f3077d14a
commit d07ee7f7f2
3 changed files with 123 additions and 57 deletions

View File

@ -812,7 +812,8 @@ their value.
Return a list whose CAR is `footnote-definition' and CDR is Return a list whose CAR is `footnote-definition' and CDR is
a plist containing `:label', `:begin' `:end', `:contents-begin', a plist containing `:label', `:begin' `:end', `:contents-begin',
`:contents-end', `:post-blank' and `:post-affiliated' keywords. `:contents-end', `:pre-blank',`:post-blank' and
`:post-affiliated' keywords.
Assume point is at the beginning of the footnote definition." Assume point is at the beginning of the footnote definition."
(save-excursion (save-excursion
@ -838,12 +839,16 @@ Assume point is at the beginning of the footnote definition."
((eq ?* (char-after (match-beginning 0))) (match-beginning 0)) ((eq ?* (char-after (match-beginning 0))) (match-beginning 0))
(t (skip-chars-forward " \r\t\n" limit) (t (skip-chars-forward " \r\t\n" limit)
(if (= limit (point)) limit (line-beginning-position)))))) (if (= limit (point)) limit (line-beginning-position))))))
(pre-blank 0)
(contents-begin (contents-begin
(progn (search-forward "]") (progn (search-forward "]")
(skip-chars-forward " \r\t\n" end) (skip-chars-forward " \r\t\n" end)
(cond ((= (point) end) nil) (cond ((= (point) end) nil)
((= (line-beginning-position) post-affiliated) (point)) ((= (line-beginning-position) post-affiliated) (point))
(t (line-beginning-position))))) (t
(setq pre-blank
(count-lines (line-beginning-position) begin))
(line-beginning-position)))))
(contents-end (contents-end
(progn (goto-char end) (progn (goto-char end)
(skip-chars-backward " \r\t\n") (skip-chars-backward " \r\t\n")
@ -855,6 +860,7 @@ Assume point is at the beginning of the footnote definition."
:end end :end end
:contents-begin contents-begin :contents-begin contents-begin
:contents-end (and contents-begin contents-end) :contents-end (and contents-begin contents-end)
:pre-blank pre-blank
:post-blank (count-lines contents-end end) :post-blank (count-lines contents-end end)
:post-affiliated post-affiliated) :post-affiliated post-affiliated)
(cdr affiliated)))))) (cdr affiliated))))))
@ -862,9 +868,18 @@ Assume point is at the beginning of the footnote definition."
(defun org-element-footnote-definition-interpreter (footnote-definition contents) (defun org-element-footnote-definition-interpreter (footnote-definition contents)
"Interpret FOOTNOTE-DEFINITION element as Org syntax. "Interpret FOOTNOTE-DEFINITION element as Org syntax.
CONTENTS is the contents of the footnote-definition." CONTENTS is the contents of the footnote-definition."
(let ((pre-blank
(min (or (org-element-property :pre-blank footnote-definition)
;; 0 is specific to paragraphs at the beginning of
;; the footnote definition, so we use 1 as
;; a fall-back value, which is more universal.
1)
;; Footnote ends after more than two consecutive empty
;; lines: limit ourselves to 2 newline characters.
2)))
(concat (format "[fn:%s]" (org-element-property :label footnote-definition)) (concat (format "[fn:%s]" (org-element-property :label footnote-definition))
" " (if (= pre-blank 0) (concat " " (org-trim contents))
contents)) (concat (make-string pre-blank ?\n) contents)))))
;;;; Headline ;;;; Headline
@ -1195,8 +1210,8 @@ STRUCT is the structure of the plain list.
Return a list whose CAR is `item' and CDR is a plist containing Return a list whose CAR is `item' and CDR is a plist containing
`:bullet', `:begin', `:end', `:contents-begin', `:contents-end', `:bullet', `:begin', `:end', `:contents-begin', `:contents-end',
`:checkbox', `:counter', `:tag', `:structure', `:post-blank' and `:checkbox', `:counter', `:tag', `:structure', `:pre-blank',
`:post-affiliated' keywords. `:post-blank' and `:post-affiliated' keywords.
When optional argument RAW-SECONDARY-P is non-nil, item's tag, if When optional argument RAW-SECONDARY-P is non-nil, item's tag, if
any, will not be parsed as a secondary string, but as a plain any, will not be parsed as a secondary string, but as a plain
@ -1223,8 +1238,10 @@ Assume point is at the beginning of the item."
(string-to-number (match-string 0 c))))))) (string-to-number (match-string 0 c)))))))
(end (progn (goto-char (nth 6 (assq (point) struct))) (end (progn (goto-char (nth 6 (assq (point) struct)))
(if (bolp) (point) (line-beginning-position 2)))) (if (bolp) (point) (line-beginning-position 2))))
(pre-blank 0)
(contents-begin (contents-begin
(progn (goto-char (progn
(goto-char
;; Ignore tags in un-ordered lists: they are just ;; Ignore tags in un-ordered lists: they are just
;; a part of item's body. ;; a part of item's body.
(if (and (match-beginning 4) (if (and (match-beginning 4)
@ -1236,7 +1253,10 @@ Assume point is at the beginning of the item."
;; If first line isn't empty, contents really ;; If first line isn't empty, contents really
;; start at the text after item's meta-data. ;; start at the text after item's meta-data.
((= (line-beginning-position) begin) (point)) ((= (line-beginning-position) begin) (point))
(t (line-beginning-position))))) (t
(setq pre-blank
(count-lines (line-beginning-position) begin))
(line-beginning-position)))))
(contents-end (and contents-begin (contents-end (and contents-begin
(progn (goto-char end) (progn (goto-char end)
(skip-chars-backward " \r\t\n") (skip-chars-backward " \r\t\n")
@ -1251,6 +1271,7 @@ Assume point is at the beginning of the item."
:checkbox checkbox :checkbox checkbox
:counter counter :counter counter
:structure struct :structure struct
:pre-blank pre-blank
:post-blank (count-lines (or contents-end begin) end) :post-blank (count-lines (or contents-end begin) end)
:post-affiliated begin)))) :post-affiliated begin))))
(org-element-put-property (org-element-put-property
@ -1275,14 +1296,18 @@ CONTENTS is the contents of the element."
(counter (org-element-property :counter item)) (counter (org-element-property :counter item))
(tag (let ((tag (org-element-property :tag item))) (tag (let ((tag (org-element-property :tag item)))
(and tag (org-element-interpret-data tag)))) (and tag (org-element-interpret-data tag))))
;; Compute indentation. (pre-blank
(ind (make-string (length bullet) 32)) (min (or (org-element-property :pre-blank item)
(item-starts-with-par-p ;; 0 is specific to paragraphs at the beginning of
(eq (org-element-type (car (org-element-contents item))) ;; the item, so we use 1 as a fall-back value,
'paragraph))) ;; which is more universal.
1)
;; Lists ends after more than two consecutive empty
;; lines: limit ourselves to 2 newline characters.
2))
(ind (make-string (length bullet) ?\s)))
;; Indent contents. ;; Indent contents.
(concat (concat bullet
bullet
(and counter (format "[@%d] " counter)) (and counter (format "[@%d] " counter))
(pcase checkbox (pcase checkbox
(`on "[X] ") (`on "[X] ")
@ -1293,8 +1318,8 @@ CONTENTS is the contents of the element."
(when contents (when contents
(let ((contents (replace-regexp-in-string (let ((contents (replace-regexp-in-string
"\\(^\\)[ \t]*\\S-" ind contents nil nil 1))) "\\(^\\)[ \t]*\\S-" ind contents nil nil 1)))
(if item-starts-with-par-p (org-trim contents) (if (= pre-blank 0) (org-trim contents)
(concat "\n" contents))))))) (concat (make-string pre-blank ?\n) contents)))))))
;;;; Plain List ;;;; Plain List
@ -4532,8 +4557,9 @@ to interpret. Return Org syntax as a string."
(and (eq type 'paragraph) (and (eq type 'paragraph)
(memq (org-element-type parent) (memq (org-element-type parent)
'(footnote-definition item)) '(footnote-definition item))
(eq data (eq data (car (org-element-contents parent)))
(car (org-element-contents parent))))))) (eq (org-element-property :pre-blank parent)
0)))))
"")))))) ""))))))
(if (memq type '(org-data plain-text nil)) results (if (memq type '(org-data plain-text nil)) results
;; Build white spaces. If no `:post-blank' property ;; Build white spaces. If no `:post-blank' property

View File

@ -2001,17 +2001,18 @@ Return a string."
;; normalized first. ;; normalized first.
(org-element-normalize-contents (org-element-normalize-contents
data data
;; When normalizing contents of the ;; When normalizing first paragraph
;; first paragraph in an item or ;; of an item or
;; a footnote definition, ignore ;; a footnote-definition, ignore
;; first line's indentation: there is ;; first line's indentation.
;; none and it might be misleading.
(when (eq type 'paragraph)
(and (and
(eq type 'paragraph)
(memq (org-element-type parent)
'(footnote-definition item))
(eq (car (org-element-contents parent)) (eq (car (org-element-contents parent))
data) data)
(memq (org-element-type parent) (eq (org-element-property :pre-blank parent)
'(footnote-definition item))))))) 0)))))
""))) "")))
(broken-link-handler (broken-link-handler
(funcall transcoder data (funcall transcoder data

View File

@ -969,7 +969,20 @@ Some other text
(org-test-with-temp-text "[fn:1]\n\n" (org-test-with-temp-text "[fn:1]\n\n"
(let ((footnote (org-element-at-point))) (let ((footnote (org-element-at-point)))
(or (org-element-property :contents-begin footnote) (or (org-element-property :contents-begin footnote)
(org-element-property :contents-end footnote)))))) (org-element-property :contents-end footnote)))))
;; Parse `:pre-blank'.
(should
(= 0
(org-test-with-temp-text "[fn:1] A"
(org-element-property :pre-blank (org-element-at-point)))))
(should
(= 1
(org-test-with-temp-text "[fn:1]\nA"
(org-element-property :pre-blank (org-element-at-point)))))
(should
(= 2
(org-test-with-temp-text "[fn:1]\n\nA"
(org-element-property :pre-blank (org-element-at-point))))))
;;;; Footnotes Reference. ;;;; Footnotes Reference.
@ -1433,8 +1446,7 @@ DEADLINE: <2012-03-29 thu.>"
- [-] item 1 - [-] item 1
- [X] item 1.1 - [X] item 1.1
- [ ] item 1.2" - [ ] item 1.2"
(org-element-map (org-element-map (org-element-parse-buffer) 'item
(org-element-parse-buffer) 'item
(lambda (item) (org-element-property :checkbox item)))))) (lambda (item) (org-element-property :checkbox item))))))
;; Item starting with special syntax. ;; Item starting with special syntax.
(should (should
@ -1447,6 +1459,19 @@ DEADLINE: <2012-03-29 thu.>"
(org-test-with-temp-text (org-test-with-temp-text
"-<point> item\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src" "-<point> item\n #+begin_src emacs-lisp\n(+ 1 1)\n #+end_src"
(= (org-element-property :end (org-element-at-point)) (point-max)))) (= (org-element-property :end (org-element-at-point)) (point-max))))
;; Parse `:pre-blank'.
(should
(= 0
(org-test-with-temp-text "-<point> A"
(org-element-property :pre-blank (org-element-at-point)))))
(should
(= 1
(org-test-with-temp-text "-<point>\n A"
(org-element-property :pre-blank (org-element-at-point)))))
(should
(= 2
(org-test-with-temp-text "-<point>\n\n A"
(org-element-property :pre-blank (org-element-at-point)))))
;; Last item in a list or sub-list has no `:post-blank' lines, since ;; Last item in a list or sub-list has no `:post-blank' lines, since
;; those belong to the plain-list. ;; those belong to the plain-list.
(should (should
@ -2562,7 +2587,14 @@ Outside list"
(ert-deftest test-org-element/footnote-definition-interpreter () (ert-deftest test-org-element/footnote-definition-interpreter ()
"Test footnote definition interpreter." "Test footnote definition interpreter."
(should (equal (org-test-parse-and-interpret "[fn:1] Test") "[fn:1] Test\n"))) (should (equal (org-test-parse-and-interpret "[fn:1] Test") "[fn:1] Test\n"))
;; Handle `:pre-blank' in definitions.
(should
(equal (org-test-parse-and-interpret "[fn:1]\nparagraph")
"[fn:1]\nparagraph\n"))
(should
(equal (org-test-parse-and-interpret "[fn:1]\n\nparagraph")
"[fn:1]\n\nparagraph\n")))
(ert-deftest test-org-element/headline-interpreter () (ert-deftest test-org-element/headline-interpreter ()
"Test headline and section interpreters." "Test headline and section interpreters."
@ -2683,6 +2715,13 @@ Outside list"
(should (should
(equal (org-test-parse-and-interpret "-\n | a | b |") (equal (org-test-parse-and-interpret "-\n | a | b |")
"- \n | a | b |\n")) "- \n | a | b |\n"))
;; Handle `:pre-blank' in items.
(should
(equal (org-test-parse-and-interpret "-\n paragraph")
"- \n paragraph\n"))
(should
(equal (org-test-parse-and-interpret "-\n\n paragraph")
"- \n\n paragraph\n"))
;; Special case: correctly handle "*" bullets. ;; Special case: correctly handle "*" bullets.
(should (org-test-parse-and-interpret " * item")) (should (org-test-parse-and-interpret " * item"))
;; Special case: correctly handle empty items. ;; Special case: correctly handle empty items.