Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ox-epub exported source block has big indent space #25

Closed
stardiviner opened this issue Oct 30, 2018 · 13 comments
Closed

ox-epub exported source block has big indent space #25

stardiviner opened this issue Oct 30, 2018 · 13 comments
Assignees

Comments

@stardiviner
Copy link

stardiviner commented Oct 30, 2018

It looks like this:

ox-epub source block large indent space

Always, the first line is 0-space indented. But following lines are all long-space indented.

org version: 9.1.14
emacs version: 26.1
platform: Arch Linux, 4.18.16
installed via: MELPA

@ofosos ofosos self-assigned this Oct 30, 2018
@ofosos
Copy link
Owner

ofosos commented Oct 30, 2018

Can you add a source snippet, which creates this image?

@stardiviner
Copy link
Author

Here is one section of headlines which corresponding to upper screenshot:

****** type hinting
       :PROPERTIES:
       :CUSTOM_ID: type hinting
       :END:

Type hinting is the bridge between the *dynamically-typed world of Clojure* (where almost
everything is treated as a ~java.lang.Object~) and the *statically-typed world of Java*. Type
hints in Clojure are optional 90% of the cases but when raw speed is important one of the
first advice is to check a function for proper type hinting (other common tips include
disabling checked-math, using primitive unboxed types, using transients and many others
techniques dependent on the specific code).

Type hints are normally a good idea during *Java interop*. The *Clojure compiler* uses the
information during the generation of the Java bytecode to avoid *reflection* (a very useful
but slow Java API for reflective invocations) and produce specialized bytecode.

To illustrate the point, the following example is about signing a request using a secret
key. The Java standard library contains all we need, so no external libraries are
required. The idea of signature is the following:

1. There is some unique string representation of the event we want to sign. We are going
   to use an URL in this example.
2. Two parties want to exchange the information but they want to be sure the information
   hasn’t been tampered with in between. So they generate and store a secret-key that
   will be used to sign the messages that no one else has access to.
3. When A wants to send a message to B, it signs the message by attaching a signature to
   the message. B receives the message and applies the same process and verifies that the
   signature that is produced is the same that was sent.

It’s probably more difficult to explain in words than code:

#+begin_src clojure
(ns crypto
  (:import java.io.ByteArrayOutputStream
           javax.crypto.spec.SecretKeySpec
           javax.crypto.Mac
           java.util.Base64
           java.net.URLEncoder
           java.nio.charset.StandardCharsets))

(set! *warn-on-reflection* true)              ; (ref:toggle warn on reflection)

(defn get-bytes [s]
  (.getBytes s (StandardCharsets/UTF_8)))

(defn create-spec [secret]
  (SecretKeySpec. (get-bytes secret) "HmacSHA256"))

(defn init-mac [spec]
  (doto (Mac/getInstance "HmacSHA256")
    (.init spec)))

(defn compute-hmac [mac canonical]
  (.doFinal mac (get-bytes canonical)))

(defn encode [hmac]
  (URLEncoder/encode
   (.encodeToString (Base64/getEncoder) hmac)))

(defn sign [canonical secret]
  (-> secret
      create-spec
      init-mac
      (compute-hmac canonical)
      encode))

(defn sign-request [url]
  (let [signature (sign url "secret-password")]
    (format "%s?signature=%s" url signature)))

(sign-request "http://example.com/tx/1")
#+end_src

#+RESULTS[<2018-05-29 15:10:34> 6374aa6342170b21824b3338e3b2166d846b39db]:
: "http://example.com/tx/1?signature=EtUPpQpumBqQ5c6aCclS8xDIItfP6cINNkKJXtlP1pc%3D"

When we look at the output during compilation, Clojure prints something similar to the
following:

#+begin_example
Reflection warning, crypto.clj:12:3 - call to method getBytes can't be resolved
(target class is unknown).
Reflection warning, crypto.clj:21:3 - call to method doFinal can't be resolved
(target class is unknown).
#+end_example

