Files
my-lisp/my-org-pukiwiki.el
T
2026-07-03 09:04:08 +09:00

175 lines
5.3 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;;; my-org-pukiwiki.el --- Simple Org to PukiWiki exporter -*- lexical-binding: t; -*-
(require 'subr-x)
(defgroup my-org-pukiwiki nil
"Simple Org to PukiWiki exporter."
:group 'tools)
(defcustom my-org-pukiwiki-coding-system 'japanese-shift-jis
"Output coding system."
:type 'coding-system
:group 'my-org-pukiwiki)
(defun my-org-pukiwiki--metadata-p (line)
(string-match-p
"^#\\+\\(TITLE\\|AUTHOR\\|OPTIONS\\|DATE\\|EMAIL\\|LANGUAGE\\|KEYWORDS\\):"
line))
(defun my-org-pukiwiki--heading (line)
(if (string-match "^\\(\\*+\\)[ \t]+\\(.*\\)$" line)
(let* ((level (length (match-string 1 line)))
(title (match-string 2 line)))
(cond
((= level 1) (concat "* " title))
((= level 2) (concat "** " title))
((= level 3) (concat "*** " title))
(t (concat "'''" title "'''"))))
line))
(defun my-org-pukiwiki--list (line)
(cond
((string-match "^[ \t]*-[ \t]+\\(.*\\)$" line)
(concat "-" (match-string 1 line)))
((string-match "^[ \t]*[0-9]+\\.[ \t]+\\(.*\\)$" line)
(concat "+" (match-string 1 line)))
(t line)))
(defun my-org-pukiwiki--table-separator-p (line)
(string-match-p "^[ \t]*|[-+| \t]+|[ \t]*$" line))
(defun my-org-pukiwiki--table (line)
(cond
((my-org-pukiwiki--table-separator-p line)
nil)
((string-match-p "^[ \t]*|" line)
(let ((s (string-trim line)))
(setq s (replace-regexp-in-string "=" "" s))
(if (string-match-p "|[ \t]*キー[ \t]*|\\|[ \t]*関数[ \t]*|\\|[ \t]*機能[ \t]*|" s)
(concat s "h")
s)))
(t line)))
(defun my-org-pukiwiki--inline (line)
(setq line
(replace-regexp-in-string
"=\\([^=\n]+\\)="
"''\\1''"
line))
(setq line
(replace-regexp-in-string
"~\\([^~\n]+\\)~"
"''\\1''"
line))
line)
(defun my-org-pukiwiki--link (line)
;; [[file:foo.png]]
(setq line
(replace-regexp-in-string
"\\[\\[file:\\([^]]+\\)\\]\\]"
"#ref(\\1)"
line))
;; [[https://example.org][label]]
(setq line
(replace-regexp-in-string
"\\[\\[\\([^]]+\\)\\]\\[\\([^]]+\\)\\]\\]"
"[[\\2>\\1]]"
line))
;; [[https://example.org]]
(setq line
(replace-regexp-in-string
"\\[\\[\\(https?://[^]]+\\)\\]\\]"
"[[\\1]]"
line))
line)
(defun my-org-pukiwiki--sjis-safe (text)
(let ((s text))
(setq s (replace-regexp-in-string "\\|—\\|―" "--" s))
(setq s (replace-regexp-in-string "“\\|”" "\"" s))
(setq s (replace-regexp-in-string "\\|" "'" s))
(setq s (replace-regexp-in-string "…" "..." s))
(setq s (replace-regexp-in-string "⏳" "Busy" s))
(setq s (replace-regexp-in-string "→" "->" s))
(setq s (replace-regexp-in-string "←" "<-" s))
(setq s (replace-regexp-in-string "⇒" "=>" s))
(setq s (replace-regexp-in-string "├──" "+-" s))
(setq s (replace-regexp-in-string "└──" "+-" s))
(setq s (replace-regexp-in-string "│" "|" s))
s))
(defun my-org-pukiwiki-convert-string (text)
"Convert Org TEXT to PukiWiki text."
(let ((lines (split-string text "\n"))
(out nil)
(in-block nil))
(dolist (line lines)
(cond
;; src/example block start
((string-match-p "^#\\+begin_\\(src\\|example\\)" line)
(setq in-block t))
;; src/example block end
((string-match-p "^#\\+end_\\(src\\|example\\)" line)
(setq in-block nil))
;; preformatted block: leading half-width space
(in-block
(push (concat " " line) out))
;; skip Org metadata
((my-org-pukiwiki--metadata-p line)
nil)
;; skip caption/name lines
((string-match-p "^#\\+\\(CAPTION\\|NAME\\):" line)
nil)
;; normal line
(t
(setq line (my-org-pukiwiki--heading line))
(setq line (my-org-pukiwiki--list line))
(setq line (my-org-pukiwiki--table line))
(when line
(setq line (my-org-pukiwiki--inline line))
(setq line (my-org-pukiwiki--link line))
(push line out)))))
(my-org-pukiwiki--sjis-safe
(string-join (nreverse out) "\n"))))
(defun my-org-pukiwiki-export-buffer ()
"Convert current Org buffer and show result in *PukiWiki*."
(interactive)
(let ((text (my-org-pukiwiki-convert-string (buffer-string))))
(with-current-buffer (get-buffer-create "*PukiWiki*")
(erase-buffer)
(insert text)
(goto-char (point-min))
(pop-to-buffer (current-buffer)))))
(defun my-org-pukiwiki-export-file (&optional file)
"Export current Org buffer to a Shift-JIS PukiWiki file."
(interactive)
(let* ((src (buffer-file-name))
(dst (or file
(if src
(concat (file-name-sans-extension src) ".pukiwiki")
"output.pukiwiki")))
(text (my-org-pukiwiki-convert-string (buffer-string)))
(coding-system-for-write my-org-pukiwiki-coding-system))
(with-temp-file dst
(insert text))
(message "Exported PukiWiki: %s" dst)))
(defun my-org-pukiwiki-copy-to-clipboard ()
"Convert current Org buffer to PukiWiki text and copy it to kill-ring."
(interactive)
(let ((text (my-org-pukiwiki-convert-string (buffer-string))))
(kill-new text)
(message "Copied PukiWiki text to clipboard")))
(provide 'my-org-pukiwiki)
;;; my-org-pukiwiki.el ends here