Persistent highlighting in Emacs

| categories: orgmode, emacs | tags:

In this recent post I showed a way to use org-mode links to color text. The main advantage of that approach is it is explicit markup in the file, so it is persistent and exportable to html. The downside of that approach is you cannot use it in code, since the markup will break the code.

An alternative approach is to use overlays to color the text. This allows you to color the text, add annotations as tooltips and to provide a variety of highlighting colors. Overlays are not explicit markup in the file, so it is necessary to think of a way to save them so they can be restored later. We do this by using hook functions to store the overlays in a file-local variable on saving, and a file-local variable to restore the overlays when the file is opened. I bind the primary function `ov-highlighter/body' to a key, in my case hyper-h, which launches a hydra to access the commands.

You can find the code here: https://github.com/jkitchin/scimax/blob/org-9/ov-highlighter.el. Probably around mid-December it will get merged into the master branch.

Here is what this looks like in my buffer:

You may want to see the video:

  1. blue green pink yellow custom
  2. Put a comment here.
  3. Markup a tpyo.
  4. Get a list of the highlights in the buffer.

These highlights are pretty awesome. They work in code blocks, and comments. They also work in non-org files (only in Emacs of course).

a = 5
b = 6

print(a+b)#print the sum of a and b

11

Overall, this is pretty handy. You can highlight your own notes, provide feedback to others, etc. without changing the actual text in the document (well, except for the local variables at the end of the buffer, but these are usually in a "comment" that does not affect the document).

Here are few limitations though:

  1. You can only edit/change the file in Emacs, and the hook functions have to enabled, or the overlay data will get corrupted. That means a merge conflict can ruin the overlays.
  2. Anyone you share the file with needs to have the ov-highlighter library loaded too. Otherwise they will not see the highlights, and any edits will make the overlay data incorrect.
  3. The highlights do not export from org-mode (although they do work with `htmlize-buffer'!).
(let* ((html-buffer (htmlize-buffer))
       (html (with-current-buffer html-buffer
               (buffer-string))))
  (with-temp-file "test.html"
    (insert html))
  (kill-buffer html-buffer))

(browse-url "test.html")
#<process open test.html>

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter

New and improved asynchronous org-babel python blocks

| categories: python, orgmode, emacs | tags:

Table of Contents

About a year ago I posted some code to run org-babel python blocks asynchronously. This year, my students asked for some enhancements related to debugging. Basically, they were frustrated by a few things when they got errors. First, they found it difficult to find the line number in the Traceback in the src block because there are no line numbers in the block, and it is annoying to do a special edit just for line numbers.

I thought about this, and figured out how to significantly improve the situation. The async python code in scimax now has the following features:

  1. When you get a Traceback, it goes in the results, and each file listed in it is hyperlinked to the source file and line so it is easy to get to them.
  2. The cursor jumps to the last line in the code block that is listed in the Traceback, and a beacon shines to show you the line
  3. You can turn on temporary line numbers in the code block to see where the lines are in the block, and these disappear when you start typing. This is done in the variable `org-babel-async-python-show-line-numbers'.
  4. You can control whether a buffer of the results shows or not via the variable `org-babel-async-python-show-results'.
  5. When you run the block, you get a clickable link in the RESULTS section to kill the process.
  6. You may also find the `autopep8' and `pylint' functions helpful.

The code for this is currently found here: https://github.com/jkitchin/scimax/blob/org-9/scimax-org-babel-python.el

Eventually, I will merge this into master, after I am sure about all the changes needed for org 9.0. That is not likely to happen until the semester ends, so I do not mess up my students who use scimax in class. So, sometime mid-December it will make into master.

To make async the default way to run a python block use this code, so that you can use C-c C-c to run them:

