yibie
2025 年1 月 23 日 14:32
1
地址:GitHub - yibie/org-numbering: A flexible and customizable numbering system for Org mode headings, supporting various international numbering schemes.
# org-numbering
一个灵活且可自定义的 Org mode 标题编号系统,支持多种国际化编号方案。

## 特性
- 多种编号方案:
- 基本:十进制(1, 2, 3...),字母(a, b, c...),罗马数字(I, II, III...)
- CJK:中文数字(一、二、三...),片假名(ア、イ、ウ...),伊吕波顺序(イ、ロ、ハ...)
- 符号:圆形数字(①, ②, ③...),项目符号(•),方框(□)
- 特殊:希腊字母(α, β, γ...),带括号格式((1),(i),(一))
- 扩展:章节风格(第一章),白色圆圈(○1)
- 灵活的配置:
- 每个标题层级可以独立设置编号方案
- 支持组合编号(例如,1.1, 1.1.1)
- 可自定义组合数字的分隔符
## 安装
### 手动安装
1. 克隆这个仓库:
```bash
git clone https://github.com/yibie/org-numbering.git
将以下内容添加到你的 Emacs 配置中:
(add-to-list 'load-path "/path/to/org-numbering")
(require 'org-numbering)
使用
基本命令
• M-x org-numbering-number: 根据上下文应用编号
• 在标题上:为当前子树编号
• 在选中的区域:为区域内的标题编号
• 空行:为缓冲区中的所有标题编号
配置示例
学术论文风格(英语):
(setq org-numbering-level-scheme
'((1 . ((scheme . decimal) ; (scheme . decimal) 表示十进制编号
(combine . nil))) ; <= `nil` 表示不组合
(2 . ((scheme . decimal) ; 1.1
(combine . t))) ; <= `t` 表示组合
(3 . ((scheme . decimal) ; 1.1.1
(combine . t))) ; <= `t` 表示组合
(4 . ((scheme . alpha) ; a)
(combine . nil)))
(5 . ((scheme . paren-num) ; (1)
(combine . nil)))))
德国风格:
(setq org-numbering-level-scheme
'((1 . ((scheme . decimal) ; 1.
(combine . nil)))
(2 . ((scheme . upper-alpha) ; A.
(combine . nil)))
(3 . ((scheme . alpha) ; a)
(combine . nil)))
(4 . ((scheme . greek) ; α)
(combine . nil)))
(5 . ((scheme . dash) ; -
(combine . nil)))))
中文学术风格:
(setq org-numbering-level-scheme
'((1 . ((scheme . chapter) ; 第一章
(combine . nil)))
(2 . ((scheme . decimal) ; 1.1
(combine . t)))
(3 . ((scheme . paren-chinese) ; (一)
(combine . nil)))
(4 . ((scheme . extended-circled) ; ⑴
(combine . nil)))
(5 . ((scheme . decimal) ; 1.
(combine . nil)))))
日本文档风格:
(setq org-numbering-level-scheme
'((1 . ((scheme . decimal) ; 1.
(combine . nil)))
(2 . ((scheme . katakana) ; ア、
(combine . nil)))
(3 . ((scheme . circled) ; ①
(combine . nil)))
(4 . ((scheme . iroha) ; イ、
(combine . nil)))
(5 . ((scheme . square) ; □
(combine . nil)))))
可用的编号方案
编号方案
示例
描述
decimal
1, 2, 3
阿拉伯数字
alpha
a, b, c
小写字母
roman
I, II, III
罗马数字
chinese
一、二、三
中文数字
upper-alpha
A, B, C
大写字母
circled
①, ②, ③
圆形数字
parenthesized
⒜, ⒝, ⒞
带括号字母
bullet
•
项目符号
dash
-
连字符
square
□
方框
greek
α, β, γ
希腊字母
paren-num
(1), (2), (3)
带括号数字
paren-roman
(i), (ii), (iii)
带括号罗马数字
katakana
ア、イ、ウ
片假名
iroha
イ、ロ、ハ
伊吕波顺序
chapter
第一章
章节风格
paren-chinese
(一)、(二)
带括号中文数字
extended-circled
⑴, ⑵, ⑶
扩展圆形数字
white-circled
○1、○2
白色圆圈
自定义
组合数字的分隔符
你可以自定义组合数字之间的分隔符:
(setq org-numbering-separator ".") ; 默认是 "."
12 个赞
mmtun
2025 年1 月 24 日 01:00
2
感谢老大,有一次将Emacs的中文本地化提高了一大步!而且可配置性(可玩型)巨大!
感谢!我是专门来送小红花的!
1 个赞
这个不错,终于不用正则+数字转中文函数来造中文编号heading了。
(setq org-numbering-level-scheme
'((2 . ((scheme . chinese)
(combine . nil)))
(3 . ((scheme . paren-chinese)
(combine . nil)))
(4 . ((scheme . decimal)
(combine . nil)))
(5 . ((scheme . paren-num)
(combine . nil)))))
想用pandoc一键导出公文格式的可以试试这个,把第一个大标题留空作为文档标题,后面按公文格式编号。
3 个赞
yibie
2025 年1 月 24 日 02:04
4
哈哈,谢谢!
org-numbering 也是一个开放性的框架,自己想要什么样的符号作为编号,都可以添加。
我已经在项目的 README 中更新了示例:
Add Your Own Numbering Scheme
You can create your own numbering scheme by registering it with org-numbering-register-scheme
. Each scheme needs four functions:
increment-fn
: Generate the next number in sequence
format-fn
: Format the number for display
parse-fn
: Parse a number from string
validate-fn
: Validate a number
Here’s a complete example of adding a custom scheme that uses emoji numbers ( , , …):
;; Define the emoji mapping
(defconst my-emoji-numbers
'((1 . "1️⃣") (2 . "2️⃣") (3 . "3️⃣") (4 . "4️⃣") (5 . "5️⃣")
(6 . "6️⃣") (7 . "7️⃣") (8 . "8️⃣") (9 . "9️⃣") (10 . "🔟"))
"Mapping between numbers and emoji numbers.")
;; Increment function: get next number
(defun my-emoji-increment (current)
"Get next emoji number after CURRENT.
If CURRENT is nil, start from 1️⃣."
(let* ((num (if current
(car (rassoc current my-emoji-numbers))
0))
(next-num (1+ num)))
(if (> next-num 10)
"1️⃣" ; Wrap around after 🔟
(cdr (assq next-num my-emoji-numbers)))))
;; Format function: just return the emoji
(defun my-emoji-format (emoji)
"Format emoji number EMOJI."
emoji)
;; Parse function: convert string to emoji
(defun my-emoji-parse (str)
"Parse emoji number from STR."
(when (string-match "^[1-9️⃣🔟]$" str)
str))
;; Validate function: check if it's a valid emoji number
(defun my-emoji-validate (emoji)
"Validate emoji number EMOJI."
(and (stringp emoji)
(rassoc emoji my-emoji-numbers)))
;; Register the new scheme
(org-numbering-register-scheme 'emoji
:increment-fn #'my-emoji-increment
:format-fn #'my-emoji-format
:parse-fn #'my-emoji-parse
:validate-fn #'my-emoji-validate)
;; Add format style for the new scheme
(add-to-list 'org-numbering-format-style
'(emoji . "%s")) ; No extra formatting needed
;; Use it in your configuration
(setq org-numbering-level-scheme
'((1 . ((scheme . emoji) ; 1️⃣
(combine . nil)))))
Each function serves a specific purpose:
increment-fn
: Takes the current number and returns the next one in sequence
Input: Current number (or nil for first number)
Output: Next number in sequence
format-fn
: Formats the number for display
Input: Number to format
Output: String to display
parse-fn
: Converts a string back to a number
Input: String to parse
Output: Number if valid, nil if invalid
validate-fn
: Checks if a number is valid for this scheme
Input: Number to validate
Output: t if valid, nil if invalid
After registering your scheme, you can use it just like any built-in scheme in your org-numbering-level-scheme
configuration.
org-numbering-level-scheme 能否搞成 list 方式,这样至少可以配置中、英两套
yibie
2025 年1 月 24 日 04:12
8
好的,我看看怎么修。如果可以的话,在 GitHub 上报 issue 比较好,在论坛里有可能会忘掉
yibie
2025 年1 月 24 日 04:31
9
你要不要尝试一下自己写配置。
目前 org-numbering 只需要一个命令就能完成排版,我可太喜欢这个极简的 UX 了,不想打断这个交互的过程。
我看了下代码,应该出在:
;; remove old number (if exists)
(if (string-match "^\\(?:[IVXLCDM]+[.]\\|[a-z][.]\\|[0-9]+[.]\\|[一二三四五六七八九十百千万亿]+、\\)?\\s-*\\(.*\\)" title)
Doom 的 D 被判断成已有的 number了。我加了个标题,以 Microsoft 开头,结果 Mic 也都被吃掉了。说明正则表达式难以判断原有编号。
所以从这个角度来说,设置多套 scheme,然后严格按照 (旧)scheme 的设定来识别,在用(新) scheme 的设定来编号,可能是解决办法。
就是默认这几个,我挨个试了一下,所以才发现跟scheme没关系。这是我的 test.org :
* 基本安装配置
** Windows 安装 doom-emacs
*** Doom-Emacs 安装
**** Microsoft 安装 doom-emacs
我发现你的展示动画里,Data source 的 D 也被吃了
yibie
2025 年1 月 24 日 07:43
16
标题字母被吃的问题,我已经修复了。至于切换 scheme,我不认为需要另外实现功能。
这里是一份配置参考。另外,我比较疲惫,我希望你能力所能及的帮助你自己。
(defvar my/org-numbering-academic-scheme
'((1 . ((scheme . decimal) ; 1.
(combine . nil)))
(2 . ((scheme . decimal) ; 1.1
(combine . t)))
(3 . ((scheme . decimal) ; 1.1.1
(combine . t)))
(4 . ((scheme . alpha) ; a)
(combine . nil)))
(5 . ((scheme . paren-num) ; (1)
(combine . nil))))
"Academic paper style numbering scheme.")
(defvar my/org-numbering-chinese-scheme
'((1 . ((scheme . chapter) ; 第一章
(combine . nil)))
(2 . ((scheme . decimal) ; 1.1
(combine . t)))
(3 . ((scheme . paren-chinese) ; (一)
(combine . nil)))
(4 . ((scheme . extended-circled) ; ⑴
(combine . nil)))
(5 . ((scheme . white-circled) ; ○1、
(combine . nil))))
"Chinese academic style numbering scheme.")
;; 定义切换函数
(defun my/org-numbering-toggle-scheme ()
"Toggle between academic and Chinese numbering schemes."
(interactive)
(if (equal org-numbering-level-scheme my/org-numbering-academic-scheme)
(progn
(setq org-numbering-level-scheme my/org-numbering-chinese-scheme)
(message "Switched to Chinese numbering scheme"))
(setq org-numbering-level-scheme my/org-numbering-academic-scheme)
(message "Switched to Academic numbering scheme")))
;; 绑定快捷键
(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-c n t") #'my/org-numbering-toggle-scheme))
2 个赞
(defun num2chn (num)
"Convert an integer NUM (0-999) to Chinese characters."
(let* ((num (if (numberp num)
num
(string-to-number num)))
(digits '("零" "一" "二" "三" "四" "五" "六" "七" "八" "九"))
(units '("" "十" "百" "千"))
(hundreds (floor num 100))
(tens (floor (mod num 100) 10))
(ones (mod num 10))
(result ""))
(if (or (< num 0) (> num 999))
(error "请输入[0,999]之间的整数"))
(when (> hundreds 0)
(setq result (concat (nth hundreds digits) "百"))
;; (if (and (= tens 0) (= ones 0))
;; (setq result (concat result "零")))
)
(when (> tens 0)
(if (and (= tens 1) (zerop hundreds))
(setq result (concat result "十"))
(setq result (concat result (nth tens digits) "十"))))
(when (> ones 0)
(if (or (zerop hundreds) (> tens 0))
(setq result (concat result (nth ones digits)))
(setq result (concat result "零" (nth ones digits)))))
(if (and (> hundreds 0) (= tens 0) (> ones 0))
(setq result (concat (nth hundreds digits) "百零" (nth ones digits))))
(if (string= result "")
"零"
result)))
(defun org-official-document-renumber ()
"为当前 1 级下属的所有 subtree 重新编号,每个层级的子标题编号完全独立。"
(interactive)
(save-excursion
;; 找到最近的 1 级标题
(unless (org-at-heading-p)
(org-previous-visible-heading 1))
(while (and (org-up-heading-safe) (> (org-outline-level) 1)))
;; 确保当前是 1 级标题
(when (= (org-outline-level) 1)
(let ((counters (make-hash-table))) ; 用哈希表存储每个层级的计数器
;; 遍历当前 1 级标题的所有 subtree
(org-map-tree
(lambda ()
(when (org-get-heading t t t t) ; 确保是标题
(let* ((level (org-outline-level)) ; 获取当前标题的层级
(heading (nth 4 (org-heading-components))) ; 获取当前标题文本
(is-comment (string-prefix-p "COMMENT " heading)))
(unless is-comment ; 如果不是评论标题,则进行重新编号
(let* ((counter (or (gethash level counters) 1)) ; 获取当前层级的计数器
(new-heading (org-official-document-format-heading level counter heading))) ; 格式化标题
(org-edit-headline new-heading) ; 修改标题文本
(puthash level (1+ counter) counters) ; 更新当前层级的计数器
;; 如果当前层级是父层级,重置子层级的计数器
(cond
((= level 2) (puthash 3 1 counters)) ; 重置 3 级标题的计数器
((= level 3) (puthash 4 1 counters)) ; 重置 4 级标题的计数器
((= level 4) (puthash 5 1 counters))))))))) ; 重置 5 级标题的计数器
(message "重新编号完成!")))))
(defun org-official-document-format-heading (level counter heading)
"根据层级和计数器格式化标题。"
(let ((new-heading (replace-regexp-in-string "^\\([一二三四五六七八九十]+、\\|([一二三四五六七八九十]+)\\|[0-9]+\\.\\|([0-9]+)\\)" "" heading))) ; 移除旧的编号
(cond
((= level 1) new-heading) ; 一级标题:留空
((= level 2) (format "%s、%s" (num2chn counter) new-heading)) ; 二级标题:一、
((= level 3) (format "(%s)%s" (num2chn counter) new-heading)) ; 三级标题:(一)
((= level 4) (format "%d.%s" counter new-heading)) ; 四级标题:1.
((= level 5) (format "(%d)%s" counter new-heading))))) ; 五级标题:(1)
只想按公文格式编号的可以参考这个函数,功能单一,但是挺快。
1 个赞
yibie
2025 年1 月 26 日 09:54
20
因为我不熟悉公文编号,所以我是想问,按公文格式编号是怎么样的
主要是开发这个包非常急就章,并没有正确地考虑现实中的格式
yibie
2025 年1 月 26 日 15:00
22
嗯,也不错。我在 Github 上看到一个疑似你的账号 Fork 了 org-numbering,但后来 Fork 记录消失了。
是你吗?