欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Typora 博文标题自动编号

程序员文章站 2022-06-02 17:27:24
...

Typora 博文标题自动编号

1、Why?

1.1、Typora标题自动编号

  • 修改 base.user.css 中的 css 代码可达到如下效果,看着还不错

Typora 博文标题自动编号

1.2、博文效果

  • 修改 base.user.css 中的 css 代码只能实现 Typora 中的视觉效果,并不是真正在标题中添加了序号

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、效果

  • 博文标题有编号,看着就舒服多了

Typora 博文标题自动编号

相关标签: Typora