(require 'scimax-org-babel-python)
(add-to-list 'org-ctrl-c-ctrl-c-hook 'org-babel-async-execute:python)

As with the past few posts, this video will make it much more clear what the post is about:

Here is a prototypical example that shows how it works. While it runs you can view the progress if you click on the link to show the results.

import time

for i in range(5):
    print(i)
    time.sleep(2)

0 1 2 3 4 Traceback (most recent call last): File "Org SRC", line 5, in <module> time.sleep(2) KeyboardInterrupt

This block has a pretty obvious issue when we run it. The cursor jumps right to the problem!

print('This line is ok')
# 5 / 0
print('We will not see this')

This line is ok We will not see this

This block shows we can access any of the links in the Traceback. Here we have an error in calling a function that is raised in an external file.

import numpy as np
from scipy.integrate import odeint

Vspan = np.linspace(0, 2) # L

# dF/dV = F
def dFdV(F, V, v0):
    return F


print(odeint(dFdV, 1.0, Vspan))

Traceback (most recent call last): File "Org SRC", line 11, in <module> print(odeint(dFdV, 1.0, Vspan)) File "/Users/jkitchin/anaconda3/lib/python3.5/site-packages/scipy/integrate/odepack.py", line 215, in odeint ixpr, mxstep, mxhnil, mxordn, mxords) TypeError: dFdV() missing 1 required positional argument: 'v0'

Here we show how nice it is to be able to kill a process. This block will not end on its own.

while True:
    pass

Traceback (most recent call last): File "Org SRC", line 2, in <module> pass KeyboardInterrupt

1 autopep8

autopep8 is a tool for reformatting Python code. We wrapped this into an Emacs command so you can quickly reformat a Python code block.

a = 4
b = 5
c = a * b  # comment
# another comment


def f(x):
    return x
print(f(5))

2 pylint

pylint is a great tool for checking your Python code for errors, style and conventions. We also wrapped this into an Emacs command so you can run it on a Python src block. The report that is generated had clickable links to help you get right to the lines in your code block with problems.

import numpy as np

a = np.array(5, 5)

def f(x): return x

print(f(6))

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter

New color link in org 9.0 using font-lock to color the text

| categories: orgmode, emacs | tags:

Table of Contents

I previously blogged about colored text in org-mode using links. Back in org 8.0, we had to do some tricky work with font-lock to get the colors to work, and particularly had to use overlays to get reliable coloring. Org 9.0 improves this a lot, with the font-lock built right in, and some other nice features like completion. The integration of font-lock means we do not have to deal with overlays! Here is what it looks like in my buffer:

And it exports nicely to HTML. You can see the code here: Code for the color link. This is good for small bits of text you want colored. It is not good for paragraphs, or text with line breaks in it, because links do not support that. It also is not useful in code blocks since the link syntax would break the code. You need an alternative approach for those applications which is more complicated than these simple links. One nice feature of this is the persistence (I mean it comes back when you open the file after closing it) of the color since it is explicit markup in the file.

(loop for color in color-name-rgb-alist
      do
      (princ (format "[[color:%s][Text colored in %s]] " (car color) (car color))))

