emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [New exporter] custom emphasis in org-emphasis-alist
@ 2013-02-10  0:32 Gregor Kappler
  2013-02-10  0:55 ` François Pinard
  2013-02-10  8:37 ` Nicolas Goaziou
  0 siblings, 2 replies; 5+ messages in thread
From: Gregor Kappler @ 2013-02-10  0:32 UTC (permalink / raw)
  To: emacs-orgmode

Cudos for all the work that has been done on migrating to the new
exporter.  I so welcome that exporting now is approaching a clean
design!

I am currently migrating my system and contribute my first stop:
custom emphasis characters that I use extensively:
- "!" is used for exclamations,
- "?" for questions, and
- "#" for in-text comments that I do not want exported.

This is my org-emphasis-alist configuration:
#+BEGIN_SRC emacs-lisp
  (setq org-emphasis-alist
        (quote (("*" bold "<b>" "</b>")
                ("/" italic "<i>" "</i>")
                ("_" underline "<span style=\"text-decoration:underline;\">" "</span>")
                ("=" (:box t :foreground "#AAF") "<code>" "</code>" verbatim)
                ("~" org-headline-done "<u>" "</u>" verbatim)
                ("+" (:strike-through t) "<del>" "</del>")
                ("?" gk-org-question "<span class=\"org-question\">" "</span>")
                ("!" gk-org-exclamation "<span class=\"org-exclamation\">" "</span>")
                ("#" font-lock-comment-face "<!--" "-->"))))
#+END_SRC

