forked from mirrors/org-mode
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:
parent
8f3077d14a
commit
d07ee7f7f2
|
@ -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."
|
||||||
(concat (format "[fn:%s]" (org-element-property :label footnote-definition))
|
(let ((pre-blank
|
||||||
" "
|
(min (or (org-element-property :pre-blank footnote-definition)
|
||||||
contents))
|
;; 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))
|
||||||
|
(if (= pre-blank 0) (concat " " (org-trim 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,20 +1238,25 @@ 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
|
||||||
;; Ignore tags in un-ordered lists: they are just
|
(goto-char
|
||||||
;; a part of item's body.
|
;; Ignore tags in un-ordered lists: they are just
|
||||||
(if (and (match-beginning 4)
|
;; a part of item's body.
|
||||||
(save-match-data (string-match "[.)]" bullet)))
|
(if (and (match-beginning 4)
|
||||||
(match-beginning 4)
|
(save-match-data (string-match "[.)]" bullet)))
|
||||||
(match-end 0)))
|
(match-beginning 4)
|
||||||
(skip-chars-forward " \r\t\n" end)
|
(match-end 0)))
|
||||||
(cond ((= (point) end) nil)
|
(skip-chars-forward " \r\t\n" end)
|
||||||
;; If first line isn't empty, contents really
|
(cond ((= (point) end) nil)
|
||||||
;; start at the text after item's meta-data.
|
;; If first line isn't empty, contents really
|
||||||
((= (line-beginning-position) begin) (point))
|
;; start at the text after item's meta-data.
|
||||||
(t (line-beginning-position)))))
|
((= (line-beginning-position) begin) (point))
|
||||||
|
(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,26 +1296,30 @@ 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] ")
|
(`off "[ ] ")
|
||||||
(`off "[ ] ")
|
(`trans "[-] ")
|
||||||
(`trans "[-] ")
|
(_ nil))
|
||||||
(_ nil))
|
(and tag (format "%s :: " tag))
|
||||||
(and tag (format "%s :: " tag))
|
(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 (= pre-blank 0) (org-trim contents)
|
||||||
(if item-starts-with-par-p (org-trim contents)
|
(concat (make-string pre-blank ?\n) contents)))))))
|
||||||
(concat "\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
|
||||||
|
|
23
lisp/ox.el
23
lisp/ox.el
|
@ -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.
|
(and
|
||||||
(when (eq type 'paragraph)
|
(eq type 'paragraph)
|
||||||
(and
|
(memq (org-element-type parent)
|
||||||
(eq (car (org-element-contents parent))
|
'(footnote-definition item))
|
||||||
data)
|
(eq (car (org-element-contents parent))
|
||||||
(memq (org-element-type parent)
|
data)
|
||||||
'(footnote-definition item)))))))
|
(eq (org-element-property :pre-blank parent)
|
||||||
|
0)))))
|
||||||
"")))
|
"")))
|
||||||
(broken-link-handler
|
(broken-link-handler
|
||||||
(funcall transcoder data
|
(funcall transcoder data
|
||||||
|
|
|
@ -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,9 +1446,8 @@ 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
|
||||||
(equal '(("- item"))
|
(equal '(("- item"))
|
||||||
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue