使用ox-hugo导出博客
2023年8月12日 2023年11月19日
说明
- | 描述 | 解决方法 |
---|---|---|
ox-hugo | 安装 | |
导出子树时, 整个文件的所有链接要求正确 | 提取options和子树文本 | |
导出子树时, 自动计算weight, 需要保留其他子树 | 计算子树的weight | |
导出文件时, 若在临时文件, 需输入导出文件名 | 导出子树, 计算子树的导出路径 | |
转换后的下划线无效 | 不用 | |
Doks主题 | markdown文件图片链接无法正确解析 | 替换图片链接 |
会检查所有文本(包括代码块)的图片链接 | 分隔"{{“和”< figure src…" | |
图片链接后接文本, 会作为图片描述 | 图片链接和文本之间空一行 | |
org-roam | 希望能跳转到博客对应标题 | 将roam节点链接替换为博客+标题链接 |
时间戳链接 | 不需要 | 删除 |
源码链接 | 不需要跳转, 需提供说明 | 在文本块中显示相对路径 |
安装ox-hugo
子树路径
获取导出文件路径
- 保存到列表
- 只能有选项SECTION, 子树使用SECTION_FRAG
- 缺乏子树从属判断
1(defun my/compute-subtree-path (export-name) 2 (interactive) 3 (let ((base-dir) 4 (section) 5 (dir) 6 (slotlist '()) 7 (cur-level) 8 (frag) 9 (new-level) 10 ) 11 (save-excursion 12 (beginning-of-buffer) 13 (when (re-search-forward (rx "#+" "HUGO_BASE_DIR" ": " (group (0+ (not "\n"))) "\n") nil t) 14 (setq base-dir (string-join (mapcar #'string (match-string 1)))) 15 (beginning-of-buffer) 16 (when (re-search-forward (rx "#+" "HUGO_SECTION" ": " (group (0+ (not "\n"))) "\n") nil t) 17 (setq section (string-join (mapcar #'string (match-string 1)))) 18 (setq dir (concat base-dir "/content/" section)) 19 ) 20 ) 21 ) 22 (add-to-list 'slotlist export-name) 23 (save-excursion 24 (setq cur-level (funcall outline-level)) 25 (while (re-search-backward (rx ":" "EXPORT_HUGO_SECTION_FRAG" ": " (group (0+ (not "\n"))) "\n" ) nil t) 26 (setq frag (string-join (mapcar #'string (match-string 1)))) 27 (setq new-level (funcall outline-level)) 28 (when (< new-level cur-level) 29 ;;(message "%d %d %s" (funcall outline-level) ocur-level ofrag) 30 ;;(sleep-for 5) 31 (add-to-list 'slotlist frag) 32 (setq cur-level new-level) 33 ) 34 ) 35 ) 36 (add-to-list 'slotlist dir) 37 ) 38 )
获取导出文件路径的字符串
1(defun my/subtree-path-str (export-name) 2 (interactive) 3 (let ((slotlist (my/compute-subtree-path export-name)) 4 (slot)) 5 (dolist (item slotlist) 6 (setq slot (concat slot "/" item)) 7 ) 8 (string-remove-prefix "/" slot) 9 ) 10 )
Doks主题
替换markdown文件图片链接格式
1;; (defun my/replace-pic-link-format (file) 2;; (interactive) 3;; (with-current-buffer (find-file-noselect file) 4;; (beginning-of-buffer) 5;; (while (re-search-forward (rx "{{" "< figure src=\"" (group (0+ (not "\""))) "\" width=\"" (group (0+ (not "\""))) "\" >}}") nil t) 6;; (let ((src (string-join (mapcar #'string (match-string 1)))) 7;; (width (string-join (mapcar #'string (match-string 2))))) 8;; (replace-match (format "<img src=\"/%s\" width=\"%s\" /> <br/>" src width)) 9;; ) 10;; ) 11;; (save-buffer) 12;; (kill-buffer) 13;; ) 14;; )
替换子树导出文件图片链接格式
1;; (defun my/amend-pic-link (export-name) 2;; (interactive) 3;; (my/replace-pic-link-format (concat (my/subtree-path-str export-name) ".md")) 4;; )
替换文件内所有子树导出文件图片链接格式
1;; (defun my/amend-pic-link-for-all () 2;; (interactive) 3;; (beginning-of-buffer) 4;; (while (re-search-forward (rx ":" "EXPORT_FILE_NAME" ": " (group (0+ (not "\n"))) "\n") nil t) 5;; (my/amend-pic-link (string-join (mapcar #'string (match-string 1)))) 6;; ) 7;; )
org-roam
需设置相对路径
1;; (setq my/roam-prefix "hugo/content/")
将roam节点链接替换为博客+标题链接
相对路径计算采用去除前缀
1(defun my/replace-roam-link () 2 (interactive) 3 (save-excursion 4 (beginning-of-buffer) 5 (while (re-search-forward (rx "[[" "id:" (group (0+ (not "]"))) "][" (group (0+ (not "]"))) "]]") nil t) 6 (let* ((id (string-join (mapcar #'string (match-string 1)))) 7 (title (string-join (mapcar #'string (match-string 2)))) 8 (title-downcase (string-replace "::" "-" (downcase title))) 9 (node (org-roam-node-from-id id)) 10 (slot)) 11 (with-current-buffer (find-file-noselect (org-roam-node-file node)) 12 (goto-char (org-roam-node-point node)) 13 (outline-next-heading) 14 (when (re-search-backward (rx ":" "EXPORT_FILE_NAME" ": " (group (0+ (not "\n"))) "\n") nil t) 15 (setq slot (my/subtree-path-str (string-join (mapcar #'string (match-string 1))))) 16 ) 17 ) 18 (when slot 19 (save-excursion 20 (beginning-of-buffer) 21 (when (search-forward (format (concat "[[" "id:%s][%s]]") id title) nil t) 22 ;;(message (format "%s %s" slot title)) 23 ;;(sleep-for 1) 24 (replace-match (format "[[file:/%s/#%s][%s]]" 25 (downcase (string-remove-prefix my/roam-prefix slot)) 26 title-downcase 27 title) nil t) 28 ) 29 ) 30 ) 31 ) 32 ) 33 ) 34 )
时间戳链接
删除时间戳链接
1(defun my/delete-timestamp-link () 2 (interactive) 3 (save-excursion 4 (beginning-of-buffer) 5 (while (re-search-forward (rx "[[" "timestamp:" (group (0+ (not "]"))) "][" (group (0+ (not "]"))) "]]") nil t) 6 (replace-match "" nil t) 7 ) 8 ) 9 )
源码链接
虚幻引擎项目源码
1(defun my/replace-ue-project-file-link () 2 (interactive) 3 (save-excursion 4 (beginning-of-buffer) 5 (while (re-search-forward (rx "[[" "uep:" (group (0+ (not "]"))) "][" (group (0+ (not ":"))) ":" (group (0+ (not "]"))) "]]" ) nil t) 6 (let ((proj (string-join (mapcar #'string (match-string 2)))) 7 (file (string-join (mapcar #'string (match-string 3))))) 8 (replace-match (format "=%s: %s=" proj file) nil t) 9 ) 10 ) 11 ) 12 )
虚幻引擎源码
1(defun my/replace-ue-engine-file-link () 2 (interactive) 3 (save-excursion 4 (beginning-of-buffer) 5 (while (re-search-forward (rx "[[" "ue:" (group (0+ (not "]"))) "][虚幻引擎:" (group (0+ (not "]"))) "]]" ) nil t) 6 (let ((file (string-join (mapcar #'string (match-string 1))))) 7 (replace-match (format "=%s=" file) nil t) 8 ) 9 ) 10 ) 11 )
虚幻引擎项目源码链接逆向恢复
1;; (defun my/recover-ue-project-file-link () 2;; (interactive) 3;; (beginning-of-buffer) 4;; (while (re-search-forward (rx "=ShootThemUp: " (group (0+ (not "="))) "=") nil t) 5;; (let ((filename (string-join (mapcar #'string (match-string 1)))) 6;; (folder)) 7;; (if (string-suffix-p ".h" filename) 8;; (setq folder "Public/")) 9;; (if (string-suffix-p ".cpp" filename) 10;; (setq folder "Private/")) 11;; (if (string-suffix-p ".cs" filename) 12;; (setq folder "")) 13;; (if (string-prefix-p "STUGameModeBase" filename) 14;; (setq folder "")) 15;; (replace-match (format (concat "[[" "uep:%s%s][ShootThemUp:%s]]") folder filename filename) nil t) 16;; ) 17;; ) 18;; )
复制文本内容
选项
1(defun my/copy-org-options () 2 (interactive) 3 (save-excursion 4 (beginning-of-buffer) 5 (let ((op-start (point))) 6 (outline-next-heading) 7 (buffer-substring-no-properties op-start (point)) 8 ) 9 ) 10 )
子树
1(defun my/copy-org-subtree () 2 (interactive) 3 (save-excursion 4 (outline-next-heading) 5 (when (re-search-backward (rx ":" "EXPORT_FILE_NAME" ": " (group (0+ (not "\n"))) "\n") nil t) 6 (outline-previous-heading) 7 (org-copy-subtree) 8 (current-kill 0) 9 ) 10 ) 11 )
计算权重
1(defun my/backward-heading-same-level () 2 (interactive) 3 (org-backward-heading-same-level 1) 4 (point) 5 ) 6(defun my/compute-weight () 7 (interactive) 8 (let ((cur-pos) 9 (weight 2001)) 10 (save-excursion 11 (outline-next-heading) 12 (when (re-search-backward (rx ":" "EXPORT_FILE_NAME" ": " (group (0+ (not "\n"))) "\n") nil t) 13 (org-previous-visible-heading 1) 14 (setq cur-pos (point)) 15 (while (/= cur-pos (my/backward-heading-same-level)) 16 (setq weight (+ weight 1)) 17 (setq cur-pos (point)) 18 ) 19 (message "%d" weight) 20 ) 21 ) 22 ) 23 )
导出子树
光标所在子树
1;; (defun my/ox-hugo-export-subtree () 2;; (interactive) 3;; (save-excursion 4;; (outline-next-heading) 5;; (when (re-search-backward (rx ":" "EXPORT_FILE_NAME" ": " (group (0+ (not "\n"))) "\n") nil t) 6;; (let* ((export-name (string-join (mapcar #'string (match-string 1)))) 7;; (subtree (my/copy-org-subtree)) 8;; (options (my/copy-org-options)) 9;; (weight (my/compute-weight)) 10;; (slotlist '()) 11;; (slotlist (my/compute-subtree-path export-name)) 12;; (slot) 13;; (section) 14;; (cur-level)) 15;; (with-temp-buffer 16;; (insert options) 17;; (insert subtree) 18;; (org-mode) 19;; (my/delete-timestamp-link) 20;; (my/replace-ue-project-file-link) 21;; (my/replace-ue-engine-file-link) 22;; (my/replace-roam-link) 23;; (pop slotlist) 24;; (setq slotlist (reverse slotlist)) 25;; (pop slotlist) 26;; (setq slotlist (reverse slotlist)) 27;; (dolist (item slotlist) 28;; (setq slot (concat slot "/" item)) 29;; ) 30;; (beginning-of-buffer) 31;; (when (re-search-forward (rx "+" "HUGO_SECTION" ": " (group (0+ (not "\n")))) nil t) 32;; (setq section (string-join (mapcar #'string (match-string 1)))) 33;; (replace-match (format (concat "+" "HUGO_SECTION" ": %s") (concat section slot))) 34;; ) 35;; (beginning-of-buffer) 36;; (when (re-search-forward (rx "EXPORT_HUGO_WEIGHT" ": " (group (0+ (not "\n")))) nil t) 37;; (replace-match (format (concat "EXPORT_HUGO_WEIGHT" ": %s") weight)) 38;; ) 39;; (beginning-of-buffer) 40;; (outline-next-heading) 41;; (setq cur-level (funcall outline-level)) 42;; (while (/= 1 cur-level) 43;; (org-shiftmetaleft) 44;; (setq cur-level (funcall outline-level)) 45;; ) 46;; (org-hugo-export-wim-to-md) 47;; (my/amend-pic-link export-name) 48;; ) 49;; ) 50;; ) 51;; ) 52;; ) 53;; (global-set-key (kbd "C-c h s") 'my/ox-hugo-export-subtree)
文件内所有子树
1;; (defun my/ox-hugo-export-all-subtrees () 2;; (interactive) 3;; (save-excursion 4;; (let ((buf-content (buffer-string))) 5;; (with-temp-buffer 6;; (insert buf-content) 7;; (org-mode) 8;; (my/delete-timestamp-link) 9;; (my/replace-ue-project-file-link) 10;; (my/replace-ue-engine-file-link) 11;; (my/replace-roam-link) 12;; (org-hugo-export-wim-to-md :all-subtrees) 13;; (my/amend-pic-link-for-all) 14;; ) 15;; ) 16;; ) 17;; ) 18;; (global-set-key (kbd "C-c h a") 'my/ox-hugo-export-all-subtrees)