emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [PATCH] ox-bb.el: Add BBCode exporter
@ 2021-02-05  8:07 Christian Garbs via General discussions about Org-mode.
  2021-04-26  7:49 ` Bastien
  0 siblings, 1 reply; 3+ messages in thread
From: Christian Garbs via General discussions about Org-mode. @ 2021-02-05  8:07 UTC (permalink / raw)
  To: emacs-orgmode

[-- 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 &lowast;asterisks&lowast;.
+")))
+
+(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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] ox-bb.el: Add BBCode exporter
  2021-02-05  8:07 [PATCH] ox-bb.el: Add BBCode exporter Christian Garbs via General discussions about Org-mode.
@ 2021-04-26  7:49 ` Bastien
       [not found]   ` <20210509124822.swutjatewu7a7xmo@cgarbs.de>
  0 siblings, 1 reply; 3+ messages in thread
From: Bastien @ 2021-04-26  7:49 UTC (permalink / raw)
  To: Christian Garbs via General discussions about Org-mode.; +Cc: Christian Garbs

Hi Christian,

Christian Garbs via "General discussions about Org-mode."
<emacs-orgmode@gnu.org> writes:

> 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.

thanks for the patch.  IMHO, such an exporter does not belongs in
Org's core repository.  

In the past, we suggested to add these contributed packages to the
contrib/ directory, but since this we will extract "contrib/" from
the Git repo soon, I don't suggest doing so.

Instead, you can host your code where you see fit and advertize it
by contributing to Worg:

https://orgmode.org/worg/worg-about.html

Thanks!


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] ox-bb.el: Add BBCode exporter
       [not found]   ` <20210509124822.swutjatewu7a7xmo@cgarbs.de>
@ 2021-05-09 14:10     ` Bastien
  0 siblings, 0 replies; 3+ messages in thread
From: Bastien @ 2021-05-09 14:10 UTC (permalink / raw)
  To: Christian Garbs; +Cc: Christian Garbs via General discussions about Org-mode.

Hi Christian,

Christian Garbs <mitch@cgarbs.de> writes:

>> Instead, you can host your code where you see fit and advertize it
>> by contributing to Worg:
>
> I should have Worg credentials on one my machines – when I find them,
> I'll add it.

Thanks in advance!  I was not able to find your email address among
code.orgmode.org users so perhaps you need a new username.  

If so please send the username you want privately and I'll add you
to Worg.

Thanks!

-- 
 Bastien


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-05-09 14:11 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-05  8:07 [PATCH] ox-bb.el: Add BBCode exporter Christian Garbs via General discussions about Org-mode.
2021-04-26  7:49 ` Bastien
     [not found]   ` <20210509124822.swutjatewu7a7xmo@cgarbs.de>
2021-05-09 14:10     ` Bastien

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).