This page looks best with JavaScript enabled

Migrate from vimwiki to Org mode

 ·   ·  ☕ 5 min read  ·  ✍️  Firmin Martin

Around 2016-2017, I used vimwiki a lot to take spare notes of Linux, programming & mathematics related topics. I moved from Vim to Emacs in June 2019 for the great Org mode and never came back. Recently, I wanted to get rid of my good old vimwiki directory and merge it in my org-mode personal wiki (using org-glaux). Since it’s a one-time task, I decided to use the only available outdated script then write some Emacs code to fix the result instead of patching the old script.

Prelude: the power of vimwiki2org

I looked for a vimwiki/Org converter, and luckily found a legacy Perl script (written 8 years ago). Well, it’s better than nothing. To install, as usual

1
2
3
git clone https://github.com/fasheng/vimwiki2org
cd vimwiki2org
sudo make install

After tweaking available options a bit, I came up with this one-liner which outputs the whole vimwiki content into one single Org file vimwiki.org.

1
vimwiki2org --no-ignore-lonely-header -L fix index.wiki > vimwiki.org

The option -L fix seems necessary to correctly detect subdirectories, such as diary/diary.wiki. The option --no-ignore-lonely-header was necessary to prevent some heading be commented out (for a good reason).

Interlude: reworking the remaining

Fix possible encoding issue

An issue I have encountered was the failure to display correctly Unicode. If you have this issue, it means that some characters in the buffer cannot be rendered properly with Unicode. In my case, I have had a .png disguised as a vimwiki file output into vimwiki.org. That messes up the forthcoming steps.

If you are not sure, evaluate (M-:) the following snippet on the vimwiki.org buffer.

1
2
3
4
5
(let ((str (decode-coding-string
            (buffer-string)
            'utf-8)))
  (erase-buffer)
  (insert str))

Now, save vimwiki.org. If there is a prompt asking you what encoding do you want to choose, then you should examine whether or not vimwiki.org contains weird stuff.

Remove vimwiki TOC

Auto-generated TOC by vimwiki seems unnecessary since Org mode produce TOC when exporting. It’s better to remove them as they create unexportable links. Inspired from this snippet, the following snippet removes the content of headline named Contents (i.e. TOCs auto-generated by vimwiki).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(let* ((data (org-element-parse-buffer))
       (str (progn
              (org-element-map data 'headline
                (lambda (el)
                  (when (equal
                         (car-safe (org-element-property :title el))
                         "Contents")
                    ;; Here we remove the contents from this headline.
                    (setf (nthcdr 2 el) nil))))
              (org-element-interpret-data data))))
  (erase-buffer)
  (insert str))

Then, a little “evil” regex %g/Contents/d cleans up the remaining empty TOC headlines. 1

Fix headlines

Fix Java multiple lines comment

I had a converted Java source block containing multi-lines comment like this

1
2
3
4
5
6
7
#+begin_src java
/* Comment
* on
* multiple
* lines
********/
#+end_src

vimwiki2org didn’t put a comma before asterisk, and that messes up Org mode headlines. I have luckily only one such case, so I corrected it manually.

Fix source block

I had some source blocks not correctly handled. Apparently, it’s possible that source block failed to convert when the content is in the same line that {{{. Fortunately, only one case hit me. Again, I fixed it manually, but it’s very easy to move one line down the content with regex.

Fix markup

You may notice that code markup (i.e. `...`) failed to convert properly. A quick evil regex do the job: %s/`\(.*?\)`/~\1~/g.

Fix mathematics equation

Well, vimwiki inline & display style maths are not converted at all. To be short,

  • {{$ ... }}$ corresponds to inline maths $ ... $ ;
  • {{$%align% ... }}$ corresponds to maths environment \[$\begin{align} ... \end{align}$\].

And both of them can span on multiple lines.

Evaluate the following evil regexps in order fixes respectively the previous issues:

  • %s/{{\$%\(\w*\)%\(\(.\|\n\)*?\)}}\$/\\[\\begin{\1}\2\\end{\1}\\]/g
  • %s/{{\$\(\(.\|\n\)*?\)}}\$/\\[\1\\]/g

Move headline content in Org files

The last step is optional, but in my opinion is better to move headlines in separated Org files as they was originally separated as vimwiki files.

We have to make link working again. Depending on whether you are using org-glaux, use one of the following evil regexps that suits you:

  • Org mode file link: %s/\[\[\(.*?\)\]\[\(.*?\)\]\]/[[file:\1.org][\2]]/g
  • org-glaux’s wiki link: %s/\[\[\(.*?\)\]\[\(.*?\)\]\]/[[wiki:../\1][\2]]/g

Separate contents in Org files

If you have kept the outline of vimwiki2org output, i.e. each second-level headings corresponding to a vimwiki file, then the following snippet, inspired from this answer, will output the content of each second-level headline to a single Org file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(defun my-org-export-each-headline (&optional scope)
  "Export each second-level headline to an Org file with the title as filename.
If SCOPE is nil headlines in the current buffer are exported.
For other valid values for SCOPE see `org-map-entries'.
Already existing files are overwritten."
  (interactive)
  ;; Widen buffer temporarily as narrowing would affect the exporting.
  (org-with-wide-buffer
   (save-mark-and-excursion
     ;; Loop through each headline.
     (org-map-entries
      (lambda ()
        ;; Get the plain headline text without statistics and make filename.
        (when (equal (org-current-level) 2)
          (let* ((title (car (last (org-get-outline-path t))))
                 (dir (file-name-directory buffer-file-name))
                 (filename (concat dir title ".org"))
                 (content))
            ;; Set the active region
            (set-mark (point))
            (org-forward-heading-same-level 1)
            (activate-mark)
            (setq content (buffer-substring (region-beginning) (region-end)))
            ;; Export the region
            (with-temp-buffer
              (insert content)
              ;; Save the buffer to file and kill it.
              (write-file filename)
              (kill-current-buffer)))))
      nil scope))))

This little section is reserved for org-glaux users, of whom I am the only one (for now!). After all those steps, we want to make sure that all internal links are correctly interconnected. This is as simple as calling M-x org-glaux-stats! That command produces a statistical overview of the current wiki including the number of broken links. As long as vimwiki files are under the same directories, no broken link is detected in my side. However, since we use relative links (see Fix internal links), they may be broken in the future as soon as they or their target Org files move. I plan to implement (not soon) a “safe move” feature inside org-glaux which preserves page’s link when page moves. But before that I should introduce caching (with e.g. l3kn/org-el-cache) to annihilate links resolving overheads.


  1. There is probably a better solution with org-element-map, but I’m not familiar enough with it. ↩︎


Firmin Martin
WRITTEN BY
Firmin Martin

What's on this Page