Emacs: 我的org-mode配置

时间:2021-05-13 00:50:00

1 前言

年纪大了, 不像以前那般记性好了, 愈发体会到记笔记的重要性了. 记笔记的软件也用过不少, 但是都有各自的问题. windows上one note是不错的, 除了基本的笔记功能以外画图, 加密都是很吸引人的功能, 唯一的问题就是只能在windows上用. 自从买了mac之后以前那台windows的电脑彻底沦为了游戏本, one note也随之不用了. 虽然mac上后来也出了one note, 但是只是个阉割版, 只能做做基本的笔记功能. 前段时间受实验室师兄影响, 准备用evernote来记笔记,一开始用着也挺爽, 尤其是它的全文搜索功能, 很方便的在所有笔记中找到想要的笔记. 但是后来觉得编辑笔记实在太痛苦了, 主要体现在格式的控制上, 比如需要添加一个list的时候就得切换到鼠标去点一下上面的按钮. Markdown也是个不错的记笔记方式, 而且Mac下的markdown软件Mou做的也不错, 但是还是有个编辑流畅性的问题, 虽然比起evernote是好多了, 但是和emacs这种专门的编辑器比起来仍尚有不足. Emacs也有记笔记的功能, 它自带的org-mode据说吸引了很多vim党叛变. 以前也用过, 但是总觉得不过如此. 如今经历过各种笔记软件的摧残, 还是回归了org-mode. org-mode最大的好处在于对emacser来说编辑起来流畅无比, 各种快捷键搭配yassnippet, 以及像markdown那样简单的字符来控制格式, 用起来很方便. 它也有它的不足, 例如不像one note, evernote, markdown那样能呈现形式丰富的内容. 但是emacs不愧是编辑器中的操作系统,经过一番配置之后能弥补大部分不足, 目前已经成为我记笔记的不二选择.

本文不会介绍基本的org-mode使用, 而是集中在如何对原生的org-mode修改配置,以达到个人认为的最佳效果. 主要包括以下几个方面:

  • 主题修改: 让org-mode表现出来的形式不像以前那样干巴巴.
  • 代码高亮: 让org-mode中的代码部分能根据语法高亮.
  • 导出: 导出的代码也能根据导出的格式高亮, 由于个人习惯, 只关注html和pdf. 另外原生的pdf导出不支持中文, 需要导出中文也要进行配置.
  • 截图: 记笔记的时候经常需要截图, 在GUI的笔记软件这点挺方便, 但是在emacs这样的文本编辑器中就不行了. 然而毕竟神的编辑器, 经过配置还是能完成这项工作.
  • 导出到博客: 这也是org-mode的一大优点, 输入一个指令就能自动将org文件转成一篇博文上传到个人博客站点.
  • 字符统计: 有时候可能会想看看我这篇笔记写了多少字了, emacs自带的统计功能C-u M-=对中文支持的不好, 网上找到了有大神自己写的插件.

2 配置

color-theme

这一小节介绍如何修改配色. 作为外貌协会资深会员, 要是org-mode像默认配置那样丑, 功能再强大我也是不会去用它的, 这也是为什么我好几年前就用过org-mode, 但是直到最近才重归它的怀抱.

前段时间在网上搜资料的时候偶然发现了这个配色: Leuven. 这是一个白底黑字类型的主题, 本来也不是很出众, 但是搭配着对org-mode做了特殊设置之后, 就很好看了. 效果如图:

Emacs: 我的org-mode配置

这个主题搭配它对应的配置将一级二级标题都设置了背景阴影, 而且将各级标题的前n-1个*用和背景色一样的颜色给隐形了, 看上去就比较简洁.

这个主题可以在这里下载, 当然前置条件是装了color-theme.

光下载还不行, 要在.emacs文件中添加一些代码来修改颜色, 具体还是参见上面的链接.

代码高亮

这一小节介绍代码高亮. 码农的笔记中代码是必不可少的, 默认的org-mode中代码部分是没有颜色的, 很丑. 要对代码进行语法高亮, 这一步很简单, 在.emacs文件中加上一行代码就行:

(setq org-src-fontify-natively t)

代码高亮这步很简单, 但是有个问题就是当导出的时候, 代码块仍然是没有颜色的. 导出为HTML时, 为了完成这个功能需要一个插件htmlize. 下载之后放到load-path里, require一下就行了. 这个插件貌似还挺难找的, 找到之后不知道被墙了还是什么原因下载很慢, 我就放了一份在自己的博客上.

http://www.kohn.com.cn/files/htmlize.el

而要导出到PDF时也高亮, 需要在.emacs文件中加入以下代码:

;; use minted to highlight code in latex
(require 'ox-latex)
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)

这几行代码的作用是让org转为tex文件时自动添加minted包.

这个minted的包是需要另外安装的, easy_install Pygments即可.

导出

导出为PDF