These emphases are currently not working .  During debugging I found
the following to be the reason: Though
org-element-text-markup-successor recognizes emphasis by using
org-emph-re, it returns only one of a set of hard-coded symbols
(`bold', `italic', `underline', `strike-through', `code' and
`verbatim').

IMHO org-element-text-markup-successor should recognize
org-emphasis-alist.  Maybe return the face configured in the cadr of
the respective org-emphasis-alist element.  However, the drawback
would be that this would disenable "quick-and-dirty" face
specification as with (:box t :foreground "#AAF") above.

On the other hand, I do not know how plans with org-emphasis-alist
are, as caddr,... for html export and org-export-latex-emphasis-alist
probably are obsolete with the new exporter.

With the following setup, the following test case worked.  However,
changing deconsts and defuns in org-element surely is not a good
approach.  Do you have any suggestions?

Thank you all for your amazing work on orgmode,

Gregor

* Test Case
With these settings these work: the test *bold*, (invisible comment?!), and .

But these still do not work: *bold*, (\leftarrow invisible comment?!), !exclamation! and ?question?.  These examples stop working after the comment.

: (org-export-to-file 'my-html "test.html")

* Changes
I played around further by adding lines to
org-element-text-markup-successor
#+BEGIN_SRC emacs-lisp
  (defun org-element-text-markup-successor (limit)
    "Search for the next text-markup object.

  LIMIT bounds the search.

  Return value is a cons cell whose CAR is a symbol among `bold',
  `italic', `underline', `strike-through', `code' and `verbatim'
  and CDR is beginning position."
    (save-excursion
      (unless (bolp) (backward-char))
      (when (re-search-forward org-emph-re limit t)
        (let ((marker (match-string 3)))
          (cons (cond
                 ((equal marker "*") 'bold)
                 ((equal marker "/") 'italic)
                 ((equal marker "_") 'underline)
                 ((equal marker "+") 'strike-through)
                 ((equal marker "~") 'code)
                 ((equal marker "=") 'verbatim)
                 ((equal marker "#") 'emph-comment)
                 ((equal marker "?") 'emph-question)
                 ((equal marker "!") 'emph-exclamation)
                 (t (error "Unknown marker at %d" (match-beginning 3))))
                (match-beginning 2))))))
#+END_SRC

and then trying to define org-element-emph-comment-parser copy-pasting
org-element-italic-parser:
#+BEGIN_SRC emacs-lisp
  (defun org-element-emph-comment-parser ()
    "Parse comment object at point.

  Return a list whose CAR is `italic' and CDR is a plist with
  `:begin', `:end', `:contents-begin' and `:contents-end' and
  `:post-blank' keywords.

  Assume point is at the first # marker."
    (save-excursion
      (unless (bolp) (backward-char 1))
      (looking-at org-emph-re)
      (let ((begin (match-beginning 2))
            (contents-begin (match-beginning 4))
            (contents-end (match-end 4))
            (post-blank (progn (goto-char (match-end 2))
                               (skip-chars-forward " \t")))
            (end (point)))
        (list 'emph-comment
              (list :begin begin
                    :end end
                    :contents-begin contents-begin
                    :contents-end contents-end
                    :post-blank post-blank)))))


  (defun org-element-emph-comment-interpreter (italic contents)
    "Interpret ITALIC object as Org syntax.
  CONTENTS is the contents of the object."
    (format "#%s#" contents))

  (defun org-element-emph-exclamation-parser ()
    "Parse exclamation object at point.

  Return a list whose CAR is `italic' and CDR is a plist with
  `:begin', `:end', `:contents-begin' and `:contents-end' and
  `:post-blank' keywords.

  Assume point is at the first # marker."
    (save-excursion
      (unless (bolp) (backward-char 1))
      (looking-at org-emph-re)
      (let ((begin (match-beginning 2))
            (contents-begin (match-beginning 4))
            (contents-end (match-end 4))
            (post-blank (progn (goto-char (match-end 2))
                               (skip-chars-forward " \t")))
            (end (point)))
        (list 'emph-exclamation
              (list :begin begin
                    :end end
                    :contents-begin contents-begin
                    :contents-end contents-end
                    :post-blank post-blank)))))


  (defun org-element-emph-exclamation-interpreter (italic contents)
    "Interpret ITALIC object as Org syntax.
  CONTENTS is the contents of the object."
    (format "?%s?" contents))

  (defun org-element-emph-question-parser ()
    "Parse question object at point.

  Return a list whose CAR is `italic' and CDR is a plist with
  `:begin', `:end', `:contents-begin' and `:contents-end' and
  `:post-blank' keywords.

  Assume point is at the first # marker."
    (save-excursion
      (unless (bolp) (backward-char 1))
      (looking-at org-emph-re)
  (message "%s" (match-string 4))
      (let ((begin (match-beginning 2))
            (contents-begin (match-beginning 4))
            (contents-end (match-end 4))
            (post-blank (progn (goto-char (match-end 2))
                               (skip-chars-forward " \t")))
            (end (point)))
        (list 'emph-question
              (list :begin begin
                    :end end
                    :contents-begin contents-begin
                    :contents-end contents-end
                    :post-blank post-blank)))))

  (defun org-element-emph-question-interpreter (italic contents)
    "Interpret ITALIC object as Org syntax.
  CONTENTS is the contents of the object."
    (format "?%s?" contents))
#+END_SRC
(Considering that I only changed the defun name and emph-comment, to me this seems a lot of duplicate code.  I am a mere elisp novice without knowledge of macros - but would this not be a case to use macros or similar in order to ease things?)

Stealing from org-html-italic 
#+BEGIN_SRC emacs-lisp
  (add-to-list 'org-html-text-markup-alist '(emph-comment . ""))
  (add-to-list 'org-html-text-markup-alist '(emph-exclamation . "<span class=\"org-exclamation\">%s</span>"))
  (add-to-list 'org-html-text-markup-alist '(emph-question . "<span class=\"org-question\">%s</span>"))

  (defun my-emph-comment (text contents info)
    "Transcode a comment emphasis element from Org to latex.
     CONTENTS is nil.  INFO is a plist used as a communication
     channel."
    (message "comment %s" contents)
    (format (or (cdr (assq 'emph-comment org-html-text-markup-alist)) "%s") contents))

  (defun my-emph-exclamation (text contents info)
    "Transcode a comment emphasis element from Org to latex.
     CONTENTS is nil.  INFO is a plist used as a communication
     channel."
    (message "exclamation %s" contents)
    (format (or (cdr (assq 'emph-exclamation org-html-text-markup-alist)) "%s") contents))

  (defun my-emph-question (text contents info)
    "Transcode a comment emphasis element from Org to latex.
     CONTENTS is nil.  INFO is a plist used as a communication
     channel."
    ;;(fux)
    (message "question %s" contents)
    (format (or (cdr (assq 'emph-question org-html-text-markup-alist)) "%s") contents))

  (org-export-define-derived-backend my-html html 
    :translate-alist ((emph-comment . my-emph-comment)
                      (emph-exclamation . my-emph-exclamation)
                      (emph-question . my-emph-question)))
#+END_SRC
(again, this seems a lot of duplicate code...)

With this change, contents always was passed nil...  
So I digged further into org-element.el - and finally by experimenting found that adding my new elements to
#+BEGIN_SRC emacs-lisp
  (defconst org-element-recursive-objects
    '(emph-question emph-exclamation emph-comment bold italic link subscript radio-target strike-through superscript
           table-cell underline)
    "List of recursive object types.")
#+END_SRC
resulted in a (partly) working solution.  Yet, bold and italic occur quite often hard-coded in org-element.el.

-- 
Dr. Gregor Kappler

Fakultät für Psychologie 
Institut für Angewandte Psychologie: Gesundheit,
		Entwicklung, Förderung
Universität Wien
Liebiggasse 5
A-1010 Wien

http://www.univie.ac.at/Psychologie
tel: +43 1 4277 47276

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

* Re: [New exporter] custom emphasis in org-emphasis-alist
  2013-02-10  0:32 [New exporter] custom emphasis in org-emphasis-alist Gregor Kappler
@ 2013-02-10  0:55 ` François Pinard
  2013-02-10  8:37 ` Nicolas Goaziou
  1 sibling, 0 replies; 5+ messages in thread
From: François Pinard @ 2013-02-10  0:55 UTC (permalink / raw)
  To: emacs-orgmode

Gregor Kappler <g.kappler@gmx.net> writes:

> Cudos for all the work that has been done on migrating to the new
> exporter.  I so welcome that exporting now is approaching a clean
> design!

Let me join my voice to the chorus!  Munch congratulations, and thanks!
There is an impressive amount of work in all this, it has been a major
undertaking.  I'm very glad to see that is now in the process of falling
back into place in a definitive way.  Very good news for us all!

François

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

* Re: [New exporter] custom emphasis in org-emphasis-alist
  2013-02-10  0:32 [New exporter] custom emphasis in org-emphasis-alist Gregor Kappler
  2013-02-10  0:55 ` François Pinard
@ 2013-02-10  8:37 ` Nicolas Goaziou
  2013-02-10  9:45   ` Nicolas Goaziou
  2013-02-14 17:42   ` Gregor Kappler
  1 sibling, 2 replies; 5+ messages in thread
From: Nicolas Goaziou @ 2013-02-10  8:37 UTC (permalink / raw)
  To: Gregor Kappler; +Cc: emacs-orgmode

Hello,

Gregor Kappler <g.kappler@gmx.net> writes:

> I am currently migrating my system and contribute my first stop:
> custom emphasis characters that I use extensively:
> - "!" is used for exclamations,
> - "?" for questions, and
> - "#" for in-text comments that I do not want exported.

Emphasis characters are now hard-coded. You cannot change them, though,
you can change how each back-end interprets them.

We are solidifying Org syntax for parsing purposes. Allowing variable
markup is asking for trouble. The plan is to make `org-emphasis-alist'
a defconst.

On the other hand, you may be able to parse custom markup with the help
of a filter:

#+begin_src emacs-lisp
(defun my-special-markup (text backend info)
  (when (and (org-export-derived-backend-p backend 'html)
             (string-match "\\([ 	('\"{]\\|^\\)\\(\\([?!#]\\)\\([^ 	
,\"']\\|[^ 	
,\"'].*?\\(?:
.*?\\)\\{0,1\\}[^ 	
,\"']\\)\\3\\)\\([- 	.,:!?;'\")}\\]\\|$\\)"
                           text))
    (format (cond ((equal (match-string 3 text) "?")
                   "<span class=\"org-question\">%s</span>")
                  ((equal (match-string 3 text) "#") "<!--%s-->")
                  (t "<span class=\"org-exclamation\">%s</span>"))
            (match-string 4 text))))
(add-to-list 'org-export-filter-plain-text-functions 'my-special-markup)
#+end_src


Regards,

-- 
Nicolas Goaziou

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

* Re: [New exporter] custom emphasis in org-emphasis-alist
  2013-02-10  8:37 ` Nicolas Goaziou
@ 2013-02-10  9:45   ` Nicolas Goaziou
  2013-02-14 17:42   ` Gregor Kappler
  1 sibling, 0 replies; 5+ messages in thread
From: Nicolas Goaziou @ 2013-02-10  9:45 UTC (permalink / raw)
  To: Gregor Kappler; +Cc: emacs-orgmode

Completing myself,

> On the other hand, you may be able to parse custom markup with the help
> of a filter:
>
> #+begin_src emacs-lisp
> (defun my-special-markup (text backend info)
>   (when (and (org-export-derived-backend-p backend 'html)
>              (string-match "\\([ 	('\"{]\\|^\\)\\(\\([?!#]\\)\\([^ 	
> ,\"']\\|[^ 	
> ,\"'].*?\\(?:
> .*?\\)\\{0,1\\}[^ 	
> ,\"']\\)\\3\\)\\([- 	.,:!?;'\")}\\]\\|$\\)"
>                            text))
>     (format (cond ((equal (match-string 3 text) "?")
>                    "<span class=\"org-question\">%s</span>")
>                   ((equal (match-string 3 text) "#") "<!--%s-->")
>                   (t "<span class=\"org-exclamation\">%s</span>"))
>             (match-string 4 text))))
> (add-to-list 'org-export-filter-plain-text-functions 'my-special-markup)
> #+end_src

This solution will not work but in the simplest cases (no special
markup allowed within the tags).

For additional syntax, a better option would be to define a macro:

  #+MACRO: excl @@html:<span class="org-exclamation">@@$1@@html:</span>@@

Then use it within the buffer:

  A paragraph and {{{excl(some *bold* text within a special
  container)}}}.

This allow for some flexibility. You can even define a babel block and
call it within a paragraph for more complicated (i.e. conditional)
stuff.


Regards,

-- 
Nicolas Goaziou

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

* Re: [New exporter] custom emphasis in org-emphasis-alist
  2013-02-10  8:37 ` Nicolas Goaziou
  2013-02-10  9:45   ` Nicolas Goaziou
@ 2013-02-14 17:42   ` Gregor Kappler
  1 sibling, 0 replies; 5+ messages in thread
From: Gregor Kappler @ 2013-02-14 17:42 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: emacs-orgmode

Hi Nicolas,

thanks for your replies.  I can see that for org syntax stability and to prevent parsing hassle using a variety of characters for emphasis is not desireable.  On the other hand, org-emphasis-alist had several advantages that seem not possible with your suggestions.

When writing, I frequently mark up and fontify text for review or important text parts (each, ? and ! used ~1000 times in my org files).  The fontification in org provided a perfect overview of what still needs attention.  

>  For additional syntax, a better option would be to define a macro:
>
>    #+MACRO: excl @@html:<span class="org-exclamation">@@$1@@html:</span>@@
>
>  Then use it within the buffer:
>
>    A paragraph and {{{excl(some *bold* text within a special
>    container)}}}.

This solution is charactery/wordy but I could work with some editing sugar (abbrevs, remove-macro-keys,...).  Yet I loose the fontification feature that I appreciate highly.  

For now, I took the road of adding a filter as you suggested, and made it configurable with format strings for latex and html.  Also, I implemented org highlighting (borrowing from `org-set-emph-re' and `org-do-emphasis-faces').
This solution now works for me.  If anyone else has custom emphasis markers, and wants to keep them (like I did) the following code might be usable.

Thank you very much for your help!

Gregor

Your markup filter code did not replace multiple occurences and had hard-coded regexps, so I adapted your markup code:
#+begin_src emacs-lisp
    (require 'ox)
    (defun gk-emphasis-markup (text backend info)
      (let ((formats (assoc backend gk-org-export-emphasis)))
        (when formats
          (let ((start 0) (result ""))
            (while (string-match gk-org-emph-re text start)
              (setq result 
                    (concat result 
                            (substring text start (match-beginning 2))
                            (format (cadr (assoc (match-string 3 text) formats))
                                    (match-string 4 text))))
              (setq start (- (match-end 0) 1)))
            (concat result
                    (if 
                        (substring text start (length text))
                        ""))))))
      (add-to-list 'org-export-filter-plain-text-functions 'gk-emphasis-markup)
#+end_src

The formating strings are configured
#+begin_src emacs-lisp
    (setq gk-org-export-emphasis
          '((html . (("?" "<span class=\"org-question\">%s</span>")
                     ("!" "<span class=\"org-exclamation\">%s</span>")
                     ("#" "")))
            (latex . (("?" "\\mycheck{%s}")
                      ("!" "\\myexcl{%s}")
                      ("#" "")))))
#+end_src

Org highlighting for the custom emphasis list (adapted from `org-set-emph-re')
#+BEGIN_SRC emacs-lisp :tangle "~/Documents/Programme/elisp/my-org-mode.el"
  (setq gk-org-emphasis-alist
        (quote (
                ("?" gk-org-question)
                ("!" gk-org-exclamation)
                ("#" font-lock-comment-face))))
  (setq gk-org-emph-re 
    (let* ((e org-emphasis-regexp-components)
           (pre (car e))
           (post (nth 1 e))
           (border (nth 2 e))
           (body (nth 3 e))
           (nl (nth 4 e))
           (body1 (concat body "*?"))
           (markers (mapconcat 'car gk-org-emphasis-alist ""))
           (vmarkers (mapconcat
                      (lambda (x) (if (eq (nth 4 x) 'verbatim) (car x) ""))
                      gk-org-emphasis-alist "")))
      ;; make sure special characters appear at the right position in the class
      (if (string-match "\\^" markers)
          (setq markers (concat (replace-match "" t t markers) "^")))
      (if (string-match "-" markers)
          (setq markers (concat (replace-match "" t t markers) "-")))
      (if (string-match "\\^" vmarkers)
          (setq vmarkers (concat (replace-match "" t t vmarkers) "^")))
      (if (string-match "-" vmarkers)
          (setq vmarkers (concat (replace-match "" t t vmarkers) "-")))
      (if (> nl 0)
          (setq body1 (concat body1 "\\(?:\n" body "*?\\)\\{0,"
                              (int-to-string nl) "\\}")))
      ;; Make the regexp
      (concat "\\([" pre "]\\|^\\)"
              "\\("
              "\\([" markers "]\\)"
              "\\("
              "[^" border "]\\|"
              "[^" border "]"
              body1
              "[^" border "]"
              "\\)"
              "\\3\\)"
              "\\([" post "]\\|$\\)")))
  
#+END_SRC


The emphasis function (adapted `org-do-emphasis-faces') then is added as font-lock 
#+BEGIN_SRC emacs-lisp :tangle "~/Documents/Programme/elisp/my-org-mode.el"
  (defun gk-org-do-emphasis-faces (limit)
    "Run through the buffer and add overlays to emphasized strings."
    (let (rtn a)
      (while (and (not rtn) (re-search-forward gk-org-emph-re limit t))
        (if (not (= (char-after (match-beginning 3))
                    (char-after (match-beginning 4))))
            (progn
              (setq rtn t)
              (setq a (assoc (match-string 3) gk-org-emphasis-alist))
              (font-lock-prepend-text-property (match-beginning 2) (match-end 2)
                                               'face
                                               (nth 1 a))
              (and (nth 4 a)
                   (org-remove-flyspell-overlays-in
                    (match-beginning 0) (match-end 0)))
              (add-text-properties (match-beginning 2) (match-end 2)
                                   '(font-lock-multiline t org-emphasis t))
              (when org-hide-emphasis-markers
                (add-text-properties (match-end 4) (match-beginning 5)
                                     '(invisible org-link))
                (add-text-properties (match-beginning 3) (match-end 3)
                                     '(invisible org-link)))))
        (backward-char 1))
      rtn))
  
  (font-lock-add-keywords
   'org-mode '(gk-org-do-emphasis-faces))
#+END_SRC

Maybe the duplication of code could be prevented by including functions `org-set-emph-re' and `org-do-emphasis-faces' with dynamical scoping?


Nicolas Goaziou <n.goaziou@gmail.com> writes:

> Hello,
>
> Gregor Kappler <g.kappler@gmx.net> writes:
>
>> I am currently migrating my system and contribute my first stop:
>> custom emphasis characters that I use extensively:
>> - "!" is used for exclamations,
>> - "?" for questions, and
>> - "#" for in-text comments that I do not want exported.
>
> Emphasis characters are now hard-coded. You cannot change them, though,
> you can change how each back-end interprets them.
>
> We are solidifying Org syntax for parsing purposes. Allowing variable
> markup is asking for trouble. The plan is to make `org-emphasis-alist'
> a defconst.
>
> On the other hand, you may be able to parse custom markup with the help
> of a filter:
>
> #+begin_src emacs-lisp
> (defun my-special-markup (text backend info)
>   (when (and (org-export-derived-backend-p backend 'html)
>              (string-match "\\([ 	('\"{]\\|^\\)\\(\\([?!#]\\)\\([^ 	
> ,\"']\\|[^ 	
> ,\"'].*?\\(?:
> .*?\\)\\{0,1\\}[^ 	
> ,\"']\\)\\3\\)\\([- 	.,:!?;'\")}\\]\\|$\\)"
>                            text))
>     (format (cond ((equal (match-string 3 text) "?")
>                    "<span class=\"org-question\">%s</span>")
>                   ((equal (match-string 3 text) "#") "<!--%s-->")
>                   (t "<span class=\"org-exclamation\">%s</span>"))
>             (match-string 4 text))))
> (add-to-list 'org-export-filter-plain-text-functions 'my-special-markup)
> #+end_src
>
>
> Regards,
>
> -- 
> Nicolas Goaziou

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

end of thread, other threads:[~2013-02-14 17:50 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-10  0:32 [New exporter] custom emphasis in org-emphasis-alist Gregor Kappler
2013-02-10  0:55 ` François Pinard
2013-02-10  8:37 ` Nicolas Goaziou
2013-02-10  9:45   ` Nicolas Goaziou
2013-02-14 17:42   ` Gregor Kappler

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