From: Christian Garbs via "General discussions about Org-mode." <emacs-orgmode@gnu.org>
To: emacs-orgmode@gnu.org
Subject: [PATCH] ox-bb.el: Add BBCode exporter
Date: Fri, 5 Feb 2021 09:07:55 +0100 [thread overview]
Message-ID: <20210205080755.ex55vibiqhczqrjb@cgarbs.de> (raw)
[-- Attachment #1: Type: text/plain, Size: 474 bytes --]
Hi,
after getting an encouraging reply to my initial proposal[1], I went
forward and finished the patch to include the BBCode exporter in Org.
The patch applies to current master and includes a suite of tests.
Please review and/or apply this patch.
Best regards,
Christian
[1] https://lists.gnu.org/archive/html/emacs-orgmode/2021-01/msg00151.html
--
....Christian.Garbs....................................https://www.cgarbs.de
It seemed like a good idea at the time.
[-- Attachment #2: 0001-ox-bb.el-Add-BBCode-exporter.patch --]
[-- Type: text/x-diff, Size: 31654 bytes --]
From 04888bb0146d0f0cf3bf82cea4f0ea1b761c1d08 Mon Sep 17 00:00:00 2001
From: Christian Garbs <mitch@cgarbs.de>
Date: Fri, 8 Jan 2021 20:39:29 +0100
Subject: [PATCH] ox-bb.el: Add BBCode exporter
* lisp/ox-bb.el: Add export backend for BBCode format.
* testing/lisp/test-ox-bb.el: Add tests for ox-bb.el.
* doc/org-manual.org (Exporting): Add section about BBCode export.
* etc/ORG-NEWS: Announce BBCode exporter as a new feature.
---
doc/org-manual.org | 29 +++
etc/ORG-NEWS | 10 +
lisp/ox-bb.el | 423 +++++++++++++++++++++++++++++++
testing/lisp/test-ox-bb.el | 503 +++++++++++++++++++++++++++++++++++++
4 files changed, 965 insertions(+)
create mode 100644 lisp/ox-bb.el
create mode 100644 testing/lisp/test-ox-bb.el
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 20a0d1d7a..ff19b5111 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -12281,6 +12281,35 @@ It's just a jump to the left...
,#+END_JUSTIFYRIGHT
#+end_example
+** BBCode Export
+:PROPERTIES:
+:DESCRIPTION: Exporting to BBCode.
+:END:
+#+cindex: BBCode export
+
+BBCode export produces an output file containing BBCode markup. Code
+sections are exported as =[code]= blocks for the GeSHI formatter found
+in some web forums.
+
+*** BBCode export commands
+:PROPERTIES:
+:UNNUMBERED: notoc
+:END:
+
+#+attr_texinfo: :sep ,
+- {{{kbd(C-c C-e b f)}}} (~org-bb-export-to-bbcode~) ::
+ #+kindex: C-c C-e b f
+ #+findex: org-bb-export-to-bbcode
+
+ Export as a BBCode formatted text file with a =.bbcode= extension,
+ overwriting without warning.
+
+- {{{kbd(C-c C-e b b)}}} (~org-bb-export-to-bbcode~) ::
+ #+kindex: C-c C-e b b
+ #+findex: org-bb-export-as-bbcode
+
+ Export to a temporary buffer. Does not create a file.
+
** Beamer Export
:PROPERTIES:
:DESCRIPTION: Producing presentations and slides.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index ba769224f..035abba1b 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -156,6 +156,16 @@ tags including from both buffer local and user defined persistent
global list (~org-tag-alist~ and ~org-tag-persistent-alist~). Now
option ~org-complete-tags-always-offer-all-agenda-tags~ is honored.
+*** New BBCode export backend
+
+A new export backend generates BBCode as used in many internet forums.
+Code blocks will be exported as =[code]= blocks for the GeSHi
+formatter. There are no configurable options.
+
+You can export to a temporary buffer using =<C-c C-e b b>= or to a
+file with extension =.bbcode= (overwritten without warning) via =<C-c
+C-e b f>=.
+
** Miscellaneous
*** =org-goto-first-child= now works before first heading
diff --git a/lisp/ox-bb.el b/lisp/ox-bb.el
new file mode 100644
index 000000000..50271e675
--- /dev/null
+++ b/lisp/ox-bb.el
@@ -0,0 +1,423 @@
+;;; ox-bb.el --- BBCode Back-End for Org Export Engine -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017-2021 Free Software Foundation, Inc.
+;; Author: org, wp, bbcode
+
+;; This file is part of ox-bb.
+
+;; GNU EMacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library implements a BBCode back-end for Org exporter. Code
+;; sections are formatted for the GeSHi formatter found in some web
+;; forums. See Org manual for more information.
+
+;;; Code:
+
+(require 'ox)
+
+;;; Backend definition
+
+(org-export-define-backend 'bb
+ '((bold . org-bb-bold)
+ (center-block . org-bb-undefined)
+ (clock . org-bb-undefined)
+ (code . org-bb-code)
+ (drawer . org-bb-undefined)
+ (dynamic-block . org-bb-undefined)
+ (entity . org-bb-entity)
+ (example-block . org-bb-undefined)
+ (export-block . org-bb-undefined)
+ (export-snippet . org-bb-undefined)
+ (fixed-width . org-bb-fixed-width)
+ (footnote-definition . org-bb-footnote-definition)
+ (footnote-reference . org-bb-footnote-reference)
+ (headline . org-bb-headline)
+ (horizontal-rule . org-bb-undefined)
+ (inline-src-block . org-bb-undefined)
+ (inlinetask . org-bb-undefined)
+ (inner-template . org-bb-inner-template)
+ (italic . org-bb-italic)
+ (item . org-bb-item)
+ (keyword . org-bb-undefined)
+ (latex-environment . org-bb-undefined)
+ (latex-fragment . org-bb-undefined)
+ (line-break . org-bb-line-break)
+ (link . org-bb-link)
+ (node-property . org-bb-undefined)
+ (paragraph . org-bb-paragraph)
+ (plain-list . org-bb-plain-list)
+ (plain-text . org-bb-plain-text)
+ (planning . org-bb-undefined)
+ (property-drawer . org-bb-undefined)
+ (quote-block . org-bb-quote-block)
+ (radio-target . org-bb-undefined)
+ (section . org-bb-section)
+ (special-block . org-bb-undefined)
+ (src-block . org-bb-geshi-block)
+ (statistics-cookie . org-bb-undefined)
+ (strike-through . org-bb-strike-through)
+ (subscript . org-bb-undefined)
+ (superscript . org-bb-undefined)
+ (table . org-bb-undefined)
+ (table-cell . org-bb-undefined)
+ (table-row . org-bb-undefined)
+ (target . org-bb-undefined)
+ (template . org-bb-template)
+ (timestamp . org-bb-undefined)
+ (underline . org-bb-underline)
+ (verbatim . org-bb-verbatim)
+ (verse-block . org-bb-undefined))
+ :menu-entry
+ '(?b "Export to BBCode"
+ ((?b "As BBCode buffer" org-bb-export-as-bbcode)
+ (?f "As BBCode file" org-bb-export-to-bbcode))))
+
+;;; Helper methods
+
+(defun org-bb--as-block (text)
+ "Format TEXT as a block with leading and trailing newline."
+ (concat "\n" text "\n"))
+
+(defun org-bb--force-leading-newline (text)
+ "Make TEXT start with exactly one newline."
+ (replace-regexp-in-string "\\`\n*" "\n" text))
+
+(defun org-bb--format-headline (text level)
+ "Format TEXT as a headline of the given LEVEL."
+ (let ((indent (cl-case level
+ (0 "")
+ (1 "# ")
+ (2 "== ")
+ (3 "+++ ")
+ (4 ":::: ")
+ (5 "----- ")
+ (t (error "Headline level `%s' is not defined yet" level)))))
+ (concat
+ (org-bb--put-in-tag
+ "b" (org-bb--put-in-tag
+ "u" (concat indent text)))
+ "\n\n")))
+
+(defun org-bb--put-in-tag (tag contents &optional attributes)
+ "Puts the BBcode tag TAG around the CONTENTS string.
+Optional ATTRIBUTES for the tag can be given as an alist of
+key/value pairs (both strings)."
+ (let ((attribute-string (if attributes
+ (mapconcat (function (lambda (attribute)
+ (let ((key (car attribute))
+ (value (cadr attribute)))
+ (format " %s=\"%s\"" key value))))
+ attributes
+ "")
+ "")))
+ (format "[%s%s]%s[/%s]" tag attribute-string contents tag)))
+
+(defun org-bb--put-in-value-tag (tag contents value)
+ "Puts the BBcode tag TAG around the CONTENTS string.
+The VALUE is assigned directly to the tag instead of a normal
+key/value pair."
+ (format "[%s=%s]%s[/%s]" tag value contents tag))
+
+(defun org-bb--fix-url (url)
+ "Fix URL returned from `url-encode-url'.
+Older versions of Emacs (eg. 24.3 used in the Travis CI minimal
+image) prepend \"/\" to urls consisting only of an \"#anchor\"
+part. We don't want this, because we need relative anchors. Fix
+this the hard way."
+ (if (string-prefix-p "/#" url)
+ (substring url 1)
+ url))
+
+(defun org-bb--put-url (contents href)
+ "Puts the CONTENTS inside a [url] tag pointing to HREF.
+Automagically escapes the target URL."
+ (let* ((target (org-bb--fix-url (url-encode-url (org-link-unescape href))))
+ (text (or contents target)))
+ (org-bb--put-in-value-tag "url" text target)))
+
+(defun org-bb--remove-leading-newline (text)
+ "Remove a leading empty line from TEXT."
+ (replace-regexp-in-string "\\`\n" "" text))
+
+(defun org-bb--remove-trailing-newline (text)
+ "Remove the trailing newline from TEXT."
+ (replace-regexp-in-string "\n\\'" "" text))
+
+(defun org-bb--map-to-geshi-language (language)
+ "Map LANGUAGE from Org to GeSHi."
+ (cond ((string= language "elisp") "lisp")
+ ((string= language "shell") "bash")
+ ((string= language "sh") "bash")
+ ((string= language "") "plaintext")
+ (language)
+ (t "plaintext")))
+
+;;; Backend callbacks
+
+(defun org-bb-bold (_bold contents _info)
+ "Transcode a BOLD element from Org to BBCode.
+CONTENTS is the bold text, as a string. INFO is
+ a plist used as a communication channel."
+ (org-bb--put-in-tag "b" contents))
+
+(defun org-bb-code (code _contents _info)
+ "Transcode a CODE element from Org to BBCode.
+CONTENTS is nil. INFO is a plist used as a communication channel."
+ (org-bb--put-in-value-tag "font" (org-element-property :value code) "monospace"))
+
+(defun org-bb-entity (entity _contents _info)
+ "Transcode an ENTITY element from Org to BBCode.
+CONTENTS is the definition itself. INFO is a plist used as a
+communication channel."
+ (org-element-property :html entity))
+
+(defun org-bb-geshi-block (code-block _contents info)
+ "Transcode a CODE-BLOCK element from Org to BBCode GeSHi plugin.
+CONTENTS is nil. INFO is a plist holding
+contextual information."
+ (format "[geshi lang=%s]%s[/geshi]"
+ (org-bb--map-to-geshi-language (org-element-property :language code-block))
+ (org-bb--remove-trailing-newline
+ (org-export-format-code-default code-block info))))
+
+(defun org-bb-fixed-width (fixed-width _contents _info)
+ "Transcode a FIXED-WIDTH element from Org to BBCode.
+CONTENTS is nil. INFO is a plist holding contextual information."
+ (org-bb--put-in-tag "code"
+ (concat "\n" (org-element-property :value fixed-width) "\n")))
+
+(defun org-bb-footnote-reference (footnote-reference _contents info)
+ "Transcode a FOOTNOTE-REFERENCE element from Org to BBCode.
+CONTENTS is nil. INFO is a plist holding contextual information."
+ (if (eq (org-element-property :type footnote-reference) 'inline)
+ (error "Inline footnotes not supported yet")
+ (let ((n (org-export-get-footnote-number footnote-reference info)))
+ (format "^%d" n))))
+
+(defun org-bb-format-footnote-definition (fn)
+ "Format the footnote definition FN."
+ (let ((n (car fn))
+ (def (cdr fn)))
+ (format "^%d: %s" n def)))
+
+(defun org-bb-footnote-section (info)
+ "Format the footnote section.
+INFO is a plist used as a communication channel."
+ (let* ((fn-alist (org-export-collect-footnote-definitions info (plist-get info :parse-tree)))
+ (fn-alist
+ (cl-loop for (n _label raw) in fn-alist collect
+ (cons n (org-trim (org-export-data raw info)))))
+ (text (mapconcat 'org-bb-format-footnote-definition fn-alist "\n")))
+ (if fn-alist
+ (concat "\n" (org-bb--format-headline "Footnotes" 0) text "\n")
+ "")))
+
+(defun org-bb-headline (headline contents info)
+ "Transcode HEADLINE element from Org to BBCode.
+CONTENTS is the headline contents. INFO is a plist used as
+a communication channel."
+ (let ((title (org-export-data (org-element-property :title headline) info))
+ (level (org-export-get-relative-level headline info)))
+ (if (org-element-property :footnote-section-p headline)
+ ""
+ (concat
+ (org-bb--format-headline title level)
+ contents))))
+
+(defun org-bb-inner-template (contents info)
+ "Return body of document string after BBCode conversion.
+CONTENTS is the transcoded contents string. INFO is a plist
+holding export options."
+ (concat
+ contents
+ (org-bb-footnote-section info)))
+
+(defun org-bb-italic (_italic contents _info)
+ "Transcode a ITALIC element from Org to BBCode.
+CONTENTS is the italic text, as a string. INFO is
+ a plist used as a communication channel."
+ (org-bb--put-in-tag "i" contents))
+
+(defun org-bb-item (item contents info)
+ "Transcode a ITEM element from Org to BBCode.
+CONTENTS is the contents of the item, as a string. INFO is
+ a plist used as a communication channel."
+ (let* ((plain-list (org-export-get-parent item))
+ (type (org-element-property :type plain-list))
+ (text (org-trim contents)))
+ (concat
+ "[*]"
+ (pcase type
+ (`descriptive
+ (let ((term (let ((tag (org-element-property :tag item)))
+ (and tag (org-export-data tag info)))))
+ (concat
+ (org-bb--put-in-tag "i" (concat (org-trim term) ":"))
+ " "
+ ))))
+ text
+ "\n")))
+
+(defun org-bb-line-break (_line-break _contents _info)
+ "Transcode a LINE-BREAK object from Org to BBCode.
+CONTENTS is nil. INFO is a plist holding contextual
+information."
+ "[br]_[/br]\n")
+
+(defun org-bb-link (link contents _info)
+ "Transcode a LINK element from Org to BBCode.
+CONTENTS is the contents of the link, as a string. INFO is
+ a plist used as a communication channel."
+ (let ((type (org-element-property :type link))
+ (path (org-element-property :path link))
+ (raw (org-element-property :raw-link link)))
+ (cond
+ ((string= type "fuzzy")
+ (cond
+ ((string-prefix-p "about:" raw)
+ (org-bb--put-url contents raw))
+ (t (error "Unknown fuzzy LINK type encountered: `%s'" raw))))
+ ((member type '("http" "https"))
+ (org-bb--put-url contents (concat type ":" path)))
+ (t (error "LINK type `%s' not yet supported" type)))))
+
+(defun org-bb-paragraph (_paragraph contents _info)
+ "Transcode a PARAGRAPH element from Org to BBCode.
+CONTENTS is the contents of the paragraph, as a string. INFO is
+ a plist used as a communication channel."
+ (org-trim contents))
+
+(defun org-bb-plain-list (plain-list contents _info)
+ "Transcode a PLAIN-LIST element from Org to BBCode.
+CONTENTS is the contents of the plain-list, as a string. INFO is
+ a plist used as a communication channel."
+ (let ((type (org-element-property :type plain-list))
+ (content-block (org-bb--as-block (org-trim contents))))
+ (concat
+ (pcase type
+ (`descriptive (org-bb--put-in-tag "list" content-block))
+ (`unordered (org-bb--put-in-tag "list" content-block))
+ (`ordered (org-bb--put-in-value-tag "list" content-block "1"))
+ (other (error "PLAIN-LIST type `%s' not yet supported" other)))
+ "\n")))
+
+(defun org-bb-plain-text (text _info)
+ "Transcode a TEXT string from Org to BBCode.
+INFO is a plist used as a communication channel."
+ text)
+
+(defun org-bb-quote-block (_quote-block contents _info)
+ "Transcode a QUOTE-BLOCK element from Org to BBCode.
+CONTENTS holds the contents of the block. INFO is a plist used
+as a communication channel."
+ (org-bb--put-in-tag "quote" (org-bb--force-leading-newline contents)))
+
+(defun org-bb-section (_section contents _info)
+ "Transcode a SECTION element from Org to BBCode.
+CONTENTS is the contents of the section, as a string. INFO is a
+ plist used as a communication channel."
+ (org-trim contents))
+
+(defun org-bb-strike-through (_strike-through contents _info)
+ "Transcode a STRIKE-THROUGH element from Org to BBCode.
+CONTENTS is the text with strike-through markup, as a string.
+ INFO is a plist used as a communication channel."
+ (org-bb--put-in-tag "s" contents))
+
+(defun org-bb-template (contents _info)
+ "Return complete document string after BBCode conversion.
+CONTENTS is the transcoded contents string. INFO is a plist
+holding export options."
+ contents)
+
+(defun org-bb-undefined (element &optional _contents _info)
+ "Throw an error when an unsupported ELEMENT is encountered."
+ (error "ELEMENT type `%s' not implemented yet" (car element)))
+
+(defun org-bb-underline (_underline contents _info)
+ "Transcode a UNDERLINE element from Org to BBCode.
+CONTENTS is the underlined text, as a string. INFO is
+ a plist used as a communication channel."
+ (org-bb--put-in-tag "u" contents))
+
+(defun org-bb-verbatim (verbatim _contents _info)
+ "Transcode a VERBATIM element from Org to BBCode.
+CONTENTS is nil. INFO is a plist used as a communication channel."
+ (org-bb--put-in-value-tag "font" (org-element-property :value verbatim) "monospace"))
+
+;;; Export methods
+
+;;;###autoload
+(defun org-bb-export-as-bbcode
+ (&optional async subtreep visible-only)
+ "Export current buffer to a BBCode buffer.
+
+If narrowing is active in the current buffer, only export its
+narrowed part.
+
+If a region is active, export that region.
+
+A non-nil optional argument ASYNC means the process should happen
+asynchronously. The resulting buffer should be accessible
+through the `org-export-stack' interface.
+
+When optional argument SUBTREEP is non-nil, export the sub-tree
+at point, extracting information from the headline properties
+first.
+
+When optional argument VISIBLE-ONLY is non-nil, don't export
+contents of hidden elements.
+
+Export is done in a buffer named \"*Org BBCode Export*\"."
+ (interactive)
+ (org-export-to-buffer 'bb "*Org BBCode Export*"
+ async subtreep visible-only (lambda () (when (require 'bbcode-mode nil :noerror)
+ (bbcode-mode)))))
+
+;;;###autoload
+(defun org-bb-export-to-bbcode
+ (&optional async subtreep visible-only)
+ "Export current buffer to a BBCode file.
+
+If narrowing is active in the current buffer, only export its
+narrowed part.
+
+If a region is active, export that region.
+
+A non-nil optional argument ASYNC means the process should happen
+asynchronously. The resulting buffer should be accessible
+through the `org-export-stack' interface.
+
+When optional argument SUBTREEP is non-nil, export the sub-tree
+at point, extracting information from the headline properties
+first.
+
+When optional argument VISIBLE-ONLY is non-nil, don't export
+contents of hidden elements.
+
+Return output file's name."
+ (interactive)
+ (let* ((extension ".bbcode")
+ (file (org-export-output-file-name extension subtreep))
+ (org-export-coding-system org-html-coding-system))
+ (org-export-to-file 'bb file
+ async subtreep visible-only)))
+
+;;; Register file
+
+(provide 'ox-bb)
+
+;;; ox-bb.el ends here
diff --git a/testing/lisp/test-ox-bb.el b/testing/lisp/test-ox-bb.el
new file mode 100644
index 000000000..b8ddc7f67
--- /dev/null
+++ b/testing/lisp/test-ox-bb.el
@@ -0,0 +1,503 @@
+;;; test-ox-bb.el --- Tests for ox-bb.el -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017-2019, 2021 Christian Garbs
+
+;; Author: Christian Garbs <mitch@cgarbs.de>
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for ox-bb.el.
+
+;;; Code:
+
+(require 'ox-bb)
+
+;;; helper functions
+
+(defun test-org-bb-verbatim-regression ()
+ "Return t if verbatim blocks generate an extra newline.
+This lets the tests react to a possible regression introduced
+with 7d9e4da447 which was released with Org 9.1.14. See
+https://lists.gnu.org/archive/html/emacs-orgmode/2021-01/msg00338.html
+for details."
+ (not (version< (org-release) "9.1.14")))
+
+;;; sanity check
+
+(ert-deftest test-org-bb/assert ()
+ (should t))
+
+;;; tests of internal functions
+
+;;; org-bb--as-block
+
+(ert-deftest test-org-bb/as-block/plain ()
+ (should (equal( org-bb--as-block "some text\nline two")
+ "\nsome text\nline two\n")))
+
+;;; org-bb--force-leading-newline
+
+(ert-deftest test-org-bb/force-leading-newline/add-missing-newline ()
+ (should (equal( org-bb--force-leading-newline "some text")
+ "\nsome text")))
+
+(ert-deftest test-org-bb/force-leading-newline/keep-existing-newline ()
+ (should (equal( org-bb--force-leading-newline "\nonly one newline")
+ "\nonly one newline")))
+
+(ert-deftest test-org-bb/force-leading-newline/remove-additional-newlines ()
+ (should (equal( org-bb--force-leading-newline "\n\nsome text")
+ "\nsome text")))
+
+(ert-deftest test-org-bb/force-leading-newline/keep-newlines-within ()
+ (should (equal( org-bb--force-leading-newline "\nline 1\nline 2\n")
+ "\nline 1\nline 2\n")))
+
+;;; org-bb--format-headline
+
+(ert-deftest test-org-bb/format-headline/level-0 ()
+ (should (equal( org-bb--format-headline "some text" 0)
+ "[b][u]some text[/u][/b]\n\n")))
+
+(ert-deftest test-org-bb/format-headline/level-1 ()
+ (should (equal( org-bb--format-headline "some text" 1)
+ "[b][u]# some text[/u][/b]\n\n")))
+
+(ert-deftest test-org-bb/format-headline/level-2 ()
+ (should (equal( org-bb--format-headline "some text" 2)
+ "[b][u]== some text[/u][/b]\n\n")))
+
+(ert-deftest test-org-bb/format-headline/level-3 ()
+ (should (equal( org-bb--format-headline "some text" 3)
+ "[b][u]+++ some text[/u][/b]\n\n")))
+
+(ert-deftest test-org-bb/format-headline/level-4 ()
+ (should (equal( org-bb--format-headline "some text" 4)
+ "[b][u]:::: some text[/u][/b]\n\n")))
+
+(ert-deftest test-org-bb/format-headline/level-5 ()
+ (should (equal( org-bb--format-headline "some text" 5)
+ "[b][u]----- some text[/u][/b]\n\n")))
+
+;;; org-bb--put-in-tag
+
+(ert-deftest test-org-bb/put-in-tag/no-attribute ()
+ (should (equal (org-bb--put-in-tag "p" "foo")
+ "[p]foo[/p]")))
+
+(ert-deftest test-org-bb/put-in-tag/single-attribute ()
+ (should (equal (org-bb--put-in-tag "style" "foo" '(("size" "30px")))
+ "[style size=\"30px\"]foo[/style]")))
+
+(ert-deftest test-org-bb/put-in-tag/multiple-attributes ()
+ (should (equal (org-bb--put-in-tag "style" "foo" '(("color" "#00FF00") ("size" "30px")))
+ "[style color=\"#00FF00\" size=\"30px\"]foo[/style]")))
+
+;;; org-bb--put-in-value-tag
+
+(ert-deftest test-org-bb/put-in-value-tag/plain ()
+ (should (equal (org-bb--put-in-value-tag "url" "foo" "file.htm")
+ "[url=file.htm]foo[/url]")))
+
+;;; org-bb--put-url
+
+(ert-deftest test-org-bb/put-url/plain ()
+ (should (equal (org-bb--put-url "some text" "https://example.com/")
+ "[url=https://example.com/]some text[/url]")))
+
+(ert-deftest test-org-bb/put-url/empty ()
+ (should (equal (org-bb--put-url nil "https://example.com/")
+ "[url=https://example.com/]https://example.com/[/url]")))
+
+(ert-deftest test-org-bb/put-url/anchor ()
+ (should (equal (org-bb--put-url "anchor text" "#anchor")
+ "[url=#anchor]anchor text[/url]")))
+
+(ert-deftest test-org-bb/put-url/encode-url-only-once ()
+ (should (equal (org-bb--put-url "baz" "http://foo/%20bar")
+ "[url=http://foo/%20bar]baz[/url]")))
+
+;;; org-bb--remove-leading-newline
+
+(ert-deftest test-org-bb/remove-leading-newline/remove ()
+ (should (equal( org-bb--remove-leading-newline "\nsome text")
+ "some text")))
+
+(ert-deftest test-org-bb/remove-leading-newline/keep-text-before-first-newline ()
+ (should (equal( org-bb--remove-leading-newline "no empty line\nsome more text\n")
+ "no empty line\nsome more text\n")))
+
+(ert-deftest test-org-bb/remove-leading-newline/only-remove-first-newline ()
+ (should (equal( org-bb--remove-leading-newline "\n\nsome text")
+ "\nsome text")))
+
+(ert-deftest test-org-bb/remove-leading-newline/keep-newlines-within ()
+ (should (equal( org-bb--remove-leading-newline "\nline 1\nline 2")
+ "line 1\nline 2")))
+
+(ert-deftest test-org-bb/remove-leading-newline/dont-fail-with-no-newline ()
+ (should (equal( org-bb--remove-leading-newline "some text")
+ "some text")))
+
+;;; org-bb--remove-trailing-newline
+
+(ert-deftest test-org-bb/remove-trailing-newline/remove ()
+ (should (equal( org-bb--remove-trailing-newline "some text\n")
+ "some text")))
+
+(ert-deftest test-org-bb/remove-trailing-newline/keep-text-after-last-newline ()
+ (should (equal( org-bb--remove-trailing-newline "some text\nno empty line")
+ "some text\nno empty line")))
+
+(ert-deftest test-org-bb/remove-trailing-newline/only-remove-last-newline ()
+ (should (equal( org-bb--remove-trailing-newline "some text\n\n")
+ "some text\n")))
+
+(ert-deftest test-org-bb/remove-trailing-newline/keep-newlines-within ()
+ (should (equal( org-bb--remove-trailing-newline "line 1\nline 2\n")
+ "line 1\nline 2")))
+
+(ert-deftest test-org-bb/remove-trailing-newline/dont-fail-with-no-newline ()
+ (should (equal( org-bb--remove-trailing-newline "some text")
+ "some text")))
+
+;;; org-bb--map-to-geshi-language
+
+(ert-deftest test-org-bb/map-to-geshi-language/unchanged ()
+ (should (equal( org-bb--map-to-geshi-language "java")
+ "java")))
+
+(ert-deftest test-org-bb/map-to-geshi-language/changed ()
+ (should (equal( org-bb--map-to-geshi-language "elisp")
+ "lisp")))
+
+(ert-deftest test-org-bb/map-to-geshi-language/nil ()
+ (should (equal( org-bb--map-to-geshi-language nil)
+ "plaintext")))
+
+(ert-deftest test-org-bb/map-to-geshi-language/empty ()
+ (should (equal( org-bb--map-to-geshi-language "")
+ "plaintext")))
+
+;;; end-to-end tests: compare given input file with expected export output
+
+(defun test-org-bb-export (input)
+ "Transform INPUT to BBCode and return the result."
+ (org-test-with-temp-text input
+ (org-bb-export-as-bbcode)
+ (with-current-buffer "*Org BBCode Export*"
+ (buffer-substring-no-properties (point-min) (point-max)))))
+
+(ert-deftest test-org-bb/export-bold ()
+ (should (equal (test-org-bb-export "foo *BAR* baz
+")
+ "foo [b]BAR[/b] baz
+")))
+
+(ert-deftest test-org-bb/export-code ()
+ (should (equal (test-org-bb-export "foo ~BAR~ baz
+")
+ "foo [font=monospace]BAR[/font] baz
+")))
+
+(ert-deftest test-org-bb/export-entity ()
+ (should (equal (test-org-bb-export "This is *bold* and this is in \\ast{}asterisks\\ast{}.
+")
+ "This is [b]bold[/b] and this is in ∗asterisks∗.
+")))
+
+(ert-deftest test-org-bb/export-fixed-width ()
+ ;; Where does the double newline before paragraph 2 come from?
+ ;; in Org 9.1 there was only a single newline as expected.
+ ;; A regression?
+ (should (equal (test-org-bb-export "paragraph 1
+
+: verbatim line
+: indented verbatim line
+
+paragraph 2")
+ (if (test-org-bb-verbatim-regression)
+ "paragraph 1
+
+[code]
+verbatim line
+ indented verbatim line
+[/code]
+
+
+paragraph 2
+"
+ "paragraph 1
+
+[code]
+verbatim line
+ indented verbatim line
+[/code]
+
+paragraph 2
+"))))
+
+(ert-deftest test-org-bb/export-footnote-multiple ()
+ (should (equal (test-org-bb-export "foo[fn:1] bar[fn:2]
+* Footnotes
+
+[fn:1] foo
+[fn:2] bar
+")
+ "foo^1 bar^2
+
+[b][u]Footnotes[/u][/b]
+
+^1: foo
+^2: bar
+")))
+
+(ert-deftest test-org-bb/export-footnote-plain ()
+ (should (equal (test-org-bb-export "bar[fn:1]
+* Footnotes
+
+[fn:1] foo
+")
+ "bar^1
+
+[b][u]Footnotes[/u][/b]
+
+^1: foo
+")))
+
+(ert-deftest test-org-bb/export-geshi-block-without-language ()
+ (should (equal (test-org-bb-export "#+BEGIN_SRC
+package foo;
+/* dummy dummy */
+#+END_SRC
+")
+ "[geshi lang=plaintext]package foo;
+/* dummy dummy */[/geshi]
+")))
+
+(ert-deftest test-org-bb/export-geshi-block ()
+ (should (equal (test-org-bb-export "#+BEGIN_SRC java
+package foo;
+/* dummy dummy */
+#+END_SRC
+")
+ "[geshi lang=java]package foo;
+/* dummy dummy */[/geshi]
+")))
+
+(ert-deftest test-org-bb/export-headline-lv1 ()
+ (should (equal (test-org-bb-export "* TOPIC
+")
+ "[b][u]# TOPIC[/u][/b]
+")))
+
+(ert-deftest test-org-bb/export-headline-lv2 ()
+ (should (equal (test-org-bb-export "* dummy
+** TOPIC
+")
+ "[b][u]# dummy[/u][/b]
+
+[b][u]== TOPIC[/u][/b]
+")))
+
+(ert-deftest test-org-bb/export-headline-lv3 ()
+ (should (equal (test-org-bb-export "* dummy
+** dummy
+*** TOPIC
+")
+ "[b][u]# dummy[/u][/b]
+
+[b][u]== dummy[/u][/b]
+
+[b][u]+++ TOPIC[/u][/b]
+")))
+
+(ert-deftest test-org-bb/export-headline-lv4 ()
+ (should (equal (test-org-bb-export "* dummy
+** dummy
+*** dummy
+**** TOPIC
+")
+ "[b][u]# dummy[/u][/b]
+
+[b][u]== dummy[/u][/b]
+
+[b][u]+++ dummy[/u][/b]
+
+[b][u]:::: TOPIC[/u][/b]
+")))
+
+(ert-deftest test-org-bb/export-headline-lv5 ()
+ (should (equal (test-org-bb-export "* dummy
+** dummy
+*** dummy
+**** dummy
+***** TOPIC
+")
+ "[b][u]# dummy[/u][/b]
+
+[b][u]== dummy[/u][/b]
+
+[b][u]+++ dummy[/u][/b]
+
+[b][u]:::: dummy[/u][/b]
+
+[b][u]----- TOPIC[/u][/b]
+")))
+
+(ert-deftest test-org-bb/export-italic ()
+ (should (equal (test-org-bb-export "foo /BAR/ baz
+")
+ "foo [i]BAR[/i] baz
+")))
+
+(ert-deftest test-org-bb/export-line-break ()
+ (should (equal (test-org-bb-export "foo\\\\
+bar
+")
+ "foo[br]_[/br]
+bar
+")))
+
+(ert-deftest test-org-bb/export-link-about ()
+ (should (equal (test-org-bb-export "[[about:config][bar]]
+")
+ "[url=about:config]bar[/url]
+")))
+
+(ert-deftest test-org-bb/export-link-empty ()
+ (should (equal (test-org-bb-export "http://example.com/
+")
+ "[url=http://example.com/]http://example.com/[/url]
+")))
+
+(ert-deftest test-org-bb/export-link-encode-url-only-once ()
+ (should (equal (test-org-bb-export "[[http://foo/%20bar][baz]]
+")
+ "[url=http://foo/%20bar]baz[/url]
+")))
+
+(ert-deftest test-org-bb/export-link-encode-url ()
+ (should (equal (test-org-bb-export "[[http://foo/ bar][baz]]
+")
+ "[url=http://foo/%20bar]baz[/url]
+")))
+
+(ert-deftest test-org-bb/export-link-http ()
+ (should (equal (test-org-bb-export "[[http://foo/][bar]]
+")
+ "[url=http://foo/]bar[/url]
+")))
+
+(ert-deftest test-org-bb/export-link-https ()
+ (should (equal (test-org-bb-export "[[https://foo/][bar]]
+")
+ "[url=https://foo/]bar[/url]
+")))
+
+(ert-deftest test-org-bb/export-multiline-paragraph ()
+ (should (equal (test-org-bb-export "foo
+bar
+")
+ "foo
+bar
+")))
+
+(ert-deftest test-org-bb/export-multiple-paragraphs ()
+ (should (equal (test-org-bb-export "foo
+
+bar
+")
+ "foo
+
+bar
+")))
+
+(ert-deftest test-org-bb/export-plain-list-descriptive ()
+ (should (equal (test-org-bb-export "- foo :: pokey
+- bar :: hokey
+")
+ "[list]
+[*][i]foo:[/i] pokey
+[*][i]bar:[/i] hokey
+[/list]
+")))
+
+(ert-deftest test-org-bb/export-plain-list-ordered ()
+ (should (equal (test-org-bb-export "1. foo
+2. bar
+")
+ "[list=1]
+[*]foo
+[*]bar
+[/list]
+")))
+
+(ert-deftest test-org-bb/export-plain-list-unordered ()
+ (should (equal (test-org-bb-export "- foo
+- bar
+")
+ "[list]
+[*]foo
+[*]bar
+[/list]
+")))
+
+(ert-deftest test-org-bb/export-quote-block ()
+ (should (equal (test-org-bb-export "#+BEGIN_QUOTE
+Somebody
+said
+this.
+#+END_QUOTE
+")
+ "[quote]
+Somebody
+said
+this.
+[/quote]
+")))
+
+(ert-deftest test-org-bb/export-single-paragraph ()
+ (should (equal (test-org-bb-export "foo
+")
+ "foo
+")))
+
+(ert-deftest test-org-bb/export-strike-through ()
+ (should (equal (test-org-bb-export "foo +BAR+ baz
+")
+ "foo [s]BAR[/s] baz
+")))
+
+(ert-deftest test-org-bb/export-underline ()
+ (should (equal (test-org-bb-export "foo _BAR_ baz
+")
+ "foo [u]BAR[/u] baz
+")))
+
+(ert-deftest test-org-bb/export-verbatim ()
+ (should (equal (test-org-bb-export "foo =BAR= baz
+")
+ "foo [font=monospace]BAR[/font] baz
+")))
+
+
+
+(provide 'test-ox-bb)
+
+;;; test-ox-bb.el ends here
--
2.20.1
next reply other threads:[~2021-02-05 8:08 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-02-05 8:07 Christian Garbs via General discussions about Org-mode. [this message]
2021-04-26 7:49 ` [PATCH] ox-bb.el: Add BBCode exporter Bastien
[not found] ` <20210509124822.swutjatewu7a7xmo@cgarbs.de>
2021-05-09 14:10 ` Bastien
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210205080755.ex55vibiqhczqrjb@cgarbs.de \
--to=emacs-orgmode@gnu.org \
--cc=mitch@cgarbs.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).