默认的配置使用latex来导出org为pdf, 但是这样的活是不支持中文的, 因此要修改配置让org导出时用xelatex(当然前提是系统里装了这个). 在.emacs文件中加入以下代码:

;; set latex to xelatex
(setq org-latex-pdf-process '("xelatex -shell-escape -interaction nonstopmode %f"
                              "xelatex -shell-escape -interaction nonstopmode %f"))

上面xelatex命令的参数-shell-escape是为了调用minted包, 如果不加这个参数,代码高亮这部分会出错.

同时在编辑org文件时, 还要在开头添加metadata:

#+LATEX_HEADER: \usepackage{xeCJK}
#+LATEX_HEADER: \setCJKmainfont{Songti SC}

这两行的作用是在生成的.tex文件中加入两行引入xeCJK包, 并设置中文的字体,这样在用xelatex编译.tex文件就不会出错.

另外我系统里装的是texlive, 如果是basic版的话, 第一次编译的时候会出现很多包找不到的错误, 打开texlive自带的texlive utilities, 一个个安装过去就行了. 如果是texlive full版, 应该没这个问题. linux和windows用户我就不是很清楚了, 不过只要搞定latex的中文问题就可以.

导出为HTML

在.emacs文件中添加以下代码:

(setq org-html-doctype "html5")
(setq org-html-xml-declaration nil)
(setq org-html-postamble nil)

这几行代码的作用是:

  1. 设置导出为HTML5格式, 默认貌似是XML.
  2. 不生成XML头信息.
  3. 默认情况下导出的HTML末尾会有几行信息, 例如由org8导出, 作者是谁谁谁,日期多少多少. 我觉得这部分信息比较low, 于是通过这行代码取消postamble的生成.

有时候希望导出的HTML文件自动包含一些js文件或者css文件, 例如我希望导出的HTML能引入bootstrap, 这样能让HTML的效果更好看, 就要设置一下:

(setq org-html-head "<link rel='stylesheet' href='http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css'>\n<link rel='stylesheet' href='http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap-theme.min.css'>\n<script src='http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js'>\n</script><script src='http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js'></script>")

org2blog

org2blog是一个能让org文件一键变为wordpress博客的插件, 安装挺简单就不说了, 使用时很方便, 填好配置信息例如博客的地址和wordpress账户, 使用命令org2blog/wp-post-buffer-and-publish就能直接发布博客了.

默认情况下每次使用上面的命令发布之后都会问一下要不要确认, 如果选择yes就会用w3m-browser打开, 如果没装这个就出错了, 我觉得比较烦人就用以下命令给取消了:

(setq org2blog/wp-show-post-in-browser nil)

截图

配置完以上的部分之后我本来已经挺满意了, 但是记了一段笔记之后, 有时候要截图总觉得很麻烦. 需要用mac的快捷键截图保存文件, 再在emacs中输入文件的路径. 有一天我终于忍不了了, 上网找了好久终于在一个隐秘的角落发现了一段代码, 他通过elisp代码调用截图命令, 保存到文件中后自动引入文件路径. 但是那段代码貌似是给linux用的, 在mac中它用的截图命令也有(需要安装), 但是调用那个命令会打开x11, 有点麻烦. 好在mac下也有自己的截图命令screencapture, 而且更加方便, 支持鼠标选择截图区域. 我根据自己的需求修改了一下原来的代码, 变成了这个样子:

(defun my-org-screenshot (basename)
  "Take a screenshot into a time stamped unique-named file in the
same directory as the org-buffer and insert a link to this file."
  (interactive "sScreenshot name: ")
  (if (equal basename "")
      (setq basename (format-time-string "%Y%m%d_%H%M%S")))
  (setq filename
        (concat (file-name-directory (buffer-file-name))
                "imgs/"
                (file-name-base (buffer-file-name))
                "_"
                basename
                ".png"))
  (call-process "screencapture" nil nil nil "-s" filename)
  (insert "#+CAPTION:")
  (insert basename)
  (insert "\n")
  (insert (concat "[[" filename "]]"))
  (org-display-inline-images))

