mirror of
https://git.savannah.gnu.org/git/emacs/org-mode.git
synced 2024-12-27 10:55:04 +00:00
org-element: Verse blocks now contain objects
* contrib/lisp/org-element.el (org-element-verse-block-parser): Verse blocks now contain objects. (org-element-verse-block-interpreter, org-element-current-element): Apply changes to verse blocks. (org-element-secondary-value-alist): Remove verse blocks from elements with a secondary string. * contrib/lisp/org-e-odt.el (org-e-odt-verse-block): Apply changes to verse blocks. * contrib/lisp/org-e-latex.el (org-e-latex-verse-block): Apply changes to verse blocks. * contrib/lisp/org-e-html.el (org-e-html-verse-block): Apply changes to verse blocks. * contrib/lisp/org-e-ascii.el (org-e-ascii-verse-block): Apply changes to verse blocks. * testing/lisp/test-org-element.el: Add tests.
This commit is contained in:
parent
c3d7d21108
commit
c6dc6e3d32
@ -1730,13 +1730,11 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
|
||||
(defun org-e-ascii-verse-block (verse-block contents info)
|
||||
"Transcode a VERSE-BLOCK element from Org to ASCII.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
CONTENTS is verse block contents. INFO is a plist holding
|
||||
contextual information."
|
||||
(let ((verse-width (org-e-ascii--current-text-width verse-block info)))
|
||||
(org-e-ascii--indent-string
|
||||
(org-e-ascii--justify-string
|
||||
(org-export-secondary-string
|
||||
(org-element-property :value verse-block) 'e-ascii info)
|
||||
verse-width 'left)
|
||||
(org-e-ascii--justify-string contents verse-width 'left)
|
||||
org-e-ascii-quote-margin)))
|
||||
|
||||
|
||||
|
@ -2949,17 +2949,14 @@ channel."
|
||||
|
||||
(defun org-e-html-verse-block (verse-block contents info)
|
||||
"Transcode a VERSE-BLOCK element from Org to HTML.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
CONTENTS is verse block contents. INFO is a plist holding
|
||||
contextual information."
|
||||
;; Replace each newline character with line break. Also replace
|
||||
;; each blank line with a line break.
|
||||
(setq contents (replace-regexp-in-string
|
||||
"^ *\\\\\\\\$" "<br/>\n"
|
||||
(replace-regexp-in-string
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " <br/>\n"
|
||||
(org-remove-indentation
|
||||
(org-export-secondary-string
|
||||
(org-element-property :value verse-block)
|
||||
'e-html info)))))
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " <br/>\n" contents)))
|
||||
;; Replace each white space at beginning of a line with a
|
||||
;; non-breaking space.
|
||||
(while (string-match "^[ \t]+" contents)
|
||||
|
@ -2118,7 +2118,8 @@ channel."
|
||||
|
||||
(defun org-e-latex-verse-block (verse-block contents info)
|
||||
"Transcode a VERSE-BLOCK element from Org to LaTeX.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
CONTENTS is verse block contents. INFO is a plist holding
|
||||
contextual information."
|
||||
(org-e-latex--wrap-label
|
||||
verse-block
|
||||
;; In a verse environment, add a line break to each newline
|
||||
@ -2129,11 +2130,7 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
(setq contents (replace-regexp-in-string
|
||||
"^ *\\\\\\\\$" "\\\\vspace*{1em}"
|
||||
(replace-regexp-in-string
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n"
|
||||
(org-remove-indentation
|
||||
(org-export-secondary-string
|
||||
(org-element-property :value verse-block)
|
||||
'e-latex info)))))
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" contents)))
|
||||
(while (string-match "^[ \t]+" contents)
|
||||
(let ((new-str (format "\\hspace*{%dem}"
|
||||
(length (match-string 0 contents)))))
|
||||
|
@ -4148,17 +4148,14 @@ channel."
|
||||
|
||||
(defun org-e-odt-verse-block (verse-block contents info)
|
||||
"Transcode a VERSE-BLOCK element from Org to HTML.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
CONTENTS is verse block contents. INFO is a plist holding
|
||||
contextual information."
|
||||
;; Replace each newline character with line break. Also replace
|
||||
;; each blank line with a line break.
|
||||
(setq contents (replace-regexp-in-string
|
||||
"^ *\\\\\\\\$" "<br/>\n"
|
||||
(replace-regexp-in-string
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " <br/>\n"
|
||||
(org-remove-indentation
|
||||
(org-export-secondary-string
|
||||
(org-element-property :value verse-block)
|
||||
'e-odt info)))))
|
||||
"\\(\\\\\\\\\\)?[ \t]*\n" " <br/>\n" contents)))
|
||||
|
||||
;; Replace each white space at beginning of a line with a
|
||||
;; non-breaking space.
|
||||
|
@ -25,45 +25,51 @@
|
||||
;; Org syntax can be divided into three categories: "Greater
|
||||
;; elements", "Elements" and "Objects".
|
||||
|
||||
;; An object can be defined anywhere on a line. It may span over more
|
||||
;; than a line but never contains a blank one. Objects belong to the
|
||||
;; following types: `emphasis', `entity', `export-snippet',
|
||||
;; `footnote-reference', `inline-babel-call', `inline-src-block',
|
||||
;; `latex-fragment', `line-break', `link', `macro', `radio-target',
|
||||
;; `statistics-cookie', `subscript', `superscript', `table-cell',
|
||||
;; `target', `time-stamp' and `verbatim'.
|
||||
|
||||
;; An element always starts and ends at the beginning of a line
|
||||
;; (excepted for `table-cell'). The only element's type containing
|
||||
;; objects is called a `paragraph'. Other types are: `comment',
|
||||
;; `comment-block', `example-block', `export-block', `fixed-width',
|
||||
;; `horizontal-rule', `keyword', `latex-environment', `babel-call',
|
||||
;; `property-drawer', `quote-section', `src-block', `table',
|
||||
;; `table-row' and `verse-block'.
|
||||
|
||||
;; Elements containing paragraphs are called greater elements.
|
||||
;; Concerned types are: `center-block', `drawer', `dynamic-block',
|
||||
;; `footnote-definition', `headline', `inlinetask', `item',
|
||||
;; `plain-list', `quote-block', `section' and `special-block'
|
||||
|
||||
;; Greater elements (excepted `headline', `item' and `section' types)
|
||||
;; and elements (excepted `keyword', `babel-call', `property-drawer'
|
||||
;; and `table-row' types) can have a fixed set of keywords as
|
||||
;; attributes. Those are called "affiliated keywords", to distinguish
|
||||
;; them from others keywords, which are full-fledged elements. In
|
||||
;; particular, the "name" affiliated keyword allows to label almost
|
||||
;; any element in an Org buffer.
|
||||
;; Elements are related to the structure of the document. Indeed, all
|
||||
;; elements are a cover for the document: each position within belongs
|
||||
;; to at least one element.
|
||||
|
||||
;; An element always starts and ends at the beginning of a line. With
|
||||
;; a few exceptions (namely `headline', `item', `section', `keyword',
|
||||
;; `babel-call' and `property-drawer' types), it can also accept
|
||||
;; a fixed set of keywords as attributes. Those are called
|
||||
;; "affiliated keywords" to distinguish them from other keywords,
|
||||
;; which are full-fledged elements.
|
||||
;;
|
||||
;; Element containing other elements (and only elements) are called
|
||||
;; greater elements. Concerned types are: `center-block', `drawer',
|
||||
;; `dynamic-block', `footnote-definition', `headline', `inlinetask',
|
||||
;; `item', `plain-list', `quote-block', `section' and `special-block'.
|
||||
;;
|
||||
;; Other element types are: `babel-call', `comment', `comment-block',
|
||||
;; `example-block', `export-block', `fixed-width', `horizontal-rule',
|
||||
;; `keyword', `latex-environment', `paragraph', `property-drawer',
|
||||
;; `quote-section', `src-block', `table', `table-cell', `table-row'
|
||||
;; and `verse-blocks'. Among them, `paragraph', `table-cell' and
|
||||
;; `verse-block' types can contain Org objects and plain text.
|
||||
;;
|
||||
;; Objects are related to document's contents. Some of them are
|
||||
;; recursive. Associated types are of the following: `emphasis',
|
||||
;; `entity', `export-snippet', `footnote-reference',
|
||||
;; `inline-babel-call', `inline-src-block', `latex-fragment',
|
||||
;; `line-break', `link', `macro', `radio-target', `statistics-cookie',
|
||||
;; `subscript', `superscript', `table-cell', `target', `time-stamp'
|
||||
;; and `verbatim'.
|
||||
;;
|
||||
;; Some elements also have special properties whose value can hold
|
||||
;; objects themselves (i.e. an item tag or an headline name). Such
|
||||
;; values are called "secondary strings". Any object belongs to
|
||||
;; either an element or a secondary string.
|
||||
;;
|
||||
;; Notwithstanding affiliated keywords, each greater element, element
|
||||
;; and object has a fixed set of properties attached to it. Among
|
||||
;; them, three are shared by all types: `:begin' and `:end', which
|
||||
;; refer to the beginning and ending buffer positions of the
|
||||
;; considered element or object, and `:post-blank', which holds the
|
||||
;; number of blank lines, or white spaces, at its end.
|
||||
|
||||
;; Some elements also have special properties whose value can hold
|
||||
;; objects themselves (i.e. an item tag, an headline name, a table
|
||||
;; cell). Such values are called "secondary strings".
|
||||
;; number of blank lines, or white spaces, at its end. Greater
|
||||
;; elements and elements containing objects will also have
|
||||
;; `:contents-begin' and `:contents-end' properties to delimit
|
||||
;; contents.
|
||||
|
||||
;; Lisp-wise, an element or an object can be represented as a list.
|
||||
;; It follows the pattern (TYPE PROPERTIES CONTENTS), where:
|
||||
@ -81,7 +87,7 @@
|
||||
;; for each type of Org syntax.
|
||||
|
||||
;; The next two parts introduce four accessors and a function
|
||||
;; retrieving the smallest element starting at point (respectively
|
||||
;; retrieving the element starting at point (respectively
|
||||
;; `org-element-type', `org-element-property', `org-element-contents',
|
||||
;; `org-element-restriction' and `org-element-current-element').
|
||||
|
||||
@ -1584,56 +1590,40 @@ CONTENTS is the contents of the table row."
|
||||
|
||||
;;;; Verse Block
|
||||
|
||||
(defun org-element-verse-block-parser (&optional raw-secondary-p)
|
||||
(defun org-element-verse-block-parser ()
|
||||
"Parse a verse block.
|
||||
|
||||
Return a list whose car is `verse-block' and cdr is a plist
|
||||
containing `:begin', `:end', `:hiddenp', `:value' and
|
||||
`:post-blank' keywords.
|
||||
Return a list whose CAR is `verse-block' and CDR is a plist
|
||||
containing `:begin', `:end', `:contents-begin', `:contents-end',
|
||||
`:hiddenp' and `:post-blank' keywords.
|
||||
|
||||
When optional argument RAW-SECONDARY-P is non-nil, verse-block's
|
||||
value will not be parsed as a secondary string, but as a plain
|
||||
string instead.
|
||||
|
||||
Assume point is at beginning or end of the block."
|
||||
Assume point is at beginning of the block."
|
||||
(save-excursion
|
||||
(let* ((case-fold-search t)
|
||||
(keywords (progn
|
||||
(end-of-line)
|
||||
(re-search-backward
|
||||
(concat "^[ \t]*#\\+BEGIN_VERSE") nil t)
|
||||
(org-element-collect-affiliated-keywords)))
|
||||
(keywords (org-element-collect-affiliated-keywords))
|
||||
(begin (car keywords))
|
||||
(hidden (progn (forward-line) (org-truely-invisible-p)))
|
||||
(value-begin (point))
|
||||
(value-end
|
||||
(contents-begin (point))
|
||||
(contents-end
|
||||
(progn
|
||||
(re-search-forward (concat "^[ \t]*#\\+END_VERSE") nil t)
|
||||
(point-at-bol)))
|
||||
(pos-before-blank (progn (forward-line) (point)))
|
||||
(end (progn (org-skip-whitespace)
|
||||
(if (eobp) (point) (point-at-bol))))
|
||||
(value
|
||||
(if raw-secondary-p
|
||||
(buffer-substring-no-properties value-begin value-end)
|
||||
(org-element-parse-secondary-string
|
||||
(buffer-substring-no-properties value-begin value-end)
|
||||
(org-element-restriction 'verse-block)))))
|
||||
(if (eobp) (point) (point-at-bol)))))
|
||||
`(verse-block
|
||||
(:begin ,begin
|
||||
:end ,end
|
||||
:contents-begin ,contents-begin
|
||||
:contents-end ,contents-end
|
||||
:hiddenp ,hidden
|
||||
:value ,value
|
||||
:post-blank ,(count-lines pos-before-blank end)
|
||||
,@(cadr keywords))))))
|
||||
|
||||
(defun org-element-verse-block-interpreter (verse-block contents)
|
||||
"Interpret VERSE-BLOCK element as Org syntax.
|
||||
CONTENTS is nil."
|
||||
(format "#+BEGIN_VERSE\n%s#+END_VERSE"
|
||||
(org-remove-indentation
|
||||
(org-element-interpret-secondary
|
||||
(org-element-property :value verse-block)))))
|
||||
CONTENTS is verse block contents."
|
||||
(format "#+BEGIN_VERSE\n%s#+END_VERSE" contents))
|
||||
|
||||
|
||||
|
||||
@ -2767,8 +2757,7 @@ still has an entry since one of its properties (`:title') does.")
|
||||
'((headline . :title)
|
||||
(inlinetask . :title)
|
||||
(item . :tag)
|
||||
(footnote-reference . :inline-definition)
|
||||
(verse-block . :value))
|
||||
(footnote-reference . :inline-definition))
|
||||
"Alist between element types and location of secondary value.")
|
||||
|
||||
|
||||
@ -2884,17 +2873,11 @@ it is quicker than its counterpart, albeit more restrictive."
|
||||
(if (save-excursion
|
||||
(re-search-forward
|
||||
(format "[ \t]*#\\+END_%s\\(?: \\|$\\)" type) nil t))
|
||||
;; Build appropriate parser. `verse-block' type
|
||||
;; elements require an additional argument, so they
|
||||
;; must be treated separately.
|
||||
(if (string= "VERSE" type)
|
||||
(org-element-verse-block-parser raw-secondary-p)
|
||||
(funcall
|
||||
(intern
|
||||
(format
|
||||
"org-element-%s-parser"
|
||||
(cdr (assoc type
|
||||
org-element-non-recursive-block-alist))))))
|
||||
(cdr (assoc type org-element-non-recursive-block-alist)))))
|
||||
(org-element-paragraph-parser)))))
|
||||
;; Inlinetask.
|
||||
((org-at-heading-p) (org-element-inlinetask-parser raw-secondary-p))
|
||||
|
@ -260,6 +260,44 @@
|
||||
(org-element-parse-buffer) 'footnote-reference 'identity))))
|
||||
|
||||
|
||||
|
||||
;;;; Verse blocks
|
||||
|
||||
(ert-deftest test-org-element/verse-block ()
|
||||
"Test verse block parsing."
|
||||
;; Standard test.
|
||||
(org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE"
|
||||
(should
|
||||
(equal
|
||||
(org-element-map (org-element-parse-buffer) 'verse-block 'identity nil t)
|
||||
'(verse-block
|
||||
(:begin 1 :end 38 :contents-begin 15 :contents-end 27 :hiddenp nil
|
||||
:post-blank 0)
|
||||
"Verse block\n"))))
|
||||
;; Ignore case.
|
||||
(org-test-with-temp-text "#+begin_verse\nVerse block\n#+end_verse"
|
||||
(should
|
||||
(equal
|
||||
(org-element-map (org-element-parse-buffer) 'verse-block 'identity nil t)
|
||||
'(verse-block
|
||||
(:begin 1 :end 38 :contents-begin 15 :contents-end 27 :hiddenp nil
|
||||
:post-blank 0)
|
||||
"Verse block\n"))))
|
||||
;; Parse folding.
|
||||
(org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE"
|
||||
(org-hide-block-all)
|
||||
(should
|
||||
(equal
|
||||
(org-element-map (org-element-parse-buffer) 'verse-block 'identity nil t)
|
||||
'(verse-block
|
||||
(:begin 1 :end 38 :contents-begin 15 :contents-end 27
|
||||
:hiddenp org-hide-block :post-blank 0)
|
||||
"Verse block\n"))))
|
||||
;; Parse objects in verse blocks.
|
||||
(org-test-with-temp-text "#+BEGIN_VERSE\nVerse \\alpha\n#+END_VERSE"
|
||||
(should (org-element-map (org-element-parse-buffer) 'entity 'identity))))
|
||||
|
||||
|
||||
|
||||
;;;; Granularity
|
||||
|
||||
@ -367,6 +405,21 @@ Paragraph \\alpha."
|
||||
'(org-data nil (paragraph (:caption (("long") "short")) "Paragraph")))
|
||||
"#+CAPTION[short]: long\nParagraph\n")))
|
||||
|
||||
(ert-deftest test-org-element/interpret-elements ()
|
||||
"Test interpretation of elements and objects."
|
||||
(let ((parse-and-interpret
|
||||
(function
|
||||
;; Parse TEXT string in an Org buffer and transcode it back
|
||||
;; to Org syntax.
|
||||
(lambda (text)
|
||||
(with-temp-buffer
|
||||
(org-mode)
|
||||
(insert text)
|
||||
(org-element-interpret-data (org-element-parse-buffer)))))))
|
||||
;; Verse blocks.
|
||||
(equal (funcall parse-and-interpret "#+BEGIN_VERSE\nTest\n#+END_VERSE")
|
||||
"#+BEGIN_VERSE\nTest\n#+END_VERSE\n")))
|
||||
|
||||
|
||||
|
||||
;;;; Normalize contents
|
||||
|
Loading…
Reference in New Issue
Block a user