這裏文章都是從我的的github博客直接複製過來的,排版可能有點亂. 原始地址
http://benq.im/2015/05/19/hexomd-07/
上一篇咱們實現了自動更新
的功能.css
在前面的6篇中,咱們基本沒作什麼創造,都只是像玩樂高那樣把零件拼接成咱們想要的東西.
今天這篇將對marked
進行簡單擴展, 增長咱們的markdown編輯器支持的語法,實現目錄
,emojis
表情兩種新語法.
以及改造codemirror
,實現咱們自定義語法的編輯器高亮顯示(這個原本是要放到下一篇,可是剛剛作完後發現內容很短,因此就又合併到這篇裏來了).html
對於不想看如何實現的朋友,直接下載v0.6.0.2,而後點擊右上角的更新按鈕更新到最新版便可.git
準備工做
首先打開marked,Fork一份到本身倉庫. 對marked的改造都將基於咱們的這個fork版本.
github
目錄語法
功能描述: 自動提取全部H標籤,造成目錄樹,在解析markdown文本時,若是遇到[TOC]
標籤則自動將其替換爲目錄.markdown
將咱們fork的版本clone到本地,打開lib/marked.js.全部代碼都在這個文件裏.hexo
修改inline.gfm
,增長目錄語法匹配正則app
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
,增長toc
和tocItem
兩個方法,用於生成目錄標籤:編輯器
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>'; }; |
修改Renderer
的heading
方法,爲其賦予id做爲點擊目錄項的錨點oop
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) { 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: + 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) { ... 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)/ , 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; } |
如今這些語法在編輯器裏有獨特的高亮效果了:

總結
經過兩個自定義語法的實現,咱們能夠總結出自定義語法的通常步驟:
- 增長語法關鍵詞的匹配正則.
- 在
Renderer
裏增長相應的標籤生成方法.
- 在
InlineLexer
裏處理匹配到的語法.
接下來的計劃:
- 導出pdf,html文件.
- 美化預覽樣式.
附件
v0.6.0.2
項目地址