自己動手開發更好用的markdown編輯器-07(擴展語法)


這里文章都是從個人的github博客直接復制過來的,排版可能有點亂. 原始地址  http://benq.im/2015/05/19/hexomd-07/
 
文章目錄
  1. 1. 准備工作
  2. 2. 目錄語法
  3. 3. emojis表情語法
    1. 3.1. 准備表情素材
    2. 3.2. 實現功能
  4. 4. 編輯器語法高亮
  5. 5. 總結
  6. 6. 附件

上一篇我們實現了自動更新的功能.

在前面的6篇中,我們基本沒做什么創造,都只是像玩樂高那樣把零件拼接成我們想要的東西.
今天這篇將對marked進行簡單擴展, 增加我們的markdown編輯器支持的語法,實現目錄,emojis表情兩種新語法.
以及改造codemirror,實現我們自定義語法的編輯器高亮顯示(這個本來是要放到下一篇,但是剛剛做完后發現內容很短,所以就又合並到這篇里來了).

對於不想看如何實現的朋友,直接下載v0.6.0.2,然后點擊右上角的更新按鈕更新到最新版即可.

准備工作

首先打開marked,Fork一份到自己倉庫. 對marked的改造都將基於我們的這個fork版本.

目錄語法

功能描述: 自動提取所有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;
}
...
}

 

這樣目錄語法就完成了,沒幾行代碼,效果如圖(預覽的樣式比較丑,這系列的某一篇會專門優化預覽樣式):

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;
}
...
}

 

完成!這個功能比目錄功能更加簡單

編輯器語法高亮

這里就不再去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


有了,class,就可以在樣式修改自定義語法的高亮顯示了,比如我現在用的樣式文件mdn-like
打開這個樣式文件,加上樣式:

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

.cm-emoji{
color:#F7A21B;
}

 

現在這些語法在編輯器里有獨特的高亮效果了:

總結

通過兩個自定義語法的實現,我們可以總結出自定義語法的一般步驟:

  1. 增加語法關鍵詞的匹配正則.
  2. Renderer里增加相應的標簽生成方法.
  3. InlineLexer里處理匹配到的語法.

接下來的計划:

  1. 導出pdf,html文件.
  2. 美化預覽樣式.

附件

v0.6.0.2
項目地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM