Typora 博文标题自动编号

Typora 博文标题自动编号

1、Why?

1.1、Typora标题自动编号

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

image-20200711104452160

1.2、博文效果

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

image-20200711104200853

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 + PicGo + Aliyun OSS + CSDN.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);

        // 先去除之前的编号
        titleContent = RemovePreviousTitleNumber(titleContent);

        // 标题等级递增
        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;
    }

    /**
     * 去除之前标题的编号
     * @param titleContent 标题内容
     * @return 去除标题编号之后的标题内容
     */
    private static String RemovePreviousTitleNumber(String titleContent) {

        // 寻找标题中的 、 字符
        Integer index = titleContent.indexOf("、");

        if (index > 0 && index < 6) {

            // 之前已经进行过标号
            return titleContent.substring(index + 1);

        } else {

            // 之前未进行过标号,直接返回
            return titleContent;
        }
    }

    /**
     * 保存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、效果

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

image-20200712190202558

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值