第一个demo
之前已经有过一篇介绍markdown解析器
然而上面的版本实际上有一个致命的错误,就是解析策略是错误的,一开始使用split将字符串分割之后丢失了很多信息,因此实际上是不可取的。
第二个demo
吸取上面的教训之后,又加上想做个实时预览的GUI界面,因此改用更加熟悉的C++来做,这样方便使用Qt来做GUI。
这个版本没有显著的策略错误,实现细节的关键在于搞清楚各种语法的不同层次或者说是作用范围,比如粗体 斜体 删除线是词法阶段的,基本不涉及上下文语义,而像6级标题 无序列表等也基本上很好处理,判断一下行首的字符即可。
GUI 的部分是采用Qt 的webkitView 插件完成预览,同步滚动做的很粗糙,只是算了一下比例,更精确的滚动我想可以通过统计生成的文档HTML的行数,以及事先统计好的各种标签的宽度来综合计算。
本次新版的特性
采用Python 开发的命令行解析工具
- 对C++的regex和字符串处理实在是不能忍了,于是换回Python;
- GUI 随便做做就好了,用起来我发现也不会真的对着那个打的,毕竟更重要的一个语法高亮的编辑器,Linux vim就可以很好的完成任务,配合一个命令行一键导出的工具,这个版本成为唯一一个让我抛弃其它markdown编辑器和生成器的实用工具,推荐大家使用后提出意见和建议~
代码短小
- 加上命令行参数处理以及更丰富的markdown功能支持也仅有300行代码(含注释)
- 结合前两次开发的经验,正确的搞清楚了各种处理的方法、时机和技巧,具体内容下面讲解和说明。
方便安装和使用
- Linux环境下只要安装有Python3就可以直接使用它来导出HTML了,可以借助chrome来完成PDF打印,也可以安装依赖中的wtkhtmltopdf工具来一键生成。
- 联网的环境下可以通过mathjax来自动完成数学公式的渲染
更多使用详情和安装说明见github首页的README
开发细节详解
源码阅读方法
- 忽略main函数,main函数中主要是用来处理命令行参数,真正起作用的是其中的run函数;
- run函数的参数是源文件(markdown)以及输出的HTML PDF文件名,暂时只需要关心HTML文件的输出即可;
- run的核心代码是for eachline in f循环中的parse函数,parse函数接收文本中每一行输入,输出对应的HTML语句写入输出文件中;
注意,markdown的语法并不都是以行为单位的,比如block codeblock order—list table 等是上下文相关的结构,因此会有特殊的处理
- 接下来就是阅读parse函数,下面的说明也主要针对parse函数进行
具体转换的parse()函数
- parse 函数进来首先进行test_state,我们暂时跳过这一步,只需要知道这一步进行和block codeblock table orderlist等上下文相关语法的处理即可;
- title 处理:
如果开头6个以内都是#则这一行被解释成为标题,根据#个数不同而设定为不同的header级别; - horizen line
这个没什么好解释的,判断某一行是3个以上的 - 符号即可,唯一需要注意的是这个的处理要放在无序列表的处理之前,处理后直接返回,否则会被当作无序列表 - 无序列表:
判断开头是否是+ * -即可,处理也很简单 - 行内引用
本来是十分简单的,但要注意这个是可以嵌套的,因此内部需要使用循环实现,通过计数有多少个>来添加对应数目的block头部 - tokenhandle
tokenHandle的位置不可太靠前,因为一些语法当中是不应用这些修改的,实现的话这里使用正则表达式finditer 找到符合条件的全部字符串并进行替换处理,注意可以相互嵌套,因此替换的时候还需要根据原长和替换后的字符串长度进行一个运算; - link_image
链接、图片、上标、下标也是利用正则表达式来实现替换。
不是行独立的几种结构的处理方法----状态机
使用enum模块的Enum类
定义几个状态类
- 对于表格,定义TABLE,取值为Init Format Table;
- 对于有序列表定义ORDERLIST,取值为Init List
- 对于块和代码块,定义BLOCK,取值为Init Block CodeBlock3个状态;
定义几个全局的状态变量
table_state orderList_state block_state
以及一个判定是否是合法代码块的is_code
下面正式进入处理这几个最复杂结构的函数test_state中
代码块的状态转换
1. 若当前行为```,且对应的block_state状态为Init,则这行输出为<blockquote>
并且状态修改为Block;
2. 若当前行为```+语言名称,且当前状态为Init,则当前行是<code></br>
修改状态为Block,并且设置is_code为True
3. 若当前状态为Block,且此时行为‘```’,则表明这个块结束
> 若is_code为True,则添加</code> ,否则,添加</blockquote>
4. 当前行为其它情况且状态为Block,表明这一行是在block内部,不做相关处理,但要将行内的空白符号全部替换为" ",以保留原缩进格式。
有序列表和表格的状态转换类似,表格稍微复杂一些(从第一行的格式无法判断是否是一个合法的表格,必须前两行都满足才可以,因此将第一行缓存起来,根据第二行的情况来判断,然后将前两行一起输出)
这些情况都可以画一个简单的状态图即可马上搞清楚。
公式支持和PDF输出
- 因为提交作业一般需要的是PDF格式,html格式一般不作为提交文件,当前可以用chrome的打印来完成,但是总觉得麻烦了一些,google之后发现一个叫做wtkhtmltopdf的命令行工具,所以就将其嵌入我们的小工具中,以实现一键输出的目的;
- 公式的支持就是利用在线的mathJax来做,效果有时候并不太好,容易出现对齐诡异的情况,但简单的公式还是可以很好的处理的。复杂的大段的公式最好就不要用markdown来写了吧。
最后
我自己的使用感受还是很棒的,对个别不满意的也可以自己手工调整HTML文件来实现。最棒的是,我想要什么格式的输出,可以通过微调整源码来实现~
欢迎大家试用~~