Source lines/column references might be different, but the message says that there are at
least two places where the compiler is unable to infer the types and is using reflection
(at runtime) to resolve the right Java method and invoke it. If we assume in our example
peaks of 100k transactions per second, we might want to review how ~sign-request~ is doing
performance-wise. Advanced tools like [[file:~/Org/Wiki/Computer%20Technology/Programming/Programming%20Languages/Clojure/Data/Clojure%20Packages/criterium.org::*Intro][Criterium]] are always the suggested choice for
*benchmarking*, but in this specific case we can clearly see what happens by just using
"~time~":

#+begin_src clojure
(time (dotimes [i 100000]
        (sign-request (str "http://example.com/tx/" i))))
#+end_src

#+RESULTS[<2018-05-29 15:18:31> 5a54c98226c54bf986c883c0184dd9085a4ea366]:
: "Elapsed time: 12195.125723 msecs"
: nil

The elapsed time is completely dependent on the computer this quick bench is
executed, so it could be different on your machine. Let’s now add type hints to the
function definitions highlighted by the compiler warnings:

#+NAME: add type hinting metadata
#+begin_src clojure
(defn get-bytes [^String s]                      ; (ref:add type hinting metadata)
  (.getBytes s (StandardCharsets/UTF_8)))

(defn compute-hmac [^Mac mac canonical]
  (.doFinal mac (get-bytes canonical)))

(time (dotimes [i 100000]
        (sign-request (str "http://example.com/tx/" i))))
#+end_src

~fn~ is the minimum common denominators for all declaring functions and macros. For
instance, *type hints* given for arguments in a function declared with ~defn~ are processed by
~fn~. Although implemented in ~fn~ *type hints* or *pre-post conditions* are usually present in
~defn~ declarations.

#+begin_src clojure
(defn hinted ^long [] 42)

