自己动手开发更好用的markdown编辑器-07(扩展语法)

时间:2021-01-16 22:39:46
这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/05/19/hexomd-07/
 

上一篇我们实现了自动更新的功能.

在前面的6篇中,我们基本没做什么创造,都只是像玩乐高那样把零件拼接成我们想要的东西.
今天这篇将对marked进行简单扩展, 增加我们的markdown编辑器支持的语法,实现目录,emojis表情两种新语法.
以及改造codemirror,实现我们自定义语法的编辑器高亮显示(这个本来是要放到下一篇,但是刚刚做完后发现内容很短,所以就又合并到这篇里来了).

对于不想看如何实现的朋友,直接下载v0.6.0.2,然后点击右上角的更新按钮更新到最新版即可.

准备工作

首先打开marked,Fork一份到自己仓库. 对marked的改造都将基于我们的这个fork版本.
自己动手开发更好用的markdown编辑器-07(扩展语法)

目录语法

功能描述: 自动提取所有H标签,形成目录树,在解析markdown文本时,如果遇到[TOC]标签则自动将其替换为目录.

将我们fork的版本clone到本地,打开lib/marked.js.所有代码都在这个文件里.

修改inline.gfm,增加目录语法匹配正则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* GFM Inline Grammar
*/ inline.gfm = merge({}, inline.normal, {
escape: replace(inline.escape)('])', '~|])')(),
url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
del: /^~~(?=\S)([\s\S]*?\S)~~/,
toc: /\s*\[TOC\]/,
text: replace(inline.text)
(']|', '~]|')
('|', '|https?://|')
()
});

修改Renderer,增加toctocItem两个方法,用于生成目录标签:

1
2
3
4
5
6
7
8
9
10
Renderer.prototype.toc = function (items) {
var html = '<div id="toc" class="toc"><ul class="toc-tree">';
html += items;
html += '</ul></div>';
return html;
} Renderer.prototype.tocItem = function (id, level, text) {
return '<li class="toc-item toc-level-' + level + '"><a class="toc-link" href="#' + id + '"><span class="toc-number"></span> <span class="toc-text">' + text + '</span></a></li>';
};

修改Rendererheading方法,为其赋予id作为点击目录项的锚点

1
2
3
4
5
6
7
8
9
Renderer.prototype.heading = function(text, level, raw) {
var escapedText = text.toLowerCase();
return '<h' + level + '><a name="' +
escapedText +
'" class="anchor" href="#' +
escapedText +
'"><span class="header-link"></span></a>' +
text + '</h' + level + '>';
};

修改 Parser.prototype.parse,在解析时预生成好目录标签备用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Parse Loop
*/ Parser.prototype.parse = function (src) {
var me = this, tocItems = '';
//预生成好目录标签
src.forEach(function (token) {
if (token.type == 'heading') {
id = token.text.toLowerCase();
tocItems += me.renderer.tocItem(id, token.depth, token.text);
}
});
this.inline = new InlineLexer(src.links, this.options, this.renderer);
this.inline.tocHTML = me.renderer.toc(tocItems); this.tokens = src.reverse(); var out = '';
while (this.next()) {
out += this.tok();
} return out;
};