Text colored in snow Text colored in ghostwhite Text colored in whitesmoke Text colored in gainsboro Text colored in floralwhite Text colored in oldlace Text colored in linen Text colored in antiquewhite Text colored in papayawhip Text colored in blanchedalmond Text colored in bisque Text colored in peachpuff Text colored in navajowhite Text colored in moccasin Text colored in cornsilk Text colored in ivory Text colored in lemonchiffon Text colored in seashell Text colored in honeydew Text colored in mintcream Text colored in azure Text colored in aliceblue Text colored in lavender Text colored in lavenderblush Text colored in mistyrose Text colored in white Text colored in black Text colored in darkslategray Text colored in darkslategrey Text colored in dimgray Text colored in dimgrey Text colored in slategray Text colored in slategrey Text colored in lightslategray Text colored in lightslategrey Text colored in gray Text colored in grey Text colored in lightgrey Text colored in lightgray Text colored in midnightblue Text colored in navy Text colored in navyblue Text colored in cornflowerblue Text colored in darkslateblue Text colored in slateblue Text colored in mediumslateblue Text colored in lightslateblue Text colored in mediumblue Text colored in royalblue Text colored in blue Text colored in dodgerblue Text colored in deepskyblue Text colored in skyblue Text colored in lightskyblue Text colored in steelblue Text colored in lightsteelblue Text colored in lightblue Text colored in powderblue Text colored in paleturquoise Text colored in darkturquoise Text colored in mediumturquoise Text colored in turquoise Text colored in cyan Text colored in lightcyan Text colored in cadetblue Text colored in mediumaquamarine Text colored in aquamarine Text colored in darkgreen Text colored in darkolivegreen Text colored in darkseagreen Text colored in seagreen Text colored in mediumseagreen Text colored in lightseagreen Text colored in palegreen Text colored in springgreen Text colored in lawngreen Text colored in green Text colored in chartreuse Text colored in mediumspringgreen Text colored in greenyellow Text colored in limegreen Text colored in yellowgreen Text colored in forestgreen Text colored in olivedrab Text colored in darkkhaki Text colored in khaki Text colored in palegoldenrod Text colored in lightgoldenrodyellow Text colored in lightyellow Text colored in yellow Text colored in gold Text colored in lightgoldenrod Text colored in goldenrod Text colored in darkgoldenrod Text colored in rosybrown Text colored in indianred Text colored in saddlebrown Text colored in sienna Text colored in peru Text colored in burlywood Text colored in beige Text colored in wheat Text colored in sandybrown Text colored in tan Text colored in chocolate Text colored in firebrick Text colored in brown Text colored in darksalmon Text colored in salmon Text colored in lightsalmon Text colored in orange Text colored in darkorange Text colored in coral Text colored in lightcoral Text colored in tomato Text colored in orangered Text colored in red Text colored in hotpink Text colored in deeppink Text colored in pink Text colored in lightpink Text colored in palevioletred Text colored in maroon Text colored in mediumvioletred Text colored in violetred Text colored in magenta Text colored in violet Text colored in plum Text colored in orchid Text colored in mediumorchid Text colored in darkorchid Text colored in darkviolet Text colored in blueviolet Text colored in purple Text colored in mediumpurple Text colored in thistle Text colored in snow1 Text colored in snow2 Text colored in snow3 Text colored in snow4 Text colored in seashell1 Text colored in seashell2 Text colored in seashell3 Text colored in seashell4 Text colored in antiquewhite1 Text colored in antiquewhite2 Text colored in antiquewhite3 Text colored in antiquewhite4 Text colored in bisque1 Text colored in bisque2 Text colored in bisque3 Text colored in bisque4 Text colored in peachpuff1 Text colored in peachpuff2 Text colored in peachpuff3 Text colored in peachpuff4 Text colored in navajowhite1 Text colored in navajowhite2 Text colored in navajowhite3 Text colored in navajowhite4 Text colored in lemonchiffon1 Text colored in lemonchiffon2 Text colored in lemonchiffon3 Text colored in lemonchiffon4 Text colored in cornsilk1 Text colored in cornsilk2 Text colored in cornsilk3 Text colored in cornsilk4 Text colored in ivory1 Text colored in ivory2 Text colored in ivory3 Text colored in ivory4 Text colored in honeydew1 Text colored in honeydew2 Text colored in honeydew3 Text colored in honeydew4 Text colored in lavenderblush1 Text colored in lavenderblush2 Text colored in lavenderblush3 Text colored in lavenderblush4 Text colored in mistyrose1 Text colored in mistyrose2 Text colored in mistyrose3 Text colored in mistyrose4 Text colored in azure1 Text colored in azure2 Text colored in azure3 Text colored in azure4 Text colored in slateblue1 Text colored in slateblue2 Text colored in slateblue3 Text colored in slateblue4 Text colored in royalblue1 Text colored in royalblue2 Text colored in royalblue3 Text colored in royalblue4 Text colored in blue1 Text colored in blue2 Text colored in blue3 Text colored in blue4 Text colored in dodgerblue1 Text colored in dodgerblue2 Text colored in dodgerblue3 Text colored in dodgerblue4 Text colored in steelblue1 Text colored in steelblue2 Text colored in steelblue3 Text colored in steelblue4 Text colored in deepskyblue1 Text colored in deepskyblue2 Text colored in deepskyblue3 Text colored in deepskyblue4 Text colored in skyblue1 Text colored in skyblue2 Text colored in skyblue3 Text colored in skyblue4 Text colored in lightskyblue1 Text colored in lightskyblue2 Text colored in lightskyblue3 Text colored in lightskyblue4 Text colored in slategray1 Text colored in slategray2 Text colored in slategray3 Text colored in slategray4 Text colored in lightsteelblue1 Text colored in lightsteelblue2 Text colored in lightsteelblue3 Text colored in lightsteelblue4 Text colored in lightblue1 Text colored in lightblue2 Text colored in lightblue3 Text colored in lightblue4 Text colored in lightcyan1 Text colored in lightcyan2 Text colored in lightcyan3 Text colored in lightcyan4 Text colored in paleturquoise1 Text colored in paleturquoise2 Text colored in paleturquoise3 Text colored in paleturquoise4 Text colored in cadetblue1 Text colored in cadetblue2 Text colored in cadetblue3 Text colored in cadetblue4 Text colored in turquoise1 Text colored in turquoise2 Text colored in turquoise3 Text colored in turquoise4 Text colored in cyan1 Text colored in cyan2 Text colored in cyan3 Text colored in cyan4 Text colored in darkslategray1 Text colored in darkslategray2 Text colored in darkslategray3 Text colored in darkslategray4 Text colored in aquamarine1 Text colored in aquamarine2 Text colored in aquamarine3 Text colored in aquamarine4 Text colored in darkseagreen1 Text colored in darkseagreen2 Text colored in darkseagreen3 Text colored in darkseagreen4 Text colored in seagreen1 Text colored in seagreen2 Text colored in seagreen3 Text colored in seagreen4 Text colored in palegreen1 Text colored in palegreen2 Text colored in palegreen3 Text colored in palegreen4 Text colored in springgreen1 Text colored in springgreen2 Text colored in springgreen3 Text colored in springgreen4 Text colored in green1 Text colored in green2 Text colored in green3 Text colored in green4 Text colored in chartreuse1 Text colored in chartreuse2 Text colored in chartreuse3 Text colored in chartreuse4 Text colored in olivedrab1 Text colored in olivedrab2 Text colored in olivedrab3 Text colored in olivedrab4 Text colored in darkolivegreen1 Text colored in darkolivegreen2 Text colored in darkolivegreen3 Text colored in darkolivegreen4 Text colored in khaki1 Text colored in khaki2 Text colored in khaki3 Text colored in khaki4 Text colored in lightgoldenrod1 Text colored in lightgoldenrod2 Text colored in lightgoldenrod3 Text colored in lightgoldenrod4 Text colored in lightyellow1 Text colored in lightyellow2 Text colored in lightyellow3 Text colored in lightyellow4 Text colored in yellow1 Text colored in yellow2 Text colored in yellow3 Text colored in yellow4 Text colored in gold1 Text colored in gold2 Text colored in gold3 Text colored in gold4 Text colored in goldenrod1 Text colored in goldenrod2 Text colored in goldenrod3 Text colored in goldenrod4 Text colored in darkgoldenrod1 Text colored in darkgoldenrod2 Text colored in darkgoldenrod3 Text colored in darkgoldenrod4 Text colored in rosybrown1 Text colored in rosybrown2 Text colored in rosybrown3 Text colored in rosybrown4 Text colored in indianred1 Text colored in indianred2 Text colored in indianred3 Text colored in indianred4 Text colored in sienna1 Text colored in sienna2 Text colored in sienna3 Text colored in sienna4 Text colored in burlywood1 Text colored in burlywood2 Text colored in burlywood3 Text colored in burlywood4 Text colored in wheat1 Text colored in wheat2 Text colored in wheat3 Text colored in wheat4 Text colored in tan1 Text colored in tan2 Text colored in tan3 Text colored in tan4 Text colored in chocolate1 Text colored in chocolate2 Text colored in chocolate3 Text colored in chocolate4 Text colored in firebrick1 Text colored in firebrick2 Text colored in firebrick3 Text colored in firebrick4 Text colored in brown1 Text colored in brown2 Text colored in brown3 Text colored in brown4 Text colored in salmon1 Text colored in salmon2 Text colored in salmon3 Text colored in salmon4 Text colored in lightsalmon1 Text colored in lightsalmon2 Text colored in lightsalmon3 Text colored in lightsalmon4 Text colored in orange1 Text colored in orange2 Text colored in orange3 Text colored in orange4 Text colored in darkorange1 Text colored in darkorange2 Text colored in darkorange3 Text colored in darkorange4 Text colored in coral1 Text colored in coral2 Text colored in coral3 Text colored in coral4 Text colored in tomato1 Text colored in tomato2 Text colored in tomato3 Text colored in tomato4 Text colored in orangered1 Text colored in orangered2 Text colored in orangered3 Text colored in orangered4 Text colored in red1 Text colored in red2 Text colored in red3 Text colored in red4 Text colored in deeppink1 Text colored in deeppink2 Text colored in deeppink3 Text colored in deeppink4 Text colored in hotpink1 Text colored in hotpink2 Text colored in hotpink3 Text colored in hotpink4 Text colored in pink1 Text colored in pink2 Text colored in pink3 Text colored in pink4 Text colored in lightpink1 Text colored in lightpink2 Text colored in lightpink3 Text colored in lightpink4 Text colored in palevioletred1 Text colored in palevioletred2 Text colored in palevioletred3 Text colored in palevioletred4 Text colored in maroon1 Text colored in maroon2 Text colored in maroon3 Text colored in maroon4 Text colored in violetred1 Text colored in violetred2 Text colored in violetred3 Text colored in violetred4 Text colored in magenta1 Text colored in magenta2 Text colored in magenta3 Text colored in magenta4 Text colored in orchid1 Text colored in orchid2 Text colored in orchid3 Text colored in orchid4 Text colored in plum1 Text colored in plum2 Text colored in plum3 Text colored in plum4 Text colored in mediumorchid1 Text colored in mediumorchid2 Text colored in mediumorchid3 Text colored in mediumorchid4 Text colored in darkorchid1 Text colored in darkorchid2 Text colored in darkorchid3 Text colored in darkorchid4 Text colored in purple1 Text colored in purple2 Text colored in purple3 Text colored in purple4 Text colored in mediumpurple1 Text colored in mediumpurple2 Text colored in mediumpurple3 Text colored in mediumpurple4 Text colored in thistle1 Text colored in thistle2 Text colored in thistle3 Text colored in thistle4 Text colored in gray0 Text colored in grey0 Text colored in gray1 Text colored in grey1 Text colored in gray2 Text colored in grey2 Text colored in gray3 Text colored in grey3 Text colored in gray4 Text colored in grey4 Text colored in gray5 Text colored in grey5 Text colored in gray6 Text colored in grey6 Text colored in gray7 Text colored in grey7 Text colored in gray8 Text colored in grey8 Text colored in gray9 Text colored in grey9 Text colored in gray10 Text colored in grey10 Text colored in gray11 Text colored in grey11 Text colored in gray12 Text colored in grey12 Text colored in gray13 Text colored in grey13 Text colored in gray14 Text colored in grey14 Text colored in gray15 Text colored in grey15 Text colored in gray16 Text colored in grey16 Text colored in gray17 Text colored in grey17 Text colored in gray18 Text colored in grey18 Text colored in gray19 Text colored in grey19 Text colored in gray20 Text colored in grey20 Text colored in gray21 Text colored in grey21 Text colored in gray22 Text colored in grey22 Text colored in gray23 Text colored in grey23 Text colored in gray24 Text colored in grey24 Text colored in gray25 Text colored in grey25 Text colored in gray26 Text colored in grey26 Text colored in gray27 Text colored in grey27 Text colored in gray28 Text colored in grey28 Text colored in gray29 Text colored in grey29 Text colored in gray30 Text colored in grey30 Text colored in gray31 Text colored in grey31 Text colored in gray32 Text colored in grey32 Text colored in gray33 Text colored in grey33 Text colored in gray34 Text colored in grey34 Text colored in gray35 Text colored in grey35 Text colored in gray36 Text colored in grey36 Text colored in gray37 Text colored in grey37 Text colored in gray38 Text colored in grey38 Text colored in gray39 Text colored in grey39 Text colored in gray40 Text colored in grey40 Text colored in gray41 Text colored in grey41 Text colored in gray42 Text colored in grey42 Text colored in gray43 Text colored in grey43 Text colored in gray44 Text colored in grey44 Text colored in gray45 Text colored in grey45 Text colored in gray46 Text colored in grey46 Text colored in gray47 Text colored in grey47 Text colored in gray48 Text colored in grey48 Text colored in gray49 Text colored in grey49 Text colored in gray50 Text colored in grey50 Text colored in gray51 Text colored in grey51 Text colored in gray52 Text colored in grey52 Text colored in gray53 Text colored in grey53 Text colored in gray54 Text colored in grey54 Text colored in gray55 Text colored in grey55 Text colored in gray56 Text colored in grey56 Text colored in gray57 Text colored in grey57 Text colored in gray58 Text colored in grey58 Text colored in gray59 Text colored in grey59 Text colored in gray60 Text colored in grey60 Text colored in gray61 Text colored in grey61 Text colored in gray62 Text colored in grey62 Text colored in gray63 Text colored in grey63 Text colored in gray64 Text colored in grey64 Text colored in gray65 Text colored in grey65 Text colored in gray66 Text colored in grey66 Text colored in gray67 Text colored in grey67 Text colored in gray68 Text colored in grey68 Text colored in gray69 Text colored in grey69 Text colored in gray70 Text colored in grey70 Text colored in gray71 Text colored in grey71 Text colored in gray72 Text colored in grey72 Text colored in gray73 Text colored in grey73 Text colored in gray74 Text colored in grey74 Text colored in gray75 Text colored in grey75 Text colored in gray76 Text colored in grey76 Text colored in gray77 Text colored in grey77 Text colored in gray78 Text colored in grey78 Text colored in gray79 Text colored in grey79 Text colored in gray80 Text colored in grey80 Text colored in gray81 Text colored in grey81 Text colored in gray82 Text colored in grey82 Text colored in gray83 Text colored in grey83 Text colored in gray84 Text colored in grey84 Text colored in gray85 Text colored in grey85 Text colored in gray86 Text colored in grey86 Text colored in gray87 Text colored in grey87 Text colored in gray88 Text colored in grey88 Text colored in gray89 Text colored in grey89 Text colored in gray90 Text colored in grey90 Text colored in gray91 Text colored in grey91 Text colored in gray92 Text colored in grey92 Text colored in gray93 Text colored in grey93 Text colored in gray94 Text colored in grey94 Text colored in gray95 Text colored in grey95 Text colored in gray96 Text colored in grey96 Text colored in gray97 Text colored in grey97 Text colored in gray98 Text colored in grey98 Text colored in gray99 Text colored in grey99 Text colored in gray100 Text colored in grey100 Text colored in darkgrey Text colored in darkgray Text colored in darkblue Text colored in darkcyan Text colored in darkmagenta Text colored in darkred Text colored in lightgreen