(-> #'hinted meta :arglists first meta :tag)
#+end_src

#+RESULTS[<2018-05-29 10:29:00> 97e5ad7523ad7f6c28d9271ffb018dc59dbbd933]:
: long

@stardiviner
Copy link
Author

I have tested with ox-html, it has no this indentation issue.

@stardiviner
Copy link
Author

stardiviner commented Nov 1, 2018

I tested with another simple example:

* source block indent in exporting

#+begin_src clojure
(defn hello
  [name]
  (prn (str "Hello, " name ".")))

(hello "Chris")
#+end_src

#+RESULTS[<2018-11-01 09:57:33> e328a3ddd2f93070d52fb07e1be6e5451c872314]:
: nil#'user/hellonil

Found the generated epub body.html looks like this:

    <div id="outline-container-orgab50461" class="outline-2">
      <h2 id="orgab50461"><span class="section-number-2">1</span> source block indent in exporting</h2>
      <div class="outline-text-2" id="text-1">
        <div class="org-src-container">
          <pre><code class="src src-clojure"><span style="color: #4f97d7;">(</span><span style="color: #4f97d7; font-weight: bold;">defn</span> <span style="color: #bc6ec5; font-weight: bold;">hello</span>
            <span style="color: #bc6ec5;">[</span>name<span style="color: #bc6ec5;">]</span>
            <span style="color: #bc6ec5;">(</span><span style="color: #4f97d7;">prn</span> <span style="color: #2d9574;">(</span><span style="color: #4f97d7;">str</span> <span style="color: #2d9574;">"Hello, "</span> name <span style="color: #2d9574;">"."</span><span style="color: #2d9574;">)</span><span style="color: #bc6ec5;">)</span><span style="color: #4f97d7;">)</span>

            <span style="color: #4f97d7;">(</span>hello <span style="color: #2d9574;">"Chris"</span><span style="color: #4f97d7;">)</span>
          </code></pre>
        </div>

        <pre class="example">
nil#'user/hellonil
        </pre>
      </div>
    </div>

Here is the screenshot

image

It's because <code> in <pre><code class="src src-clojure"> <span ....> .... perserves the space in HTML file source code.

I tried to change some options:

(setq org-html-indent t)
(setq org-src-preserve-indentation t)

But still does not work.

@ofosos
Copy link
Owner

ofosos commented Nov 1, 2018

Thanks, will look into it in the evening

@stardiviner
Copy link
Author

I checked out ox-html exported HTML source code, it does not removed whitespaces.
Here it is:

<div class="org-src-container">
<pre><code class="src src-clojure">
<span class="org-rainbow-delimiters-depth-1">(</span><span class="org-keyword">defn</span> <span class="org-function-name">hello</span>
  <span class="org-rainbow-delimiters-depth-2">[</span>name<span class="org-rainbow-delimiters-depth-2">]</span>
  <span class="org-rainbow-delimiters-depth-2">(</span><span class="org-builtin">prn</span> <span class="org-rainbow-delimiters-depth-3">(</span><span class="org-builtin">str</span> <span class="org-string">"Hello, "</span> name <span class="org-string">"."</span><span class="org-rainbow-delimiters-depth-3">)</span><span class="org-rainbow-delimiters-depth-2">)</span>
  <span class="org-rainbow-delimiters-depth-2">(</span><span class="org-keyword">when</span> 1
    <span class="org-rainbow-delimiters-depth-3">(</span><span class="org-builtin">prn</span> <span class="org-string">"kk"</span><span class="org-rainbow-delimiters-depth-3">)</span>
    <span class="org-rainbow-delimiters-depth-3">(</span><span class="org-keyword">if</span> <span class="org-string">"a"</span>
      <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-builtin">println</span> <span class="org-string">"hi"</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span><span class="org-rainbow-delimiters-depth-2">)</span><span class="org-rainbow-delimiters-depth-1">)</span>

<span class="org-rainbow-delimiters-depth-1">(</span>hello <span class="org-string">"Chris"</span><span class="org-rainbow-delimiters-depth-1">)</span>

</code></pre>
</div>

If ox-epub use ox-html, I think must be ox-epub problem made some change on HTML file.

@ofosos
Copy link
Owner

ofosos commented Nov 14, 2018

I think I can fix this. But I'm currently a little bit pressed for time, please allow me to look into it on the weekend.

@stardiviner
Copy link
Author

Sorry for this if my update give you stress. Don't mind this. I update detail info here so that you can get more description about the problem. And Thanks.

@stardiviner
Copy link
Author

@ofosos Sorry for my distribution. I'm exporting a book to epub format with Org Mode content. So I need to fix this problem. What's update on this issue?

@ofosos
Copy link
Owner

ofosos commented Dec 5, 2018

I'll finally have some time on sunday to debug this :)

@ofosos
Copy link
Owner

ofosos commented Dec 9, 2018

Hey @stardiviner,
If I set

(setq org-html-indent t)

I get the same behaviour you have. If I set

(setq org-html-indent nil)

It's back to the right way.

Does that help?

@stardiviner
Copy link
Author

Confirmed, with minimal Emacs config, it works fine. Thanks @ofosos

@stardiviner
Copy link
Author

I found why now, it is caused by package aggressive-indent.
Here is my config:

(use-package aggressive-indent
  :ensure t
  :delight aggressive-indent-mode
  :init (setq aggressive-indent-sit-for-time 0.1)
  :config
  ;; global
  ;; only work if `global-aggressive-indent-mode'
  (add-to-list 'aggressive-indent-excluded-modes 'python-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'haskell-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'lua-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'coq-mode)
  (add-to-list 'aggressive-indent-excluded-modes 'snippet-mode)
  ;; (global-aggressive-indent-mode 1)

  ;; specific
  (defun my/aggressive-indent-enable ()
    (unless (or (member major-mode aggressive-indent-excluded-modes)
                (member major-mode aggressive-indent-dont-electric-modes))
      (aggressive-indent-mode 1)))
  (add-hook 'prog-mode-hook #'my/aggressive-indent-enable)

  (add-to-list 'aggressive-indent-dont-electric-modes 'python-mode)

  ;; The variable `aggressive-indent-dont-indent-if' lets you customize when you
  ;; **don't** want indentation to happen.  For instance, if you think it's
  ;; annoying that lines jump around in `c++-mode' because you haven't typed the
  ;; `;' yet, you could add the following clause:
  (add-to-list 'aggressive-indent-dont-indent-if
               '(and (derived-mode-p 'c++-mode)
                     (null (string-match "\\([;{}]\\|\\b\\(if\\|for\\|while\\)\\b\\)"
                                         (thing-at-point 'line)))))
  )

Can you try it out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants