;;; 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