1 Code for the color link

(require 's)

(defun color-comp (&optional arg)
  "Completion function for color links."
  (let ((color-data (prog2
                        (save-selected-window
                          (list-colors-display))
                        (with-current-buffer (get-buffer "*Colors*")
                          (mapcar (lambda (line)
                                    (append (list line)
                                            (s-split " " line t)))
                                  (s-split "\n" (buffer-string))))
                      (kill-buffer "*Colors*"))))
    (format "color:%s"
            (s-trim (cadr (assoc (completing-read "Color: " color-data) color-data))))))


(defun color-link-face (path)
  "Face function for color links."
  (or (cdr (assoc path org-link-colors))
      `(:foreground ,path)))


(defun color-link-export (path description backend)
  "Export function for color links."
  (cond
   ((eq backend 'html)
    (let ((rgb (assoc (downcase path) color-name-rgb-alist))
          r g b)
      (setq r (* 255 (/ (nth 1 rgb) 65535.0))
            g (* 255 (/ (nth 2 rgb) 65535.0))
            b (* 255 (/ (nth 3 rgb) 65535.0)))
      (format "<span style=\"color: rgb(%s,%s,%s)\">%s</span>"
              (truncate r) (truncate g) (truncate b)
              (or description path))))))

(org-link-set-parameters "color"         
                         :face 'color-link-face
                         :complete 'color-comp
                         :export 'color-link-export)
:face color-link-face :complete color-comp :export color-link-export

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter

Better equation numbering in LaTeX fragments in org-mode

| categories: latex, orgmode, emacs | tags:

In org-mode we can use LaTeX equations, and toggle an overlay that shows what the rendered equation will look like. One thing that has always bothered me though, is that each fragment is created in isolation. That means numbering is almost always wrong, and typically with each numbered equation starting with (1). Here we look at a way to fix that. Fixing it means we have to find a way to not create each fragment image in isolation; each one needs a context that enables the numbering to be correct. The idea we try here is simple: we just figure out in advance what the numbering for each equation should be, and then figure out how to get that information to the image generation.

See this video of the post in action:

Here are some example equations to see how it works.

This should be numbered (1)

\begin{equation} \int x^2 dx \end{equation}

This is a numbered equation with a custom number. This should have (E1) as the number.

\begin{equation}\tag{E1} \int x^2 dx \end{equation}

But equation* is not numbered

\begin{equation*} \int x^2 dx \end{equation*}

LaTeX align environments are numbered. The first line is (2), the second line is not numbered (because we put \nonumber in the line), and the third line is (3).

\begin{align} a = 5 \\ b=6 \nonumber \\ c = 8 \end{align}

But align* environments are not numbered.

\begin{align*} a = 5 \\ b=6 \end{align*}

This should be numbered (4).

\begin{equation} \int x^3 dx \end{equation}

These should be numbered (5), (6) and (7).

\begin{align} a = 5 \\ b=6 \\ c = 8 \end{align}

This should be numbered with (E2).

\begin{equation}\tag{E2} \int x^2 dx \end{equation}

And this should be numbered (8).

\begin{equation} \int x^2 dx \end{equation}

Note: This will be numbered (1) because it is exactly the same equation as a previous one!

\begin{equation} \int x^2 dx \end{equation}

We can change the numbering of an equation with code like this. After this code, the next equation will be numbered (5).

The only fragments that should be numbered are equation environments, and align environments (these are the main ones that we consider here). The align environment is tricky since there is potentially more than one number in the environment.

So, we get all the fragments, and generate a list of which ones should be numbered, and if they should what the number should be. That means we will need to count the number of numbered equations in an align environment. We will do that by getting the number of line breaks, and subtracting the number of nonumbers.

Here is the code block that does that, using advice again. A downside of this approach is that we generate the list for every fragment, which is not efficient, since it should not change in a synchronous approach to generating them.

(defun org-renumber-environment (orig-func &rest args)
  (let ((results '()) 
        (counter -1)
        (numberp))

    (setq results (loop for (begin .  env) in 
                        (org-element-map (org-element-parse-buffer) 'latex-environment
                          (lambda (env)
                            (cons
                             (org-element-property :begin env)
                             (org-element-property :value env))))
                        collect
                        (cond
                         ((and (string-match "\\\\begin{equation}" env)
                               (not (string-match "\\\\tag{" env)))
                          (incf counter)
                          (cons begin counter))
                         ((string-match "\\\\begin{align}" env)
                          (prog2
                              (incf counter)
                              (cons begin counter)                          
                            (with-temp-buffer
                              (insert env)
                              (goto-char (point-min))
                              ;; \\ is used for a new line. Each one leads to a number
                              (incf counter (count-matches "\\\\$"))
                              ;; unless there are nonumbers.
                              (goto-char (point-min))
                              (decf counter (count-matches "\\nonumber")))))
                         (t
                          (cons begin nil)))))

    (when (setq numberp (cdr (assoc (point) results)))
      (setf (car args)
            (concat
             (format "\\setcounter{equation}{%s}\n" numberp)
             (car args)))))
  
  (apply orig-func args))

(advice-add 'org-create-formula-image :around #'org-renumber-environment)

You can remove the advice like this.

(advice-remove 'org-create-formula-image #'org-renumber-environment)

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter

Justifying LaTeX preview fragments in org-mode

| categories: latex, orgmode, emacs | tags:

A colleague asked if I knew how to center the preview images of LaTeX equations in an org-buffer. This might make org-mode notes look nicer when lecturing, for example. We thought it might be possible to just offset the overlay with a before-string made up of the right number of spaces. I worked out a full solution that lets you "justify" the preview images. You have to add a :justify option to org-format-latex-options, and the option is either 'center or 'right (anything else means left-justified as the default). This will only justify equations that start at the beginning of a line to avoid modifying fragments that are in text. You should see the video to see this in action:

Equation 1: \(e^{i\pi} + 1 = 0\)

An \(x^2 = -1\) equation in the text is not affected.

A display equation with some space after the equation: \[e^{i \cdot \pi} + 1 = 0\]

This is a numbered equation.

\begin{equation} \int x^2 dx \end{equation}

The idea is pretty simple, we get the width of the window, and the width of the image, and compute the offset that approximately centers or right justifies the overlay, and then add the before-string property to the overlay. While we are at it, I will add a tooltip to the image so you can see the LaTeX code that created it, and make it clickable so you can toggle it back to the code. I apply the functions as after advice to the function that creates the overlay, so we do not have to adapt the org code at all. Here is the code that does it.

;; specify the justification you want
(plist-put org-format-latex-options :justify 'center)

(defun org-justify-fragment-overlay (beg end image imagetype)
  "Adjust the justification of a LaTeX fragment.
The justification is set by :justify in
`org-format-latex-options'. Only equations at the beginning of a
line are justified."
  (cond
   ;; Centered justification
   ((and (eq 'center (plist-get org-format-latex-options :justify)) 
         (= beg (line-beginning-position)))
    (let* ((img (create-image image 'imagemagick t))
           (width (car (image-size img)))
           (offset (floor (- (/ (window-text-width) 2) (/ width 2)))))
      (overlay-put (ov-at) 'before-string (make-string offset ? ))))
   ;; Right justification
   ((and (eq 'right (plist-get org-format-latex-options :justify)) 
         (= beg (line-beginning-position)))
    (let* ((img (create-image image 'imagemagick t))
           (width (car (image-display-size (overlay-get (ov-at) 'display))))
           (offset (floor (- (window-text-width) width (- (line-end-position) end)))))
      (overlay-put (ov-at) 'before-string (make-string offset ? ))))))

(defun org-latex-fragment-tooltip (beg end image imagetype)
  "Add the fragment tooltip to the overlay and set click function to toggle it."
  (overlay-put (ov-at) 'help-echo
               (concat (buffer-substring beg end)
                       "mouse-1 to toggle."))
  (overlay-put (ov-at) 'local-map (let ((map (make-sparse-keymap)))
                                    (define-key map [mouse-1]
                                      `(lambda ()
                                         (interactive)
                                         (org-remove-latex-fragment-image-overlays ,beg ,end)))
                                    map)))

;; advise the function to a
(advice-add 'org--format-latex-make-overlay :after 'org-justify-fragment-overlay)
(advice-add 'org--format-latex-make-overlay :after 'org-latex-fragment-tooltip)

That is it. If you get tired of the advice, remove it like this:

(advice-remove 'org--format-latex-make-overlay 'org-justify-fragment-overlay)
(advice-remove 'org--format-latex-make-overlay 'org-latex-fragment-tooltip)

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter
« Previous Page -- Next Page »