每次需要截图时, 调用这个命令(可以把它绑定到一个快捷键上). 然后会要求输入文件名, 如果不输入直接回车则会根据系统时间生成一个. 然后在这个名字前添加org文件的文件名作为前缀, 最后调用screencapture命令, 以最终的文件名为名截图并保存到当前目录下的imgs目录下(我习惯把图片都放在一起, 不喜欢的可以把上面代码中”imgs/”那行给删了. 接下来往org文件中鼠标所在的位置插入文件的路径, 并调用org-display-inline-images命令显示图片.

字符统计

百度贴吧上搜到了一位大神自己写的插件, 虽然比较简单还是挺好用的, 在字符数不是非常多的情况下效率也还OK. 可以从我的博客上下载:

http://www.kohn.com.cn/files/word-like-count-mode.el

保存到load-path中, require一下就行. 另外我设置成了打开org文件自动启用这个模式:

(add-hook 'org-mode-hook 'word-like-count-mode)

启用后会在底部状态栏上多出5个数字, 分表表示总单词数, 字符数(不计空格),字符数(计算空格), 非中文单词数, 中文单词数.

3 小技巧

这里分享几个方便编辑的小技巧:

  1. 想要插入代码块的时候, 输入<s, 然后按下tab键就行, 这个快捷键会插入一个块让你编辑代码. 指定了这个代码块属于什么语言之后, 可以按Ctrl-C ‘,新建一个buffer输入代码, 输完之后再按同样的快捷键就能将那个buffer里的代码复制到org-mode的代码块中了.
  2. 导出时如果需要目录(类似本文开头), 要在org文件的开头出添加metadata. 另外默认情况下org-mode会把^, _, []等字符当做latex中的上标,下标和脚注, 如果不想让org-mode误会, 也要在metadata中注明. 于是, 每次写笔记时, 开头就要加一行:

    #+OPTIONS: ^:nil _:nil f:nil toc:t
    
  3. 前面说到导出pdf需要在org文件开始处添加几行, 刚刚又说要加一行#+OPTIONS, 算上title, author这种信息, 每次写一篇新的笔记要加的东西就比较多了. 我习惯用yassnippet来解决这个麻烦, 这是一个很有用的插件,写代码的时候可以用它来插入代码模板, 写笔记的时候也可以这么用. 每次新建笔记的时候在开头输入head, 然后按下tab就能自动跳出一坨metadata了.以下是我的yassnippet模板:

    # -*- mode: snippet -*-
    # name: head
    # key: head
    # --
    #+OPTIONS: ^:nil _:nil f:nil toc:t num:t
    #+title: $1
    #+STARTUP: showeverything
    #+LATEX_HEADER: \usepackage{xeCJK}
    #+LATEX_HEADER: \setCJKmainfont{Songti SC}
    #+LATEX_HEADER: \usemintedstyle{autumn}
    
  4. 有些童鞋(比如我)可能喜欢开启auto-fill mode, 这就使得每行如果超过80个字符时自动换行, 看上去更美观一些. 但是在导出为HTML时, org-mode会把回车转成空格, 在纯英文环境中没问题, 因为英文本来就是以空格作为分隔符的,但是在中文环境下, 并不以空格为分隔符, 会导致导出的html出现很多不该有的空格. 我在水木清华上找到了有人给出的解决方法, 就是使用advice函数,让org-mode在导出每个段落到HTML之前, 先将出现中文的两行拼起来, 再调用org-html-paragraph函数. 我用了一下, 发现大多数段落都没问题了, 但是如果上一行行末是中文, 而下一行行首是英文, 导出的HTML中文和英文之间仍然有个空格. 这本身是个见仁见智的问题, 我不太喜欢这样的空格, 于是对这份代码做了一点修改, 只要行末是中文字符, 就吃掉最后一个换行符, 最终代码如下:

    ;; 防止org-mode在导出HTML时把行末的回车输出为空格
    (defadvice org-html-paragraph (before fsh-org-html-paragraph-advice
                                          (paragraph contents info) activate)
      "Join consecutive Chinese lines into a single long line without
    unwanted space when exporting org-mode to html."
      (let ((fixed-contents)
            (orig-contents (ad-get-arg 1))
            (reg-han "[[:multibyte:]]"))
        (setq fixed-contents (replace-regexp-in-string
                              ;; 这一行是匹配上一行末和下一行头都是中文的情况, 但是这样的话遇上"中文\nenglish"就仍然有空格
                              ;; (concat "\\(" reg-han "\\) *\n *\\(" reg-han "\\)")
                              (concat "\\(" reg-han "\\) *\n *")
                              "\\1" orig-contents))
        (ad-set-arg 1 fixed-contents)))
    

4 备注

由于emacs的配置比较个性化, 上文中提到的插件可能有依赖其他插件的, 而我很久以前已经配置过了, 所以上文中有些功能可能还需要一些我没写出来的其他插件. 例如Leuven主题是依赖color theme的, 而color theme我已经用了好几年了, 以为它是默认安装了的, 所以就不会去提到它. 还有就是捣腾这些东西完了很久我才开始写这篇博客, 可能有些细节忘了说明, 如果有什么疑问, 欢迎留言.

这篇博文是从我的个人博客上复制过来的, CSDN发表文章竟然不能直接编辑HTML=, =. 显示效果可能不太好, 有需要的可以移步我的个人博客

5 Reference

  1. The Org Manual by Carsten Dominik etc.
  2. www.orgmode.org
  3. planet.emacsen.org
  4. http://vxlabs.com/
  5. org-mode export-html的中文问题
  6. 中文字符统计
  7. 以及好多当时查到了但是忘记记录下来的网址:)