最后是修改InlineLexer,在匹配到[TOC]时将其替换为完整的目录标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Lexing/Compiling
*/ InlineLexer.prototype.output = function(src) {
var out = ''
, link
, text
, href
, cap;
while (src) {
//toc语法
if (cap = this.rules.toc.exec(src)) {
src = src.substring(cap[0].length);
out += this.tocHTML;
continue;
}
...
}

这样目录语法就完成了,没几行代码,效果如图(预览的样式比较丑,这系列的某一篇会专门优化预览样式):
自己动手开发更好用的markdown编辑器-07(扩展语法)

emojis表情语法

准备表情素材

我将要实现的emoji表情库基于http://www.emoji-cheat-sheet.com/这个项目,大家可以通过这个页面查看所有表情的命名.
我将这里所有表情上传一份到我的七牛空间里,这样访问会快一些.

实现功能

emojis语法的实现跟目录类似.

修改inline.gfm,增加emojis语法匹配正则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* GFM Inline Grammar
*/ inline.gfm = merge({}, inline.normal, {
escape: replace(inline.escape)('])', '~|])')(),
url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
del: /^~~(?=\S)([\s\S]*?\S)~~/,
emoji: /^:([A-Za-z0-9_\-\+]+?):/,
toc: /\s*\[TOC\]/,
text: replace(inline.text)
(']|', ':~]|')
('|', '|https?://|')
()
});

Renderer增加emoji方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Renderer.prototype.emoji = function (emoji) {
//图片使用自己的七牛空间
return '<img src="'
+ 'http://7xj6bw.com1.z0.glb.clouddn.com/'
+ encodeURIComponent(emoji)
+ '.png"'
+ ' alt=":'
+ escape(emoji)
+ ':"'
+ ' title=":'
+ escape(emoji)
+ ':"'
+ ' class="emoji" align="absmiddle" height="20" width="20">';
};

最后,在InlineLexer里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Lexing/Compiling
*/ InlineLexer.prototype.output = function(src) {
var out = ''
, link
, text
, href
, cap;
while (src) {
...
// emoji (gfm)
if (cap = this.rules.emoji.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.emoji(cap[1]);
continue;
}
...
}

完成!这个功能比目录功能更加简单

自己动手开发更好用的markdown编辑器-07(扩展语法)

编辑器语法高亮

这里就不再去fork codemirror这个项目了,有兴趣的可以去fork,修改完后提交给官方.
我们直接简单粗暴的修改lib/codemirror/mode/markdown/markdown.js.

增加toc和emoji的正则:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
, toc = 'toc'
, emoji = 'emoji'
..
..
var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
, ulRE = /^[*\-+]\s+/
, olRE = /^[0-9]+\.\s+/
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
, atxHeaderRE = /^#+/
, tocRE = /\[TOC\]/
, emojiRE = /^:([A-Za-z0-9_\-\+]+?):/
..

blockNormal方法里为匹配到的标签返回独立的class:

1
2
3
4
5
6
7
...
} else if(match = stream.match(tocRE)){
return toc;
} else if(match = stream.match(emojiRE)){
return emoji;
}
...

这样就搞定了,编辑器会为匹配到的代码加上相应的class
自己动手开发更好用的markdown编辑器-07(扩展语法)
自己动手开发更好用的markdown编辑器-07(扩展语法)
有了,class,就可以在样式修改自定义语法的高亮显示了,比如我现在用的样式文件mdn-like
打开这个样式文件,加上样式:

1
2
3
4
5
6
.cm-toc{
background:#ccc;
}
.cm-emoji{
color:#F7A21B;
}

现在这些语法在编辑器里有独特的高亮效果了:
自己动手开发更好用的markdown编辑器-07(扩展语法)

总结

通过两个自定义语法的实现,我们可以总结出自定义语法的一般步骤:

  1. 增加语法关键词的匹配正则.
  2. Renderer里增加相应的标签生成方法.
  3. InlineLexer里处理匹配到的语法.

接下来的计划:

  1. 导出pdf,html文件.
  2. 美化预览样式.

附件

v0.6.0.2
项目地址

自己动手开发更好用的markdown编辑器-07(扩展语法)的更多相关文章

  1. 自己动手开发更好用的markdown编辑器-04&lpar;实时预览&rpar;

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/25/hexomd-04/   程序打包   文章目录 1. 打开新窗口 ...

  2. 自己动手开发更好用的markdown编辑器-06&lpar;自动更新&rpar;

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/05/12/hexomd-06/   文章目录 1. 自动更新方案 2. 实现 ...

  3. 自己动手开发更好用的markdown编辑器-05&lpar;粘贴上传图片&rpar;

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/28/hexomd-05/   文章目录 1. 七牛云存储 1.1. 系统 ...

  4. 自己动手制作更好用的markdown编辑器-01

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址  http://benq.im   文章目录 1. 简介 2. 项目结构 3. 程序主界面 4. 拖动窗口 5. app ...

  5. 自己动手制作更好用的markdown编辑器-03

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im/2015/04/24/hexomd-03/ 文章目录 1. 系统模块 2. 记录上次打开的 ...

  6. 自己动手制作更好用的markdown编辑器-02

    这里文章都是从个人的github博客直接复制过来的,排版可能有点乱. 原始地址 http://benq.im 文章目录 1. 工具条 1.1. 样式 1.2. 工具条截图 2. 状态栏消息 3. 文件 ...

  7. Markdown编辑器推荐与语法教程--图片版

    请参考Markdown编辑器推荐与语法教程--展示版或者Markdown编辑器推荐与语法教程--展示版看具体效果,当然,大家也可以下载Mou亲自体验一把 End

  8. Markdown编辑器推荐与语法教程--展示版

    ---恢复内容开始--- 前言 作为一名高级码农,怎能不知道Markdown的正确打开方式,Markdown现在可以说是无处不在,如果你还不知道简书中的代码块是怎么写出来的,小白无疑了.在此特别推荐一 ...

  9. 任由文字肆意流淌,更*的开源 Markdown 编辑器

    对于创作平台来说内容编辑器是十分重要的功能,强大的编辑器可以让创作者专注于创作"笔"下生花.而最好取悦程序员创作者的方法之一就是支持 Markdown 写作,因为大多数程序员都是用 ...

随机推荐

  1. Python&colon; 列表的基本用法

    列表是可变的,可以改变的序列,它能够保存任何数据类型. >>> list = []        #定义一个空列表>>> list.append(1)        ...

  2. TextView展开和收回

    第一步:接口请求返回数据 第二步:使用handler和textview.getLineCount方法判断是否超过指定行数: community_desc_more.setVisibility(View ...

  3. word-break、word-wrap和其他文字属性

    word-break: break-all; 控制是否断词.(粗暴方式断词)break-all,是断开单词.在单词到边界时,下个字母自动到下一行.主要解决了长串英文的问题. word-wrap: br ...

  4. 用apache的httpclient发请求和接受数据

    此处发请求的是用httpclient4,请自己下载所需要的jar包. 发post请求,并得到数据. String url = "http://localhost:8080/lee" ...

  5. Biztalk2010安装及配置问题集

    在安装Biztalk2010时,碰到很多问题,有的是粗心有的也是比较bt的,如: 1)在win7 64下引入x86 的cab,有点粗心,幸亏给我报错版本不兼容(呵呵): 2)安装的时候 不知道为什么计 ...

  6. ajax跨域请求的方案

    $.get("@Hosts.Default.Www/api/XXXXX/Getxxx/"+@Model.UserId, function(data) { $("#tota ...

  7. UVA 674 Coin Change 换硬币 经典dp入门题

    题意:有1,5,10,25,50五种硬币,给出一个数字,问又几种凑钱的方式能凑出这个数. 经典的dp题...可以递推也可以记忆化搜索... 我个人比较喜欢记忆化搜索,递推不是很熟练. 记忆化搜索:很白 ...

  8. oracle有三个默认的用户名和密码&comma;但是都无法登录的解决方法

    system/change_on_install, system/manager是较旧版的预设密码, 在安装较新版时会提示你设定密码, 若没有或忘了设定, 请参考以下重设: sqlplus / as ...

  9. WPF Prism框架下基于MVVM模式的命令、绑定、事件

    Prism框架下的自定义路由事件和命令绑定 BaseCode XAML代码: <Button x:Class="IM.UI.CommandEx.PrismCommandEx" ...

  10. Struts&lpar;二十七&rpar;:使用token或tokenSession防止表单重复提交

    什么是表单重复提交 表单重复提交包括以下几种情况: 前提:不刷新表单页面 1.多次点击“提交”按钮后,重复提交了多次: 2.已经提交成功之后,按“回退”按钮之后,在点击“提交”按钮后,提交成功: 3. ...