Typora 博文标题自动编号
程序员文章站
2022-06-02 17:27:24
...
Typora 博文标题自动编号
1、Why?
1.1、Typora标题自动编号
- 修改 base.user.css 中的 css 代码可达到如下效果,看着还不错
1.2、博文效果
- 修改 base.user.css 中的 css 代码只能实现 Typora 中的视觉效果,并不是真正在标题中添加了序号
2、Let’s Do it
2.1、目标
- 编写 Java 脚本,读取 MD 文件的内容,并在每个标题之前添加标题序号
2.2、代码
- 大致思路:
- 每一级标题都有自己的计数器,由于一级标题不参与编号,从二级标题开始算起,Typora 最多支持六级标题,我们用一个长度为 5 的一维数组代表 2~6 级标题计数器
- 每读取一行,便判断此行是否为标题行,如果是标题行,则为当前标题添加标题序号
- 标题计数器初始默认值为 0 ,每次用的时候便执行 += 1 操作
public class TitleAutoNumbering {
public static void main(String[] args) {
// MD 文件位置
String destMdFilePath;
// 从命令行读取 MD 文件位置,否则使用默认值
if (args == null || args.length == 0) {
destMdFilePath = "C:\\Users\\Heygo\\Desktop\\typora markdown 标题自动编号.md";
} else {
destMdFilePath = args[0];
}
// 执行标题自动编号
doTitleAutoNumbering(destMdFilePath);
}
/**
* 执行标题自动编号
* @param destMdFilePath MD 文件路径
*/
private static void doTitleAutoNumbering(String destMdFilePath) {
// 获取标题自动编号的MD文件内容
String mdFileContent = getAutoTitledMdContent(destMdFilePath);
// 执行保存(覆盖源文件)
SaveMdContentToFile(destMdFilePath, mdFileContent);
}
/**
* 获取标题自动编号的MD文件内容
* @param destMdFilePath MD 文件路径
* @return
*/
private static String getAutoTitledMdContent(String destMdFilePath) {
// 如果不是 MD 文件,滚蛋
Boolean isMdFile = destMdFilePath.endsWith(".md");
if (!isMdFile) {
return "";
}
// 标题编号
/*
标题编号规则:
- 一级标题为文章的题目,不对一级标题编号
- 二级、三级、四级标题需要级联编号
- 五级、六级标题无需级联编号,只需看上一级标题的脸色,递增即可
*/
Integer[] titleNumber = new Integer[]{0, 0, 0, 0, 0};
// 存储md文件内容
StringBuilder sb = new StringBuilder();
// 当前行内容
String curLine;
// 装饰者模式:FileReader无法一行一行读取,所以使用BufferedReader装饰FileReader
try (
FileReader fr = new FileReader(destMdFilePath);
BufferedReader br = new BufferedReader(fr);
) {
// 当前行有内容
while ((curLine = br.readLine()) != null) {
// 判断是否为标题行,如果是标题,是几级标题
Integer curTitleLevel = calcTitleLevel(curLine);
if (curTitleLevel != -1) {
// 插入标题序号
curLine = insertTitleNumber(curLine, titleNumber);
// 重新计算标题计数器
RecalcTitleCounter(curTitleLevel, titleNumber);
}
// 向缓冲区中追加内容
sb.append(curLine + "\r\n");
}
// 返回 MD 文件内容
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
// 失败返回空字符串
return "";
}
}
/**
* 计算当前标题等级
* @param curLine 当前行的内容
* @return -1 :非标题行;大于等于 2 的正数:当前行的标题等级
*/
private static Integer calcTitleLevel(String curLine) {
// 由于一级标题无需编号,所以从二级标题开始判断
boolean isTitle = curLine.startsWith("##");
if (!isTitle) {
// 返回 -1 表示非标题行
return -1;
}
// 现在来看看是几级标题
Integer titleLevel = curLine.indexOf(" ");
return titleLevel;
}
/**
* 重新计算标题计数器的值
* @param titleLevel 当前行的标题等级
* @param titleNumber 标题计数器
*/
private static void RecalcTitleCounter(Integer titleLevel, Integer[] titleNumber) {
// 二级标题更新时,三级及三级以下的标题序号重置为 0
Integer startIndex = titleLevel - 1;
for (int i = startIndex; i < titleNumber.length; i++) {
titleNumber[i] = 0;
}
}
/**
* 向标题行中插入标题序号
* @param curLine 当前行内容
* @param titleNumber 标题计数器
* @return
*/
private static String insertTitleNumber(String curLine, Integer[] titleNumber) {
// 标题等级(以空格分隔的前提是 Typora 开启严格模式)
Integer titleLevel = curLine.indexOf(" ");
// 标题等级部分
String titleLevelStr = curLine.substring(0, titleLevel);
// 标题内容部分
String titleContent = curLine.substring(titleLevel + 1);
// 标题等级递增
Integer titleIndex = titleLevel - 2;
titleNumber[titleIndex] += 1;
// 标题序号
String titleNumberStr = "";
switch (titleLevel) {
case 2:
titleNumberStr = titleNumber[0].toString();
break;
case 3:
titleNumberStr = titleNumber[0].toString() + "." + titleNumber[1];
break;
case 4:
titleNumberStr = titleNumber[0].toString() + "." + titleNumber[1] + "." + titleNumber[2];
break;
case 5:
titleNumberStr = titleNumber[3].toString();
break;
case 6:
titleNumberStr = titleNumber[4].toString() + " ) ";
break;
}
titleNumberStr += "、";
// 插入标题序号
titleContent = titleNumberStr + titleContent;
System.out.println("已增加标题序号:" + titleContent);
// 返回带序号的标题
curLine = titleLevelStr + " " + titleContent;
return curLine;
}
/**
* 保存MD文件
* @param destMdFilePath MD文件路径
* @param mdFileContent MD文件内容
*/
public static void SaveMdContentToFile(String destMdFilePath, String mdFileContent) {
// 不保存空文件
if (mdFileContent == null || mdFileContent == "") {
return;
}
// 执行保存
try (FileWriter fw = new FileWriter(destMdFilePath)) {
fw.write(mdFileContent);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3、效果
- 博文标题有编号,看着就舒服多了