;;; pkg-writing.el --- Writing and blogging tools -*- lexical-binding: t -*-
;; Copyright (C) 2024 Cytrogen
;; This file contains:
;; - ox-hugo configuration for blog publishing
;; - YASnippet for text expansion
;; - Newsletter generation tools
;; - Writing-related utilities
;;; Commentary:
;; Tools and configurations specifically for writing, blogging, and content creation.
;; Includes Hugo integration and custom newsletter generation functions.
;;; Code:
;; Ox-Hugo Configuration
;; Blog publishing with Hugo
(unless (package-installed-p 'ox-hugo)
(package-refresh-contents)
(package-install 'ox-hugo))
(with-eval-after-load 'ox
(require 'ox-hugo)
(setq org-hugo-base-dir "~/Projects/blog"))
;; Universal Elisp Tags Filter
;; Support for Hexo-style block tags
(with-eval-after-load 'ox
(defun my/org-export-hexo-universal-block (block backend info)
(when (org-export-derived-backend-p backend 'md)
(let* ((type (downcase (org-element-property :type block)))
(content (org-export-data (org-element-contents block) info)))
(format "{%% %s %%}\n%s{%% end%s %%}" type content type))))
(add-to-list 'org-export-filter-special-block-functions 'my/org-export-hexo-universal-block))
;; YASnippet Configuration
;; Text expansion system
(unless (package-installed-p 'yasnippet)
(package-install 'yasnippet))
(with-eval-after-load 'yasnippet
(yas-global-mode 1)
(yas-reload-all))
;; Newsletter Tools
;; Custom functions for newsletter generation
(defun my/slugify-title (title)
"Convert title to anchor-compatible slug, similar to the nodejs script."
(let ((slug title))
;; Remove 《》 brackets
(setq slug (replace-regexp-in-string "《\\|》" "" slug))
;; Remove punctuation, keep Unicode letters, numbers, spaces, hyphens
(setq slug (replace-regexp-in-string "[^[:alnum:]\\s-]" "" slug))
;; Replace spaces with hyphens
(setq slug (replace-regexp-in-string "\\s+" "-" slug))
;; Convert to lowercase
(downcase slug)))
(defun my/generate-newsletter-toc ()
"Generate TOC for newsletter, similar to the nodejs script."
(interactive)
(save-excursion
(goto-char (point-min))
;; Find the end of details block
(if (not (re-search-forward "#+END_details" nil t))
(message "Error: Could not find #+END_details block")
(let ((toc-sections '())
(current-section nil))
;; Parse content after the details block
(while (not (eobp))
(beginning-of-line)
(let ((line (thing-at-point 'line t)))
(cond
;; Match H2 headings (exported as ## in markdown)
((string-match "^\\* \\(.*\\)$" line)
(let ((section-title (match-string 1 line)))
(setq current-section (list :title section-title :articles '()))
(push current-section toc-sections)))
;; Match H4 headings with article links (exported as #### [...](url))
((and current-section
(string-match "^\\*\\*\\*\\* \\[\\[.*?\\]\\[《\\(.*?\\)》\\]\\]" line))
(let ((article-title (match-string 1 line)))
(push article-title (plist-get current-section :articles))))))
(forward-line 1))
;; Reverse to get correct order
(setq toc-sections (reverse toc-sections))
(dolist (section toc-sections)
(setq section (plist-put section :articles (reverse (plist-get section :articles)))))
;; Generate TOC markdown
(let ((toc-content "\n## 输入\n\n"))
(dolist (section toc-sections)
(let ((section-title (plist-get section :title))
(articles (plist-get section :articles)))
(when articles
(setq toc-content
(concat toc-content
(format "### %s\n\n" section-title)))
(dolist (article articles)
(let ((display-title article)
(anchor (my/slugify-title article)))
(setq toc-content
(concat toc-content
(format "- [%s](#%s)\n" display-title anchor))))
(setq toc-content (concat toc-content "\n")))))
;; Find the details block and replace its content
(goto-char (point-min))
(if (re-search-forward "#+BEGIN_details 本期导读" nil t)
(progn
(forward-line 4) ; Skip PROPERTIES block
(let ((start (point)))
(if (re-search-forward "#+END_details" nil t)
(progn
(beginning-of-line)
(delete-region start (point))
(insert toc-content)
(message "Newsletter TOC generated and inserted!"))
(message "Error: Could not find #+END_details"))))
(message "Error: Could not find details block"))))))))
;; Writing Utilities
;; Additional writing helper functions
(defun my/insert-newsletter-toc ()
"Deprecated: Use my/generate-newsletter-toc instead."
(interactive)
(message "Please use M-x my/generate-newsletter-toc instead."))
(provide 'pkg-writing)
;;; pkg-writing.el ends here