1. Hexo 与 Org-mode
Hexo 是一个快速、简洁且高效的用于生成静态网页的博客框架, 生成的网页可以方便地托管到 Github Pages , 可以快速方便地创建个人博客.
2. hexo-renderer-org
hexo-renderer-org 是 hexo 的一个插件, 用于将 org 文档生成 hexo 的博客文章. Hexo 解析 Markdown 文档的能力保持不变, 相当于给 Hexo 加上 org 文档的支持.
使用我维护的 hexo-renderer-org 项目1.
2.1. 安装
在 Hexo 博客文件夹下安装 hexo-renderer-org
npm install https://github.com/mpwang/hexo-renderer-org#master --save |
2.2. 工作原理
安装了 hexo-renderer-org 之后, hexo 命令会使用 emacsclient 去连接 emacs server, 使 用 org export 功能将 org 文档转换成 html, 所以要求 emacs 开启 server 进程.
2.3. emacs 的配置
spacemacs 用户配置
启用 emacs server 进程以及设置 emacs server socket 文件所在的文件夹.
;; If non-nil, start an Emacs server if one is not already running. |
2.4. hexo 的配置
在 hexo 博客的配置文件 _config.yml
中添加
org: # 指定 emacs 执行文件路径 emacs: '/usr/local/bin/emacs' # 指定 emacsclient 执行文件路径 emacsclient: '/usr/local/bin/emacsclient' # 是否生成自动注脚, t 为开启, nil 为关闭 common: | #+OPTIONS: html-postamble:nil clean_cache: true daemonize: true # emacs server file 路径, 如果不指定, 以下为默认值 server_file: "~/.emacs.d/server/server" |
2.4.1. 不要使用 emacs htmlize 功能
README 文档里的 htmlize 功能我没有成功过, 设置 htmlize: true
之后生成的内容会
出错. 不过这个问题影响不大, 代码高亮交给 highlight.js
去处理.
2.5. Enjoy
现在在开启 emacs 的情况下, 执行以下命令即可以享受用强大的 org-mode 写文档, 并生成 Hexo 博客.
hexo clean && hexo generate && hexo server --debug |
3. 配合使用 Hexo 的文章资源文件夹
Hexo3 加入了文章资源文件夹 的支持, 文章相关的图片可以放在文章同名文件夹里面, 并使用以下代码引用图片
{% asset_img slug [title] %}
而在 org 文档中配合 org-download, 我们还可以实现更强大的功能. 在 Hexo 博客文件夹中创建 .dir-local.el
, 添加以下代码
((nil . |
现在你可以将图片支持拖拽到 org 文档中, emacs 会自动创建文章同名文件夹并异步下载图片, 在光标所在处自动插入对应的引用代码.
使用本功能必须配置 org 文档导出时忽略 _
字符的处理, 否则处理 asset_img
时会引起报错.
(with-eval-after-load 'ox |
4. 配合 Hexo 主题: NexT
本网站采用了漂亮成熟的主题 NexT 6.0.
以下是我使用 hexo-renderer-org 配合 NexT 主题的的一些调优.
spacemacs 用户配置
使用方式: 在 .spacemacs
文件的 dotspacemacs/user-config
中添加
(load "~/.emacs.d/config-hexo.el") |
在 ~/.emacs.d/
文件夹中创建 config-hexo.el
添加以下代码
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
result))))
(with-eval-after-load 'ox
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; disable interpret "_" and "^" for export ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setq org-export-with-sub-superscripts nil)
(add-to-list 'org-export-filter-link-functions #'hexo-theme-next/link-add-font-awesome)
(add-to-list 'org-export-filter-center-block-functions #'hexo-theme-next/center-quote)
)
(with-eval-after-load 'ox-html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; todo keyword use theme/next label CSS class ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setq org-html-todo-kwd-class-prefix "label warning ")
(setq org-html-postamble-format
'(("en"
"
Generated using %c
")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; generate TOC using hexo theme/next tabs plugin at the top of article ;;
;; ;;
;; redefine org-html-toc ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun org-html-toc (depth info &optional scope)
"Build a table of contents.
DEPTH is an integer specifying the depth of the table. INFO is
a plist used as a communication channel. Optional argument SCOPE
is an element defining the scope of the table. Return the table
of contents as a string, or nil if it is empty."
(let ((toc-entries
(mapcar (lambda (headline)
(cons (org-html--format-toc-headline headline info)
(org-export-get-relative-level headline info)))
(org-export-collect-headlines info depth scope))))
(when toc-entries
(let ((toc (concat "
- \n"
(hexo-theme-next/toc-nav-tabs toc-entries)
""
"
(hexo-theme-next/toc-text toc-entries)
"
)))
(if scope toc
(concat "
toc
"
))))
(defun hexo-theme-next/get-href (headline)
(if (string-match "href=\"#\\(.*\\)\"" headline)
(match-string-no-properties 1 headline)
""))
(defun hexo-theme-next/toc-nav-tabs (toc-entries)
(mapconcat
(lambda (entry)
(let ((headline (car entry))
(level (cdr entry)))
(when (= level 1)
(let ((ahref (hexo-theme-next/get-href headline)))
(concat "
(replace-regexp-in-string
"
"
(replace-regexp-in-string ahref (concat "tab-" ahref) headline))
"\n")))))
toc-entries ""))
(defun hexo-theme-next/toc-text (toc-entries)
"Return innards of a table of contents, as a string.
TOC-ENTRIES is an alist where key is an entry title, as a string,
and value is its relative level, as an integer."
(let* ((prev-level (1- (cdar toc-entries)))
(start-level prev-level))
(concat
(mapconcat
(lambda (entry)
(let ((headline (car entry))
(level (cdr entry)))
(let ((ahref (hexo-theme-next/get-href headline )))
(concat
(let* ((cnt (- level prev-level))
(times (if (> cnt 0) (1- cnt) (- cnt))))
(setq prev-level level)
(if (> cnt 0)
(cond ((= level 1)
(format "
- \n"
(concat "tab-" ahref)))
((> level 1)
"
(cond ((= level 1)
(format "\n
- \n"
- \n" ))))
(concat "tab-" ahref)))
((> level 1)
"\n
headline))))
toc-entries "")
"
)))
)
功能 | |
---|---|
hexo-theme-next/link-add-font-awesome | 将所有链接后插入 font awesome 箭头图标 |
hexo-theme-next/center-quote | 将 org 的 #+BEGIN_CENTER 代码块转换为 NexT 的 centerquote 标签 |
org-html-todo-kwd-class-prefix | 给标题中的 TODO 关键字加上 NexT 的 label 样式 |
org-html-postamble-format | 配合 _config.yml 中 postamble:t 使用, 自动在文章末尾加上 Generated 文字 |
org-html-toc | 将文章开头 org 自动生成的 Table of Content 转换成 NexT 漂亮的 tabs 标签 |
5. 技巧
有时候需要直接在 org 文档中包含 hexo 特殊标签字符时会 hexo g
会出错, 或者输出会消失, 比如本文中的
{%% asset_img %%} {% asset_img %} .
这是因为这些字符会被 Hexo 引擎特殊处理, 当你需要输出这些特殊字符, 可以在外面用 raw 标签包围, 这样 hexo 引擎就不会处理这些字符了.
{% raw %} {% asset_img %} {% endraw %}
Footnotes:
hexo-renderer-org 最早是 coldnew 开发的, 但是已经停止维护了,emacs 升级到 26.1 之后不能使用.
MephistoMMM 修复了问题, 我自己 fork 之后又修复了几个问题, 目前已经可以稳定使用.